Change Stacked Widget index automatically after 3 seconds in PyQt5

Viewed 1419

I am trying to display the first page of the stacked widget ( the welcome page ) and after 3 seconds replace it by the second page ( the Menu page ) automatically.

I tried this approach but it doesn't work..

..........

self.stackedWidget.setCurrentIndex(0)
time.sleep(3) 
self.stackedWidget.setCurrentIndex(1) 
QtCore.QMetaObject.connectSlotsByName(smartUpdaterUI)

..............
2 Answers

Qt uses a GUI event loop to handle events, and update the UI. Any updates to the GUI won't be visible until control is handed over to the event loop and that update is picked up and processed.

The code you write in Python happens in the same thread as the GUI. So while your code is running, the event loop is not, and changes are not being processed.

Side note: this is why your application can 'hang' if you try and do something long-running like accessing an API, without using a separate thread.

In your code, you're setting the current index to 0, then using Python time.sleep() to wait, before updating to index 1.

self.stackedWidget.setCurrentIndex(0)
time.sleep(3) # no event loop running here
self.stackedWidget.setCurrentIndex(1) 

While the time.sleep(3) is happening, the execution is held at this point. This means control is not handed back to the Qt event loop, and the first change is not processed. Once the timeout completes, the second index is set, and only then your function returns control back to Qt.

The event loop now applies both changes, but immediately one after another. So all you see is index being set to 1, without first showing 0 and without any delay.

To avoid this, you need to return control back to the event loop after setting the initial index. The simplest way to do this is to just set it, and then trigger the subsequent update using an asynchronous QTimer.

self.stackedWidget.setCurrentIndex(0)
QTimer.singleShot(3000, lambda: self.stackedWidget.setCurrentIndex(1))

The lambda: is used as an anonymous function, so we can pass 1 to setCurrentIndex by delaying execution until the timer is triggered. If you're only ever jumping to a single page you could do this instead:

def go_to_page_1(self):
    self.stackedWidget.setCurrentIndex(1)

self.stackedWidget.setCurrentIndex(0)
QTimer.singleShot(3000, go_to_page_1)

void QTimer::singleShot(int msec, const QObject *receiver, const char *member)

This static function calls a slot after a given time interval.

Try it:

import sys
from PyQt5.QtCore    import *
from PyQt5.QtGui     import *
from PyQt5.QtWidgets import *

class stackedExample(QWidget):
    def __init__(self):
        super(stackedExample, self).__init__()
        
        self.leftlist = QListWidget()
        self.leftlist.insertItem (0, 'Contact'     )
        self.leftlist.insertItem (1, 'Personal'    )
        self.leftlist.insertItem (2, 'Educational' )
        
        self.stack1 = QWidget()
        self.stack2 = QWidget()
        self.stack3 = QWidget()
        
        self.stack1UI()
        self.stack2UI()
        self.stack3UI()
        
        self.stackWidget = QStackedWidget(self)
        self.stackWidget.addWidget(self.stack1)
        self.stackWidget.addWidget(self.stack2)
        self.stackWidget.addWidget(self.stack3)
        
        hbox = QHBoxLayout(self)
        hbox.addWidget(self.leftlist)
        hbox.addWidget(self.stackWidget)
        
        self.setLayout(hbox)
        self.leftlist.currentRowChanged.connect(self.display)
        self.setGeometry(300, 50, 10, 10)
        self.setWindowTitle('StackedWidget demo')
        
        self.stackWidget.setCurrentIndex(0)
        QTimer.singleShot(3000, lambda: self.display(1))
        QTimer.singleShot(6000, lambda: self.display(2))
        QTimer.singleShot(9000, lambda: self.display(0))
        
        self.show()
        
    def stack1UI(self):
        layout = QFormLayout()
        layout.addRow("Name",    QLineEdit())
        layout.addRow("Address", QLineEdit())
        #self.setTabText(0,"Contact Details")
        self.stack1.setLayout(layout)
        
    def stack2UI(self):
        layout = QFormLayout()
        sex    = QHBoxLayout()
        sex.addWidget(QRadioButton("Male"))
        sex.addWidget(QRadioButton("Female"))
        layout.addRow(QLabel("Sex"),sex)
        layout.addRow("Date of Birth",QLineEdit())
        self.stack2.setLayout(layout)
        
    def stack3UI(self):
        layout = QHBoxLayout()
        layout.addWidget(QLabel("subjects"))
        layout.addWidget(QCheckBox("Physics"))
        layout.addWidget(QCheckBox("Maths"))
        self.stack3.setLayout(layout)
        
    def display(self, i):
        self.stackWidget.setCurrentIndex(i)

def main():
    app = QApplication(sys.argv)
    ex  = stackedExample()
    sys.exit(app.exec_())
    
if __name__ == '__main__':
    main()        
Related