从另一个线程或进程更新Gtk.ProgressBar
问题内容:
我有一个带有进度条的GUI。它应该显示第二个线程的工作进度。我希望在工作的每个步骤中都可以有一个事件,例如线程可以立即将其发送到GUI进度条。但是我不知道如何做到这一点。
Python本身提供了一个Event
用于线程情况的类。但是由于Event.wait()
方法的原因,它将阻塞GUI主线程。
如果第二个线程是一个进程,它将如何改变情景和可能的解决方案?
我的示例基于PyGObject(Pythons
Gtk),但也与所有其他GUI库有关。当前的解决方案有效,但这只是IMO的一种解决方法。GUI(作为主线程)和第二(工作线程)线程通过threadsafe共享数据queue.Queue
。GUI线程中有一个计时器事件,用于检查“固定间隔”中的队列是否有来自线程的新数据并更新进度条。
#!/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()
问题答案:
根据我的问题的评论,我修改了我的示例。请谨慎使用,因为对于该解决方案是否是线程安全的,我仍然不清楚。
我尝试了,GLib.idle_add()
并删除了自己的计时器和队列。 注意
:文档对方法的签名/参数不正确。原来是
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方法。
有 用于过程无解的 ,因为他们在一个单独的Python解释器实例中运行。无法GLib.idle_add()
在两个进程之间进行调用。