LINUX.ORG.RU

Python 3 + PyGObject Как графически отобразить процесс инициализации класса

 , ,


0

1

Подскажите как правильно это реализовать. Пока такой вариант, но понимаю, что он в корне не правильный:

import time
import threading
from gi.repository import Gtk, Gdk, GLib, GObject


class ProgressBarWindow(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="ProgressWin")

        self.set_border_width(20)
        self.connect("delete-event", self.ex)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)

        self.progressbar = Gtk.ProgressBar()
        vbox.pack_start(self.progressbar, True, True, 0)
        self.show_all()

    def ex(self, *args):
        self.destroy()
        Gtk.main_quit()

    def main(self):
        threading.Thread(target=Gtk.main, daemon=True).start()

    def on_activity(self, *args):
        GLib.idle_add(self.progressbar.set_fraction, args[0])


class TopWindow(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="TopWin")

        self.connect("delete-event", self.m_ex)

        self.s = ProgressBarWindow()
        self.s.main()

        time.sleep(0.5)
        GLib.idle_add(self.s.on_activity, 0.20)

        time.sleep(0.5)
        GLib.idle_add(self.s.on_activity, 0.40)

        self.set_border_width(5)

        time.sleep(0.5)
        GLib.idle_add(self.s.on_activity, 0.60)

        self.set_default_size(200, 150)

        time.sleep(0.5)
        GLib.idle_add(self.s.on_activity, 0.80)

        self.spinner = Gtk.Spinner()
        self.spinner.start()

        time.sleep(0.5)
        GLib.idle_add(self.s.on_activity, 1.00)

        self.add(self.spinner)

        self.show_all()

        self.s.ex()

        self.main()

    def m_ex(self, *args):
        for x in range(Gtk.main_level()):
            Gtk.main_quit()

    def main(self):
        Gtk.main()


GObject.threads_init()
TopWindow()

Ответ на: комментарий от Gvidon

Процесс инициализации бывает долгим и на это время запустить прогрессбар.

ivsatel ()

Весь Gtk лучше в один поток. И тогда вместо GLib.idle_add(self.s.on_activity, 0.60) будет

self.s.on_activity = 0.60
GLib.yield_()

monk ★★★★★ ()
Ответ на: комментарий от monk

Если отображается первое запущенное окно, значит запущен и цикл Gtk.main и тут без второго потока по крайней мере у меня не получается.

ivsatel ()
Ответ на: комментарий от ivsatel

GLib.yield_() отдаёт управление в поток отрисовки, а после отрисовки возвращает управление обратно.

monk ★★★★★ ()
Ответ на: комментарий от monk

GLib.yield_() отдаёт управление в поток отрисовки

Это понятно, я про то, что после отрисовки первого окна запускается цикл Gtk.main который актуален (на сколько я понимаю) для первого окна и дочерних ему. Я же хотел запустить два TOPLEVEL окна которые друг от друга не зависели бы. Т.е. Первое отрисовалось законектилось слушать свой Gtk.main, второе окно инициализировалось законектилось и стало слушать свой Gtk.main, при этом не пересекаясь на подобии: /usr/lib/python3/dist-packages/gi/overrides/GLib.py:523: Warning: Source ID 27 was not found when attempting to remove it Вот что я хотел. Сейчас пока, временно вот такая простая конструкция:

import time
import threading
from gi.repository import Gtk, Gdk, GLib, GObject


class Dialog_Progress(Gtk.Dialog):

    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "Progress", parent)

        self.set_default_size(350, 30)
        self.set_border_width(5)

        self.label = Gtk.ProgressBar()

        box = self.get_content_area()
        self.remove(box)
        self.add(self.label)
        self.show_all()


class TopWindow(Gtk.Window):


    def __init__(self):

        self = Gtk.Window()
        #
        dialog = Dialog_Progress(self)
        GLib.threads_init()
        self.tr = GLib.Thread.self()
        threading.Thread(target=dialog.run, daemon=True).start()
        self.tr.yield_()
        #

        time.sleep(0.5)
        dialog.label.set_fraction(0.20)
        self.tr.yield_()

        time.sleep(0.5)
        dialog.label.set_fraction(0.40)
        self.tr.yield_()

        time.sleep(0.5)
        dialog.label.set_fraction(0.60)
        self.tr.yield_()

        time.sleep(0.5)
        dialog.label.set_fraction(0.80)
        self.tr.yield_()

        self.spinner = Gtk.Spinner()
        self.spinner.start()

        time.sleep(0.5)
        dialog.label.set_fraction(1.00)
        self.tr.yield_()

        self.add(self.spinner)

        dialog.destroy()

        self.connect("destroy", Gtk.main_quit)

        self.set_default_size(200, 200)
        self.show_all()


GObject.threads_init()
TopWindow()
Gtk.main()
Но это работает только на такой простой конструкции. Если конструкцию усложнить добавлением «connect» элементов второго окна при инициализации, то они каким-то образом цепляют первый цикл Gtk.main от первого окна на прослушку сигналов что приводит к непредсказуемым последствиям.

ivsatel ()

В приведенном выше примере кода я не создавал два «топливел» окна, а отрисовал сначала дочернее, запустил для него Gtk.main и продолжил отрисовку «топливел». Но завершение его отрисовки должно заканчиваться Gtk.main. Вот как его подвесить на первый Gtk.main я не пойму. По тому, что если первый Gtk.main не запустить в

threading.Thread(target=dialog.run, daemon=True).start()
то сигналы из конструктора не доходят в данном случае до дочернего окна.

ivsatel ()
Ответ на: комментарий от ivsatel

Еще интересно, почему такой же код по концепции, т.е. создать дочернее окно в момент инициализации:

import time
import threading
from gi.repository import Gtk, GLib, GObject


class ProgressBarWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="ProgressWin")
        self.set_border_width(20)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        self.add(vbox)
        self.progressbar = Gtk.ProgressBar()
        vbox.pack_start(self.progressbar, True, True, 0)
        self.show_all()

    def main(self):
        Gtk.main()

    def on_activity(self, *args):
        self.progressbar.set_fraction(args[0])


class TopWindow(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="TopWin")

        self.s = ProgressBarWindow()
        self.s.set_transient_for(self)
        threading.Thread(target=self.s.main, daemon=True).start()

        time.sleep(0.5)
        self.s.on_activity(0.20)
        time.sleep(0.5)
        self.s.on_activity(0.40)
        self.set_border_width(5)
        time.sleep(0.5)
        self.s.on_activity(0.60)
        self.set_default_size(200, 150)
        time.sleep(0.5)
        self.s.on_activity(0.80)

        self.spinner = Gtk.Spinner()
        self.spinner.start()

        time.sleep(0.5)
        self.s.on_activity(1.00)

        self.s.destroy()

        self.add(self.spinner)
        self.show_all()

        self.connect("destroy", Gtk.main_quit)

        Gtk.main()


GObject.threads_init()
TopWindow()
Приводит к:
Gdk-WARNING **: Test-23.py: Fatal IO error 11 (Ресурс временно недоступен) on X server :0.

ivsatel ()

Это понятно, я про то, что после отрисовки первого окна запускается цикл Gtk.main который актуален (на сколько я понимаю) для первого окна и дочерних ему.

Вообще-то (по построению) для библиотеки GObject (Gtk).

они каким-то образом цепляют первый цикл Gtk.main от первого окна на прослушку сигналов что приводит к непредсказуемым последствиям

Именно.

Зачем тебе два главных цикла событий? И зачем тебе полоска прогресса в диалоге (суть которого исключительно в том, чтобы сделать блокирующий вызов). Делай обычное окно с полоской и обновляй её по ходу дела.

Если неудобство в том, что окно с полоской не отвечает на нажатия мышкой (так как цикла событий ещё нет), то тоже решается просто: сначала запускай Gtk.main(), а уже потом инициализируй (через Glib.timeout_add() или как-то так).

monk ★★★★★ ()
Ответ на: комментарий от monk

Делай обычное окно с полоской и обновляй её по ходу дела.

Если не сложно, то как это реализовать на данном примере:

import time
import threading
from gi.repository import Gtk, GLib, GObject


class ProgressBarWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="ProgressWin")
        self.connect("destroy", Gtk.main_quit)
        self.set_border_width(20)

        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)

        self.progressbar = Gtk.ProgressBar()
        vbox.pack_start(self.progressbar, True, True, 0)

        self.add(vbox)

        self.show_all()

    def on_activity(self, *args):
        self.progressbar.set_fraction(args[0])


class TopWindow(Gtk.Window):

    def __init__(self):

        Gtk.Window.__init__(self, title="TopWin")

        self.connect("destroy", Gtk.main_quit)

        self.spinner = Gtk.Spinner()
        self.spinner.start()

        self.add(self.spinner)
        self.show_all()

ivsatel ()
from gi.repository import Gtk, Gdk, GLib, GObject
import time

class Dialog_Progress:

    def __init__(self):
        self.label = Gtk.ProgressBar()
        self.win = Gtk.Window()

        self.win.set_default_size(350, 30)
        self.win.set_border_width(5)

        self.win.add(self.label)
        self.win.show_all()

def yield_(Context):
    while GLib.MainContext.pending(Context):
        GLib.MainContext.iteration(Context,False)

class TopWindow(Gtk.Window):


    def __init__(self):
        t = 2

        self = Gtk.Window()
        dialog = Dialog_Progress()
        Context = GLib.MainContext.default()
        yield_(Context)

        time.sleep(t)
        dialog.label.set_fraction(0.20)
        yield_(Context)

        time.sleep(t)
        dialog.label.set_fraction(0.40)
        yield_(Context)

        time.sleep(t)
        dialog.label.set_fraction(0.60)
        yield_(Context)

        time.sleep(t)
        dialog.label.set_fraction(0.80)
        yield_(Context)

        self.spinner = Gtk.Spinner()
        self.spinner.start()

        time.sleep(t)
        dialog.label.set_fraction(1.00)
        yield_(Context)

        self.add(self.spinner)

        dialog.win.destroy()

        self.connect("destroy", Gtk.main_quit)

        self.set_default_size(200, 200)
        self.show_all()

def run():    
    TopWindow()
    False

GLib.timeout_add(100, run)
Gtk.main()
monk ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.