PyQt5 cannot update progress bar from thread and r

2019-08-31 17:53发布

问题:

I am retired and teaching myself to write code. I am working on a program that requires a thread to operate in the background (GUI developed with PYQT5) so I can still use the GUI buttons (to pause, resume, stop, etc.). I have the thread counting to 10, and I want the steps sent back to the progress bar's setValue. That is not working. I can see the thread counting to 10 and I can see the data coming back from the thread. Just can't get the progress bar to move. I have spent the last two days searching the internet and have reviewed and tried to follow many examples. To be honest, I am not sure I understand the answers.
I have crated an example of what I am seeing in my program. In this example I have a progress bar in a GUI with two buttons. Start will start the thread and test will just print out test statements while the thread is running. I am using Designer, so the GUI is in a separate file.
I am not even sure if I am asking the questions correctly in my searches. I decided to post in the error I see a lot, but hopefully you can see what is happening when you run the code.

Main program

#!/usr/bin/env python3

import sys, sqlite3, os.path, string, time
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, 
QProgressBar
from PyQt5.QtCore import pyqtSlot, QObject, QThread, pyqtSignal
from Thread_Test import Ui_MainWindow

class ExecuteSession(QThread):

    PBValueSig = pyqtSignal(int)

    def __init__(self, dur=0):
        QThread.__init__(self)
        self.dur = dur

    def __del__(self):
        self.wait()

    def run(self):
        i = 0
        while i <= self.dur:
            self.RPTApp = RPTApp()
            print(i)
            i = i + 1
            self.PBValueSig.emit(self.RPTApp.updateProgressBar(i))    
            time.sleep(1)



class RPTApp(QMainWindow, Ui_MainWindow):


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

        self.pushButton.clicked.connect(self.PB)
        self.pushButton_2.clicked.connect(self.PB2)

    def PB2(self):
        print("TEST")


    def PB(self):
        dur = 10
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(dur)
        self.progressBar.setValue(0)
        #thread
        self.exeThread = ExecuteSession(dur)
        self.exeThread.start()        


    @pyqtSlot(int)
    def updateProgressBar(self, int):
        print("INT + " +str(int))
        #self.classES.PBValueSig.connect(self.progressBar.setValue)
        self.progressBar.setValue(int)

def main():

    app = QApplication(sys.argv)
    window = RPTApp()
    window.show()
    app.exec_()    


if __name__ == '__main__':
    main()

This is the GUI code:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'Thread_Test.ui'
#
# Created by: PyQt5 UI code generator 5.7
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(640, 480)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(90, 320, 471, 23))
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(270, 140, 91, 29))
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setGeometry(QtCore.QRect(270, 200, 91, 29))
        self.pushButton_2.setObjectName("pushButton_2")
        MainWindow.setCentralWidget(self.centralwidget)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "START"))
        self.pushButton_2.setText(_translate("MainWindow", "TEST"))

This is the error I am receiving:

(python3:11942): WARNING: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files QObject: Cannot create children for a parent that is in a different thread. (Parent is QApplication(0x26a1a58), parent's thread is QThread(0x26a6218), current thread is ExecuteSession(0x28f4048)

I am not a software developer, so much of this is new to me. I appreciate any help I can receive, and please...don't assume I know anything. Please be descriptive. Thank you!

回答1:

Your code has several errors since PyQt has certain minimum rules:

  • The thread where the GUI application is created is called GUI thread because there must be created and live any graphic component, but you are creating unnecessary RPTApp inside the QThread, I say unnecessary since the thread is created inside RPTApp so it does not it is necessary to create another.

  • Another error is in the emission of the signal in the wire, you do not have to call the function that uses the data that emits the signal but you must connect it to the slot. The application will be in charge of transporting the data and invoking the slot.

All of the above is corrected in the following section:

class ExecuteSession(QThread):
    PBValueSig = pyqtSignal(int)
    [...]
    def run(self):
        i = 0
        while i <= self.dur:
            print(i)
            i = i + 1
            self.PBValueSig.emit(i)
            time.sleep(1)


class RPTApp(QMainWindow, Ui_MainWindow):
    [..]
    def PB(self):
        dur = 10
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(dur)
        self.progressBar.setValue(0)
        # thread
        self.exeThread = ExecuteSession(dur)
        self.exeThread.PBValueSig.connect(self.updateProgressBar)
        self.exeThread.start()

    @pyqtSlot(int)
    def updateProgressBar(self, value):
        print("INT + " + str(value))
        self.progressBar.setValue(value)

Note: It is not recommended to use int as a variable since it is the name of a preloaded function, there are thousands of other names.