2016-11-10 61 views
0

我想写一个pyqt5应用程序与长时间运行,但不是CPU密集型过程。我希望能够在不挂上UI的情况下运行它,所以我试图使用线程,但是因为它看起来并不像我可以运行一个线程并在其代码完成后停止,以便它可以再次运行,我已经尝试设置线程以等待变量在运行前更改。在python qt5应用程序中线程的正确模式是什么?

我知道这不能成为在pyqt应用程序中运行长进程的正确模式。

import time 
import threading 
from PyQt5 import QtWidgets, uic 


class MyApp(QtWidgets.QMainWindow): 
    _run_thread = False 

    def __init__(self): 
     QtWidgets.QMainWindow.__init__(self) 
     self.ui = uic.loadUi('myapp.ui', self) 

     self.ui.start_thread_button.clicked.connect(self._run_thread_function) 

     self._thread = threading.Thread(target=self._run_thread_callback) 
     self._thread.daemon = True 
     self._thread.start() 

     self.ui.show() 

    def _run_thread_callback(self): 
     while True: 
      if self._run_thread: 
       print("running thread code...") 
       time.sleep(10) 
       print("thread code finished") 
       self._run_thread = False 

    def _run_thread_function(self): 
     print("starting thread...") 
     self._run_thread = True 


def main(): 
    app = QtWidgets.QApplication(sys.argv) 
    MyApp() 
    sys.exit(app.exec_()) 

if __name__ == '__main__': 
    main() 
+0

那么,你会碰到问题,因为你有一个名为'self._run_thread'和一个同名的实例属性。在某个时候,你用布尔值重写你的方法。但不管如何,你应该切换到QThreads。看看http://stackoverflow.com/documentation/pyqt/2775/using-threads-with-pyqt#t=20161111002348120811,它应该很容易适应PyQt5。关于页面底部的线程安全也有一些指导。 –

+0

好吧,我已经改变了函数的名称,但这只是一个例子,而不是我的问题的主要观点。因此,从我听说的本地python“线程”模块不适合与Qt一起使用。对于任何UI应用程序都是如此?例如,如果我从Qt切换到Tkinter,我可以使用'threading'模块吗? – waspinator

+0

我在Qt中成功使用'threading'模块,但是你不应该([见这里](http://stackoverflow.com/q/1595649/1994235))。如果你想从你的线程与GUI进行交互,你应该使用QThreads和信号/插槽(因为从线程直接访问GUI对象是被禁止的)。但是,如果您确实想使用python线程,则使用'QApplication.postEvent'在辅助线程和主线程之间进行通信似乎很安全。我有一个基于这个想法的项目(在这里)(https://bitbucket.org/philipstarkey/qtutils/wiki/invoke_in_main)(目前只有PyQt4 –

回答

1

下面是显示如何启动和停止工作线程,并安全地与GUI线程comminucate一个简单的演示。

import sys 
from PyQt5 import QtCore, QtWidgets 

class Worker(QtCore.QThread): 
    dataSent = QtCore.pyqtSignal(dict) 

    def __init__(self, parent=None): 
     super(Worker, self).__init__(parent) 
     self._stopped = True 
     self._mutex = QtCore.QMutex() 

    def stop(self): 
     self._mutex.lock() 
     self._stopped = True 
     self._mutex.unlock() 

    def run(self): 
     self._stopped = False 
     for count in range(10): 
      if self._stopped: 
       break 
      self.sleep(1) 
      data = { 
       'message':'running %d [%d]' % (
        count, QtCore.QThread.currentThreadId()), 
       'time': QtCore.QTime.currentTime(), 
       'items': [1, 2, 3], 
       } 
      self.dataSent.emit(data) 

class Window(QtWidgets.QWidget): 
    def __init__(self): 
     super(Window, self).__init__() 
     self.edit = QtWidgets.QPlainTextEdit() 
     self.edit.setReadOnly(True) 
     self.button = QtWidgets.QPushButton('Start') 
     self.button.clicked.connect(self.handleButton) 
     layout = QtWidgets.QVBoxLayout(self) 
     layout.addWidget(self.edit) 
     layout.addWidget(self.button) 
     self._worker = Worker() 
     self._worker.started.connect(self.handleThreadStarted) 
     self._worker.finished.connect(self.handleThreadFinished) 
     self._worker.dataSent.connect(self.handleDataSent) 

    def handleThreadStarted(self): 
     self.edit.clear() 
     self.button.setText('Stop') 
     self.edit.appendPlainText('started') 

    def handleThreadFinished(self): 
     self.button.setText('Start') 
     self.edit.appendPlainText('stopped') 

    def handleDataSent(self, data): 
     self.edit.appendPlainText('message [%d]' % 
      QtCore.QThread.currentThreadId()) 
     self.edit.appendPlainText(data['message']) 
     self.edit.appendPlainText(data['time'].toString()) 
     self.edit.appendPlainText(repr(data['items'])) 

    def handleButton(self): 
     if self._worker.isRunning(): 
      self._worker.stop() 
     else: 
      self._worker.start() 

if __name__ == '__main__': 

    app = QtWidgets.QApplication(sys.argv) 
    window = Window() 
    window.setGeometry(500, 100, 400, 400) 
    window.show() 
    sys.exit(app.exec_()) 
+0

是否可以直接操作'Worker'内的UI元素?我想操作一个线程中的几个元素,我需要从线程发送一个json字符串,然后在更改UI之前在窗口中解析它吗? – waspinator

+1

@waspinator直接在主线程之外操作gui不是你不需要使用json:可以发送一个普通的python'dict',它可以包含其他复杂类型(但显然不是gui元素)。我更新了示例,以便工作者发送一个'dict'回到主线程。 – ekhumoro

相关问题