PyQt6如何创建多窗口

在之前的教程中,我们已经介绍了如何打开对话框窗口。这些特殊的窗口(默认情况下)抓住用户的焦点,并运行自己的事件循环,有效地阻止了应用程序其余部分的执行。

然而,您经常希望在不中断主窗口的情况下在应用程序中打开第二个窗口——例如,显示一些长时间运行的进程的输出,或者显示图形或其他可视化。或者,您可能希望创建一个应用程序,允许您在各自的窗口中同时处理多个文档。

打开新窗口相对简单,但有几件事要记住,以确保它们正常工作。在本教程中,我们将逐步了解如何创建一个新窗口,以及如何根据需要显示和隐藏外部窗口。

创建一个新窗口

在Qt中,任何没有父组件的小部件都是窗口。这意味着,要显示一个新窗口,您只需要创建一个小部件的新实例。这可以是任何小部件类型(严格来说是QWidget的任何子类),如果您愿意,还可以包括另一个QMainWindow。

QMainWindow实例的数量没有限制。如果你在第二个窗口上需要工具栏或菜单,你将不得不使用QMainWindow来实现这一点。然而,这可能会让用户感到困惑,所以要确保这是必要的。

与主窗口一样,创建一个窗口是不够的,还必须显示它。

from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window")
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        w = AnotherWindow()
        w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

如果运行这个程序,您将看到主窗口。单击按钮可能会显示第二个窗口,但如果您看到它,它只会在几分之一秒内可见。这是为什么呢?

 def show_new_window(self, checked):
        w = AnotherWindow()
        w.show()

在这个方法中,我们创建了窗口(小部件)对象,将其存储在变量w中并显示出来。然而,一旦我们离开这个方法,我们就不再有对w变量的引用(它是一个局部变量),所以它将被清除——窗口也将被销毁。为了解决这个问题,我们需要在某个地方保留一个对窗口的引用,例如在self对象上。

 def show_new_window(self, checked):
        self.w = AnotherWindow()
        self.w.show()

现在,当您单击按钮以显示新窗口时,它将持续存在。

但是,如果再次单击按钮会发生什么?窗口将被重新创建!这个新窗口将取代自我中的旧窗口。self.w变量,因为现在没有对它的引用-前一个窗口将被销毁。

如果您将窗口定义更改为每次创建标签时在标签中显示一个随机数,就可以看到这一点。

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)

__init__代码块只在创建窗口时运行。如果您继续单击按钮,数字将改变,显示窗口正在重新创建。

一种解决方案是在创建窗口之前简单地检查窗口是否已经被创建。下面的示例演示了这一点。

from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = None  # No external window yet.
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        if self.w is None:
            self.w = AnotherWindow()
        self.w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

使用按钮,您可以弹出窗口,并使用窗口控件关闭它。如果再次单击该按钮,将重新显示相同的窗口。

这种方法适用于临时创建的窗口——例如,如果您想弹出一个窗口来显示特定的图形或日志输出。然而,对于许多应用程序,您都有许多标准窗口,您希望能够按需显示/隐藏它们。

在下一部分中,我们将看看如何使用这些类型的窗口。

切换窗口

通常,您需要使用工具栏或菜单上的操作来切换窗口的显示。正如我们前面看到的,如果没有保留对窗口的引用,它将被丢弃(并关闭)。我们可以使用这种行为来关闭一个窗口,用下面的代码替换前面示例中的show_new_window方法

def show_new_window(self, checked):
    if self.w is None:
        self.w = AnotherWindow()
        self.w.show()

    else:
        self.w = None  # Discard reference, close window.

通过设置self.w为None,则对窗口的引用将丢失,并且窗口将关闭。

如果我们将它设置为任何其他值None,窗口仍然会关闭,但是If self.w = None测试将不会在下次单击按钮时通过,因此我们将无法重新创建窗口。

只有当您没有在其他地方保留对该窗口的引用时,这才有效。为了确保窗口无论如何都会关闭,您可能希望显式地对其调用.close()。完整示例如下所示。

from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = None  # No external window yet.
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        if self.w is None:
            self.w = AnotherWindow()
            self.w.show()

        else:
            self.w.close()  # Close window.
            self.w = None  # Discard reference.


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

持续的窗户

到目前为止,我们已经了解了如何按需创建新窗口。但是,有时您有许多标准应用程序窗口。在这种情况下,与其在想要显示窗口时创建窗口,不如在启动时创建窗口,然后在需要时使用.show()显示它们,这样做通常更有意义。

在下面的例子中,我们在主窗口的__init__块中创建了外部窗口,然后我们的show_new_window方法简单地调用self.w.show()来显示它。

from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = AnotherWindow()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.show_new_window)
        self.setCentralWidget(self.button)

    def show_new_window(self, checked):
        self.w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

如果运行此命令,单击该按钮将显示与以前一样的窗口。但是,请注意,窗口只创建一次,在已经可见的窗口上调用.show()没有任何效果。

显示和隐藏持久窗口

一旦创建了持久窗口,就可以显示和隐藏它,而无需重新创建它。一旦隐藏,窗口仍然存在,但将不可见,并接受鼠标/其他输入。但是,您可以继续调用窗口上的方法并更新它的状态——包括更改它的外观。一旦重新显示,任何更改都将可见。

下面我们更新主窗口,创建toggle_window方法,使用.isVisible()检查当前窗口是否可见。如果它不可见,则使用.show()显示,如果它已经可见,则使用.hide()隐藏它。

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.w = AnotherWindow()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.toggle_window)
        self.setCentralWidget(self.button)

    def toggle_window(self, checked):
        if self.w.isVisible():
            self.w.hide()

        else:
            self.w.show()

这个持久窗口和切换显示/隐藏状态的完整工作示例如下所示。

from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget

import sys

from random import randint


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent, it
    will appear as a free-floating window as we want.
    """
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0,100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.w = AnotherWindow()
        self.button = QPushButton("Push for Window")
        self.button.clicked.connect(self.toggle_window)
        self.setCentralWidget(self.button)

    def toggle_window(self, checked):
        if self.w.isVisible():
            self.w.hide()

        else:
            self.w.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

再次注意,窗口只创建了一次——每次窗口重新显示时,窗口的__init__块不会重新运行(因此标签中的数字不会改变)。

多重窗口

您可以使用相同的原则来创建多个窗口——只要保持对窗口的引用,事情就会按预期工作。最简单的方法是创建一个单独的方法来切换每个窗口的显示。

import sys
from random import randint

from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent,
    it will appear as a free-floating window.
    """

    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0, 100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.window1 = AnotherWindow()
        self.window2 = AnotherWindow()

        l = QVBoxLayout()
        button1 = QPushButton("Push for Window 1")
        button1.clicked.connect(self.toggle_window1)
        l.addWidget(button1)

        button2 = QPushButton("Push for Window 2")
        button2.clicked.connect(self.toggle_window2)
        l.addWidget(button2)

        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

    def toggle_window1(self, checked):
        if self.window1.isVisible():
            self.window1.hide()

        else:
            self.window1.show()

    def toggle_window2(self, checked):
        if self.window2.isVisible():
            self.window2.hide()

        else:
            self.window2.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

但是,您也可以创建一个通用方法来处理所有窗口的切换。下面的示例演示了如何使用lambda函数拦截来自每个按钮的信号并通过适当的窗口。

import sys
from random import randint

from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)


class AnotherWindow(QWidget):
    """
    This "window" is a QWidget. If it has no parent,
    it will appear as a free-floating window.
    """

    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.label = QLabel("Another Window % d" % randint(0, 100))
        layout.addWidget(self.label)
        self.setLayout(layout)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.window1 = AnotherWindow()
        self.window2 = AnotherWindow()

        l = QVBoxLayout()
        button1 = QPushButton("Push for Window 1")
        button1.clicked.connect(
            lambda checked: self.toggle_window(self.window1)
        )
        l.addWidget(button1)

        button2 = QPushButton("Push for Window 2")
        button2.clicked.connect(
            lambda checked: self.toggle_window(self.window2)
        )
        l.addWidget(button2)

        w = QWidget()
        w.setLayout(l)
        self.setCentralWidget(w)

    def toggle_window(self, window):
        if window.isVisible():
            window.hide()

        else:
            window.show()


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()

另外说明一下上述lambda表达式,以免有小伙伴看不懂,lambda有两种形式,简单点的如这种

x = lambda a : a + 10
print(x(5))

我相信这种很多小伙伴不懂也能猜出来,在表达式中a作为参数传入

语法规则:lambda 参数 : 表达式,执行表达式并返回结果,当前也支持多个参数,这里不再举例。

另一种形式为:

def myfunc(n):
  return lambda a : a * n

mydoubler = myfunc(2)

print(mydoubler(11))

输出结果是11*2。

button1.clicked.connect(
        lambda checked: self.toggle_window(self.window1)
    )
def toggle_window(self, window):
    if window.isVisible():
        window.hide()

    else:
        window.show()

这个代码参数checked并没有使用到

展开阅读全文

页面更新:2024-04-30

标签:这一点   表达式   示例   变量   单击   新窗口   应用程序   按钮   窗口   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top