2012-06-12 135 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) 

我覺得像這樣很好。