PyQt 布局:创建具有专业外观的 GUI 应用程序

举报
Yuchuan 发表于 2021/12/06 16:33:51 2021/12/06
【摘要】 创建高质量的GUI 应用程序需要将所有图形组件以连贯和优美的排列方式布置。在 PyQt 中,一种有效的方法是使用 PyQt 的布局管理器,它提供了一种用户友好且高效的方式来完成这项任务。

目录

PyQt 的布局管理器提供了一种在 GUI 上排列图形组件或小部件的用户友好且高效的方式。正确布置小部件将使您的GUI 应用程序看起来精美和专业。学习高效地这样做是您开始使用 Python 和 PyQt 进行 GUI 应用程序开发的一项基本技能。

在本教程中,您将学习:

  • 使用 PyQt 的布局管理器有什么好处
  • 如何使用 PyQt 的布局管理器以编程方式在 GUI 上布置小部件
  • 如何为您的 GUI 应用程序选择正确的布局管理器
  • 如何铺陈在小部件主窗口为基础的基于对话框的应用程序

凭借这些知识和技能,您将能够使用 Python 和 PyQt 创建具有专业外观的 GUI 应用程序。

为了更好地理解如何使用布局管理器,一些关于如何创建PyQt GUI 应用程序以及如何使用PyQt 小部件的先前知识会有所帮助。

在 GUI 上布置图形元素

当您创建图形用户界面 (GUI)应用程序时,一个常见的问题是如何让您的图形组件(按钮菜单、工具栏标签等)在您的窗体和窗口上连贯地布置。此过程称为GUI 布局,它是创建 GUI 应用程序的重要步骤。

过去,如果您想在窗口上布置图形组件或小部件,则可以采用以下方法之一:

  1. 决定并手动设置窗口上每个小部件的静态大小和位置。
  2. 动态计算和设置每个小部件的大小和位置。

第一种方法相当直接,但它至少有以下缺点:

  • 您的窗口将无法调整大小,这可能会导致在不同屏幕分辨率上显示它们时出现问题。
  • 您的标签可能无法正确支持本地化,因为给定文本的长度因语言而异。
  • 您的小部件在不同平台上的显示方式不同,这使得编写看起来不错的多平台应用程序变得困难。

第二种方法更灵活。但是,它也有缺点:

  • 您必须进行大量手动计算才能确定每个小部件的正确大小位置
  • 您必须进行一些额外的计算才能正确响应窗口大小调整
  • 每次修改窗口布局时,都必须重做所有计算。

尽管您仍然可以使用这两种方法中的任何一种来布置您的 GUI,但大多数时候您会想要使用大多数现代GUI 框架或工具包实现的第三种更方便的方法:布局管理器。

注意:在一些 GUI 框架中,例如Tkinter,布局管理器也被称为几何管理器

布局管理器会根据您的特定需求在 GUI 上自动排列小部件。它们避免了第一种方法的兼容性缺陷以及第二种方法的烦人和复杂的计算。

在以下部分中,您将了解 PyQt 的内置布局管理器以及如何使用它们来有效地布置 GUI 应用程序的图形组件。

掌握 PyQt 布局库

在 PyQt 中,小部件是用作 GUI 应用程序构建块的图形组件。当你在一个窗口上放置一堆小部件来创建一个 GUI 时,你需要给它们一些顺序。您需要设置窗口小部件的大小和位置,还需要定义它们在用户调整底层窗口大小时的行为。

注意:不幸的是,PyQt5 的官方文档有一些不完整的部分。要解决此问题,您可以查看PyQt4 文档Qt for Python 文档或原始Qt 文档

在本教程中,您会发现大多数链接都会将您带到原始 Qt 文档,在大多数情况下,这是更好的信息来源。

要在 PyQt 中的窗口或表单上排列小部件,您可以使用以下技术:

  • 在您的小部件上使用.resize().move()以提供绝对大小和位置。
  • 重新实现.resizeEvent()并动态计算小部件的大小和位置。
  • 使用布局管理器,让他们为您完成所有的计算和繁重的工作。

这些技术通常对应于您在上一节中看到的用于布置 GUI 的三种不同方法。

同样,动态计算大小和位置可能是一个很好的方法,但大多数时候使用布局管理器会更好。在 PyQt 中,布局管理器是提供所需功能以自动管理布局中小部件的大小、位置和调整大小行为的类。

使用布局管理器,您可以在任何或容器、小部件中自动排列小部件。使用布局管理器将确保您充分利用 GUI 上的可用空间,并且当用户调整窗口大小时,您的应用程序仍然可用。

布局管理器用作小部件和其他布局的容器。要将小部件添加到布局管理器,您可以调用.addWidget()手头的布局。要将布局添加到另一个布局,请调用.addLayout()手头的布局。您将在嵌套布局以构建复杂 GUI部分中更深入地研究嵌套布局。

将所有必需的小部件添加到布局管理器后,您可以使用.setLayout(). 您可以在 的任何子类上设置布局管理器QWidget,包括窗口或表单。

注意: QMainWindow是一个 PyQt 类,可用于创建主要寡妇风格的应用程序。这个类有它自己的内置布局管理器。因此,如果您使用的是QMainWindow,那么您通常不需要在主窗口对象上设置布局管理器。

布局中的所有小部件都会自动设置为安装布局的小部件的子代,而不是布局本身的子代。那是因为小部件只能将其他小部件而不是布局作为其父级。

PyQt 的布局管理器提供了一些很酷的功能,让您在创建漂亮的 GUI 应用程序时变得更加轻松:

  • 无需任何计算即可处理小部件的大小位置
  • 当用户调整底层窗口大小时处理小部件的调整大小重新定位
  • 调整标签大小以更好地支持国际化
  • 多平台应用程序提供本机窗口布局

从长远来看,使用布局管理器还可以显着提高您的生产力并提高代码的可维护性。

PyQt 提供了四个通用布局管理器类:

  1. QHBoxLayout 在水平框中排列小部件。
  2. QVBoxLayout 在垂直框中排列小部件。
  3. QGridLayout 在网格中排列小部件。
  4. QFormLayout 在两列中排列小部件。

在接下来的几节中,您将学习如何使用这些通用布局管理器的基础知识。

使用通用布局管理器

使用 PyQt 创建 GUI 应用程序时,您通常会使用您在上一节末尾看到的四种通用布局中的一种或多种,​​以在窗口和窗体上布置小部件。

在接下来的几节中,您将在一些示例的帮助下学习如何创建和使用四个通用布局管理器。

建筑水平布局: QHBoxLayout

框布局管理器从父布局或小部件中获取空间,将其划分为多个框或单元格,并使布局中的每个小部件填充一个框。

QHBoxLayout是 PyQt 中两种可用的框布局之一。此布局管理器允许您水平排列小部件,一个挨着另一个。小部件从左到右添加到布局中。这意味着您首先在代码中添加的小部件将是布局中最左侧的小部件。

要将小部件添加到QHBoxLayout对象,请调用.addWidget(widget, stretch, alignment)布局对象。此方法采用一个必需参数和两个可选参数:

  1. widget 是一个必需参数,用于保存要添加到布局中的特定小部件。

  2. stretch是一个可选参数,它包含一个整数,表示要应用于的拉伸因子widget。具有更高拉伸系数的小部件在调整窗口大小时增长得更多。它默认为0,这意味着小部件没有分配拉伸因子。

  3. alignment是一个可选参数,用于保存水平和垂直标志。您可以组合这些标志以在其包含的单元格内生成所需的小部件对齐方式。它默认为0,这意味着小部件将填充整个单元格。

这是一个小应用程序,展示了如何使用QHBoxLayout. 在此示例中,您将使用QPushButton对象根据将小部件添加到代码中的顺序更好地可视化每个小部件在布局中的放置位置:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QHBoxLayout,
 6    QPushButton,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QHBoxLayout Example")
14        # Create a QHBoxLayout instance
15        layout = QHBoxLayout()
16        # Add widgets to the layout
17        layout.addWidget(QPushButton("Left-Most"))
18        layout.addWidget(QPushButton("Center"), 1)
19        layout.addWidget(QPushButton("Right-Most"), 2)
20        # Set the layout on the application's window
21        self.setLayout(layout)
22        print(self.children())
23
24if __name__ == "__main__":
25    app = QApplication(sys.argv)
26    window = Window()
27    window.show()
28    sys.exit(app.exec_())

在第 15 行,您创建了一个QHBoxLayout名为的对象layout。在第 17 到 19 行,您将三个按钮添加到layoutusing .addWidget()。请注意,您分别将1和传递2CenterRight-Most按钮中的stretch参数。在第 21 行,您使用.layout.setLayout()

注意:如果您不熟悉使用 PyQt 进行 GUI 编程,那么您可以查看Python 和 PyQt:构建 GUI 桌面计算器,以更好地了解如何使用 PyQt 创建 GUI 应用程序。

如果您运行此应用程序,您将在屏幕上看到以下窗口:

QHBoxLayout 示例

该窗口包含以水平方式排列的三个按钮。请注意,最左侧按钮对应于您在代码中添加的第一个按钮。因此,按钮的显示顺序(从左到右)与您在代码中添加的顺序(从上到下)相同。

中心最右边的按钮具有不同的拉伸因素的影响,所以他们按比例这些因素扩大,当你调整窗口的大小。

此外,其中的所有按钮layout和布局本身都设置为Window. 这是由内部调用.setParent()每个小部件的布局对象自动完成的。print()对第 22 行的调用Window在您的终端上打印了 的子项列表作为此行为的证据。

建筑垂直布局: QVBoxLayout

QVBoxLayout垂直排列小部件,一个在另一个下方。您可以使用此类来创建垂直布局并从上到下排列您的小部件。由于QVBoxLayout是另一种盒子布局,它的.addWidget()方法与QHBoxLayout.

这是一个 PyQt 应用程序,它展示了如何创建和使用QVBoxLayout对象来在您的 GUI 中创建垂直排列的小部件:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QPushButton,
 6    QVBoxLayout,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QVBoxLayout Example")
14        self.resize(270, 110)
15        # Create a QVBoxLayout instance
16        layout = QVBoxLayout()
17        # Add widgets to the layout
18        layout.addWidget(QPushButton("Top"))
19        layout.addWidget(QPushButton("Center"))
20        layout.addWidget(QPushButton("Bottom"))
21        # Set the layout on the application's window
22        self.setLayout(layout)
23
24if __name__ == "__main__":
25    app = QApplication(sys.argv)
26    window = Window()
27    window.show()
28    sys.exit(app.exec_())

在第 16 行,您创建了一个QVBoxLayout. 在第 18 到 20 行,您将三个按钮添加到layout。最后,您设置layout为窗口的顶级布局。

如果您运行此应用程序,您将看到以下窗口:

QVBoxLayout 示例

您的窗口显示垂直排列的三个按钮,一个在另一个下方。这些按钮的显示顺序(从上到下)与您在代码中添加它们的顺序(从上到下)相同。

在网格中排列小部件: QGridLayout

您可以使用在行和列QGridLayout网格中排列小部件。每个小部件在网格中都有一个相对位置。要定义小部件的位置或网格中的单元格,您可以使用表单的一对坐标(row, column)。这些坐标应该是从零开始的整数

QGridLayout获取其父级上的可用空间,将其划分为行和列,并将每个小部件放入其自己的单元格或框中。QGridLayout根据小部件的数量及其坐标自动计算出最终布局将有多少行和列。如果您不向给定单元格添加小部件,则QGridLayout将该单元格留空。

要将小部件添加到网格布局,请调用.addWidget()布局。该方法有两种不同的重载实现:

  1. addWidget(widget, row, column, alignment)添加widget到 ( rowcolumn)处的单元格。
  2. addWidget(widget, fromRow, fromColumn, rowSpan, columnSpan, alignment)添加widget到单元格,跨越多行、多列或两者兼而有之。

第一个实现采用以下参数:

  1. widget 是一个必需参数,用于保存您需要添加到布局中的特定小部件。
  2. row 是一个必需的参数,它保存一个整数,表示网格中一行的坐标。
  3. column 是一个必需的参数,它包含一个表示网格中列坐标的整数。
  4. alignment是一个可选参数,用于保存小部件在其包含单元格内的对齐方式。它默认为0,这意味着小部件将填充整个单元格。

以下是如何使用QGridLayout创建小部件网格的示例:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QGridLayout,
 6    QPushButton,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QGridLayout Example")
14        # Create a QGridLayout instance
15        layout = QGridLayout()
16        # Add widgets to the layout
17        layout.addWidget(QPushButton("Button at (0, 0)"), 0, 0)
18        layout.addWidget(QPushButton("Button at (0, 1)"), 0, 1)
19        layout.addWidget(QPushButton("Button at (0, 2)"), 0, 2)
20        layout.addWidget(QPushButton("Button at (1, 0)"), 1, 0)
21        layout.addWidget(QPushButton("Button at (1, 1)"), 1, 1)
22        layout.addWidget(QPushButton("Button at (1, 2)"), 1, 2)
23        layout.addWidget(QPushButton("Button at (2, 0)"), 2, 0)
24        layout.addWidget(QPushButton("Button at (2, 1)"), 2, 1)
25        layout.addWidget(QPushButton("Button at (2, 2)"), 2, 2)
26        # Set the layout on the application's window
27        self.setLayout(layout)
28
29if __name__ == "__main__":
30    app = QApplication(sys.argv)
31    window = Window()
32    window.show()
33    sys.exit(app.exec_())

在第 15 行,您创建了QGridLayout对象。然后,在第 17 到 25 行,您使用 将小部件添加到布局中.addWidget()。要查看网格布局如何在没有分配小部件的情况下管理单元格,请注释掉其中的一行或多行并再次运行应用程序。

如果你从命令行运行这段代码,你会得到一个这样的窗口:

QGridLayout 示例

QGridLayout对象中的每个小部件占用由您在 中提供的坐标对定义的单元格.addWidget()。每个按钮上的文本反映了这些坐标。坐标从零开始,因此第一个单元格位于(0, 0)

在 的第二个实现中.addWidget(),参数widgetalignment保持不变,您还有四个额外的参数,允许您将小部件放置在多行或多列中:

  1. fromRow 采用一个整数表示小部件将在其中开始的行。
  2. fromColumn 采用一个整数表示小部件将在其中启动的列。
  3. rowSpan 采用一个整数表示小部件将在网格中占据的行数。
  4. columnSpan 采用一个整数表示小部件将在网格中占据的列数。

这是一个应用程序,显示了这种变体的.addWidget()工作原理:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QGridLayout,
 6    QPushButton,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QGridLayout Example")
14        # Create a QGridLayout instance
15        layout = QGridLayout()
16        # Add widgets to the layout
17        layout.addWidget(QPushButton("Button at (0, 0)"), 0, 0)
18        layout.addWidget(QPushButton("Button at (0, 1)"), 0, 1)
19        layout.addWidget(QPushButton("Button Spans two Cols"), 1, 0, 1, 2)
20        # Set the layout on the application's window
21        self.setLayout(layout)
22
23if __name__ == "__main__":
24    app = QApplication(sys.argv)
25    window = Window()
26    window.show()
27    sys.exit(app.exec_())

在第 19 行,您使用 的第二个实现.addWidget()添加一个占据网格中两列的按钮。该按钮从第二行 ( fromRow=1) 和第一列 ( fromColumn=0) 开始。最后,按钮占据一行 ( rowSpan=1) 和两列 ( columnSpan=2)。

注意:由于PyQt 是Qt的 Python 绑定Qt是一组C++库,因此有时在调用 PyQt 方法时不能使用关键字参数。上一段中使用的关键字参数的唯一目的是显示分配给每个参数的值。

如果您运行此应用程序,您将在屏幕上看到以下窗口:

QGridLayout 跨度示例

在这种布局中,您可以使一个小部件占据多个单元格,就像您使用Button Spas two Cols按钮所做的那样。

快速创建表单: QFormLayout

如果您经常创建表单来执行诸如将数据输入数据库之类的操作,那么QFormLayout适合您。此类以两列布局排列小部件。第一列通常显示一个标签描述预期输入,和第二列通常包含输入窗口小部件,例如QLineEditQComboBox,或QSpinBox允许用户输入或编辑的数据。

要将小部件添加到表单布局,请使用.addRow(). 此方法有多种变体,但大多数情况下,您将从以下两种中进行选择:

  1. .addRow(label, field)在表单布局的底部添加一个新行。该行应包含一个QLabel对象 ( label) 和一个输入小部件 ( field)。

  2. .addRow(labelText, field)自动创建并添加一个新QLabel对象labelText作为其文本。field持有一个输入小部件。

这是一个使用QFormLayout对象来排列小部件的示例应用程序:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QFormLayout,
 6    QLabel,
 7    QLineEdit,
 8    QWidget,
 9)
10
11class Window(QWidget):
12    def __init__(self):
13        super().__init__()
14        self.setWindowTitle("QFormLayout Example")
15        self.resize(270, 110)
16        # Create a QFormLayout instance
17        layout = QFormLayout()
18        # Add widgets to the layout
19        layout.addRow("Name:", QLineEdit())
20        layout.addRow("Job:", QLineEdit())
21        emailLabel = QLabel("Email:")
22        layout.addRow(emailLabel, QLineEdit())
23        # Set the layout on the application's window
24        self.setLayout(layout)
25
26if __name__ == "__main__":
27    app = QApplication(sys.argv)
28    window = Window()
29    window.show()
30    sys.exit(app.exec_())

在第 17 行,您创建了一个QFormLayout对象。然后,在第 19 到 22 行,向布局添加一些行。请注意,在第 19 行和第 20 行,您使用了该方法的第二个变体,而在第 22 行,您使用了第一个变体,将一个QLabel对象作为第一个参数传递给.addRow()

如果您运行此代码,您将在屏幕上看到以下窗口:

QFormLayout 示例

使用QFormLayout,您可以将小部件组织成两列排列。第一列包含要求用户提供一些信息的标签。第二列显示允许用户输入或编辑该信息的小部件。

嵌套布局以构建复杂的 GUI

您可以使用嵌套布局来创建使用通用 PyQt 布局管理器之一难以创建的复杂 GUI。要做到这一点,你需要调用.addLayout()上的外部布局。这样,内部布局就成为外部布局的子项。

假设您需要创建一个对话框,在表单布局中显示标签和行编辑,并且您希望在这些小部件下方以垂直布局放置多个复选框。这是您的对话框应该是什么样子的模型:

嵌套布局图

蓝色矩形代表您的外部布局。绿色矩形是用于保存标签和行编辑的表单布局。红色矩形是用于放置选项复选框的垂直布局。绿色布局和红色布局都嵌套在蓝色布局中,这是一种垂直布局。

以下是如何使用 PyQt 构建此布局的示例:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QCheckBox,
 6    QFormLayout,
 7    QLineEdit,
 8    QVBoxLayout,
 9    QWidget,
10)
11
12class Window(QWidget):
13    def __init__(self):
14        super().__init__()
15        self.setWindowTitle("Nested Layouts Example")
16        # Create an outer layout
17        outerLayout = QVBoxLayout()
18        # Create a form layout for the label and line edit
19        topLayout = QFormLayout()
20        # Add a label and a line edit to the form layout
21        topLayout.addRow("Some Text:", QLineEdit())
22        # Create a layout for the checkboxes
23        optionsLayout = QVBoxLayout()
24        # Add some checkboxes to the layout
25        optionsLayout.addWidget(QCheckBox("Option one"))
26        optionsLayout.addWidget(QCheckBox("Option two"))
27        optionsLayout.addWidget(QCheckBox("Option three"))
28        # Nest the inner layouts into the outer layout
29        outerLayout.addLayout(topLayout)
30        outerLayout.addLayout(optionsLayout)
31        # Set the window's main layout
32        self.setLayout(outerLayout)
33
34if __name__ == "__main__":
35    app = QApplication(sys.argv)
36    window = Window()
37    window.show()
38    sys.exit(app.exec_())

这是您在此代码中所做的事情:

  • 在第 17 行,您创建外部或顶级布局,您将使用它作为父布局和窗口的主要布局。在这种情况下,您使用QVBoxLayout是因为您希望小部件在表单上垂直排列。在您的模型中,这是蓝色布局。
  • 在第 19 行,您创建了一个表单布局来保存标签和行编辑。
  • 在第 21 行,您将所需的小部件添加到布局中。这相当于您的绿色布局。
  • 在第 23 行,您创建了一个垂直布局来放置复选框。
  • 在第 25 到 27 行,添加所需的复选框。这是你的红色布局。
  • 在第 29 和 30 行,您嵌套topLayoutoptionsLayoutouterLayout.

就是这样!如果您运行该应用程序,您将看到如下所示的窗口:

嵌套布局示例

在此应用程序中,您将两个不同的布局嵌套在外部布局下以创建窗口的总体布局。在窗口顶部,您使用水平布局放置标签和行编辑。然后使用垂直布局在其下方放置一些复选框。

使用多页布局和小部件

到目前为止,您已经了解了如何使用传统的或通用的布局管理器来排列应用程序窗口中的小部件。这些布局管理器将在单页布局上排列小部件。换句话说,您的 GUI 将始终向用户显示相同的小部件集。

有时,您需要创建一个布局来显示一组不同的小部件,以响应 GUI 上的某些用户操作。例如,如果您正在为给定的应用程序创建首选项对话框,那么您可能希望向用户显示基于选项卡或多页的布局,其中每个选项卡或页面都包含一组不同的密切相关的选项。每次用户单击选项卡或页面时,应用程序都会显示一组不同的小部件。

PyQt 提供了一个名为的内置布局QStackedLayout和一些方便的小部件,例如QTabWidget,将允许您创建这种多页布局。接下来的几节将带您了解其中的一些工具。

创建一堆小部件

QStackedLayout提供了一个布局管理器,允许您将小部件排列在一个堆栈上,一个在另一个之上。在这种布局中,在给定时间只有一个小部件可见。

要使用小部件填充堆叠布局,您需要调用.addWidget()布局对象。这会将每个小部件添加到布局的内部小部件列表的末尾。您还可以使用.insertWidget(index).removeWidget(widget)分别在小部件列表中的给定位置插入或删除小部件。

小部件列表中的每个小部件都显示为一个独立的页面。如果要在页面上显示多个小部件,QWidget则为每个页面使用一个对象,并为页面小部件设置适当的小部件布局。如果需要获取布局中的小部件(页面)总数,则可以调用.count().

使用QStackedLayout对象时要记住的一个重要点是您需要明确提供一种机制来在页面之间切换。否则,您的布局将始终向用户显示相同的页面。要在页面之间切换,您需要调用.setCurrentIndex()布局对象。

下面的示例展示了如何使用带有组合框的堆叠布局在页面之间切换:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QComboBox,
 6    QFormLayout,
 7    QLineEdit,
 8    QStackedLayout,
 9    QVBoxLayout,
10    QWidget,
11)
12
13class Window(QWidget):
14    def __init__(self):
15        super().__init__()
16        self.setWindowTitle("QStackedLayout Example")
17        # Create a top-level layout
18        layout = QVBoxLayout()
19        self.setLayout(layout)
20        # Create and connect the combo box to switch between pages
21        self.pageCombo = QComboBox()
22        self.pageCombo.addItems(["Page 1", "Page 2"])
23        self.pageCombo.activated.connect(self.switchPage)
24        # Create the stacked layout
25        self.stackedLayout = QStackedLayout()
26        # Create the first page
27        self.page1 = QWidget()
28        self.page1Layout = QFormLayout()
29        self.page1Layout.addRow("Name:", QLineEdit())
30        self.page1Layout.addRow("Address:", QLineEdit())
31        self.page1.setLayout(self.page1Layout)
32        self.stackedLayout.addWidget(self.page1)
33        # Create the second page
34        self.page2 = QWidget()
35        self.page2Layout = QFormLayout()
36        self.page2Layout.addRow("Job:", QLineEdit())
37        self.page2Layout.addRow("Department:", QLineEdit())
38        self.page2.setLayout(self.page2Layout)
39        self.stackedLayout.addWidget(self.page2)
40        # Add the combo box and the stacked layout to the top-level layout
41        layout.addWidget(self.pageCombo)
42        layout.addLayout(self.stackedLayout)
43
44    def switchPage(self):
45        self.stackedLayout.setCurrentIndex(self.pageCombo.currentIndex())
46
47if __name__ == "__main__":
48    app = QApplication(sys.argv)
49    window = Window()
50    window.show()
51    sys.exit(app.exec_())

在第 21 到 23 行,您创建了一个QComboBox对象,允许您在布局中的页面之间切换。然后将两个选项添加到列表中的组合框并将其连接到.switchPage(),用于处理页面切换。

在内部.switchPage(),您调用.setCurrentIndex()布局对象,将组合框的当前索引作为参数传递。这样,当用户更改组合框中的选项时,堆叠布局上的页面也会相应更改。

在第 25 行,您创建了QStackedLayout对象。在第 27 到 32 行,您将第一页添加到布局,并在第 34 到 39 行,您添加第二页。每个页面都由一个QWidget对象表示,该对象在一个方便的布局中包含多个小部件。

使一切正常工作的最后一步是将组合框和布局添加到应用程序的主布局中。

以下是您的应用程序现在的行为方式:

QStackedLayout 示例

在这种情况下,您的应用程序布局中有两个页面。每个页面都由一个QWidget对象表示。当您在窗口顶部的组合框中选择新页面时,布局会更改以显示所选页面。

注意: PyQt 提供了一个名为 的方便类QStackedWidget,它构建在QStackedLayout. 您还可以使用此类来创建多页布局。

此类提供了一堆小部件,其中一次只能看到一个小部件。就像堆叠布局一样,QStackedWidget不提供在页面之间切换的内在机制。

除了堆叠布局和堆叠小部件,您还可以使用它QTabWidget来创建多页用户界面。您将在下一节中了解如何操作。

使用 PyQt 的 Tab 小部件

在 PyQt 中创建多页排列的另一种流行方法是使用名为QTabWidget. 这个类提供了一个标签栏和一个页面区域。您可以使用标签栏在页面之间切换,使用页面区域来显示与所选标签关联的页面。

标签栏默认位于页面区域的顶部。但是,您可以使用.setTabPosition()和 四个可能的选项卡位置之一更改此行为:

标签位置 标签栏位置
QTabWidget.North 页面顶部
QTabWidget.South 页面底部
QTabWidget.West 页面左侧
QTabWidget.East 页面右侧

要将选项卡添加到选项卡小部件,请使用.addTab(). 此方法有两种变体或重载实现:

  1. .addTab(page, label)
  2. .addTab(page, icon, label)

在这两种情况下,该方法都会添加一个新选项卡,label作为选项卡的标题。page需要是一个小部件,代表与手头标签关联的页面。

在方法的第二个变体中,icon需要是一个QIcon对象。如果您将图标传递给.addTab(),则该图标将显示在选项卡标题的左侧。

创建选项卡小部件时的常见做法是QWidget为每个页面使用一个对象。这样,您就可以使用包含所需小部件的布局向页面添加额外的小部件。

大多数情况下,您将使用选项卡小部件为 GUI 应用程序创建对话框。这种布局允许您在相对较小的空间中向用户呈现多个选项。您还可以利用选项卡系统根据某些分类标准来组织您的选项。

这是一个示例应用程序,展示了如何创建和使用QTabWidget对象的基础知识:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QCheckBox,
 6    QTabWidget,
 7    QVBoxLayout,
 8    QWidget,
 9)
10
11class Window(QWidget):
12    def __init__(self):
13        super().__init__()
14        self.setWindowTitle("QTabWidget Example")
15        self.resize(270, 110)
16        # Create a top-level layout
17        layout = QVBoxLayout()
18        self.setLayout(layout)
19        # Create the tab widget with two tabs
20        tabs = QTabWidget()
21        tabs.addTab(self.generalTabUI(), "General")
22        tabs.addTab(self.networkTabUI(), "Network")
23        layout.addWidget(tabs)
24
25    def generalTabUI(self):
26        """Create the General page UI."""
27        generalTab = QWidget()
28        layout = QVBoxLayout()
29        layout.addWidget(QCheckBox("General Option 1"))
30        layout.addWidget(QCheckBox("General Option 2"))
31        generalTab.setLayout(layout)
32        return generalTab
33
34    def networkTabUI(self):
35        """Create the Network page UI."""
36        networkTab = QWidget()
37        layout = QVBoxLayout()
38        layout.addWidget(QCheckBox("Network Option 1"))
39        layout.addWidget(QCheckBox("Network Option 2"))
40        networkTab.setLayout(layout)
41        return networkTab
42
43if __name__ == "__main__":
44    app = QApplication(sys.argv)
45    window = Window()
46    window.show()
47    sys.exit(app.exec_())

在此示例中,您使用选项卡小部件向用户呈现一个简洁的对话框,其中显示与假设首选项菜单的常规网络部分相关的选项。在第 20 行,您创建了QTabWidget对象。然后使用 将两个选项卡添加到选项卡小部件.addTab()

.generalTabUI()和 中networkTabUI(),您为每个选项卡创建特定的 GUI。为此,您可以使用一个QWidget对象、一个QVBoxLayout对象和一些复选框来保存选项。

如果您现在运行该应用程序,您将在屏幕上看到以下对话框:

QTabWidget 示例

就是这样!您有一个功能齐全的基于选项卡的 GUI。请注意,要在页面之间切换,您只需单击相应的选项卡。

布置应用程序的主窗口

如果您使用 PyQt 来创建您的 GUI 应用程序,那么大部分时间您将用于QMainWindow在它之上创建一个 GUI。此类允许您创建主窗口样式的应用程序QMainWindow带有自己的预定义布局。此布局将允许您将以下图形组件添加到主窗口:

对于大多数应用程序,所有这些图形组件都是可选的,除了中央小部件,它是使您的应用程序工作所必需的。

注意:如果您使用 来创建 GUI 应用程序QMainWindow,那么您必须有一个中央小部件,即使它只是一个占位符。

某些应用程序使用独特且功能齐全的小部件作为其中心小部件。例如,如果您正在编写一个文本编辑器,那么您可能会使用一个QTextEdit对象作为您编辑器的中央小部件。

其他类型的 GUI 应用程序可能需要更精细的中央小部件。在这种情况下,您可以使用一个QWidget对象作为中央小部件,然后创建一个布局,其中包含应用程序 GUI 所需的特定小部件排列。最后一步是将该布局设置为中央小部件的布局。

大多数时候,QMainWindow提供的布局足以创建任何类型的 GUI 应用程序。这种布局将有效地管理窗口小部件的行为,因此您不必担心。

布置应用程序的对话框

GUI 应用程序通常使用一个主窗口和一个或多个对话框构建。对话框是允许您与用户交流的小窗口。PyQt 提供QDialog处理对话框的创建。

与 不同QMainWindowQDialog没有预定义或默认的顶级布局。那是因为对话框可以非常多样化,并且包括范围广泛的小部件排列和组合。

将所有小部件放置在对话框的 GUI 上后,您需要在该对话框上设置顶级布局。为此,您必须.setLayout()像使用任何其他小部件一样调用对话框对象。

这是一个对话框样式的应用程序,展示了如何为对象设置顶级布局QDialog

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QDialog,
 6    QDialogButtonBox,
 7    QFormLayout,
 8    QLineEdit,
 9    QVBoxLayout,
10)
11
12class Dialog(QDialog):
13    def __init__(self):
14        super().__init__()
15        self.setWindowTitle("QDialog's Top-Level Layout Example")
16        dlgLayout = QVBoxLayout()
17        # Create a form layout and add widgets
18        formLayout = QFormLayout()
19        formLayout.addRow("Name:", QLineEdit())
20        formLayout.addRow("Job:", QLineEdit())
21        formLayout.addRow("Email:", QLineEdit())
22        # Add a button box
23        btnBox = QDialogButtonBox()
24        btnBox.setStandardButtons(
25            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
26        )
27        # Set the layout on the dialog
28        dlgLayout.addLayout(formLayout)
29        dlgLayout.addWidget(btnBox)
30        self.setLayout(dlgLayout)
31
32if __name__ == "__main__":
33    app = QApplication(sys.argv)
34    dlg = Dialog()
35    dlg.show()
36    sys.exit(app.exec_())

在这种情况下,应用程序的窗口继承自QDialog,因此您有一个对话框样式的应用程序。在第 16 行,您创建了将用作对话框顶级布局的布局。在第 18 到 21 行,您创建了一个表单布局来在表单中排列一些小部件。

在第 24 行,您添加了一个QDialogButtonBox对象。您将经常使用QDialogButtonBox来处理对话框上的按钮。在本例中,您使用两个按钮,一个确定按钮和一个取消按钮。这些按钮没有任何功能——它们只是为了使对话框更逼真。

一旦您准备好所有小部件和布局,您就可以将它们添加到顶级布局中。这就是您在第 28 行和第 29 行所做的。最后一步,在第 30 行,是使用 将顶层布局设置为对话框的布局.setLayout()

如果您运行此应用程序,您将在屏幕上看到以下窗口:

QDialog 顶层布局示例

为所有对话框设置顶级布局是最佳实践。这确保了当用户调整底层窗口大小时,对话框的 GUI 将行为一致。否则,您的对话框在用户眼中可能会显得杂乱无章。

在 PyQt 布局中管理空间

当谈到使用 PyQt 的布局管理器在窗口或窗体上排列小部件时,管理空间——空白空间、小部件之间的空间等等——是一个常见的问题。能够管理这个空间是一项重要的技能。

在内部,布局使用以下一些小部件属性管理窗口上的可用空间:

布局使用这些属性自动定位和调整小部件的大小,根据可用空间为每个小部件分配给定的空间量。这确保了小部件的排列一致并保持可用。

在接下来的三个部分中,您将了解 PyQt 中不同类型的布局如何管理空间。

管理框布局中的空间

盒子布局在小部件之间分配可用空间方面做得很好。但是,有时它们的默认行为还不够,您需要手动处理可用空间。为了帮助您解决这种情况,PyQt 提供了QSpacerItem. 此类允许您向框布局添加空格(或空框)。

通常,您不需要QSpacerItem直接使用。相反,您可以在框布局对象上调用以下一些方法:

  • .addSpacing(i)i向布局添加固定大小的不可拉伸空间(或空框)。i必须是一个整数,以像素为单位表示空间的大小。

  • .addStretch(i)为框布局添加一个最小尺寸0和拉伸因子的可拉伸空间ii必须是整数。

  • .insertSpacing(index, size)在位置 插入一个不可拉伸的空间index,大小为size。如果index是负数,则在框布局的末尾添加空格。

  • insertStretch(index, stretch)在位置 插入一个可拉伸的空间,index最小尺寸为0,拉伸系数为stretch。如果index是负数,则在框布局的末尾添加空格。

当用户调整底层窗口的大小时,可拉伸的垫片将膨胀或收缩以填充空白空间。无论底层窗口的大小如何变化,不可拉伸的间隔物都将保持相同的大小。

回到如何使用垂直布局的示例并再次运行该应用程序。如果您拉下窗口的边框,您会注意到拉得越远,按钮之间会出现更多空间:

不可拉伸垫片示例

发生这种情况是因为布局通过自动扩展其框来处理新的可用空间。您可以通过QSpacerItem在布局末尾添加一个可拉伸对象来更改此行为。

在您的示例代码中,Window按如下方式更新初始值设定项:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QVBoxLayout Example")
        self.resize(270, 110)
        # Create a QVBoxLayout instance
        layout = QVBoxLayout()
        # Add widgets to the layout
        layout.addWidget(QPushButton("Top"))
        layout.addWidget(QPushButton("Center"))
        layout.addWidget(QPushButton("Bottom"))
        layout.addStretch()
        # Set the layout on the application's window
        self.setLayout(layout)

在突出显示的行中,您QSpacerItem通过调用布局将可拉伸对象添加到布局的末尾.addStretch()。如果您再次运行该应用程序,您将获得以下行为:

可伸缩垫片示例

现在所有额外的空间都会自动分配给QSpacerItem布局底部的可拉伸对象,而不会影响其余小部件的位置或大小。您可以使用这种和其他空间管理技术来使您的 GUI 应用程序看起来更漂亮、更精致。

管理网格和表单布局中的空间

网格和表单布局以不同的方式处理可用空间。在这些类型的布局中,您只能处理小部件之间的垂直和水平空间。这些布局提供了三种方法来管理这些空间:

  1. setSpacing(spacing)将小部件之间的垂直和水平间距设置为spacing
  2. setVerticalSpacing(spacing)仅将布局中小部件之间的垂直间距设置为spacing
  3. setHorizontalSpacing(spacing)仅将布局中小部件之间的水平间距设置为spacing

在所有情况下,spacing是表示像素的整数。现在回到关于如何创建表单布局并Window像这样更新初始值设定项的示例:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QFormLayout Example")
        self.resize(270, 110)
        # Create a QHBoxLayout instance
        layout = QFormLayout()
        # Add widgets to the layout
        layout.setVerticalSpacing(30)
        layout.addRow("Name:", QLineEdit())
        layout.addRow("Job:", QLineEdit())
        emailLabel = QLabel("Email:")
        layout.addRow(emailLabel, QLineEdit())
        # Set the layout on the application's window
        self.setLayout(layout)

在突出显示的行中,您将小部件之间的垂直空间设置为30像素。如果您再次运行该应用程序,您将看到以下窗口:

QFormLayout 间距示例

现在小部件行之间有更多空间。您还可以尝试通过添加一些垂直或水平空间来修改如何使用网格布局的示例,以查看所有这些间距机制是如何工作的。

结论

创建高质量的GUI 应用程序需要将所有图形组件以连贯和优美的排列方式布置。在 PyQt 中,一种有效的方法是使用 PyQt 的布局管理器,它提供了一种用户友好且高效的方式来完成这项任务。

在本教程中,您学习了:

  • 在 GUI 上正确布置小部件有什么好处
  • 如何使用 PyQt 的内置布局管理器以编程方式排列小部件
  • 哪个布局管理器用于您的特定用例
  • 如何在 PyQt 中布置主窗口风格对话框风格的应用程序

有了这些知识,您将能够使用 PyQt 的内置布局创建美观且专业的 GUI 应用程序。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。