Home > database >  Have a QtThread reference members of my GUI class - PyQt5
Have a QtThread reference members of my GUI class - PyQt5

Time:04-25

So I'm trying to multithread a button click on my GUI and the issue I'm having is the function will need to reference things defined in my main class which seems impossible... Every example I see looks something like this:

class Window(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.clicksCount = 0
        self.setupUi()

    def setupUi(self):
        # Create and connect widgets
        self.countBtn = QPushButton("Click me!", self)
        self.countBtn.clicked.connect(self.countClicks)

    def countClicks(self):
        self.clicksCount  = 1
        self.clicksLabel.setText(f"Counting: {self.clicksCount} clicks")

    def reportProgress(self, n):
        self.stepLabel.setText(f"Long-Running Step: {n}")

    def runLongTask(self):
        """Long-running task in 5 steps."""
        for i in range(5):
            sleep(1)
            self.reportProgress(i   1)

app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())

[found here: https://realpython.com/python-pyqt-qthread/]

And they will go about making the runLongTask a new thread. But it doesn't reference a single member of the Window class or user input.

My GUI has a lot of user inputs which would need to be referenced in the thread. Here is a stripped down version of my example:

class Window(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.connectSignalsSlots()

        self.line_names = ["Trigger", "Slave1"]
        self.sources = [self.cmbTriggerSource, self.cmbSlaveLED1_Source]

        for info in QtSerialPort.QSerialPortInfo.availablePorts():
            self.cmbSerialPort.addItem(info.portName())

        self.cmbSourceClosed()

    def connectSignalsSlots(self):
    self.btnSerialPortScan.clicked.connect(self.btnSerialPortScanClicked)
        self.btnConfigure.clicked.connect(self.btnConfigureClicked)
        self.btnTMInit.clicked.connect(self.btnTesterModule)

    def btnSerialPortScanClicked(self):
        self.cmbSerialPort.clear()
        for info in QtSerialPort.QSerialPortInfo.availablePorts():
            self.cmbSerialPort.addItem(info.portName())

    def btnConfigureClicked(self):
        # Open the serial port
        port = self.openSerialPort()
        message.append(self.sources[i].currentIndex())
        self.sendData(port, message)
        self.closeSerialPort(port)

    def btnTesterModule(self):
        port = self.openSerialPort2(self)

        devices = cm.devices_list()
        com_port = self.cmbSerialPort_tester.currentText()

        a = self.LEDfrom.value()
        b = self.LEDto.value()   1

        for i in range(a, b):
            self.SetButtonColour(i, 0)
            message.append((self.tester_LED[2].value() >> 24) & 0xff)
            message.append((self.tester_LED[2].value() >> 16) & 0xff)
            message.append((self.tester_LED[2].value() >> 8) & 0xff)
            message.append(self.tester_LED[2].value() & 0xff)
            self.selectObject(port, 3)
            self.sendData(port, message)

Where the btnTesterModule function is the one that takes a while and needs to be in a new thread. As you can see it references a lot of self. objects and so moving it to a new thread would no longer allow it to references any of these variables from the GUI or functions.

Does anyone know how I can make a new thread that still has access to my window and GUI and just initialize the thread to run said function when that button is pressed? I would just use basic python threading but to make this more complicated I need a progress bar for this button once it can run without crashing GUI so will need slots and signals which I understand is easier with QtThreads

CodePudding user response:

Don't worry I figured it out, if you need to do this it's best to just use the standard python threading because PyQt5 doesn't have anything in place to handle this situation efficiently.

Like so:

import threading

    def ScanButtonThread(self):
        t = threading.Thread(target=self.btnTesterModule)
        t.start()

Then connect the button to this function instead of the other one.

  • Related