从另一个线程或进程更新Gtk.ProgressBar [英] Update a Gtk.ProgressBar from another thread or process
问题描述
我有一个带有进度条的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).
对于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屋!