Jaitnium
Jaitnium

Reputation: 650

Pyqt5 Wait until widget is visible

I'm looking for a way to wait until a QWidget is fully shown before calling a function. I have two windows: a parent and a child.

The parent form is a window with a button that calls openChild which hides parent, shows child, and then execute child's primary function busyFunc:

from PyQt5 import QtCore, QtGui, QtWidgets
from Child import Child_Form
import sys

class Parent_Form(QtWidgets.QWidget):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        self.setupUi(self)
        self.Child = Child_Form(self)

    def setupUi(self, Parent):
        Parent.setObjectName("Parent")
        Parent.resize(400, 300)
        self.nextWindow = QtWidgets.QPushButton(Parent)
        self.nextWindow.setGeometry(QtCore.QRect(150, 120, 91, 31))
        self.nextWindow.setObjectName("nextWindow")

        self.retranslateUi(Parent)
        QtCore.QMetaObject.connectSlotsByName(Parent)

    def retranslateUi(self, Parent):
        _translate = QtCore.QCoreApplication.translate
        Parent.setWindowTitle(_translate("Parent", "Parent Window"))
        self.nextWindow.setText(_translate("Parent", "Next Window"))

        self.nextWindow.clicked.connect(self.openChild)

    def openChild(self):
        self.hide()
        self.Child.show()
        self.Child.busyFunc()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = Parent_Form()
    ex.show()
    sys.exit(app.exec_())

The child form just has a label and a scroll area that has a text edit.

from PyQt5 import QtCore, QtGui, QtWidgets

class Child_Form(QtWidgets.QWidget):
    def __init__(self, Parent_Form):
        QtWidgets.QWidget.__init__(self)
        self.setupUi(self)
        self.parent = Parent_Form

    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(400, 300)
        self.label = QtWidgets.QLabel(Form)
        self.label.setGeometry(QtCore.QRect(20, 10, 61, 16))
        self.label.setObjectName("label")
        self.scrollArea = QtWidgets.QScrollArea(Form)
        self.scrollArea.setGeometry(QtCore.QRect(20, 40, 361, 241))
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 359, 239))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.textEdit = QtWidgets.QTextEdit(self.scrollAreaWidgetContents)
        self.textEdit.setGeometry(QtCore.QRect(0, 0, 361, 241))
        self.textEdit.setObjectName("textEdit")
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Child Window"))
        self.label.setText(_translate("Form", "Status:"))

    def busyFunc(self):
        for i in range(0, 1000000000):
            pass

The problem is when busyFunc is called (the actual function is an enormous program so I've omitted it for clarity. I'm simulating the problem using a loop).

Parent is hidden, but Child looks like this until busyFunc is done.

How can I make child load completely like this and then execute busyFunc?

Upvotes: 0

Views: 2796

Answers (1)

eyllanesc
eyllanesc

Reputation: 243897

One solution is to launch the busyFunc function after some time through a timer.

def openChild(self):
    self.hide()
    self.Child.show()
    QtCore.QTimer.singleShot(100, self.Child.busyFunc)

The problem would be that the interface is blocked while running this function, to solve this would recommend running that task on a thread

class Thread(QtCore.QThread):
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        for i in range(0, 1000000000):
            pass

class Child_Form(QtWidgets.QWidget):
    def __init__(self, Parent_Form):
        [...]
    def busyFunc(self):
        self.thread = Thread(self)
        self.thread.start() 

Obviously as the current code of busyFunc does not interact with the GUI, ie does not update any value of the GUI there would be no problem but if this one has to update some data of the GUI it must do through the signals, not directly:

class Thread(QtCore.QThread):
    signal = QtCore.pyqtSignal(str)
    signal2 = QtCore.pyqtSignal(list)
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        self.signal.emit("start")
        for i in range(0, 1000000000):
            pass
        self.signal.emit("finish")
        self.signal2.emit([1, 2, 3, 4])

class Child_Form(QtWidgets.QWidget):
    def __init__(self, Parent_Form):
        [...]
    def busyFunc(self):
        self.thread = Thread(self)
        self.thread.signal.connect(lambda text: self.textEdit.append(text))
        self.thread.signal2.connect(lambda l: print(l))
        self.thread.start() 

class Parent_Form(QtWidgets.QWidget):
    def __init__(self):
        [...]
    def openChild(self):
        self.hide()
        self.Child.show()
        self.Child.busyFunc()

Upvotes: 1

Related Questions