PySide: простой способ обновления GUI из другого потока
У меня есть GUI PySide (Qt), который генерирует несколько потоков. Иногда потокам требуется обновить GUI. Я решил это следующим образом:
class Signaller(QtCore.QObject) :
my_signal = QtCore.Signal(QListWidgetItem, QIcon)
signaller = Signaller()
class MyThread(threading.Thread):
def __init__(self):
super(IconThread, self).__init__()
# ...
def run(self) :
# ...
# Need to update the GUI
signaller.my_signal.emit(self.item, icon)
#
# MAIN WINDOW
#
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# ...
# Connect signals
signaller.my_signal.connect(self.my_handler)
@QtCore.Slot(QListWidgetItem, QIcon)
def my_handler(self, item, icon):
item.setIcon(icon)
def do_something(self, address):
# ...
# Start new thread
my_thread = MyThread(newItem)
my_thread.start()
# ...
Есть ли более простой способ? Для создания сигналов, обработчиков и их подключения требуется несколько строк кода.
Ответы
Ответ 1
Недавно я начал кодирование с помощью PySide, и мне нужен был эквивалент поведения PyGObject GLib.idle_add
. Я основывал код на вашем ответе (fooobar.com/questions/373425/...), но он использует события вместо того, чтобы самостоятельно использовать очередь.
from PySide import QtCore
class InvokeEvent(QtCore.QEvent):
EVENT_TYPE = QtCore.QEvent.Type(QtCore.QEvent.registerEventType())
def __init__(self, fn, *args, **kwargs):
QtCore.QEvent.__init__(self, InvokeEvent.EVENT_TYPE)
self.fn = fn
self.args = args
self.kwargs = kwargs
class Invoker(QtCore.QObject):
def event(self, event):
event.fn(*event.args, **event.kwargs)
return True
_invoker = Invoker()
def invoke_in_main_thread(fn, *args, **kwargs):
QtCore.QCoreApplication.postEvent(_invoker,
InvokeEvent(fn, *args, **kwargs))
Используется таким же образом в приведенной выше ссылке ответа.
Ответ 2
Это то, что у меня есть до сих пор. Я написал следующий код где-то в вспомогательном модуле:
from Queue import Queue
class Invoker(QObject):
def __init__(self):
super(Invoker, self).__init__()
self.queue = Queue()
def invoke(self, func, *args):
f = lambda: func(*args)
self.queue.put(f)
QMetaObject.invokeMethod(self, "handler", QtCore.Qt.QueuedConnection)
@Slot()
def handler(self):
f = self.queue.get()
f()
invoker = Invoker()
def invoke_in_main_thread(func, *args):
invoker.invoke(func,*args)
Затем мои потоки могут очень легко запускать код для обновления графического интерфейса в основном потоке. Нет необходимости создавать и подключать сигналы для каждой операции.
class MyThread(threading.Thread):
def __init__(self):
super(IconThread, self).__init__()
# ...
def run(self) :
# ...
# Need to update the GUI
invoke_in_main_thread(self.item.setIcon, icon)
Я думаю, что что-то вроде этого довольно приятно.