Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Update a Image every cycle in a different thread?



  • Hi,
    I have difficulties realizing the following:
    I have a two threads. The main thread and a worker thread which is a subclass of QThread, which runs a while true loop in its run() method. Each loop a QImage is updated (with the fill() method) which gets shown in the main thread. Also a signal is emited every loop such that the main thread knows when to update itself. Now my problem:

    The GUI stays kinda responsive (i can press buttons). But some stuff seems to be really laggy, for example moving the window or drawing.
    Here is a GIF of the described behaviour:
    WARNING:flashing colors

    How can I solve this issue? I want the main thread to stay untouched and intact while changing the Image on the right


  • Moderators

    @Thomas-Stein
    try

    def run(self) -> None:
            self.timer = qtc.QTimer()
            self.timer.setInterval(1000)
            self.timer.timeout.connect(self.compute)
            self.timer.start()
            self.exec()// Starting the event loop -> The thread itself
    

  • Qt Champions 2019

    @Thomas-Stein said in Update a Image every cycle in a different thread?:

    But some stuff seems to be really laggy

    How often does your second thread update the image and send it to the main thread?



  • @jsulm Every cycle i think,its a while true loop with no sleep


  • Moderators

    @Thomas-Stein there you have it, you're probably emitting several thousand signals per second.

    You should reduce that. You can't see/display/update the ui at that framerate anyway



  • @J-Hilk Oh yeah that makes sense. How would i reduce it to lets say 60 frames per second? Is a

    self.sleep(1/60)
    

    enough in the QThread subclass in my while True method? Or is there a way hat does not involve a sleep since sleeps are often considered a bad programming style?


  • Moderators

    @Thomas-Stein said in Update a Image every cycle in a different thread?:

    sleeps are often considered a bad programming style

    they are, also 1 / 60 == 0 ;-)

    Suggestion,

    store the latest image in a class member. Create a QTimer as class member (make it single shot)
    on timeout emit the signal with the member image as variable
    after each calculation, start the timer, since it's a single shot timer, start should not reset the timer



  • @J-Hilk
    Hey, so i tried what you said but i doesnt quiet work

    class Worker(qtc.QThread):
    
        computed = qtc.pyqtSignal()
    
    
        def __init__(self, image: qtg.QImage):
            super(Worker, self).__init__()
            self.image = image
            self.timer = qtc.QTimer(self)
            self.timer.setSingleShot(True)
            self.timer.timeout.connect(self.emit)
    
    
        def run(self) -> None:
            while True:
                self.timer.start(1000)
                color = qtg.QColor(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
                self.image.fill(color)
    
    
        def emit(self):
            print("emiting")
            self.computed.emit(self.image)
    

    The console never prints "emiting" nor does the timer seem to wait 1 second


  • Moderators

    @Thomas-Stein
    I'm not sure, how the python implementation handles qtMacro keywords, but I would suggest changing the signal name to somthing else but emit :)

    If start does indeed resets the single shot time, you'll have to check for active == true beforehand.

    Active is true, when the timer is running and false when it's not!



  • @J-Hilk Hey, so i checked if the isActive function of the timer returns True every loop, and it does. Somehow though the timeout is never emitted. Is it even possible to use QTimers in different QThreads? ALso i changed the funtion name to emitImage() but still no timeout is emitted


  • Moderators

    @Thomas-Stein
    yeah, that's my bad, the while loop stops the Timer from running properly 😔

    Change
    QTimer -> QTime

    call start on it, (on the first run) and then check the elapsed() time and emit according to that.



  • @J-Hilk Hi, so i had a different approach in mind:
    Rather than usng a while True: loop and making it wait, i thought i'd use a repeating QTimer like this:

    class Worker(qtc.QThread):
    
        computed = qtc.pyqtSignal(qtg.QImage)
    
    
        def __init__(self, image: qtg.QImage):
            super(Worker, self).__init__()
            self.image = image
            self.timer = qtc.QTimer()
            self.timer.setInterval(1000)
            self.timer.timeout.connect(self.compute)
    
        def run(self):
            self.timer.start()
    
        def compute(self):
            print("computing")
            color = qtg.QColor(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
            self.image.fill(color)
            self.computed.emit(self.image)
    

    But still, the timeout is never emitted and "computing" is never printed. I really dont understant this, arent QTimers by default repeating?


  • Moderators

    @Thomas-Stein since you're not creating timer inside run function, but try to start it inside run, start is never called.

    You may not start or stop Qtimers that live in other threads.

    Stuff you create in init live inside the old thread, stuff you created inside run live in the new one



  • @J-Hilk Thats what i thought aswell, so i did the following:

    class Worker(qtc.QThread):
    
        computed = qtc.pyqtSignal(qtg.QImage)
    
    
        def __init__(self, image: qtg.QImage):
            super(Worker, self).__init__()
            self.image = image
    
    
        def run(self) -> None:
            self.timer = qtc.QTimer()
            self.timer.setInterval(1000)
            self.timer.timeout.connect(self.compute)
            self.timer.start()
    
    
        def compute(self):
            print("computing")
            color = qtg.QColor(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
            self.image.fill(color)
            self.computed.emit(self.image)
    

    But still, no timeout is emitted....


  • Moderators

    @Thomas-Stein
    try

    def run(self) -> None:
            self.timer = qtc.QTimer()
            self.timer.setInterval(1000)
            self.timer.timeout.connect(self.compute)
            self.timer.start()
            self.exec()// Starting the event loop -> The thread itself
    


  • Does this thread only do the randint and fill thing?
    If it is true, I think there's no need to use thread, the timer will be enough.


Log in to reply