2013-06-02 200 views
23

Tôi đang cố gắng tìm hiểu cách sử dụng QThreads trong ứng dụng PyQt Gui. Tôi có các công cụ chạy trong một thời gian, với (thường là các điểm mà tôi có thể cập nhật một Gui, nhưng tôi muốn chia công việc chính ra thành chủ đề riêng của mình (đôi khi mọi thứ bị kẹt, và sẽ rất tuyệt khi cuối cùng có một nút hủy/thử lại, điều này rõ ràng không hoạt động nếu Gui bị đóng băng vì vòng lặp chính bị chặn).Ví dụ về cách đúng để sử dụng QThread trong PyQt?

Tôi đã đọc https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/. Trang đó nói rằng việc triển khai lại phương thức run không phải là cách để thực hiện. Vấn đề tôi gặp phải là tìm một ví dụ PyQt có một luồng chính làm Gui và một chuỗi công nhân không thực hiện theo cách đó. Bài đăng trên blog là dành cho C++, vì vậy trong khi các ví dụ của chúng tôi trợ giúp, tôi vẫn bị mất một chút. Ai đó có thể xin vui lòng chỉ cho tôi một ví dụ về đúng cách để làm điều đó trong Python?

+4

này trông giống như một dup của [thread nền với QThread trong PyQt] (http://stackoverflow.com/questions/6783194/background-thread-with-qthread-in-pyqt). Ví dụ thứ hai trong câu trả lời được chấp nhận trông giống như một bản dịch đơn giản của mã C++ từ blog bạn đã liên kết. – abarnert

+0

Ngoài ra, bạn có viết bất kỳ đoạn mã nguồn gốc Python nào (với 'luồng.Thread', v.v.) không? Nếu không, bạn có thể muốn làm việc thông qua một số ví dụ về điều đó trước tiên. (Cũng xem [Threading trong một ứng dụng PyQt: Sử dụng các chủ đề Qt hoặc các chủ đề Python] (http://stackoverflow.com/questions/1595649/threading-in-a-pyqt-application-use-qt-threads-or-python- chủ đề) để xem liệu bạn có cần QThread ở đây không.) – abarnert

+0

@abarnert Cảm ơn, tôi nghĩ rằng liên kết đó chỉ là những gì tôi đang tìm kiếm. Tôi đã nhìn thấy liên kết thứ hai, và quyết định rằng tôi nên sử dụng QThreads vì tôi muốn có thể gửi các khe/tín hiệu giữa các luồng. Tôi đã biết rằng 'threading.Thread' tồn tại, nhưng chưa từng sử dụng nó trước đây. Tôi đã làm rất nhiều tìm kiếm, và thậm chí nhìn thấy liên kết đầu tiên, lướt qua nó, thấy 'def run' và tiếp tục, không nhận ra được cả hai cách! – Azendale

Trả lời

7

Dưới đây là ví dụ làm việc về chuỗi công nhân riêng biệt có thể gửi và nhận tín hiệu để cho phép nó giao tiếp với GUI.

Tôi đã thực hiện hai nút đơn giản, một nút bắt đầu tính toán dài trong một chuỗi riêng biệt và một nút ngay lập tức chấm dứt phép tính và đặt lại chuỗi công việc.

Buộc chấm dứt một chuỗi như được thực hiện ở đây thường không phải là cách tốt nhất để làm việc, nhưng có những tình huống trong đó luôn thoát duyên dáng không phải là một tùy chọn.

from PyQt4 import QtGui, QtCore 
import sys 
import random 

class Example(QtCore.QObject): 

    signalStatus = QtCore.pyqtSignal(str) 

    def __init__(self, parent=None): 
     super(self.__class__, self).__init__(parent) 

     # Create a gui object. 
     self.gui = Window() 

     # Setup the worker object and the worker_thread. 
     self.worker = WorkerObject() 
     self.worker_thread = QtCore.QThread() 
     self.worker.moveToThread(self.worker_thread) 
     self.worker_thread.start() 

     # Make any cross object connections. 
     self._connectSignals() 

     self.gui.show() 

    def _connectSignals(self): 
     self.gui.button_start.clicked.connect(self.worker.startWork) 
     self.gui.button_cancel.clicked.connect(self.forceWorkerReset) 
     self.signalStatus.connect(self.gui.updateStatus) 
     self.worker.signalStatus.connect(self.gui.updateStatus) 

     self.parent().aboutToQuit.connect(self.forceWorkerQuit) 

    def forceWorkerReset(self): 
     if self.worker_thread.isRunning(): 
      self.worker_thread.terminate() 
      self.worker_thread.wait() 

      self.signalStatus.emit('Idle.') 
      self.worker_thread.start() 

    def forceWorkerQuit(self): 
     if self.worker_thread.isRunning(): 
      self.worker_thread.terminate() 
      self.worker_thread.wait() 


class WorkerObject(QtCore.QObject): 

    signalStatus = QtCore.pyqtSignal(str) 

    def __init__(self, parent=None): 
     super(self.__class__, self).__init__(parent) 

    @QtCore.pyqtSlot()   
    def startWork(self): 
     for ii in range(7): 
      number = random.randint(0,5000**ii) 
      self.signalStatus.emit('Iteration: {}, Factoring: {}'.format(ii, number)) 
      factors = self.primeFactors(number) 
      print('Number: ', number, 'Factors: ', factors) 
     self.signalStatus.emit('Idle.') 

    def primeFactors(self, n): 
     i = 2 
     factors = [] 
     while i * i <= n: 
      if n % i: 
       i += 1 
      else: 
       n //= i 
       factors.append(i) 
     if n > 1: 
      factors.append(n) 
     return factors 


class Window(QtGui.QWidget): 

    def __init__(self): 
     QtGui.QWidget.__init__(self) 
     self.button_start = QtGui.QPushButton('Start', self) 
     self.button_cancel = QtGui.QPushButton('Cancel', self) 
     self.label_status = QtGui.QLabel('', self) 

     layout = QtGui.QVBoxLayout(self) 
     layout.addWidget(self.button_start) 
     layout.addWidget(self.button_cancel) 
     layout.addWidget(self.label_status) 

     self.setFixedSize(400, 200) 

    @QtCore.pyqtSlot(str) 
    def updateStatus(self, status): 
     self.label_status.setText(status) 


if __name__=='__main__': 
    app = QtGui.QApplication(sys.argv) 
    example = Example(app) 
    sys.exit(app.exec_()) 
+0

Khi tôi chạy điều này, tôi nhận được. 'Qt đã bắt được một ngoại lệ được ném từ một trình xử lý sự kiện. Việc ném ngoại lệ từ trình xử lý sự kiện không được hỗ trợ trong Qt. Bạn phải reimplement QApplication :: notify() và bắt tất cả các ngoại lệ ở đó.' –

+0

Bất kỳ ý tưởng ngoại lệ là gì? Tôi đã thử nghiệm đoạn mã này bằng PyQt4 với Python 2.7 trên OS X mà không có bất kỳ lỗi nào. Khi tôi cố gắng sử dụng điều này với Python 3.4, đôi khi tôi nhận được lỗi phân đoạn. Tôi không chắc chắn nếu vấn đề này là với mã như bằng văn bản hoặc cụ thể (phiên bản cụ thể) thực hiện. – amicitas

+0

Tôi đã không điều tra những gì đang ném nhưng dường như bạn có một cái gì đó mà thỉnh thoảng ném một ngoại lệ vào một vòng lặp tin nhắn, mà không bao giờ phải xảy ra. –

0

Bạn nói đúng là có một chuỗi công việc đang xử lý trong khi chủ đề chính đang thực hiện GUI. Ngoài ra, PyQt đang cung cấp thiết bị đo luồng với một cơ chế tín hiệu/khe cắm là luồng an toàn.

This may sound of interest. Trong ví dụ, họ xây dựng một GUI

import sys, time 
from PyQt4 import QtCore, QtGui 

class MyApp(QtGui.QWidget): 
def __init__(self, parent=None): 
    QtGui.QWidget.__init__(self, parent) 

    self.setGeometry(300, 300, 280, 600) 
    self.setWindowTitle('threads') 

    self.layout = QtGui.QVBoxLayout(self) 

    self.testButton = QtGui.QPushButton("test") 
    self.connect(self.testButton, QtCore.SIGNAL("released()"), self.test) 
    self.listwidget = QtGui.QListWidget(self) 

    self.layout.addWidget(self.testButton) 
    self.layout.addWidget(self.listwidget) 

def add(self, text): 
    """ Add item to list widget """ 
    print "Add: " + text 
    self.listwidget.addItem(text) 
    self.listwidget.sortItems() 

def addBatch(self,text="test",iters=6,delay=0.3): 
    """ Add several items to list widget """ 
    for i in range(iters): 
    time.sleep(delay) # artificial time delay 
    self.add(text+" "+str(i)) 

def test(self): 
    self.listwidget.clear() 
    # adding entries just from main application: locks ui 
    self.addBatch("_non_thread",iters=6,delay=0.3) 

(ui simpel chứa một widget danh sách mà chúng tôi sẽ bổ sung thêm một số mặt hàng để bằng cách nhấn một nút)

Sau đó, bạn có thể tạo lớp thread riêng của chúng tôi, một trong những dụ là

class WorkThread(QtCore.QThread): 
def __init__(self): 
    QtCore.QThread.__init__(self) 

def __del__(self): 
    self.wait() 

def run(self): 
    for i in range(6): 
    time.sleep(0.3) # artificial time delay 
    self.emit(QtCore.SIGNAL('update(QString)'), "from work thread " + str(i)) 

    self.terminate() 

Bạn xác định lại phương pháp run(). Bạn có thể tìm cách thay thế cho terminate(), xem hướng dẫn.

+9

OP đặc biệt nói rằng anh ta muốn sử dụng cơ chế 'moveToThread', thay vì cơ chế' QThread.run'. Tôi không chắc liệu anh ta có lý do chính đáng hay không, nhưng anh vẫn không trả lời câu hỏi của anh ta. – abarnert

+1

OP là đúng: bạn không nên phân lớp QThread. Xem http: //blog.qt.io/blog/2010/06/17/youre-do-it-wrong/ – blokeley

+0

[Tài liệu Qt về luồng] (http://doc.qt.io/qt-4.8/thread-basics.html#gui-thread -and-worker-thread) đề cập đến phân lớp QThread mặc dù ... –