Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - PyQt Signals - Emit and send argument to different class

This is only my second question here so please do bear with me. I have a python script that currently contains two classes. One of which manages a GUI and the other is my 'worker thread' (a pyqt thread) . In order for me to be able to update the GUI from the 'worker thread' I understand that I can setup pyqt signals and emit these at different points. Back in the GUI thread I have set up connect statements which should connect to a function within the GUI thread which will update the GUI.

In order for this GUI update function to know what it has to do with the GUI, the signal is also emitting an extra argument but this is not being picked up in the GUI thread. I think I know why this is happening but I can't work out how I can get it to work correctly.

I would include some code but there are so many possible parts that could be included I thought I would wait and see what bits anyone specifically asks for.

I hope someone can help me with this.

EDIT: Thank you for your answer farshed-. I should have put this in the original question, I am using PyQt 4. I know that a thread class should be run using 'start()' but when I do, the program crashes instantly and stops working. This has led me to use 'run()' to commence the thread after initializing it.

Also, in your code segment the updateProgressBar method is called without being handed any arguments directly in the connect statement and only within the brackets where the method is defined, is it shown that the method will receive an argument. I have tried this with mine but it just tells me what you would expect to hear if you were calling any normal function and not providing the necessary number of arguments. Possibly this is because I am using PyQt 4 or it could be because I am directly calling 'run()' instead of 'start()'. Hopefully that can aid you to answer my question further if possible. Once again, please do mention if there are any code segments you would like me to include.

Thanks,

BoshJailey

like image 586
NobodyTellsMe Avatar asked Dec 19 '22 13:12

NobodyTellsMe


1 Answers

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import time
import sys
from PyQt5.QtWidgets import QApplication, QWidget,  QProgressBar
from PyQt5.QtCore import QThread, pyqtSignal

class Thread(QThread):

    progress = pyqtSignal(int)

    def __init__(self):
        super(Thread, self).__init__()

    def __del__(self):
        self.wait()

    def run(self):
        for i in range(100):
            time.sleep(1);
            self.progress.emit(i)



class Window(QWidget):

    def __init__(self):
        super(Window, self).__init__()

        self.initUI()

    def initUI(self):
        self.progressBar = QProgressBar(self)
        self.progressBar.setValue(0)

        self.thread = Thread()
        self.thread.progress.connect(self.updateProgressBar)
        self.thread.start()


        self.setGeometry(100, 100, 200, 200)
        self.setWindowTitle("Threading in PyQt5")
        self.show()

    def updateProgressBar(self, value):
        self.progressBar.setValue(value);



if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    sys.exit(app.exec_())

Let's see how threads work in PyQt5. So first you you need to create your custom Thread inherited from QThread. After ordinary initialization your main logic should be written in run method since this is the first method which will actually run after QThread.start() (right after initialization of course). So after you have written your custom thread you want to implement it in your application. So to do that we need to initialize your custom thread. Nothing special, this is just like initializing any class in python. But initializing your thread doesn't mean it's start. You should make it start. To do that we call start() method of QThread class. So still my example is the simplest way to use QThread. Our next goal is to get message from our custom thread. As we can see first we need to determine what kind of message our thread is supposed to send (in our case it is int). The message is said to be signal in PyQt5 so now we are to send our message through pyqtSignal. You can see how I created this signal before __init__ in our custom Thread. So now to send any signal we just need to call <any_signal>.emit(<message>) method. Moreover don't forget to bind it with some method in our main logic. In our case we bind it to self.updateProgressBar method. So at every loop in run method our signal is sent to self.updateProgressBar method. A bit confusing thing is that we don't have to obviously declare that self.updateProgressBar method is going to get some parameters

self.thread.progress.connect(self.updateProgressBar)

But still it means that our thread is going to send some signal to this method. I know that it is a bit confusing and my answer is obviously not enough to understand this powerful tool. Hope I could help you at least to get basics.

like image 132
farshed- Avatar answered Mar 06 '23 06:03

farshed-