Pyqt5 等到小部件可见

Pyqt5 Wait until widget is visible

我正在寻找一种方法来等待 QWidget 完全显示后再调用函数。我有两个 windows:一个 parent 和一个 child.

parent 表单是一个 window,带有一个调用 openChild 的按钮,隐藏 parent,显示 child,然后执行 child的主要功能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_())

child 表单只有一个标签和一个包含文本编辑的滚动区域。

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

问题出在调用 busyFunc 时(实际函数是一个巨大的程序,所以为了清楚起见我省略了它。我正在使用循环模拟问题)。

Parent 是隐藏的,但是 Child 看起来像这样,直到 busyFunc 完成。

如何让child像这样完全加载然后执行busyFunc

一种解决方案是在一段时间后通过计时器启动 busyFunc 函数。

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

问题是在 运行 这个函数时接口被阻塞,要解决这个问题,建议 运行 在线程上执行那个任务

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() 

显然,因为 busyFunc 的当前代码不与 GUI 交互,即不更新 GUI 的任何值,所以不会有问题,但是如果这个代码必须更新 GUI 的某些数据,则必须通过信号,不是直接的:

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()