0

Using PyQt5, I want to implement a two windows displaying one after another automatically, without the user interacting with any window. Something like this:

While True:
    Show Window1    
    wait 2 seconds
    Close Window1
    Show Window2
    wait 2 seconds
    Close Window2

The problem I am having is that the main UI thread is stuck in app.exec_() function, so it cannot implement the opening and closing logic.

import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic


class Win1(QMainWindow):
    def __init__(self):
        super(Win1, self).__init__()
        uic.loadUi('win1.ui', self)
        self.show()
        
class Win2(QMainWindow):
    def __init__(self):
        super(Win2, self).__init__()
        uic.loadUi('win2.ui', self)
        self.show()

if __name__ == '__main__':

    app = QApplication(sys.argv)

    while True:
        win = Win1()
        time.sleep(1) 
        win.close()
        
        win = Win2()
        time.sleep(1)
        win.close()
        
        app.exec_() # <--------- Program blocks here

I would appreciate if someone can share a minimal example for this working without blocking. Or please point to the mechanism that should be used.

Malik
  • 87
  • 3
  • 11

1 Answers1

1

If you are going to work with Qt then you should forget about sequential logic but you have to implement the logic using events. For example, in your case you want one window to be shown every time T and another to be hidden, so that can be implemented with a QTimer and a flag:

import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic


class Win1(QMainWindow):
    def __init__(self):
        super(Win1, self).__init__()
        uic.loadUi('win1.ui', self)
        self.show()
        

class Win2(QMainWindow):
    def __init__(self):
        super(Win2, self).__init__()
        uic.loadUi('win2.ui', self)
        self.show()


if __name__ == "__main__":

    app = QApplication(sys.argv)

    timer = QTimer()
    timer.setProperty("flag", True)

    win1 = Win1()
    win2 = Win2()

    def on_timeout():
        flag = timer.property("flag")
        if flag:
            win1.show()
            win2.close()
        else:
            win2.show()
            win1.close()
        timer.setProperty("flag", not flag)

    timer.timeout.connect(on_timeout)
    timer.start(1 * 1000)
    on_timeout()

    app.exec_()

You should not use while loop or time.sleep since they block the eventloop in which the GUI lives, that is: they freeze the windows

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Hi eyllanesc, Thanks for your answer. It solves the current problem that I posted. For this I guess the key is to use QTimer. Thanks for pointing that out. The greater problem I want to solve it to create a state based UI where based on events happening inside another thread, a certain screen is displayed from a collection on screen automatically. I guess with this approach, I can set a small timeout interval and implement the state machine inside timer which can check a global variable in the program to determine the current state. Please comment if there is a better mechanism to solve this. – Malik Dec 17 '20 at 07:18
  • @Malik 1) please read [ask] and [answer], 2) I only solve the problem shown by the OP in the post, nothing more so I will not expand on other topics so if you have other problems then create another post. 3) In general (I do not know if it works in your case) you should not update the GUI from another thread but notify these states through signals, for more information read https://stackoverflow.com/questions/9957195/updating-gui-elements-in-multithreaded-pyqt – eyllanesc Dec 17 '20 at 07:24
  • @Malik 4) on the other hand there are thousands of posts (literally) about how to update the GUI using the information from another thread in PyQt so I recommend you first investigate other posts on the site. – eyllanesc Dec 17 '20 at 07:25
  • Much appreciated. – Malik Dec 17 '20 at 07:29