2012-06-12 31 views
14

Tôi có giao diện PySide (Qt) sinh ra nhiều luồng. Các chủ đề đôi khi cần cập nhật GUI. Tôi đã giải quyết vấn đề này theo cách sau:PySide: Cách dễ dàng hơn để cập nhật GUI từ một chủ đề khác

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

    # ... 

Có cách nào dễ dàng hơn không? Tạo các tín hiệu, trình xử lý và kết nối chúng đòi hỏi một vài dòng mã.

+0

Tại sao bạn không sử dụng 'QThread'? – Avaris

+0

Nếu nó dễ dàng hơn với một 'QThread', tôi sẽ xem xét sử dụng một. Vấn đề là mã hiện có thường có xu hướng sử dụng 'threading.Thread'. – Petter

+1

Tốt hơn, vì 'QThread' hỗ trợ tín hiệu. Bạn sẽ không cần lớp 'Signaller' của bạn. Nhưng về cơ bản, cách của bạn là con đường. Bạn cần tín hiệu và khe để giao tiếp giữa các luồng và GUI. – Avaris

Trả lời

17

Tôi bắt đầu mã hóa với PySide gần đây và tôi cần tương đương với hành vi GLib.idle_add của PyGObject. Tôi dựa vào mã của câu trả lời của bạn (https://stackoverflow.com/a/11005204/1524507) nhưng mã này sử dụng các sự kiện thay vì sử dụng hàng đợi.

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

Được sử dụng theo cách tương tự trong liên kết trả lời ở trên.

+0

Tôi nghĩ rằng điều này có vẻ tốt. – Petter

+0

Điều này thật tuyệt. Và ngay cả cách giải quyết nhỏ về việc gói 'registerEventType' một lần nữa trong' QEvent.Type' để làm cho nó hoạt động trong PySide đã mở mắt của tôi. Cảm ơn, sẽ sử dụng mã. – Trilarion

6

Đây là những gì tôi có cho đến nay. Tôi đã viết code sau ở đâu đó trong một mô-đun helper:

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) 

Sau đó, chủ đề của tôi có thể dễ dàng chạy mã để cập nhật các GUI trong các chủ đề chính. Không cần phải tạo và kết nối các tín hiệu cho mọi hoạt động.

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) 

Tôi nghĩ điều gì đó như thế này khá hay.

Các vấn đề liên quan