我有一个带有进度条的 GUI,它应该显示第二个线程完成工作的进度。我希望有像事件一样的东西,线程可以在每一步工作后立即发送给GUI的进度条。但我不知道怎么做。
Python本身提供了一个用于线程情况的 "Event" 类。但由于 "Event.wait()" 方法,它将阻塞 GUI 主线程。
如果第二个线程是一个进程,它如何改变情况和可能的解决方案?
我的例子基于 PyGObject(Python 的 Gtk),但与所有其他 GUI 库相关。当前的解决方案虽然可行,但我认为它只是一个解决方法。GUI(作为主线程)和第二个(工作)线程通过线程安全的 "queue.Queue" 共享数据。GUI 线程中有一个定时器事件,在**固定时间间隔内**检查队列中是否有来自线程的新数据,并更新进度条。
Python本身提供了一个用于线程情况的 "Event" 类。但由于 "Event.wait()" 方法,它将阻塞 GUI 主线程。
如果第二个线程是一个进程,它如何改变情况和可能的解决方案?
我的例子基于 PyGObject(Python 的 Gtk),但与所有其他 GUI 库相关。当前的解决方案虽然可行,但我认为它只是一个解决方法。GUI(作为主线程)和第二个(工作)线程通过线程安全的 "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()
MyThread(self._bar, self._max)
,并在def run(self): ... self.bar.set_fraction(...
中使用。 - stovflThread
是唯一一个调用self.bar.set_fraction(...)
的线程,就不会产生冲突。试试吧,你要么得到一个错误,要么它可以工作。 - stovflGLib.idle_add(update_progress, i)
的使用方式类似于self._queue.put(1)
。函数def example_target()
相当于你的def run(self):
,在一个线程中运行。 - stovfl