PyQt5 display variable from Thread to LCD



  • Hello,
    I am learning python3.6 and Qt5 on Ubuntu one month so far.

    I made this program where I print the value of "i" in the terminal every second, 0 to 9.
    I would like to show "i" in the LCD of the GUI, but after a few hours of attempt with signals-slots I am stuck.
    How to I get the values of "i" into the variable "v"?
    What if I also want to use in the Thread the status of the checkbox?

    Note that the slider and progress bar only serve the purpose of verifying that the GUI is not frozen.

    import sys
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, QProgressBar,
        QVBoxLayout, QApplication, QCheckBox )
    from PyQt5.QtCore import QThread, pyqtSignal
    
    import time
    
    class Example(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
            
        def initUI(self):
            cb  = QCheckBox("enable countdown")
            lcd = QLCDNumber(self)
            pro = QProgressBar(self)
            sld = QSlider(Qt.Horizontal, self)
    
            vbox = QVBoxLayout()
            vbox.addWidget(cb)
            vbox.addWidget(lcd)
            vbox.addWidget(pro)
            vbox.addWidget(sld)
    
            self.setLayout(vbox)
            
            #link the slider to the progress bar
            sld.valueChanged.connect(pro.setValue)
            
            #show i in the lcd
            v=15
            lcd.display(v)
            
            self.setGeometry(300, 300, 250, 150)
            self.setWindowTitle('Signal and slot')
            self.show()
            
            self.countdown()
        
        def countdown(self):
            self.wt=WorkerThread()
            self.wt.start()
    
               
    
    class WorkerThread(QThread):
        def __init__(self, parent=None):
            super(WorkerThread, self).__init__(parent)
        
        def run(self):
            x = 10
            for i in range(x):
               time.sleep(1)
               print(i) 
            
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = Example()
        sys.exit(app.exec_())
    

  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You should emit a signal from your thread that you’ll connect to a slot that will update the LCD and the variable.

    And also add a slot to your thread that you’ll connect to your checkbox.



  • Hi, thanks for your hint.
    I understand the logic you pointed out, but I know so little PyQt that I don't know how to implement what you suggested.

    I managed to solve half of the problem. The signal for the value of "i" works, for the checkbox it only works with the method I commented out. When I insert it in the "run" method, I get the error: "TypeError: run() missing 1 required positional argument: 'b'". I don't know how to address it

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    Created on Sat Aug 11 15:48:57 2018
    
    """
    
    import sys
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, QProgressBar,
        QVBoxLayout, QApplication, QCheckBox )
    from PyQt5.QtCore import QThread, pyqtSignal
    
    import time
    
               
    
    class WorkerThread(QThread):
        signal_i=pyqtSignal( int, name='Signal_i') ### 1) declare the signal
        
        def __init__(self, parent=None):
            QThread.__init__(self)
            #super(WorkerThread, self).__init__(parent)
       
        def run(self, b):
            if b == 0:
                print ("non checked")
            else:
                print("checked")
            
            x = 10
            for i in range(x):
               time.sleep(1)
               #print(i) 
               self.signal_i.emit(i)  ### 2) emitt the signal
            
            
    class Example(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
            self.wt=WorkerThread() # This is the thread object
            self.wt.start()
            # Connect the signal from the thread to the finished method
            self.wt.signal_i.connect(self.slot_method)  ### 3) connect to the slot
            
        def initUI(self):
            cb  = QCheckBox("enable countdown")
            self.lcd = QLCDNumber(self)
            pro = QProgressBar(self)
            sld = QSlider(Qt.Horizontal, self)
    
            vbox = QVBoxLayout()
            vbox.addWidget(cb)
            vbox.addWidget(self.lcd)
            vbox.addWidget(pro)
            vbox.addWidget(sld)
            self.setLayout(vbox)
       
            self.setGeometry(300, 300, 250, 150)
            self.setWindowTitle('Signal and slot')
            self.show()
            
            sld.valueChanged.connect(pro.setValue)#link the slider to the progress bar
            #cb.stateChanged.connect(self.buttonchange)
            cb.stateChanged.connect(WorkerThread.run)
            
        def slot_method(self, i):   ### 4) this is the slot that receive the signal
            #print("i:",+i)
            self.lcd.display(i)
            
    #    def buttonchange(self, b):
    #        if b == 0:
    #            print ("non checked")
    #        else:
    #            print("checked")
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = Example()
        sys.exit(app.exec_())
    
    

  • Lifetime Qt Champion

    Why are you trying to connect the checkbox to the run method ? That's not the goal of that method at all.



  • @SGaist I want to use that check box to exit the while loop I will have in the Thread (reading data from the serial port, and sending it to the lcd).
    Otherwise when I close the GUI the Thread keeps running.


  • Lifetime Qt Champion

    You can use QThread:: requestInterruption and in your run method isInterruptionRequested.

    But the check box is a wrong GUI idea, you should rather use a normal button.



  • This post is deleted!


  • The idea of the checkbox is to ebnable/disable the serial data read when the checkbox is selected/unselected.
    It works with the method cbchange. Is this what you meant?

    If I connect the interruption to the pushbutton, the gui hangs at boot

    btn.clicked.connect(self.wt.requestInterruption()) #THIS HANGS
    
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    Created on Sat Aug 11 15:48:57 2018
    
    @author: gio
    """
    
    
    import sys
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider, QProgressBar,
        QVBoxLayout, QApplication, QCheckBox, QPushButton)
    from PyQt5.QtCore import QThread, pyqtSignal
    
    import time
    
               
    
    class WorkerThread(QThread):
        mysignal_i=pyqtSignal( int, name='Signal_i') ### 1) declare the signal
        
        def __init__(self, parent=None):
            QThread.__init__(self)
            #super(WorkerThread, self).__init__(parent)
       
        def run(self):
            x = 10
            for i in range(x):
               time.sleep(1)
               print(i) 
               self.mysignal_i.emit(i)  ### 2) emitt the signal
               if self.isInterruptionRequested():
                   print ("exit loop")
                   break
               
            
            
    class Example(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
            self.wt=WorkerThread() # This is the thread object
            #self.wt.start()
            # Connect the signal from the thread to the slot_method
            self.wt.mysignal_i.connect(self.slot_method)  ### 3) connect to the slot
            
        def initUI(self):
            cb  = QCheckBox("enable countdown")
            cb.setChecked(False)
            self.lcd = QLCDNumber(self)
            pro = QProgressBar(self)
            sld = QSlider(Qt.Horizontal, self)
            btn = QPushButton(self)
            btn.setText('stop thread')
    
            vbox = QVBoxLayout()
            vbox.addWidget(cb)
            vbox.addWidget(btn)
            vbox.addWidget(self.lcd)
            vbox.addWidget(pro)
            vbox.addWidget(sld)
            self.setLayout(vbox)
    
    
            self.setGeometry(300, 300, 300, 300)
            self.setWindowTitle('Signal and slot')
            self.show()
            
            sld.valueChanged.connect(pro.setValue)#link the slider to the progress bar
            cb.stateChanged.connect(self.cbchange)
            #cb.stateChanged.connect(WorkerThread.run)
            
            #btn.clicked.connect(self.wt.requestInterruption()) #THIS HANGS
            
        def slot_method(self, i):   ### 4) this is the slot that receive the signal
            #print("i:",+i)
            self.lcd.display(i)
            
        def cbchange(self, b):
            if b == 0:
                print ("non checked")
                self.wt.requestInterruption()
            else:
                print("checked")
                self.wt.start()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        ex = Example()
        sys.exit(app.exec_())
    
    

  • Qt Champions 2017

    Hi
    Does break in python do the same as return in c++ ?

    void long_task() {
         forever {
            if ( QThread::currentThread()->isInterruptionRequested() ) {
                return;
            }
        }
    }
    

    If its break like in c++ , you only ask it to skip for loop but not bail out of run()
    (i think/assume)



  • @mrjj
    Python break == C++ break
    Python return == C++ return
    :)


  • Qt Champions 2017

    @JonB
    Thanks so exactly the same.
    so i guess it hangs as he only exit the for loop and run
    repeats a moment later.



  • @webgiorgio

    btn.clicked.connect(self.wt.requestInterruption()) #THIS HANGS

    You're misunderstanding how you're intended to use connect in PyQt. It's the same principle as in C++, just different syntax. The point is, you must connect to the function ("pointer"), you must not call the function in the argument to connect.

    So all your PyQt connects need to look like:

    btn.clicked.connect(self.wt.requestInterruption)
    

    Note that requestInterruption does not have a () at the end of it! Do you follow the vital difference? It's also the same principle as why you have to write btn.clicked.connect and not btn.clicked().connect.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.