从另一个线程或进程更新Gtk.ProgressBar [英] Update a Gtk.ProgressBar from another thread or process

查看:159
本文介绍了从另一个线程或进程更新Gtk.ProgressBar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有进度条的GUI.它应该显示第二个线程所做的工作进度.我希望在工作的每个步骤中都可以有一个事件,例如线程可以立即将其发送到GUI进度条.但是我不知道该怎么办.

I have a GUI with a progressbar. It should show the progress of the work a second thread does. I would like to have something like an event the thread can send to the GUIs progressbar immediatly on each step of the work. But I don't see how this could be done.

Python本身为线程情况提供了一个Event类.但这会由于Event.wait()方法而阻塞GUI主线程.

Python itself offers a Event class for threading situations. But it would block the GUI main thread because of the Event.wait() methode.

如果第二个线程是一个进程,它将如何改变情景和可能的解决方案?

How does it change the situaton and possible solutions if the second thread is a process?

这里的示例基于PyGObject(Pythons Gtk),但也与所有其他GUI库有关. 当前的解决方案有效,但这只是IMO的一种解决方法. GUI(作为主线程)和第二(工作线程)线程通过线程安全queue.Queue共享数据. GUI线程中有一个计时器事件,用于检查** fixed intervalls *中的队列是否有线程中的新数据并更新进度条.

My example here is based on PyGObject (Pythons Gtk) but is related to all other GUI libraries, too. The current solution works but it is IMO just a workaround. The GUI (as main thread) and the second (worker) thread sharing data via a threadsafe queue.Queue. There is a timer event in the GUI thread checking the qeueu in **fixed intervalls* for new data from the thread and updates the progressbar.

#!/usr/bin/env python3
import time
import threading
import queue
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib


class MyThread(threading.Thread):
    def __init__(self, queue, n_tasks):
        threading.Thread.__init__(self)
        self._queue = queue
        self._max = n_tasks

    def run(self):
        for i in range(self._max):
            # simulate a task 
            time.sleep(1)
            # put something in the data queue
            self._queue.put(1)


class MyWindow(Gtk.Window):
    def __init__(self, n_tasks):
        Gtk.Window.__init__(self)

        # max and current number of tasks
        self._max = n_tasks
        self._curr = 0

        # queue to share data between threads
        self._queue = queue.Queue()

        # gui: progressbar
        self._bar = Gtk.ProgressBar(show_text=True)
        self.add(self._bar)
        self.connect('destroy', Gtk.main_quit)

        # install timer event to check the queue for new data from the thread
        GLib.timeout_add(interval=250, function=self._on_timer)
        # start the thread
        self._thread = MyThread(self._queue, self._max)
        self._thread.start()

    def _on_timer(self):
        # if the thread is dead and no more data available...
        if not self._thread.is_alive() and self._queue.empty():
            # ...end the timer
            return False

        # if data available
        while not self._queue.empty():
            # read data from the thread
            self._curr += self._queue.get()
            # update the progressbar
            self._bar.set_fraction(self._curr / self._max)

        # keep the timer alive
        return True

if __name__ == '__main__':
    win = MyWindow(30)
    win.show_all()
    Gtk.main()

推荐答案

基于我对问题的评论,我修改了示例.请谨慎使用,因为对于该解决方案是否是线程安全的,我仍然不清楚.

Based on the comments of my question I modified my example. Please use this with caution because it is still unclear for me if the solution is thread safe or not.

我尝试了 GLib.idle_add() ,并删除了我自己的计时器和队列. 注意:文档中关于方法签名/参数的信息不正确.原来是

I tried the GLib.idle_add() and removed my own timer and the queue. Attention: The docu is no correct about the signature/parameters of the methode. Originaly it is

idle_add(function, *user_data, **kwargs)

可能的线程解决方案

#!/usr/bin/env python3
import time
import threading
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib


class MyThread(threading.Thread):
    def __init__(self, callback, n_tasks):
        threading.Thread.__init__(self)
        self._callback = callback
        self._max = n_tasks

    def run(self):
        for i in range(self._max):
            # simulate a task 
            time.sleep(1)
            # increment/update progress
            GLib.idle_add(self._callback)


class MyWindow(Gtk.Window):
    def __init__(self, n_tasks):
        Gtk.Window.__init__(self)

        # max and current number of tasks
        self._max = n_tasks
        self._curr = 0

        # gui: progressbar
        self._bar = Gtk.ProgressBar(show_text=True)
        self.add(self._bar)
        self.connect('destroy', Gtk.main_quit)

        # start the thread
        self._thread = MyThread(self._update_progress, self._max)
        self._thread.start()

    def _update_progress(self):
        # increment
        self._curr += 1
        # update the progressbar
        self._bar.set_fraction(self._curr / self._max)

        # end this event handler
        return False

if __name__ == '__main__':
    win = MyWindow(30)
    win.show_all()
    Gtk.main()

GLib.idle_add()的作用是什么?

我不是Gtk的专家或核心开发人员.以我的理解,我会说您可以_install__事件处理程序方法进入Gtk主循环.换句话说,第二个线程告诉Gtk主循环(第一个线程)在没有其他事情要做时(通常在GUI循环中退出)来调用givin方法.

What GLib.idle_add() does?

I am not an expert or core developer of Gtk. In my understanding I would say you can _install__ event handler metodes into the Gtk main loop. In other words the second thread tells the Gtk main loop (which is the first thread) to call the givin methode when nothing else is todo (which is quit often in a GUI loop).

对于,没有解决方案,因为它们在单独的Python解释器实例中运行.无法在两个进程之间调用GLib.idle_add().

There is no solution for Process because they run in a separate Python interpreter instance. There is no way to call GLib.idle_add() between two process.

这篇关于从另一个线程或进程更新Gtk.ProgressBar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆