在之前的教程中,我们已经介绍了如何打开对话框窗口。这些特殊的窗口(默认情况下)抓住用户的焦点,并运行自己的事件循环,有效地阻止了应用程序其余部分的执行。 然而,您经常希望在不中断主窗口的情况下在应用程序中打开第二个窗口例如,显示一些长时间运行的进程的输出,或者显示图形或其他可视化。或者,您可能希望创建一个应用程序,允许您在各自的窗口中同时处理多个文档。 打开新窗口相对简单,但有几件事要记住,以确保它们正常工作。在本教程中,我们将逐步了解如何创建一个新窗口,以及如何根据需要显示和隐藏外部窗口。创建一个新窗口 在Qt中,任何没有父组件的小部件都是窗口。这意味着,要显示一个新窗口,您只需要创建一个小部件的新实例。这可以是任何小部件类型(严格来说是QWidget的任何子类),如果您愿意,还可以包括另一个QMainWindow。 QMainWindow实例的数量没有限制。如果你在第二个窗口上需要工具栏或菜单,你将不得不使用QMainWindow来实现这一点。然而,这可能会让用户感到困惑,所以要确保这是必要的。 与主窗口一样,创建一个窗口是不够的,还必须显示它。fromPyQt6。QtWidgetsimportQApplication,QMainWindow,QPushButton,QLabel,QVBoxLayout,QWidgetimportsysclassAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindowaswewant。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindow)layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。buttonQPushButton(PushforWindow)self。button。clicked。connect(self。shownewwindow)self。setCentralWidget(self。button)defshownewwindow(self,checked):wAnotherWindow()w。show()appQApplication(sys。argv)wMainWindow()w。show()app。exec() 如果运行这个程序,您将看到主窗口。单击按钮可能会显示第二个窗口,但如果您看到它,它只会在几分之一秒内可见。这是为什么呢?defshownewwindow(self,checked):wAnotherWindow()w。show() 在这个方法中,我们创建了窗口(小部件)对象,将其存储在变量w中并显示出来。然而,一旦我们离开这个方法,我们就不再有对w变量的引用(它是一个局部变量),所以它将被清除窗口也将被销毁。为了解决这个问题,我们需要在某个地方保留一个对窗口的引用,例如在self对象上。defshownewwindow(self,checked):self。wAnotherWindow()self。w。show() 现在,当您单击按钮以显示新窗口时,它将持续存在。 但是,如果再次单击按钮会发生什么?窗口将被重新创建!这个新窗口将取代自我中的旧窗口。self。w变量,因为现在没有对它的引用前一个窗口将被销毁。 如果您将窗口定义更改为每次创建标签时在标签中显示一个随机数,就可以看到这一点。fromrandomimportrandintclassAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindowaswewant。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout) init代码块只在创建窗口时运行。如果您继续单击按钮,数字将改变,显示窗口正在重新创建。 一种解决方案是在创建窗口之前简单地检查窗口是否已经被创建。下面的示例演示了这一点。fromPyQt6。QtWidgetsimportQApplication,QMainWindow,QPushButton,QLabel,QVBoxLayout,QWidgetimportsysfromrandomimportrandintclassAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindowaswewant。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。wNoneNoexternalwindowyet。self。buttonQPushButton(PushforWindow)self。button。clicked。connect(self。shownewwindow)self。setCentralWidget(self。button)defshownewwindow(self,checked):ifself。wisNone:self。wAnotherWindow()self。w。show()appQApplication(sys。argv)wMainWindow()w。show()app。exec() 使用按钮,您可以弹出窗口,并使用窗口控件关闭它。如果再次单击该按钮,将重新显示相同的窗口。 这种方法适用于临时创建的窗口例如,如果您想弹出一个窗口来显示特定的图形或日志输出。然而,对于许多应用程序,您都有许多标准窗口,您希望能够按需显示隐藏它们。 在下一部分中,我们将看看如何使用这些类型的窗口。切换窗口 通常,您需要使用工具栏或菜单上的操作来切换窗口的显示。正如我们前面看到的,如果没有保留对窗口的引用,它将被丢弃(并关闭)。我们可以使用这种行为来关闭一个窗口,用下面的代码替换前面示例中的shownewwindow方法defshownewwindow(self,checked):ifself。wisNone:self。wAnotherWindow()self。w。show()else:self。wNoneDiscardreference,closewindow。 通过设置self。w为None,则对窗口的引用将丢失,并且窗口将关闭。 如果我们将它设置为任何其他值None,窗口仍然会关闭,但是Ifself。wNone测试将不会在下次单击按钮时通过,因此我们将无法重新创建窗口。 只有当您没有在其他地方保留对该窗口的引用时,这才有效。为了确保窗口无论如何都会关闭,您可能希望显式地对其调用。close()。完整示例如下所示。fromPyQt6。QtWidgetsimportQApplication,QMainWindow,QPushButton,QLabel,QVBoxLayout,QWidgetimportsysfromrandomimportrandintclassAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindowaswewant。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。wNoneNoexternalwindowyet。self。buttonQPushButton(PushforWindow)self。button。clicked。connect(self。shownewwindow)self。setCentralWidget(self。button)defshownewwindow(self,checked):ifself。wisNone:self。wAnotherWindow()self。w。show()else:self。w。close()Closewindow。self。wNoneDiscardreference。appQApplication(sys。argv)wMainWindow()w。show()app。exec()持续的窗户 到目前为止,我们已经了解了如何按需创建新窗口。但是,有时您有许多标准应用程序窗口。在这种情况下,与其在想要显示窗口时创建窗口,不如在启动时创建窗口,然后在需要时使用。show()显示它们,这样做通常更有意义。 在下面的例子中,我们在主窗口的init块中创建了外部窗口,然后我们的shownewwindow方法简单地调用self。w。show()来显示它。fromPyQt6。QtWidgetsimportQApplication,QMainWindow,QPushButton,QLabel,QVBoxLayout,QWidgetimportsysfromrandomimportrandintclassAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindowaswewant。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。wAnotherWindow()self。buttonQPushButton(PushforWindow)self。button。clicked。connect(self。shownewwindow)self。setCentralWidget(self。button)defshownewwindow(self,checked):self。w。show()appQApplication(sys。argv)wMainWindow()w。show()app。exec() 如果运行此命令,单击该按钮将显示与以前一样的窗口。但是,请注意,窗口只创建一次,在已经可见的窗口上调用。show()没有任何效果。显示和隐藏持久窗口 一旦创建了持久窗口,就可以显示和隐藏它,而无需重新创建它。一旦隐藏,窗口仍然存在,但将不可见,并接受鼠标其他输入。但是,您可以继续调用窗口上的方法并更新它的状态包括更改它的外观。一旦重新显示,任何更改都将可见。 下面我们更新主窗口,创建togglewindow方法,使用。isVisible()检查当前窗口是否可见。如果它不可见,则使用。show()显示,如果它已经可见,则使用。hide()隐藏它。classMainWindow(QMainWindow):definit(self):super()。init()self。wAnotherWindow()self。buttonQPushButton(PushforWindow)self。button。clicked。connect(self。togglewindow)self。setCentralWidget(self。button)deftogglewindow(self,checked):ifself。w。isVisible():self。w。hide()else:self。w。show() 这个持久窗口和切换显示隐藏状态的完整工作示例如下所示。fromPyQt6。QtWidgetsimportQApplication,QMainWindow,QPushButton,QLabel,QVBoxLayout,QWidgetimportsysfromrandomimportrandintclassAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindowaswewant。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。wAnotherWindow()self。buttonQPushButton(PushforWindow)self。button。clicked。connect(self。togglewindow)self。setCentralWidget(self。button)deftogglewindow(self,checked):ifself。w。isVisible():self。w。hide()else:self。w。show()appQApplication(sys。argv)wMainWindow()w。show()app。exec() 再次注意,窗口只创建了一次每次窗口重新显示时,窗口的init块不会重新运行(因此标签中的数字不会改变)。多重窗口 您可以使用相同的原则来创建多个窗口只要保持对窗口的引用,事情就会按预期工作。最简单的方法是创建一个单独的方法来切换每个窗口的显示。importsysfromrandomimportrandintfromPyQt6。QtWidgetsimport(QApplication,QLabel,QMainWindow,QPushButton,QVBoxLayout,QWidget,)classAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindow。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。window1AnotherWindow()self。window2AnotherWindow()lQVBoxLayout()button1QPushButton(PushforWindow1)button1。clicked。connect(self。togglewindow1)l。addWidget(button1)button2QPushButton(PushforWindow2)button2。clicked。connect(self。togglewindow2)l。addWidget(button2)wQWidget()w。setLayout(l)self。setCentralWidget(w)deftogglewindow1(self,checked):ifself。window1。isVisible():self。window1。hide()else:self。window1。show()deftogglewindow2(self,checked):ifself。window2。isVisible():self。window2。hide()else:self。window2。show()appQApplication(sys。argv)wMainWindow()w。show()app。exec() 但是,您也可以创建一个通用方法来处理所有窗口的切换。下面的示例演示了如何使用lambda函数拦截来自每个按钮的信号并通过适当的窗口。importsysfromrandomimportrandintfromPyQt6。QtWidgetsimport(QApplication,QLabel,QMainWindow,QPushButton,QVBoxLayout,QWidget,)classAnotherWindow(QWidget):ThiswindowisaQWidget。Ifithasnoparent,itwillappearasafreefloatingwindow。definit(self):super()。init()layoutQVBoxLayout()self。labelQLabel(AnotherWindowdrandint(0,100))layout。addWidget(self。label)self。setLayout(layout)classMainWindow(QMainWindow):definit(self):super()。init()self。window1AnotherWindow()self。window2AnotherWindow()lQVBoxLayout()button1QPushButton(PushforWindow1)button1。clicked。connect(lambdachecked:self。togglewindow(self。window1))l。addWidget(button1)button2QPushButton(PushforWindow2)button2。clicked。connect(lambdachecked:self。togglewindow(self。window2))l。addWidget(button2)wQWidget()w。setLayout(l)self。setCentralWidget(w)deftogglewindow(self,window):ifwindow。isVisible():window。hide()else:window。show()appQApplication(sys。argv)wMainWindow()w。show()app。exec() 另外说明一下上述lambda表达式,以免有小伙伴看不懂,lambda有两种形式,简单点的如这种xlambdaa:a10print(x(5)) 我相信这种很多小伙伴不懂也能猜出来,在表达式中a作为参数传入 语法规则:lambda参数:表达式,执行表达式并返回结果,当前也支持多个参数,这里不再举例。 另一种形式为:defmyfunc(n):returnlambdaa:anmydoublermyfunc(2)print(mydoubler(11)) 输出结果是112。button1。clicked。connect(lambdachecked:self。togglewindow(self。window1))deftogglewindow(self,window):ifwindow。isVisible():window。hide()else:window。show() 这个代码参数checked并没有使用到