2012-06-12 105 views
14

我有一个派生多线程的PySide(Qt)GUI。线程有时需要更新GUI。我已经通过以下方式解决了这个问题:PySide:从另一个线程更新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() 

    # ... 

有没有更简单的方法?创建信号,处理程序并连接它们需要几行代码。

+0

为什么不使用'QThread'? – Avaris

+0

如果使用'QThread'更容易,我会考虑使用一个。问题是现有的代码通常倾向于使用'threading.Thread'。 – Petter

+1

这是更好的,因为'QThread'支持信号。你不需要你的'Signaller'类。但基本上,你的方式就是这样。您需要信号和插槽来在线程和GUI之间进行通信。 – Avaris

回答

17

我最近开始使用PySide进行编码,我需要一个等效的PyGObject的GLib.idle_add行为。我根据您的答案(https://stackoverflow.com/a/11005204/1524507)的代码为基础,但这个人使用事件,而不是自己使用队列。

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)) 

这是用在上面的回答链接的方式相同。

+0

我觉得这看起来不错。 – Petter

+0

这太好了。即使是在'QEvent.Type'中再次使用'registerEventType'封装PySide以使其在PySide中工作的微小解决方法也打开了我的视线。谢谢,会使用代码。 – Trilarion

6

这是我到目前为止。我一个辅助模块中的某处写了下面的代码:

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) 

然后我的线程可以非常容易地运行代码更新在主线程中的GUI。不需要为每个操作创建和连接信号。

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) 

我觉得像这样很好。