简体   繁体   English

PyQt5:使用进度更新启动、停止和暂停线程

[英]PyQt5: start, stop and pause a thread with progress updates

I've got some issues with threading in my program.我的程序中的线程存在一些问题。 I have a main program and also a GUI-Class, created with PyQt.我有一个用 PyQt 创建的主程序和一个 GUI 类。 I've created a small example of my problems.我创建了一个小例子来说明我的问题。 When I start the python script and press start, the progress bar is working fine, but I get the following warnings:当我启动 python 脚本并按开始时,进度条工作正常,但我收到以下警告:

QBackingStore::endPaint() called with active painter on backingstore paint device
QObject::setParent: Cannot set parent, new parent is in a different thread

These alarms occur due to different threads.这些警报是由于不同的线程而发生的。 But to be honest, I have no idea how to handle the objects, like this progress bar of the gui-class.但老实说,我不知道如何处理对象,比如这个 gui 类的进度条。 I've tried many ways but haven't found a good solution for this.我尝试了很多方法,但没有找到一个好的解决方案。 By pressing start, it starts, by pressing stop the progress bar stops.按开始,它开始,按停止,进度条停止。 But here is the next issue, mostly my kernel dies when I press stop.但这是下一个问题,当我按下停止时,我的内核大部分都死了。 I guess it's connected to the same problem with the threading?!我猜它与线程的相同问题有关?!

The reason for the threading is so that the script can still be interacted with while it is running.线程化的原因是脚本在运行时仍然可以与之交互。

Attached are my two python files: my program and also the gui.附件是我的两个 python 文件:我的程序和 gui。

MAINPROGRAM:主程序:

#MAINPROGRAM

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import threading
import time

class exampleprogram(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
    def __init__(self, parent=None):
        super(exampleprogram, self).__init__(parent)
        self.setupUi(self)
        self.pushButton_Start.clicked.connect(self.start)
        self.pushButton_Stop.clicked.connect(self.stop)
        self.running = False
            
    def start(self):
        if(self.running == False):
            print("Start")
            self.thread = threading.Thread(target=self.run, args=())
            self.thread.start()            
            
    def stop(self):
        print("Stop")
        self.running = False
        
    def run(self):
        self.running = True
        x = 0
        thread = threading.currentThread()
        while getattr(thread, "do_run", True):
            self.thread.do_run = self.running
            if(x == 100):
                thread.do_run = False
            self.progressBar.setValue(x)
            time.sleep(0.1)
            x = x+1
        self.stop()

def main():
    app = QApplication(sys.argv)
    form = exampleprogram()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

GUI:图形用户界面:

#GUI

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(573, 92)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.layoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.layoutWidget.setGeometry(QtCore.QRect(20, 20, 195, 30))
        self.layoutWidget.setObjectName("layoutWidget")
        self.formLayout_3 = QtWidgets.QFormLayout(self.layoutWidget)
        self.formLayout_3.setContentsMargins(0, 0, 0, 0)
        self.formLayout_3.setObjectName("formLayout_3")
        self.pushButton_Start = QtWidgets.QPushButton(self.layoutWidget)
        self.pushButton_Start.setObjectName("pushButton_Start")
        self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_Start)
        self.pushButton_Stop = QtWidgets.QPushButton(self.layoutWidget)
        self.pushButton_Stop.setObjectName("pushButton_Stop")
        self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.pushButton_Stop)
        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(230, 20, 311, 23))
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Test Gui"))
        self.pushButton_Start.setText(_translate("MainWindow", "Start"))
        self.pushButton_Stop.setText(_translate("MainWindow", "Stop"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Does anyone have an idea, how to fix it?有没有人有想法,如何解决它?

EDIT: After stopping the run-function it should be possible to run it again by pressing the start button.编辑:停止运行功能后,应该可以通过按下开始按钮再次运行它。 Therefore it doesn't matter if it was stopped by the counter x or by pressing the stop button.因此,它是被计数器 x 停止还是按下停止按钮都没有关系。 It's just important not too start multiple times the function.重要的是不要多次启动该功能。

Many thanks in advance!提前谢谢了!

Andrew安德鲁

Whilst it's possible to use Python threads with PyQt, it's generally better to use QThread with a separate worker object, since it more easily allows thread-safe communication between the worker thread and main thread via signals and slots.虽然可以将 Python 线程与 PyQt 一起使用,但通常最好将QThread与单独的工作对象一起使用,因为它更容易允许工作线程和主线程之间通过信号和插槽进行线程安全通信。 One of the main problems with your example is precisely that you are attempting to perform GUI-related operations outside of the main thread, which is not supported by Qt (hence the warning messages).您的示例的主要问题之一正是您试图在主线程之外执行与 GUI 相关的操作,这是 Qt 不支持的(因此出现警告消息)。

If you want to start, stop and pause the worker from the GUI, it will require some careful handling to ensure the worker and thread shut down cleanly if the user tries to close the window while they are still running.如果您想从 GUI 启动、停止和暂停工作程序,则需要小心处理以确保工作程序和线程在用户尝试关闭仍在运行的窗口时干净地关闭。 In the example below, I have used a simple abort mechanism in the closeEvent ;在下面的示例中,我在closeEvent中使用了一个简单的中止机制; if you want more sophisticated handling, you could, for example, ignore() the close-event while the worker is still running.如果您想要更复杂的处理,例如,您可以在工作人员仍在运行时忽略()关闭事件。 Pressing Start will start/contine the worker, and pressing Stop will pause it.按 Start 将启动/继续工作程序,按 Stop 将暂停它。 Once the worker has finished, pressing Start will completely re-start it (ie it will go back to zero):一旦工人完成,按下开始将完全重新启动它(即它将回到零):

class Worker(QtCore.QObject):
    progress = QtCore.pyqtSignal(int)
    finished = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()
        self._paused = True
        self._count = -1

    def start(self):
        self._paused = False
        if self._count < 0:
            print('Start')
            self._run()
        else:
            print('Continue')

    def stop(self, *, abort=False):
        self._paused = True
        if abort:
            print('Abort')
            self._count = -1
        else:
            print('Pause')

    def _run(self):
        self._count = 0
        self._paused = False
        while 0 <= self._count <= 100:
            if not self._paused:
                QtCore.QThread.msleep(100)
                self._count += 1
                self.progress.emit(self._count)
        self.stop(abort=True)
        self.finished.emit()
        print('Finished')


class exampleprogram(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(exampleprogram, self).__init__(parent)
        self.setupUi(self)
        self.thread = QtCore.QThread(self)
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.start)
        self.worker.finished.connect(self.thread.quit)
        self.worker.progress.connect(self.progressBar.setValue)
        self.pushButton_Start.clicked.connect(self.start)
        self.pushButton_Stop.clicked.connect(self.stop)

    def start(self):
        if self.thread.isRunning():
            self.worker.start()
        else:
            self.thread.start()

    def stop(self):
        if self.thread.isRunning():
            self.worker.stop()

    def closeEvent(self, event):
        self.worker.stop(abort=True)
        self.thread.quit()
        self.thread.wait()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM