1. 本库回忆
graph LR
subgraph 窗口组件
A[QMainWindow] --> B(主窗口 - 应用程序的主窗口,通常包含菜单栏、工具栏等)
A --> C(QDialog - 对话框窗口,用于显示信息或获取用户输入)
A --> D(QWidget - 所有用户界面对象的基类,可以作为其他组件的容器)
end
subgraph 布局管理器
E[QBoxLayout] --> F(QHBoxLayout - 水平布局管理器,将组件水平排列)
E --> G(QVBoxLayout - 垂直布局管理器,将组件垂直排列)
E --> H(QFormLayout - 表单布局管理器,用于创建表单)
I(QGridLayout - 网格布局管理器,将组件放置在网格中)
end
subgraph 按钮组件
J[QPushButton - 普通按钮,点击时触发操作]
K(QToolButton - 工具按钮,通常用于工具栏)
L(QRadioButton - 单选按钮,一组按钮中只能选择一个)
M(QCheckBox - 复选框,可以选中或不选中)
end
subgraph 输入组件
N[QLineEdit - 单行文本输入框]
O(QTextEdit - 多行文本编辑器,支持富文本格式)
P(QPlainTextEdit - 多行纯文本编辑器)
Q(QSpinBox - 数字微调框,允许用户选择一个数字)
R(QComboBox - 下拉列表框,允许用户从列表中选择一个选项)
end
subgraph 显示组件
S[QLabel - 用于显示文本或图像的标签]
T(QPixmap - 用于显示图像)
U(QMovie - 用于显示动画)
V(QProgressBar - 进度条,显示任务的进度)
W(QCalendarWidget - 日历控件,允许用户选择日期)
end
subgraph 列表/树组件
X[QListView - 用于显示列表数据的视图]
Y(QTreeView - 用于显示树形数据的视图)
Z(QTableWidget - 用于显示表格数据的控件)
end
subgraph 其他组件
AA[QMenuBar - 菜单栏,通常位于主窗口顶部]
BB(QStatusBar - 状态栏,通常位于主窗口底部,显示状态信息)
CC(QMessageBox - 消息框,用于显示警告、错误等消息)
DD(QFileDialog - 文件对话框,用于打开或保存文件)
end
1.2. 创建子组件放入父容器,启用布局管理
创建子组件: 实例化需要添加的子组件,例如 QLabel
。
label = QLabel()
这里,QLabel
是子组件,self.scrollAreaWidgetContents
是父容器。
设置子组件属性: 设置子组件的各种属性,例如文本、大小、显示内容等。
pixmap = QPixmap(image_path)
label.setPixmap(pixmap.scaled(150, 150, Qt.KeepAspectRatio, Qt.SmoothTransformation))
label.setFixedSize(150, 150)
为父容器设置布局: 获取父容器的布局管理器。如果父容器没有布局管理器,则调用setLayout
方法创建一个。
# 为父组件调用setLayout方法配置布局
if not self.scrollAreaWidgetContents.layout():
from PySide6.QtWidgets import QVBoxLayout
self.scrollAreaWidgetContents.setLayout(QVBoxLayout())
# 将layout实例化
layout = self.scrollAreaWidgetContents.layout()
这里,如果 self.scrollAreaWidgetContents
没有布局,则创建一个 QVBoxLayout
垂直布局。
将子组件添加到布局: 使用布局管理器的 addWidget()
方法将子组件添加到父容器的布局中。
# 方法一
layout.addWidget(label)
# 方法二
label = QLabel(self.scrollAreaWidgetContents)
布局管理器会自动管理子组件的位置和大小。
2. 本库重点
2.1. show()和exec()方法对比
1. show()
- 非阻塞: 调用
show()
后,对话框会显示出来,但主程序不会停止执行,而是会继续往下运行。
- 非模态: 通过
show()
打开的对话框是非模态的。这意味着用户可以同时与主窗口以及多个通过 show()
打开的对话框进行交互。
- 生命周期管理:
- 需要手动管理生命周期。 推荐使用
self.dialog = QDialog()
或其他变量来持有对对话框对象的引用,以防止对象被垃圾回收,导致窗口一闪而过。
- 如果使用
self.dialog = QDialog()
持有引用,窗口关闭时,如果设置了 Qt.WA_DeleteOnClose
属性,对象会被销毁;否则,对象仍然存在,但窗口已经关闭。
案例:
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QDialog, QVBoxLayout
import sys
class MyDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("My Dialog")
layout = QVBoxLayout(self)
label = QLabel("This is a non-modal dialog")
layout.addWidget(label)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Main Window")
layout = QVBoxLayout(self)
button = QPushButton("Open Dialog")
layout.addWidget(button)
button.clicked.connect(self.open_dialog)
self.dialog = None # 持有对话框的引用
def open_dialog(self):
self.dialog = MyDialog()
self.dialog.show()
print("Dialog opened, but main window is still responsive.")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
在这个例子中,点击 "Open Dialog" 按钮会打开一个非模态对话框。主窗口仍然可以操作,并且可以在打开多个对话框。self.dialog = MyDialog()
用于持有对话框的引用,确保对话框不会被立即销毁。
2. exec()
- 阻塞: 调用
exec()
后,主程序会暂停执行,直到对话框被关闭后,才会继续执行后面的代码。
- 模态: 通过
exec()
打开的对话框是模态的。这意味着用户只能与该对话框进行交互,而不能操作主窗口。
- 生命周期管理:
- 自动管理生命周期。 不需要使用
self.dialog
或其他变量来持有对对话框对象的引用。
- 对话框关闭后,会自动销毁。
案例:
from PySide6.QtWidgets import QApplication, QWidget, QPushButton, QDialog, QVBoxLayout, QLabel, QDialogButtonBox
import sys
class MyDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("My Modal Dialog")
layout = QVBoxLayout(self)
label = QLabel("This is a modal dialog")
layout.addWidget(label)
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Main Window")
layout = QVBoxLayout(self)
button = QPushButton("Open Modal Dialog")
layout.addWidget(button)
button.clicked.connect(self.open_dialog)
def open_dialog(self):
dialog = MyDialog()
result = dialog.exec()
if result == QDialog.Accepted:
print("Dialog accepted")
else:
print("Dialog rejected")
print("Main window is responsive again after dialog is closed.")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
在这个例子中,点击 "Open Modal Dialog" 按钮会打开一个模态对话框。在对话框关闭之前,主窗口无法操作。dialog.exec()
会阻塞主程序,直到对话框被接受或拒绝。对话框对象在 exec()
方法返回后自动销毁,无需手动管理。
3. 适用场景
show()
: 适用于需要同时显示多个窗口、窗口为非模态、允许用户同时操作主窗口和多个弹窗的场景。
exec()
: 适用于只允许显示一个弹窗、需要用户先处理弹窗再返回主窗口的场景(例如“保存/取消”对话框)。
总结
show()
: 非模态、非阻塞、需手动管理生命周期(推荐用 self.dialog
持有)。
exec()
: 模态、阻塞、自动管理生命周期。
您的观察是正确的:exec()
方法不依赖于使用 self
属性进行生命周期管理,因为它会自动阻塞并管理窗口对象,关闭后自动销毁。
1 前言
1.1 开发框架的选择
GUI程序的开发方式太多了,这里肯定就是Python语言了,至于为什么,就不多描述了;
那么基于Python开发GUI程序的话,也是有多种框架的,常见的有TKinter、PyQt、PySide、wxPython、Kivy、PyGTK
;
针对这6个常见的框架怎么选择,这里简单的对比一下:
Tkinter
- 优点:
- 是Python的标准GUI库,无需额外安装。
- 简单易用,适合初学者和快速开发。
- 跨平台支持,并且在大多数操作系统上都有良好的兼容性。
- 提供了基本的GUI组件和功能,如按钮、标签、文本框等。
- 缺点:
- 界面风格相对较简单,可能不适合创建复杂和精美的界面。
- 可选的GUI组件和样式较少,功能相对较少。
PyQt
- 优点:
- 提供了大量的GUI组件和功能,可用于构建复杂和精美的界面。
- 跨平台支持,并且在多个操作系统上提供一致的用户体验。
- 具有良好的文档、示例和社区支持。
- 缺点:
- 使用GPL或商业许可证,可能需要注意许可证要求。
- 学习曲线相对较陡峭,对于初学者来说可能需要更多时间来掌握。
PySide
- 优点:
- 提供了与PyQt类似的功能和GUI组件。
- 使用LGPL许可证,可以更灵活地使用。
- 跨平台支持,对多个操作系统提供一致的用户体验。
- 缺点:
- 文档相对较少,相比PyQt来说,社区支持可能相对较少。
wxPython
- 优点:
- 提供了丰富的GUI组件和功能,适用于创建各种类型的应用程序。
- 跨平台支持,并且在多个操作系统上都有良好的兼容性。
- 有活跃的社区支持和文档资源。
- 缺点:
- 学习曲线较陡峭,相对于其他GUI包来说,可能需要更多时间来学习和掌握。
Kivy
- 优点:
- 开源框架,用于创建创新的用户界面,如多点触控应用程序。
- 跨平台支持,并且适用于移动应用程序等。
- 提供丰富的GUI组件和功能,支持多点触控和动画效果。
- 缺点:
- 学习曲线相对较陡峭,需要一定的时间来熟悉框架的工作原理。
PyGTK
- 优点:
- 使用GTK+库,提供了丰富的GUI组件和功能,尤其在Linux和Unix系统上广泛使用。
- 跨平台支持,并且在多个操作系统上都有良好的兼容性。
- 提供了良好的文档和社区支持。
- 缺点:
- 学习曲线较陡峭,对于初学者来说可能需要更多时间来掌握。
后面三种的话,就是学习起来会比较麻烦,我们还是专注NLP,不专注GUI程序开发,所以不选择;
Tkinter的话,就是太简单了,很多东西不能实现,所以不选择;
而PyQt和PySide都是基于Qt框架开发的,PyQt和PySide具有相似的API和功能,学习难度也都差不多;另外PySide使用 LGPL 许可证,可以免费商业使用。但是PyQt使用GPL或商业许可证,商业许可证是付费的;而且Qt打算着力培养PySide,所以PySide是更有前途的,所以选择Pyside作为GUI程序开发的框架;
PySide现在主要的就是PySide2
和PySide6
两个版本,PySide6
是基于PySide2
向后兼容的,而且PySide6相比PySide2
有更多的新特性和改进,包括对Qt 6
的支持、更好的性能和稳定性,也提供了更多的API和工具,使得开发者可以更轻松的创建高质量的GUI应用程序,所以我们选择PySide6
。
2 PySide6的安装与使用
2.1 安装
这里用的python是3.8的版本,pyside6
要求3.6以上的版本,所以这里大家要注意选择,建议新建一个python虚拟环境,包的依赖和版本管理更清晰;
直接pip
安装即可,-i
及后面都是镜像源,下载速度更快,这里用的是清华的镜像源,清华的镜像源完整而且下载速度快,所以是个不错的选择;
pip install PySide6 -i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 写一个简单的窗体
新建一个项目,写入下面的代码,运行这个脚本即可;
每行代码的含义也都注明了;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 20:14
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
# 这一行导入了 QApplication、QWidget 和 QLabel 类,它们是 PySide6 中用于创建应用程序和窗口组件的类。
from PySide6.QtWidgets import QApplication, QWidget,QLabel
# 创建了一个 QApplication 实例,用于管理整个应用程序的事件循环和资源分配。
app = QApplication()
# 创建一个空白的 QWidget 对象,它代表着我们的窗体。
window = QWidget()
# 设置窗体的标题为 "Simple Window"。
window.setWindowTitle("Simple Window")
# 将窗体的大小固定为宽度为 400 像素、高度为 300 像素。
window.setFixedSize(400, 300)
# 创建一个 QLabel 对象,并将其作为子组件添加到窗体上。同时,设置标签的显示文本为 "Hello PySide6!"。
label = QLabel("Hello PySide6!", window)
# 创建一个 QLabel 对象,并将其作为子组件添加到窗体上。同时,设置标签的显示文本为 "Hello PySide6!"。
label.move(150, 125)
# 显示窗体
window.show()
# 启动应用程序的事件循环,等待事件的触发和处理,使窗体保持可响应状态。
app.exec()

3 界面设计工具——Qt Designer
3.1 简介
首先,说一说上面的开发,窗体所有内容都可以用python来写,包括窗体、布局、组件等,但是如果说全部的这些布局的内容都用python来手敲,会不会太繁杂了,所以要想办法解决这个问题;
Qt Designer就随之而出;
Qt Designer 是一个可视化的界面设计工具,它允许通过拖放和设置属性的方式,轻松创建应用程序的用户界面。类似于搭积木一样,可以在设计师中选择和放置各种小部件(例如按钮、文本框、图像等),然后调整它们的位置和大小。
Qt Designer 提供了一个直观的图形用户界面,可以在其中对界面进行布局,并设置小部件的外观和行为属性。可以通过编辑器轻松调整字体、颜色、对齐方式等属性,以及连接信号和槽来处理用户交互。
设计完成后,Qt Designer 会生成一个特定格式的界面文件(通常是 .ui
文件),其中包含了界面的结构、布局和属性信息。可以使用 PySide6 中的 QUiLoader
类将该界面文件加载到应用程序中,使界面在运行时动态显示和交互。
使用 Qt Designer,无需手动编写复杂的界面代码,而是可以通过直观的操作来创建界面。这,您可以更快速地实现所需的界面,并且能够更好地分离界面设计和应用程序逻辑,使开发工作更加高效、简洁和易于维护。
这里记录简单的使用,不过多讲解,后续可能会专门出一篇关于Qt Designer的博客来记录更多使用技巧和方法;
3.2 安装
安装方式有很多,可以直接下载安装包安装,也可以使用其他办法安装;
如果安装了PySide的话,它会自动安装到PySide的包的目录下:
D:\Coding_env\Python_env\nlp\Lib\site-packages\PySide6
也就是你python安装的目录下的\Lib\site-packages\PySide6
文件夹内:

3.3 使用教程
这里选择一个空白的窗体即可;
- 空白模板(Widget):这是最基本的模板,它创建一个空白的窗体,您可以自由地在其中添加需要的小部件并进行布局。
- 主窗体模板(Main Window):这个模板创建一个具有标准菜单栏和工具栏的主窗体。它还包含一个中心部件,您可以在其中设计应用程序的主要界面。
- 对话框模板(Dialog with Buttons Bottom):这个模板创建了一个对话框窗体,包含了预设的按钮(如确定和取消按钮)并将它们置于窗体底部。这种模板适用于需要与用户进行简单交互的对话框。
其余两个也是对话框模板,可以点击看看

这里反正就是布局、按钮、文本域等组件的添加,具体的这里不过多讲解;
这里用到的就两个组件,一个叫做Push Button,另一个是Text Browser;
你不太会的话,就直接把这些组件往画布上拖,就可以了,注意在右侧的属性编辑器中编辑每个组件的objectName即可;
不会也没关系,往下走;

点击文件菜单栏,选择保存,即可得到一个ui文件;
打开它,你会发现其实就是xml编写的内容;
到这里Qt Designer的任务就已经完成了,后面就来看看PySide怎么使用ui文件了;
如果你还是不太会使用Qt Designer的话,先手动新建一个文本文件,然后改后缀为ui,然后把下面的代码复制进去;
(先把流程走通嘛,Qt Designer后续再学呗)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>336</width>
<height>313</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="4">
<widget class="QTextBrowser" name="textBrowser">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="bt_1">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>1</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="bt_2">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>2</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="bt_3">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>3</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="bt_add">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="bt_4">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>4</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="bt_5">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>5</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="bt_6">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>6</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="bt_minus">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="bt_7">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>7</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="bt_8">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>8</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="bt_9">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>9</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QPushButton" name="bt_multiply">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>×</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="bt_CE">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>CE</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="bt_0">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="bt_equal">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>=</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QPushButton" name="bt_divide">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>➗</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
3.4 PySide引入ui文件
ui文件有两种使用方法,一种是直接引用进来,但是复杂的程序可能会有兼容性的问题,而且打包也不是很友好;
另一种是将ui转换为py文件后使用,这种方式的适配更友好;
这里我们两种方法都演示一下;
3.4.1 直接使用
详细引入的方法见代码注释;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication
app = QApplication([])
# 加载UI文件
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 创建UI加载器
loader = QUiLoader()
# 加载UI文件并实例化为窗口对象
window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
# 显示窗口
window.show()
# 运行应用程序
app.exec()
运行结果:

OK,没问题!
- 上面的代码还是太简单了,我们定义一个类作为主窗口,重构下代码
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QPushButton
# 定义主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 加载UI文件
loader = QUiLoader()
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 加载UI文件并实例化为窗口对象
self.window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
if __name__ == "__main__":
# 创建一个QApplication对象,它是PySide6应用程序的核心,负责处理事件和管理应用程序的生命周期。
app = QApplication([])
# 创建一个MainWindow对象,即主窗口类的实例。
main_window = MainWindow()
# 调用主窗口对象的show()方法,将主窗口显示在屏幕上。
main_window.window.show()
# 启动应用程序的事件循环,使得应用程序能够响应用户的输入和系统事件,保持运行状态。
app.exec()
为了使用信号和槽的机制,这里要定义一个主窗口类,但是定义主窗口类的作用远不只有这个作用;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QPushButton
# 定义主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 加载UI文件
loader = QUiLoader()
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 加载UI文件并实例化为窗口对象
self.window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
# 获取UI文件中的小部件对象
self.button = self.window.findChild(QPushButton, "bt_1")
# 连接信号和槽,实
self.button.clicked.connect(self.bt_1_click)
# 按钮点击事件处理函数
@Slot()
def bt_1_click(self):
print("1")
if __name__ == "__main__":
app = QApplication([])
main_window = MainWindow()
main_window.window.show()
app.exec()
主要的代码就这几行,其实还是很容易看懂的;
# 获取UI文件中的小部件对象
self.button = self.window.findChild(QPushButton, "bt_1")
# 连接信号和槽
self.button.clicked.connect(self.bt_1_click)
# 按钮点击事件处理函数
@Slot()
def bt_1_click(self):
print("1")
如下,点击后成果输出了;
那么说明这个流程已经打通了,接下来就是看怎么完善这个插槽函数,以及进一步复杂的逻辑了;

这里直接附上最终代码,不一步一步讲解了;
其实这个代码非常的冗余,也是因为制作ui文件的时候,对象名有问题;
这里都是简单的逻辑,复杂的还需要进一步优化,这里只是简单的先实现这个程序,也就到这个程度为止;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:03
# @Author : MinChess
# @File : main.py
# @Software: PyCharm
import sys
from PySide6.QtCore import QFile, QIODevice, Slot
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QPushButton, QTextBrowser
# 定义主窗口类
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 加载UI文件
loader = QUiLoader()
ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
print("无法打开UI文件")
sys.exit(-1)
# 加载UI文件并实例化为窗口对象
self.window = loader.load(ui_file)
# 关闭UI文件
ui_file.close()
# 获取UI文件中的小部件对象
self.textbrowser = self.window.findChild(QTextBrowser, "textBrowser")
self.bt_1 = self.window.findChild(QPushButton, "bt_1")
self.bt_2 = self.window.findChild(QPushButton, "bt_2")
self.bt_3 = self.window.findChild(QPushButton, "bt_3")
self.bt_4 = self.window.findChild(QPushButton, "bt_4")
self.bt_5 = self.window.findChild(QPushButton, "bt_5")
self.bt_6 = self.window.findChild(QPushButton, "bt_6")
self.bt_7 = self.window.findChild(QPushButton, "bt_7")
self.bt_8 = self.window.findChild(QPushButton, "bt_8")
self.bt_9 = self.window.findChild(QPushButton, "bt_9")
self.bt_0 = self.window.findChild(QPushButton, "bt_0")
self.bt_add = self.window.findChild(QPushButton, "bt_add")
self.bt_divide = self.window.findChild(QPushButton, "bt_divide")
self.bt_CE = self.window.findChild(QPushButton, "bt_CE")
self.bt_minus = self.window.findChild(QPushButton, "bt_minus")
self.bt_multiply = self.window.findChild(QPushButton, "bt_multiply")
self.bt_equal = self.window.findChild(QPushButton, "bt_equal")
# 连接信号和槽
self.bt_1.clicked.connect(self.bt_click)
self.bt_2.clicked.connect(self.bt_click)
self.bt_3.clicked.connect(self.bt_click)
self.bt_4.clicked.connect(self.bt_click)
self.bt_5.clicked.connect(self.bt_click)
self.bt_6.clicked.connect(self.bt_click)
self.bt_7.clicked.connect(self.bt_click)
self.bt_8.clicked.connect(self.bt_click)
self.bt_9.clicked.connect(self.bt_click)
self.bt_0.clicked.connect(self.bt_click)
self.bt_add.clicked.connect(self.bt_add_click)
self.bt_equal.clicked.connect(self.bt_equal_click)
self.bt_divide.clicked.connect(self.bt_divide_click)
self.bt_CE.clicked.connect(self.bt_CE_click)
self.bt_multiply.clicked.connect(self.bt_multiply_click)
self.bt_minus.clicked.connect(self.bt_minus_click)
# 加号函数
@Slot()
def bt_add_click(self):
self.textbrowser.insertPlainText('+')
# 减号函数
@Slot()
def bt_minus_click(self):
self.textbrowser.insertPlainText('-')
# 乘号函数
@Slot()
def bt_multiply_click(self):
self.textbrowser.insertPlainText('*')
# 除号函数
@Slot()
def bt_divide_click(self):
self.textbrowser.insertPlainText('/')
# 等号函数
@Slot()
def bt_equal_click(self):
result = eval(self.textbrowser.toPlainText())
self.textbrowser.insertPlainText('=')
self.textbrowser.append(str(result))
# 清空函数
@Slot()
def bt_CE_click(self):
self.textbrowser.clear()
# 点击数字按钮,获取当前按钮的值
@Slot()
def bt_click(self):
button = self.sender()
self.textbrowser.insertPlainText(button.text())
if __name__ == "__main__":
# 创建一个QApplication对象,它是PySide6应用程序的核心,负责处理事件和管理应用程序的生命周期。
app = QApplication([])
# 创建一个MainWindow对象,即主窗口类的实例。
main_window = MainWindow()
# 调用主窗口对象的show()方法,将主窗口显示在屏幕上。
main_window.window.show()
# 启动应用程序的事件循环,使得应用程序能够响应用户的输入和系统事件,保持运行状态。
app.exec()
运行效果也是完全ok的:

3.4.2 将ui文件转为py文件使用
pyside6自带将ui文件转换为py文件的工具,在ui文件下所在目录下运行下面的命令就可以完成转换了;
main.ui
是待转换的文件;
main_ui.py
是转换后的文件;
pyside6-uic main.ui -o main_ui.py
这里也可以指定存放的目录
pyside6-uic mainwindow.ui -o ./output/mainwindow.py
可以使用-w
或--wrapper
选项来指定主类的名称:
pyside6-uic input.ui -o output.py -w Ui_MainWindow
主要的代码就几行:
from main_ui import Ui_Form
引入转换后的ui文件;
self.ui = Ui_Form()
初始化一个对象;
self.ui.setupUi(self)
使用setupUi()方法创建和设置UI界面;
self.textbrowser = self.ui.xxx
直接获取对象;
# -*- coding: utf-8 -*-
# @Time : 2023/9/25 22:29
# @Author : MinChess
# @File : demo.py
# @Software: PyCharm
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QTextBrowser, QPushButton
from main_ui import Ui_Form
class MyMainWindow(QWidget):
def __init__(self):
super().__init__()
# 初始化 UI
self.ui = Ui_Form()
self.ui.setupUi(self)
# 获取UI文件中的小部件对象
self.textbrowser = self.ui.textBrowser
self.bt_1 = self.ui.bt_1
self.bt_2 = self.ui.bt_2
self.bt_3 = self.ui.bt_3
self.bt_4 = self.ui.bt_4
self.bt_5 = self.ui.bt_5
self.bt_6 = self.ui.bt_6
self.bt_7 = self.ui.bt_7
self.bt_8 = self.ui.bt_8
self.bt_9 = self.ui.bt_9
self.bt_0 = self.ui.bt_0
self.bt_add = self.ui.bt_add
self.bt_divide = self.ui.bt_divide
self.bt_CE = self.ui.bt_CE
self.bt_minus = self.ui.bt_minus
self.bt_multiply = self.ui.bt_multiply
self.bt_equal = self.ui.bt_equal
# 连接信号和槽
self.bt_1.clicked.connect(self.bt_click)
self.bt_2.clicked.connect(self.bt_click)
self.bt_3.clicked.connect(self.bt_click)
self.bt_4.clicked.connect(self.bt_click)
self.bt_5.clicked.connect(self.bt_click)
self.bt_6.clicked.connect(self.bt_click)
self.bt_7.clicked.connect(self.bt_click)
self.bt_8.clicked.connect(self.bt_click)
self.bt_9.clicked.connect(self.bt_click)
self.bt_0.clicked.connect(self.bt_click)
self.bt_add.clicked.connect(self.bt_add_click)
self.bt_equal.clicked.connect(self.bt_equal_click)
self.bt_divide.clicked.connect(self.bt_divide_click)
self.bt_CE.clicked.connect(self.bt_CE_click)
self.bt_multiply.clicked.connect(self.bt_multiply_click)
self.bt_minus.clicked.connect(self.bt_minus_click)
# 加号函数
def bt_add_click(self):
self.textbrowser.insertPlainText('+')
# 减号函数
def bt_minus_click(self):
self.textbrowser.insertPlainText('-')
# 乘号函数
def bt_multiply_click(self):
self.textbrowser.insertPlainText('*')
# 除号函数
def bt_divide_click(self):
self.textbrowser.insertPlainText('/')
# 等号函数
def bt_equal_click(self):
result = eval(self.textbrowser.toPlainText())
self.textbrowser.insertPlainText('=')
self.textbrowser.append(str(result))
# 清空函数
def bt_CE_click(self):
self.textbrowser.clear()
# 点击数字按钮,获取当前按钮的值
def bt_click(self):
button = self.sender()
self.textbrowser.insertPlainText(button.text())
if __name__ == '__main__':
app = QApplication([])
mainWindow = MyMainWindow()
mainWindow.show()
app.exec()

4. 基本组件介绍
1. 容器组件 (Container Components)
引言:容器组件是构建图形用户界面的基础,它们用于承载和组织所有可见的 UI 组件。在 PySide6 中,最常用的顶级容器是 QMainWindow
、QWidget
和 QDialog
。
- QWindow: 相对底层,通常不直接用于创建复杂的应用程序窗口,更多地被
QWidget
等封装。
- QWidget: 是所有用户界面对象的基类。它既可以作为独立的顶级窗口(没有菜单栏、工具栏等预设结构),也可以嵌入到其他窗口部件中,是最通用的窗口或控件基类。如果不确定窗口的具体类型或需要将其嵌入其他窗口,通常基于
QWidget
创建。
- QDialog: 设计用于创建对话框窗口。对话框通常用于显示临时信息、获取用户特定输入或提供选项。它们可以是模态的(阻止与应用程序其他部分的交互)或非模态的。
- QMainWindow: 设计用于创建应用程序的主窗口。它内置了对菜单栏、工具栏、状态栏和可停靠部件(Dock Widgets)的支持,提供了一个经典的应用程序框架。
选择哪个基类取决于窗口的用途:
- 顶级对话框:一般基于
QDialog
创建。
- 应用程序主窗口:一般基于
QMainWindow
创建。
- 通用窗口或可嵌入控件:一般基于
QWidget
创建。
1.1 QMainWindow
桌面软件主窗口一般由标题栏,菜单栏,工具栏,工作区和状态栏组成。

工作区通常由各种各样的锚接控件(Dock Widgets)和中心控件(Central Widget)组成,例如 Pycharm 的主窗口:

可以看到 Pycharm 的主窗口工作区默认有三个锚接控件(文件浏览器、Debug 窗口)和一个中心控件(代码编辑器)。QMainWindow
提供了构建这种经典主窗口所需的组件和布局管理,简化了开发过程。