文章目录
- 1. 引言
- 2. PySide6 简介与安装
- 2.1 什么是PySide6
- 2.2 PySide6 vs. PyQt6
- 2.3 安装PySide6
- 2.4 开发环境配置建议
- 3. Qt 设计原理
- 3.1 Qt对象模型
- 3.2 信号与槽机制
- 3.3 Qt坐标系统
- 3.4 Qt样式表(QSS)
- 4. 创建第一个应用
- 4.1 基本应用结构
- 4.2 主窗口与应用生命周期
- 4.3 使用面向对象方法重构
- 4.4 简单交互示例
- 5. 常用控件详解
- 5.1 按钮与输入控件
- 5.1.1 QPushButton (按钮)
- 5.1.2 QLabel (标签)
- 5.1.3 QLineEdit (单行文本框)
- 5.1.4 QTextEdit (多行文本编辑器)
- 5.1.5 QCheckBox (复选框)
- 5.1.6 QRadioButton (单选按钮)
- 5.1.7 QComboBox (下拉列表)
- 5.1.8 QSlider (滑块)
- 5.1.9 QSpinBox (数字输入)
- 5.2 容器控件
- 5.2.1 QGroupBox (分组框)
- 5.2.2 QTabWidget (选项卡)
- 5.2.3 QScrollArea (滚动区域)
- 5.2.4 QStackedWidget (堆叠部件)
- 5.3 对话框
- 5.3.1 QMessageBox (消息框)
- 5.3.2 QFileDialog (文件对话框)
- 5.3.3 QInputDialog (输入对话框)
- 5.3.4 QColorDialog (颜色对话框)
- 5.3.5 QFontDialog (字体对话框)
- 5.4 菜单与工具栏
- 5.4.1 QMenuBar 和 QMenu (菜单栏和菜单)
- 5.4.2 QToolBar (工具栏)
- 5.4.3 QStatusBar (状态栏)
- 6. 布局管理系统
- 6.1 QVBoxLayout (垂直布局)
- 6.2 QHBoxLayout (水平布局)
- 6.3 QGridLayout (网格布局)
- 6.4 QFormLayout (表单布局)
- 6.5 嵌套布局
- 6.6 高级布局技巧
- 6.6.1 布局与控件的尺寸策略
- 6.6.2 间隔器的使用
- 6.6.3 边距和间距
- 6.6.4 对齐方式
- 7. 信号与槽机制
- 7.1 基础概念
- 7.2 信号种类与发射
- 7.2.1 内置信号
- 7.2.2 自定义信号
- 7.3 槽的种类与连接
- 7.3.1 函数槽
- 7.3.2 方法槽
- 7.3.3 Lambda表达式
- 7.3.4 部分函数
- 7.4 信号连接管理
- 7.4.1 断开连接
- 7.4.2 阻塞信号
- 7.4.3 连接类型
- 7.5 实例:自定义信号与槽
- 8. 样式与主题定制
- 8.1 基本样式属性
- 8.2 Qt样式表(QSS)
- 8.3 常用样式属性
- 8.3.1 背景与前景
- 8.3.2 边框与轮廓
- 8.3.3 文本格式
- 8.3.4 尺寸与布局
- 8.4 状态相关样式
- 8.5 自定义应用主题
- 8.6 动态切换主题
- 9. Qt Designer 可视化设计
- 9.1 Qt Designer 基础
- 9.2 创建界面
- 9.3 加载.ui文件
- 9.3.1 使用QUiLoader
- 9.3.2 使用uic编译
- 9.4 多界面管理
- 9.5 表单继承
- 9.6 资源文件管理
- 9.6.1 创建资源文件
- 9.6.2 编译资源文件
- 9.6.3 使用资源
- 10. 多线程与并发处理
- 10.1 QThread 基本用法
- 10.2 QRunnable 和线程池
- 11. 数据持久化
- 11.1 QSettings
- 11.2 SQLite数据库集成
- 12. 高级图形与动画
- 12.1 自定义绘图
- 12.2 动画效果
- 13. 国际化与本地化
- 13.1 使用QTranslator
- 13.2 使用tr()进行文本标记
- 14. 实战项目开发
- 14.1 应用架构设计
- 14.2 项目质量保证
- 15. 打包与发布
- 15.1 使用PyInstaller打包
- 15.2 使用cx_Freeze打包
- 16. 总结
1. 引言
在Python应用程序开发领域,图形用户界面(GUI)是提升用户体验的关键因素。PySide6作为Qt框架的Python绑定,为开发者提供了强大而灵活的GUI开发工具。本文将全面介绍PySide6的核心概念、组件和最佳实践,帮助读者快速掌握这一现代GUI开发技术。
无论你是GUI开发新手,还是想从其他GUI框架迁移到PySide6,本指南都将为你提供系统化的学习路径,帮助你构建专业、美观且功能强大的Python桌面应用。
2. PySide6 简介与安装
2.1 什么是PySide6
PySide6是Qt for Python项目的一部分,它提供了对Qt 6.0的官方Python绑定。Qt是一个跨平台的C++应用程序开发框架,以其丰富的UI组件、优秀的性能和跨平台能力而闻名。PySide6继承了Qt的这些优点,同时结合了Python的简洁性和开发效率。
PySide6的主要特点包括:
- 跨平台兼容性:支持Windows、macOS、Linux等多种操作系统
- 丰富的UI组件库:提供200多种现成的UI控件
- 信号与槽机制:独特的事件处理方式
- 强大的图形能力:支持2D/3D图形渲染
- 内置的国际化支持:轻松实现多语言界面
- Qt Designer集成:可视化界面设计工具
2.2 PySide6 vs. PyQt6
PySide6和PyQt6都是Qt的Python绑定,功能几乎相同,主要区别在于许可证:
- PySide6:使用更为宽松的LGPL许可证
- PyQt6:使用GPL许可证,商业应用可能需要购买商业许可
代码方面,两者差异很小,通常只需修改导入语句就能在两者间切换。
2.3 安装PySide6
使用pip安装PySide6非常简单:
pip install pyside6
验证安装是否成功:
import PySide6.QtCore
print(PySide6.__version__)
print(PySide6.QtCore.__version__)
对于Anaconda用户,也可以使用conda安装:
conda install -c conda-forge pyside6
2.4 开发环境配置建议
推荐的PySide6开发环境:
- Python:3.6+(推荐3.8+)
- IDE:
- PyCharm(专业版提供更好的Qt支持)
- Visual Studio Code(配合Python和Qt相关插件)
- 调试工具:Qt Designer(界面设计)和Qt Creator(完整IDE)
- 版本控制:Git(特别是处理.ui文件时)
3. Qt 设计原理
3.1 Qt对象模型
Qt对象模型是理解PySide6的基础,它基于以下几个核心概念:
- QObject:几乎所有Qt类的基类,提供信号与槽机制的基础
- 元对象系统:提供运行时类型信息、动态属性系统等功能
- 属性系统:允许在对象上动态添加属性
- 事件系统:处理用户交互和系统事件
例如,一个基本的QObject派生类:
from PySide6.QtCore import QObject, Signal, Slotclass MyObject(QObject):# 定义信号valueChanged = Signal(int)def __init__(self):super().__init__()self._value = 0# 定义槽@Slot(int)def setValue(self, value):if self._value != value:self._value = valueself.valueChanged.emit(value)
3.2 信号与槽机制
信号与槽是Qt最独特的特性之一,它提供了一种类型安全的回调机制:
- 信号(Signal):对象发出的通知,表示某些事件已经发生
- 槽(Slot):响应信号的函数或方法
信号与槽的连接:
from PySide6.QtWidgets import QApplication, QPushButtonapp = QApplication([])
button = QPushButton("Click me")# 连接按钮的clicked信号到自定义函数
def onButtonClicked():print("Button clicked!")button.clicked.connect(onButtonClicked)
button.show()
app.exec()
3.3 Qt坐标系统
Qt使用自己的坐标系统,原点(0,0)通常位于组件的左上角:
- 物理坐标:实际设备上的像素位置
- 逻辑坐标:考虑DPI缩放的虚拟坐标
- 设备独立像素:跨平台一致性的关键
3.4 Qt样式表(QSS)
Qt样式表类似于CSS,用于自定义UI外观:
button = QPushButton("Styled Button")
button.setStyleSheet("""QPushButton {background-color: #4CAF50;border: none;color: white;padding: 8px 16px;border-radius: 4px;font-size: 14px;}QPushButton:hover {background-color: #45a049;}QPushButton:pressed {background-color: #3d8b40;}
""")
4. 创建第一个应用
4.1 基本应用结构
所有PySide6应用都遵循类似的基本结构:
import sys
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout# 1. 创建应用实例
app = QApplication(sys.argv)# 2. 创建主窗口
window = QWidget()
window.setWindowTitle("My First PySide6 App")
window.setGeometry(100, 100, 400, 200) # x, y, width, height# 3. 创建布局和组件
layout = QVBoxLayout()
label = QLabel("Hello, PySide6!")
layout.addWidget(label)
window.setLayout(layout)# 4. 显示窗口
window.show()# 5. 执行应用
sys.exit(app.exec())
4.2 主窗口与应用生命周期
应用的生命周期管理是GUI程序的重要部分:
- 初始化:创建QApplication实例和主窗口
- 事件循环:app.exec()启动事件循环,处理用户输入和系统事件
- 终止:事件循环结束,应用关闭
4.3 使用面向对象方法重构
更实用的做法是将应用窗口封装为类:
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidgetclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("OOP PySide6 App")self.setGeometry(100, 100, 400, 200)self.setupUI()def setupUI(self):# 创建中央部件和布局central_widget = QWidget()layout = QVBoxLayout(central_widget)# 添加控件label = QLabel("Hello from OOP PySide6 App!")layout.addWidget(label)# 设置中央部件self.setCentralWidget(central_widget)if __name__ == "__main__":app = QApplication(sys.argv)window = MainWindow()window.show()sys.exit(app.exec())
4.4 简单交互示例
添加按钮和事件响应,创建一个简单的交互应用:
import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton
)
from PySide6.QtCore import Qtclass CounterApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Counter App")self.setGeometry(100, 100, 300, 200)# 计数器初始值self.counter = 0# 设置UIself.setupUI()def setupUI(self):# 中央部件和布局central_widget = QWidget()layout = QVBoxLayout(central_widget)# 显示计数的标签self.label = QLabel(f"Count: {self.counter}")self.label.setAlignment(Qt.AlignCenter)layout.addWidget(self.label)# 增加按钮increment_button = QPushButton("Increment")increment_button.clicked.connect(self.increment)layout.addWidget(increment_button)# 重置按钮reset_button = QPushButton("Reset")reset_button.clicked.connect(self.reset)layout.addWidget(reset_button)# 设置中央部件self.setCentralWidget(central_widget)def increment(self):self.counter += 1self.label.setText(f"Count: {self.counter}")def reset(self):self.counter = 0self.label.setText(f"Count: {self.counter}")if __name__ == "__main__":app = QApplication(sys.argv)window = CounterApp()window.show()sys.exit(app.exec())
这个简单的计数器应用展示了PySide6的基本功能:窗口创建、控件布局、事件处理和状态管理。
5. 常用控件详解
PySide6提供了丰富的控件库,以下是最常用的控件及其用法。
5.1 按钮与输入控件
5.1.1 QPushButton (按钮)
最基本的交互控件:
from PySide6.QtWidgets import QPushButton# 创建按钮
button = QPushButton("Click Me")# 设置快捷键
button.setShortcut("Ctrl+C")# 连接点击事件
button.clicked.connect(on_button_clicked)# 设置按钮图标
from PySide6.QtGui import QIcon
button.setIcon(QIcon("path/to/icon.png"))
5.1.2 QLabel (标签)
用于显示文本或图像:
from PySide6.QtWidgets import QLabel
from PySide6.QtGui import QPixmap
from PySide6.QtCore import Qt# 文本标签
text_label = QLabel("Hello World")
text_label.setAlignment(Qt.AlignCenter) # 文本居中# 图像标签
image_label = QLabel()
pixmap = QPixmap("image.jpg")
image_label.setPixmap(pixmap.scaled(200, 200, Qt.KeepAspectRatio))
5.1.3 QLineEdit (单行文本框)
用户文本输入控件:
from PySide6.QtWidgets import QLineEdit# 创建文本框
text_input = QLineEdit()
text_input.setPlaceholderText("Enter your name") # 设置占位文本
text_input.setMaxLength(50) # 设置最大长度# 获取文本
def get_text():print(text_input.text())# 文本变化信号
text_input.textChanged.connect(lambda text: print(f"Text changed: {text}"))
5.1.4 QTextEdit (多行文本编辑器)
用于多行文本输入和显示:
from PySide6.QtWidgets import QTextEdit# 创建多行文本编辑器
text_editor = QTextEdit()
text_editor.setPlaceholderText("Write your message here...")# 设置和获取文本
text_editor.setText("Initial text")
content = text_editor.toPlainText() # 获取纯文本
html_content = text_editor.toHtml() # 获取HTML格式内容# 文本变化信号
text_editor.textChanged.connect(lambda: print("Text changed"))
5.1.5 QCheckBox (复选框)
用于布尔选择:
from PySide6.QtWidgets import QCheckBox# 创建复选框
checkbox = QCheckBox("Enable feature")
checkbox.setChecked(True) # 默认选中# 状态变化信号
checkbox.stateChanged.connect(lambda state: print(f"State: {state}"))
5.1.6 QRadioButton (单选按钮)
用于互斥选择:
from PySide6.QtWidgets import QRadioButton, QButtonGroup, QVBoxLayout, QWidget# 需要用QButtonGroup组织多个单选按钮
group = QButtonGroup()# 创建单选按钮
radio1 = QRadioButton("Option 1")
radio2 = QRadioButton("Option 2")
radio3 = QRadioButton("Option 3")# 添加到按钮组
group.addButton(radio1, 1) # 第二个参数是ID
group.addButton(radio2, 2)
group.addButton(radio3, 3)# 默认选中
radio1.setChecked(True)# 信号连接
group.buttonClicked.connect(lambda button: print(f"Selected: {button.text()}"))
5.1.7 QComboBox (下拉列表)
用于从预定义列表中选择:
from PySide6.QtWidgets import QComboBox# 创建下拉列表
combo = QComboBox()
combo.addItem("Option 1")
combo.addItem("Option 2")
combo.addItem("Option 3")# 带数据的项
combo.addItem("Red", "#FF0000")
combo.addItem("Green", "#00FF00")
combo.addItem("Blue", "#0000FF")# 获取选择
def get_selection():index = combo.currentIndex()text = combo.currentText()data = combo.currentData()print(f"Selected: {text} at index {index} with data {data}")# 选择变化信号
combo.currentIndexChanged.connect(get_selection)
5.1.8 QSlider (滑块)
用于范围值选择:
from PySide6.QtWidgets import QSlider
from PySide6.QtCore import Qt# 创建水平滑块
slider = QSlider(Qt.Horizontal)
slider.setMinimum(0)
slider.setMaximum(100)
slider.setValue(50) # 初始值
slider.setTickPosition(QSlider.TicksBelow) # 显示刻度
slider.setTickInterval(10) # 刻度间隔# 值变化信号
slider.valueChanged.connect(lambda value: print(f"Value: {value}"))
5.1.9 QSpinBox (数字输入)
用于数字输入:
from PySide6.QtWidgets import QSpinBox# 创建数字输入框
spin = QSpinBox()
spin.setMinimum(0)
spin.setMaximum(100)
spin.setValue(50) # 初始值
spin.setSingleStep(5) # 步长
spin.setPrefix("$") # 前缀
spin.setSuffix(" units") # 后缀# 值变化信号
spin.valueChanged.connect(lambda value: print(f"Value: {value}"))
5.2 容器控件
5.2.1 QGroupBox (分组框)
用于组织相关控件:
from PySide6.QtWidgets import QGroupBox, QVBoxLayout, QRadioButton# 创建分组框
group_box = QGroupBox("Select Option")layout = QVBoxLayout()# 添加内容
radio1 = QRadioButton("Option 1")
radio2 = QRadioButton("Option 2")
layout.addWidget(radio1)
layout.addWidget(radio2)# 设置布局
group_box.setLayout(layout)
5.2.2 QTabWidget (选项卡)
创建选项卡界面:
from PySide6.QtWidgets import QTabWidget, QWidget, QVBoxLayout, QLabel# 创建选项卡部件
tabs = QTabWidget()# 创建第一个选项卡
tab1 = QWidget()
tab1_layout = QVBoxLayout(tab1)
tab1_layout.addWidget(QLabel("This is the first tab"))# 创建第二个选项卡
tab2 = QWidget()
tab2_layout = QVBoxLayout(tab2)
tab2_layout.addWidget(QLabel("This is the second tab"))# 添加选项卡
tabs.addTab(tab1, "Tab 1")
tabs.addTab(tab2, "Tab 2")# 选项卡切换信号
tabs.currentChanged.connect(lambda index: print(f"Tab switched to: {index}"))
5.2.3 QScrollArea (滚动区域)
为大型内容提供滚动功能:
from PySide6.QtWidgets import QScrollArea, QWidget, QVBoxLayout, QLabel# 创建滚动区域
scroll = QScrollArea()
scroll.setWidgetResizable(True) # 允许内容部件调整大小# 创建内容部件
content = QWidget()
layout = QVBoxLayout(content)# 添加大量内容
for i in range(100):layout.addWidget(QLabel(f"Item {i}"))# 设置内容部件
scroll.setWidget(content)
5.2.4 QStackedWidget (堆叠部件)
在同一区域显示多个部件,但一次只显示一个:
from PySide6.QtWidgets import QStackedWidget, QWidget, QVBoxLayout, QLabel, QPushButton# 创建堆叠部件
stack = QStackedWidget()# 创建页面
page1 = QWidget()
page1_layout = QVBoxLayout(page1)
page1_layout.addWidget(QLabel("This is page 1"))page2 = QWidget()
page2_layout = QVBoxLayout(page2)
page2_layout.addWidget(QLabel("This is page 2"))# 添加页面
stack.addWidget(page1)
stack.addWidget(page2)# 切换页面
stack.setCurrentIndex(0) # 显示第一个页面# 切换按钮
def switch_page():current = stack.currentIndex()stack.setCurrentIndex(1 - current) # 在0和1之间切换button = QPushButton("Switch Page")
button.clicked.connect(switch_page)
5.3 对话框
5.3.1 QMessageBox (消息框)
用于显示消息和获取用户响应:
from PySide6.QtWidgets import QMessageBox# 信息对话框
def show_info():QMessageBox.information(None, # 父窗口"Information", # 标题"Operation completed successfully.", # 消息QMessageBox.Ok # 按钮)# 警告对话框
def show_warning():QMessageBox.warning(None,"Warning","This action might be dangerous.",QMessageBox.Ok | QMessageBox.Cancel)# 错误对话框
def show_error():QMessageBox.critical(None,"Error","A critical error has occurred.",QMessageBox.Ok)# 问题对话框
def ask_question():result = QMessageBox.question(None,"Confirmation","Are you sure you want to proceed?",QMessageBox.Yes | QMessageBox.No)if result == QMessageBox.Yes:print("User confirmed")else:print("User cancelled")
5.3.2 QFileDialog (文件对话框)
用于文件选择:
from PySide6.QtWidgets import QFileDialog# 打开文件对话框
def open_file():file_path, _ = QFileDialog.getOpenFileName(None, # 父窗口"Open File", # 标题"", # 起始目录"Text Files (*.txt);;All Files (*)" # 文件过滤器)if file_path:print(f"Selected file: {file_path}")# 保存文件对话框
def save_file():file_path, _ = QFileDialog.getSaveFileName(None,"Save File","","Text Files (*.txt);;All Files (*)")if file_path:print(f"Save to: {file_path}")# 选择目录对话框
def select_directory():directory = QFileDialog.getExistingDirectory(None,"Select Directory")if directory:print(f"Selected directory: {directory}")# 多文件选择
def select_multiple_files():file_paths, _ = QFileDialog.getOpenFileNames(None,"Select Files","","Images (*.png *.jpg);;All Files (*)")if file_paths:print(f"Selected {len(file_paths)} files")
5.3.3 QInputDialog (输入对话框)
用于获取用户输入:
from PySide6.QtWidgets import QInputDialog# 获取文本输入
def get_text_input():text, ok = QInputDialog.getText(None, # 父窗口"Input", # 标题"Enter your name:" # 提示)if ok and text:print(f"User entered: {text}")# 获取数字输入
def get_int_input():number, ok = QInputDialog.getInt(None,"Input","Enter your age:",25, # 默认值0, # 最小值120 # 最大值)if ok:print(f"User entered: {number}")# 获取下拉选择
def get_item_selection():items = ["Red", "Green", "Blue", "Yellow"]item, ok = QInputDialog.getItem(None,"Select Color","Choose your favorite color:",items,0, # 默认选择索引False # 是否可编辑)if ok and item:print(f"User selected: {item}")
5.3.4 QColorDialog (颜色对话框)
用于颜色选择:
from PySide6.QtWidgets import QColorDialog
from PySide6.QtGui import QColor# 颜色选择对话框
def choose_color():color = QColorDialog.getColor(QColor(255, 0, 0), # 初始颜色None, # 父窗口"Select Color" # 标题)if color.isValid():print(f"Selected color: RGB({color.red()}, {color.green()}, {color.blue()})")print(f"Hex: {color.name()}")
5.3.5 QFontDialog (字体对话框)
用于字体选择:
from PySide6.QtWidgets import QFontDialog
from PySide6.QtGui import QFont# 字体选择对话框
def choose_font():initial_font = QFont("Arial", 12)font, ok = QFontDialog.getFont(initial_font, # 初始字体None, # 父窗口"Select Font" # 标题)if ok:print(f"Selected font: {font.family()}, {font.pointSize()}pt")if font.bold():print("Font is bold")if font.italic():print("Font is italic")
5.4 菜单与工具栏
5.4.1 QMenuBar 和 QMenu (菜单栏和菜单)
创建应用菜单:
from PySide6.QtWidgets import QMainWindow, QMenuBar, QMenu, QAction
from PySide6.QtGui import QIcon, QKeySequenceclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Menu Example")self.setGeometry(100, 100, 800, 600)# 创建菜单栏menu_bar = self.menuBar()# 创建文件菜单file_menu = menu_bar.addMenu("&File")# 创建新建动作new_action = QAction(QIcon("new.png"), "&New", self)new_action.setShortcut(QKeySequence("Ctrl+N"))new_action.setStatusTip("Create a new file")new_action.triggered.connect(self.new_file)file_menu.addAction(new_action)# 创建打开动作open_action = QAction("&Open...", self)open_action.setShortcut(QKeySequence("Ctrl+O"))open_action.triggered.connect(self.open_file)file_menu.addAction(open_action)# 添加分隔符file_menu.addSeparator()# 创建退出动作exit_action = QAction("E&xit", self)exit_action.setShortcut(QKeySequence("Ctrl+Q"))exit_action.triggered.connect(self.close)file_menu.addAction(exit_action)# 创建编辑菜单edit_menu = menu_bar.addMenu("&Edit")# 添加子菜单format_menu = edit_menu.addMenu("&Format")format_menu.addAction("&Bold")format_menu.addAction("&Italic")def new_file(self):print("New file")def open_file(self):print("Open file")
5.4.2 QToolBar (工具栏)
创建工具栏:
from PySide6.QtWidgets import QMainWindow, QToolBar, QAction
from PySide6.QtGui import QIconclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Toolbar Example")# 创建工具栏toolbar = QToolBar("Main Toolbar")self.addToolBar(toolbar)# 添加动作new_action = QAction(QIcon("new.png"), "New", self)new_action.triggered.connect(self.new_file)toolbar.addAction(new_action)open_action = QAction(QIcon("open.png"), "Open", self)open_action.triggered.connect(self.open_file)toolbar.addAction(open_action)# 添加分隔符toolbar.addSeparator()# 添加可选择的动作bold_action = QAction(QIcon("bold.png"), "Bold", self)bold_action.setCheckable(True) # 可选择的bold_action.toggled.connect(lambda checked: print(f"Bold: {checked}"))toolbar.addAction(bold_action)def new_file(self):print("New file")def open_file(self):print("Open file")
5.4.3 QStatusBar (状态栏)
添加状态栏:
from PySide6.QtWidgets import QMainWindow, QStatusBar, QLabelclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("StatusBar Example")# 创建状态栏status_bar = QStatusBar()self.setStatusBar(status_bar)# 添加永久消息status_bar.addPermanentWidget(QLabel("Ready"))# 显示临时消息(5秒)status_bar.showMessage("Loading...", 5000)
6. 布局管理系统
布局管理是GUI应用中非常重要的部分,PySide6提供了多种布局管理器来组织控件,使界面在不同大小的窗口下保持合理的排列。
6.1 QVBoxLayout (垂直布局)
控件从上到下垂直排列:
from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButtonwidget = QWidget()
layout = QVBoxLayout(widget) # 直接设置父部件# 添加控件
layout.addWidget(QPushButton("Button 1"))
layout.addWidget(QPushButton("Button 2"))
layout.addWidget(QPushButton("Button 3"))# 添加间距
layout.addSpacing(20) # 添加20像素的垂直间距# 设置控件之间的间距
layout.setSpacing(10) # 所有控件之间的间距为10像素# 设置边距
layout.setContentsMargins(10, 10, 10, 10) # 左, 上, 右, 下
6.2 QHBoxLayout (水平布局)
控件从左到右水平排列:
from PySide6.QtWidgets import QWidget, QHBoxLayout, QPushButtonwidget = QWidget()
layout = QHBoxLayout(widget)# 添加控件
layout.addWidget(QPushButton("Left"))
layout.addWidget(QPushButton("Center"))
layout.addWidget(QPushButton("Right"))# 控制拉伸因子
# 当窗口大小变化时,各控件获得不同比例的空间
layout = QHBoxLayout()
layout.addWidget(QPushButton("Small"), 1) # 拉伸因子为1
layout.addWidget(QPushButton("Medium"), 2) # 拉伸因子为2
layout.addWidget(QPushButton("Large"), 3) # 拉伸因子为3
6.3 QGridLayout (网格布局)
控件在二维网格中排列:
from PySide6.QtWidgets import QWidget, QGridLayout, QPushButton, QLabel, QLineEditwidget = QWidget()
layout = QGridLayout(widget)# 添加控件 - addWidget(widget, row, column, rowSpan, columnSpan)
layout.addWidget(QLabel("Name:"), 0, 0) # 第0行,第0列
layout.addWidget(QLineEdit(), 0, 1) # 第0行,第1列layout.addWidget(QLabel("Email:"), 1, 0) # 第1行,第0列
layout.addWidget(QLineEdit(), 1, 1) # 第1行,第1列# 跨越多行或多列
layout.addWidget(QPushButton("Submit"), 2, 0, 1, 2) # 第2行,跨越2列
6.4 QFormLayout (表单布局)
专门用于表单的布局管理器,自动处理标签和字段的对齐:
from PySide6.QtWidgets import QWidget, QFormLayout, QLineEdit, QSpinBox, QComboBoxwidget = QWidget()
layout = QFormLayout(widget)# 添加行 - addRow(label, field)
layout.addRow("Name:", QLineEdit())
layout.addRow("Age:", QSpinBox())
layout.addRow("Country:", QComboBox())# 使用已有控件作为标签
label = QLabel("Email:")
field = QLineEdit()
layout.addRow(label, field)# 仅添加标签
layout.addRow("Contact Information")# 仅添加控件
layout.addRow(QPushButton("Submit"))
6.5 嵌套布局
布局可以嵌套,创建复杂的界面:
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QLabel
)class NestedLayoutExample(QWidget):def __init__(self):super().__init__()self.setWindowTitle("Nested Layouts")self.setGeometry(100, 100, 600, 400)# 主布局main_layout = QVBoxLayout(self)# 上部分:标题main_layout.addWidget(QLabel("Message Composer"))# 中部分:编辑区域main_layout.addWidget(QTextEdit())# 下部分:按钮行(水平布局)button_layout = QHBoxLayout()button_layout.addWidget(QPushButton("Save Draft"))button_layout.addWidget(QPushButton("Discard"))button_layout.addStretch(1) # 添加可拉伸的空间,使按钮靠左对齐button_layout.addWidget(QPushButton("Send"))# 将按钮布局添加到主布局main_layout.addLayout(button_layout)
6.6 高级布局技巧
6.6.1 布局与控件的尺寸策略
控制控件在布局中的大小行为:
from PySide6.QtWidgets import QPushButton, QSizePolicybutton = QPushButton("Resizable Button")# 设置尺寸策略
button.setSizePolicy(QSizePolicy.Expanding, # 水平策略QSizePolicy.Fixed # 垂直策略
)# 可用的尺寸策略:
# - QSizePolicy.Fixed: 控件具有固定大小
# - QSizePolicy.Minimum: 控件的最小大小提示是其最小尺寸
# - QSizePolicy.Maximum: 控件的最大大小提示是其最大尺寸
# - QSizePolicy.Preferred: 控件可以扩展,但没有必要
# - QSizePolicy.Expanding: 控件应该优先扩展
# - QSizePolicy.MinimumExpanding: 控件应该扩展,但可以缩小到最小大小
# - QSizePolicy.Ignored: 忽略控件的大小提示
6.6.2 间隔器的使用
使用弹性间隔器控制控件的位置:
from PySide6.QtWidgets import QHBoxLayout, QPushButton, QWidgetwidget = QWidget()
layout = QHBoxLayout(widget)# 添加一个按钮靠左
layout.addWidget(QPushButton("Left"))# 添加可拉伸空间
layout.addStretch(1)# 添加一个按钮靠右
layout.addWidget(QPushButton("Right"))# 结果:按钮将分别靠左和靠右,中间有弹性空间
6.6.3 边距和间距
调整布局的边距和间距:
from PySide6.QtWidgets import QVBoxLayoutlayout = QVBoxLayout()# 设置所有边距(左、上、右、下)
layout.setContentsMargins(10, 20, 10, 20)# 设置控件之间的间距
layout.setSpacing(15)
6.6.4 对齐方式
控制控件在布局中的对齐方式:
from PySide6.QtWidgets import QHBoxLayout, QPushButton
from PySide6.QtCore import Qtlayout = QHBoxLayout()# 创建按钮
button = QPushButton("Aligned Button")# 添加控件时指定对齐方式
layout.addWidget(button, 0, Qt.AlignTop | Qt.AlignLeft)# 可用的对齐标志:
# - Qt.AlignLeft: 水平左对齐
# - Qt.AlignRight: 水平右对齐
# - Qt.AlignHCenter: 水平居中对齐
# - Qt.AlignTop: 垂直顶部对齐
# - Qt.AlignBottom: 垂直底部对齐
# - Qt.AlignVCenter: 垂直居中对齐
# - Qt.AlignCenter: 水平和垂直都居中对齐
7. 信号与槽机制
信号与槽是Qt/PySide6最为独特的机制,它实现了组件之间的解耦通信,是理解和掌握PySide6开发的核心。
7.1 基础概念
信号与槽的基本概念:
- 信号(Signal):在特定事件发生时发出的通知
- 槽(Slot):接收信号的函数或方法
- 连接(Connection):将信号连接到槽,建立事件响应关系
from PySide6.QtWidgets import QApplication, QPushButtonapp = QApplication([])
button = QPushButton("Click Me")# 信号:button的clicked信号
# 槽:print_message函数
def print_message():print("Button clicked!")# 连接:将clicked信号连接到print_message槽
button.clicked.connect(print_message)button.show()
app.exec()
7.2 信号种类与发射
7.2.1 内置信号
PySide6控件都有预定义的信号:
# 常见控件的内置信号
button.clicked.connect(handler) # 按钮被点击
checkbox.stateChanged.connect(handler) # 复选框状态变化
spinbox.valueChanged.connect(handler) # 数值改变
lineedit.textChanged.connect(handler) # 文本改变
slider.valueChanged.connect(handler) # 滑块值改变
7.2.2 自定义信号
在自定义类中定义信号:
from PySide6.QtCore import QObject, Signalclass Counter(QObject):# 定义一个无参数的信号countChanged = Signal()# 定义带一个int参数的信号valueChanged = Signal(int)# 定义带多个参数的信号rangeChanged = Signal(int, int)# 定义带不同类型参数的多个重载dataChanged = Signal([int], [str])def __init__(self):super().__init__()self._value = 0def setValue(self, value):if self._value != value:self._value = value# 发射信号self.valueChanged.emit(value)self.countChanged.emit()
使用自定义信号:
# 创建实例
counter = Counter()# 连接信号
counter.valueChanged.connect(lambda val: print(f"Value changed to: {val}"))
counter.countChanged.connect(lambda: print("Count changed"))# 触发信号
counter.setValue(10) # 输出 "Value changed to: 10" 和 "Count changed"
7.3 槽的种类与连接
7.3.1 函数槽
使用普通函数作为槽:
def handle_click():print("Button clicked")button.clicked.connect(handle_click)
7.3.2 方法槽
使用类方法作为槽:
class MyWidget(QWidget):def __init__(self):super().__init__()self.button = QPushButton("Click Me")self.button.clicked.connect(self.handle_click)layout = QVBoxLayout(self)layout.addWidget(self.button)def handle_click(self):print("Button clicked in class context")
7.3.3 Lambda表达式
使用Lambda表达式进行简单的信号处理:
# 无参数
button.clicked.connect(lambda: print("Clicked!"))# 有参数
spinbox.valueChanged.connect(lambda value: print(f"Value: {value}"))# 调用其他函数并传递额外参数
slider.valueChanged.connect(lambda value: self.updateValue(value, "slider"))
7.3.4 部分函数
使用functools.partial创建部分应用的函数:
from functools import partialdef handle_value(source, value):print(f"Value from {source}: {value}")# 连接到部分应用的函数
slider1.valueChanged.connect(partial(handle_value, "slider1"))
slider2.valueChanged.connect(partial(handle_value, "slider2"))
7.4 信号连接管理
7.4.1 断开连接
断开信号与槽的连接:
# 断开特定槽
button.clicked.disconnect(specific_handler)# 断开所有连接到该信号的槽
button.clicked.disconnect()
7.4.2 阻塞信号
临时阻止信号发射:
# 阻塞所有信号
widget.blockSignals(True)# 进行一些不需要触发信号的操作
widget.setValue(100)# 恢复信号
widget.blockSignals(False)
7.4.3 连接类型
Qt提供了不同类型的信号连接:
from PySide6.QtCore import Qt# 默认连接 - 如果信号和槽在同一线程,直接调用;否则排队
button.clicked.connect(handler)# 直接连接 - 信号发射时立即调用槽,无论线程
button.clicked.connect(handler, Qt.DirectConnection)# 队列连接 - 总是将调用排队,即使在同一线程
button.clicked.connect(handler, Qt.QueuedConnection)# 阻塞队列连接 - 如果在不同线程,将阻塞直到槽返回
button.clicked.connect(handler, Qt.BlockingQueuedConnection)
7.5 实例:自定义信号与槽
创建一个带有自定义信号与槽的温度转换器:
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,QLabel, QDoubleSpinBox, QComboBox
)
from PySide6.QtCore import Signal, Slotclass TemperatureConverter(QWidget):# 定义信号temperatureChanged = Signal(float, str)def __init__(self):super().__init__()self.setWindowTitle("Temperature Converter")self.initUI()self.setupConnections()def initUI(self):main_layout = QVBoxLayout(self)# 创建输入布局input_layout = QHBoxLayout()self.temp_spinbox = QDoubleSpinBox()self.temp_spinbox.setRange(-273.15, 1000000)self.temp_spinbox.setValue(0)self.temp_spinbox.setDecimals(2)self.unit_combo = QComboBox()self.unit_combo.addItems(["Celsius", "Fahrenheit", "Kelvin"])input_layout.addWidget(self.temp_spinbox)input_layout.addWidget(self.unit_combo)main_layout.addLayout(input_layout)# 创建输出布局self.result_layout = QVBoxLayout()self.celsius_label = QLabel("0.00 °C")self.fahrenheit_label = QLabel("32.00 °F")self.kelvin_label = QLabel("273.15 K")self.result_layout.addWidget(self.celsius_label)self.result_layout.addWidget(self.fahrenheit_label)self.result_layout.addWidget(self.kelvin_label)main_layout.addLayout(self.result_layout)def setupConnections(self):# 连接控件信号到自定义槽self.temp_spinbox.valueChanged.connect(self.onTemperatureInput)self.unit_combo.currentTextChanged.connect(self.onUnitChanged)# 连接自定义信号到自定义槽self.temperatureChanged.connect(self.updateTemperatures)@Slot(float)def onTemperatureInput(self, value):unit = self.unit_combo.currentText()self.temperatureChanged.emit(value, unit)@Slot(str)def onUnitChanged(self, unit):value = self.temp_spinbox.value()self.temperatureChanged.emit(value, unit)@Slot(float, str)def updateTemperatures(self, value, unit):if unit == "Celsius":celsius = valuefahrenheit = celsius * 9/5 + 32kelvin = celsius + 273.15elif unit == "Fahrenheit":fahrenheit = valuecelsius = (fahrenheit - 32) * 5/9kelvin = celsius + 273.15elif unit == "Kelvin":kelvin = valuecelsius = kelvin - 273.15fahrenheit = celsius * 9/5 + 32self.celsius_label.setText(f"{celsius:.2f} °C")self.fahrenheit_label.setText(f"{fahrenheit:.2f} °F")self.kelvin_label.setText(f"{kelvin:.2f} K")if __name__ == '__main__':app = QApplication([])window = TemperatureConverter()window.show()app.exec()
这个示例展示了:
- 创建自定义信号(
temperatureChanged
) - 使用
@Slot
装饰器定义槽函数 - 连接内置控件信号到自定义槽
- 连接自定义信号到自定义槽
- 在槽函数中处理业务逻辑
8. 样式与主题定制
PySide6提供了多种方式来定制应用的外观和风格,从简单的样式调整到完整的主题定制。
8.1 基本样式属性
通过样式表(QSS)为单个控件设置样式:
from PySide6.QtWidgets import QPushButtonbutton = QPushButton("Styled Button")# 设置单一样式属性
button.setStyleSheet("background-color: #4CAF50; color: white;")# 多行样式
button.setStyleSheet("""background-color: #4CAF50;color: white;border: none;padding: 8px 16px;font-size: 14px;
""")
8.2 Qt样式表(QSS)
QSS类似于CSS,允许使用选择器和属性定义样式:
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidgetapp = QApplication([])
window = QMainWindow()
central_widget = QWidget()
layout = QVBoxLayout(central_widget)# 添加一些按钮
normal_button = QPushButton("Normal Button")
danger_button = QPushButton("Danger Button")
danger_button.setObjectName("dangerButton")
success_button = QPushButton("Success Button")
success_button.setProperty("type", "success")layout.addWidget(normal_button)
layout.addWidget(danger_button)
layout.addWidget(success_button)window.setCentralWidget(central_widget)# 应用样式表
window.setStyleSheet("""/* 默认按钮样式 */QPushButton {background-color: #e0e0e0;border: none;padding: 8px 16px;color: #333;font-weight: bold;border-radius: 4px;}QPushButton:hover {background-color: #d0d0d0;}QPushButton:pressed {background-color: #c0c0c0;}/* 通过对象名选择器 */QPushButton#dangerButton {background-color: #f44336;color: white;}QPushButton#dangerButton:hover {background-color: #d32f2f;}/* 通过属性选择器 */QPushButton[type="success"] {background-color: #4CAF50;color: white;}QPushButton[type="success"]:hover {background-color: #388E3C;}
""")window.show()
app.exec()
8.3 常用样式属性
8.3.1 背景与前景
/* 背景颜色 */
background-color: #f0f0f0;/* 背景图像 */
background-image: url(background.png);
background-repeat: no-repeat;
background-position: center;/* 文本颜色 */
color: #333333;
8.3.2 边框与轮廓
/* 边框 */
border: 1px solid #999999;
border-width: 1px;
border-style: solid;
border-color: #999999;/* 特定边的边框 */
border-left: 2px dashed red;
border-top: 1px dotted blue;/* 圆角 */
border-radius: 4px;
border-top-left-radius: 8px;
8.3.3 文本格式
/* 字体 */
font-family: "Arial", sans-serif;
font-size: 14px;
font-weight: bold;/* 文本对齐 */
text-align: center;/* 文本装饰 */
text-decoration: underline;
8.3.4 尺寸与布局
/* 尺寸 */
width: 100px;
min-width: 50px;
max-width: 200px;
height: 30px;/* 内边距 */
padding: 8px;
padding-left: 16px;/* 外边距 */
margin: 5px;
margin-top: 10px;
8.4 状态相关样式
为控件的不同状态定义样式:
/* 基本状态 */
QPushButton {background-color: #e0e0e0;
}/* 悬停状态 */
QPushButton:hover {background-color: #d0d0d0;
}/* 按下状态 */
QPushButton:pressed {background-color: #c0c0c0;
}/* 选中状态 */
QCheckBox:checked {color: green;
}/* 禁用状态 */
QPushButton:disabled {background-color: #a0a0a0;color: #707070;
}/* 焦点状态 */
QLineEdit:focus {border: 2px solid #0078d7;
}
8.5 自定义应用主题
创建完整的应用主题:
class ThemeManager:@staticmethoddef apply_dark_theme(app):app.setStyleSheet("""/* 全局样式 */QWidget {background-color: #2b2b2b;color: #e0e0e0;font-family: "Segoe UI", Arial, sans-serif;}/* 主窗口 */QMainWindow {background-color: #1e1e1e;}/* 菜单 */QMenuBar {background-color: #2b2b2b;}QMenuBar::item {background-color: transparent;padding: 6px 10px;}QMenuBar::item:selected {background-color: #3a3a3a;}QMenu {background-color: #2b2b2b;border: 1px solid #555555;}QMenu::item {padding: 6px 20px;}QMenu::item:selected {background-color: #3a3a3a;}/* 按钮 */QPushButton {background-color: #0078d7;color: white;border: none;padding: 8px 16px;border-radius: 4px;}QPushButton:hover {background-color: #0086f0;}QPushButton:pressed {background-color: #006ac1;}QPushButton:disabled {background-color: #555555;color: #888888;}/* 输入框 */QLineEdit, QTextEdit, QPlainTextEdit {background-color: #333333;border: 1px solid #555555;border-radius: 3px;padding: 4px;color: #e0e0e0;}QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {border: 1px solid #0078d7;}/* 下拉框 */QComboBox {background-color: #333333;border: 1px solid #555555;border-radius: 3px;padding: 4px 8px;color: #e0e0e0;}QComboBox::drop-down {subcontrol-origin: padding;subcontrol-position: top right;width: 20px;border-left: 1px solid #555555;}/* 复选框 */QCheckBox {spacing: 8px;}QCheckBox::indicator {width: 16px;height: 16px;}/* 选项卡 */QTabWidget::pane {border: 1px solid #555555;}QTabBar::tab {background-color: #2b2b2b;border: 1px solid #555555;padding: 6px 12px;}QTabBar::tab:selected {background-color: #333333;}/* 滚动条 */QScrollBar:vertical {background-color: #2b2b2b;width: 12px;margin: 12px 0 12px 0;}QScrollBar::handle:vertical {background-color: #505050;min-height: 20px;border-radius: 6px;}QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {border: none;background: none;}""")@staticmethoddef apply_light_theme(app):# 实现浅色主题...pass# 使用示例
if __name__ == "__main__":app = QApplication([])ThemeManager.apply_dark_theme(app)# 创建应用主窗口和其他界面元素...# ...app.exec()
8.6 动态切换主题
实现主题切换功能:
class ThemeSwitchableApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Theme Switcher")self.setGeometry(100, 100, 800, 600)# 创建中央部件central_widget = QWidget()self.setCentralWidget(central_widget)# 创建布局layout = QVBoxLayout(central_widget)# 添加一些控件title = QLabel("Theme Switching Demo")title.setAlignment(Qt.AlignCenter)title.setFont(QFont("Arial", 18))self.text_edit = QTextEdit()self.text_edit.setPlaceholderText("Type something here...")self.combo = QComboBox()self.combo.addItems(["Option 1", "Option 2", "Option 3"])self.checkbox = QCheckBox("Enable feature")# 主题切换按钮theme_layout = QHBoxLayout()self.light_theme_btn = QPushButton("Light Theme")self.light_theme_btn.clicked.connect(self.apply_light_theme)self.dark_theme_btn = QPushButton("Dark Theme")self.dark_theme_btn.clicked.connect(self.apply_dark_theme)theme_layout.addWidget(self.light_theme_btn)theme_layout.addWidget(self.dark_theme_btn)# 添加控件到布局layout.addWidget(title)layout.addWidget(self.text_edit)layout.addWidget(self.combo)layout.addWidget(self.checkbox)layout.addLayout(theme_layout)# 设置初始主题self.current_theme = "light"self.apply_light_theme()def apply_light_theme(self):if self.current_theme == "light":returnself.current_theme = "light"# 提供一个简单的浅色主题self.setStyleSheet("""QWidget {background-color: #f0f0f0;color: #333333;}QPushButton {background-color: #e0e0e0;border: 1px solid #d0d0d0;padding: 6px 12px;border-radius: 4px;}QPushButton:hover {background-color: #d0d0d0;}QTextEdit, QComboBox {background-color: white;border: 1px solid #d0d0d0;border-radius: 3px;padding: 4px;}""")def apply_dark_theme(self):if self.current_theme == "dark":returnself.current_theme = "dark"# 提供一个简单的深色主题self.setStyleSheet("""QWidget {background-color: #2b2b2b;color: #e0e0e0;}QPushButton {background-color: #0078d7;color: white;border: none;padding: 6px 12px;border-radius: 4px;}QPushButton:hover {background-color: #0086f0;}QTextEdit, QComboBox {background-color: #333333;border: 1px solid #555555;border-radius: 3px;padding: 4px;color: #e0e0e0;}""")
9. Qt Designer 可视化设计
Qt Designer是一个强大的可视化界面设计工具,与PySide6集成使用可以大大提高开发效率。
9.1 Qt Designer 基础
Qt Designer随PySide6一起安装,可以通过命令行启动:
pyside6-designer
主要功能区域:
- 控件面板:可用的控件和布局
- 对象检查器:显示控件层次结构
- 属性编辑器:修改选中控件的属性
- 信号/槽编辑器:可视化连接信号和槽
- 资源浏览器:管理图像等资源
- 动作编辑器:创建和管理应用动作
9.2 创建界面
在Qt Designer中设计界面的基本步骤:
- 选择模板(如Widget、Main Window、Dialog)
- 从控件面板拖放控件到设计区域
- 设置布局(右键区域选择布局类型)
- 调整控件属性(在属性编辑器中)
- 设置控件的对象名(objectName属性)
- 保存为.ui文件
9.3 加载.ui文件
9.3.1 使用QUiLoader
动态加载.ui文件:
from PySide6.QtWidgets import QApplication
from PySide6.QtUiTools import QUiLoader
from PySide6.QtCore import QFile, QIODeviceapp = QApplication([])# 加载UI文件
ui_file = QFile("mainwindow.ui")
ui_file.open(QIODevice.ReadOnly)loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()# 访问UI中的控件
window.pushButton.clicked.connect(lambda: print("Button clicked!"))
window.lineEdit.textChanged.connect(lambda text: print(f"Text: {text}"))window.show()
app.exec()
9.3.2 使用uic编译
将.ui文件转换为Python代码:
pyside6-uic mainwindow.ui -o ui_mainwindow.py
然后在代码中使用:
from PySide6.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindowclass MainWindow(QMainWindow):def __init__(self):super().__init__()# 设置UIself.ui = Ui_MainWindow()self.ui.setupUi(self)# 连接信号self.ui.pushButton.clicked.connect(self.on_button_clicked)self.ui.lineEdit.textChanged.connect(self.on_text_changed)def on_button_clicked(self):print("Button clicked!")def on_text_changed(self, text):print(f"Text: {text}")app = QApplication([])
window = MainWindow()
window.show()
app.exec()
9.4 多界面管理
管理多个界面文件:
class Application:def __init__(self):self.app = QApplication([])self.main_window = self.load_ui("mainwindow.ui")self.dialog = Nonedef load_ui(self, ui_file_name):ui_file = QFile(ui_file_name)ui_file.open(QIODevice.ReadOnly)loader = QUiLoader()window = loader.load(ui_file)ui_file.close()return windowdef show_dialog(self):# 懒加载对话框if not self.dialog:self.dialog = self.load_ui("dialog.ui")# 连接对话框信号self.dialog.accepted.connect(self.on_dialog_accepted)self.dialog.show()def on_dialog_accepted(self):print("Dialog accepted")def run(self):# 连接主窗口信号self.main_window.actionShowDialog.triggered.connect(self.show_dialog)self.main_window.show()return self.app.exec()if __name__ == "__main__":app = Application()sys.exit(app.run())
9.5 表单继承
继承Designer创建的UI类:
from PySide6.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindow# 继承UI类
class MainWindow(QMainWindow, Ui_MainWindow):def __init__(self):super().__init__()# 设置UIself.setupUi(self)# 添加信号连接self.pushButton.clicked.connect(self.on_button_clicked)self.actionExit.triggered.connect(self.close)def on_button_clicked(self):self.statusBar().showMessage("Button clicked!")self.label.setText(f"Hello, {self.lineEdit.text()}")app = QApplication([])
window = MainWindow()
window.show()
app.exec()
9.6 资源文件管理
9.6.1 创建资源文件
创建资源文件(resources.qrc):
<!DOCTYPE RCC>
<RCC version="1.0"><qresource prefix="/images"><file>icons/new.png</file><file>icons/open.png</file><file>icons/save.png</file></qresource><qresource prefix="/styles"><file>styles/dark.qss</file><file>styles/light.qss</file></qresource>
</RCC>
9.6.2 编译资源文件
pyside6-rcc resources.qrc -o resources_rc.py
9.6.3 使用资源
在.ui文件中引用资源:
import resources_rc
在代码中使用资源:
from PySide6.QtGui import QIcon
from PySide6.QtCore import QFile, QTextStream# 使用图标
icon = QIcon(":/images/icons/save.png")
button.setIcon(icon)# 加载样式表
style_file = QFile(":/styles/styles/dark.qss")
style_file.open(QFile.ReadOnly | QFile.Text)
stream = QTextStream(style_file)
style_sheet = stream.readAll()
app.setStyleSheet(style_sheet)
10. 多线程与并发处理
在GUI应用程序中,长时间运行的任务应该在单独的线程中执行,以避免界面冻结。
10.1 QThread 基本用法
使用QThread创建独立线程:
from PySide6.QtCore import QThread, Signal
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QProgressBar, QVBoxLayout, QWidget
import timeclass Worker(QThread):# 定义信号progress = Signal(int)completed = Signal()def run(self):# 线程的主要执行代码for i in range(101):# 执行耗时操作time.sleep(0.1) # 模拟耗时操作# 发出进度信号self.progress.emit(i)# 发出完成信号self.completed.emit()class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("QThread Example")self.resize(400, 200)# 创建中央部件central_widget = QWidget()self.setCentralWidget(central_widget)# 创建布局layout = QVBoxLayout(central_widget)# 创建进度条self.progress_bar = QProgressBar()layout.addWidget(self.progress_bar)# 创建按钮self.start_button = QPushButton("Start Task")self.start_button.clicked.connect(self.start_task)layout.addWidget(self.start_button)# 创建工作线程self.worker = Worker()self.worker.progress.connect(self.update_progress)self.worker.completed.connect(self.task_completed)def start_task(self):self.start_button.setEnabled(False)self.progress_bar.setValue(0)self.worker.start()def update_progress(self, value):self.progress_bar.setValue(value)def task_completed(self):self.start_button.setEnabled(True)self.start_button.setText("Start Again")app = QApplication([])
window = MainWindow()
window.show()
app.exec()
10.2 QRunnable 和线程池
使用QThreadPool管理多个并发任务:
from PySide6.QtCore import QRunnable, QThreadPool, QObject, Signal, Slot# 信号需要QObject
class WorkerSignals(QObject):progress = Signal(int)completed = Signal()error = Signal(str)class Task(QRunnable):def __init__(self, fn, *args, **kwargs):super().__init__()self.fn = fnself.args = argsself.kwargs = kwargsself.signals = WorkerSignals()@Slot()def run(self):try:# 执行函数result = self.fn(*self.args, **self.kwargs)except Exception as e:self.signals.error.emit(str(e))else:self.signals.completed.emit()# 使用线程池
def start_tasks():# 获取全局线程池pool = QThreadPool.globalInstance()print(f"使用最大 {pool.maxThreadCount()} 个线程")# 创建和提交任务for i in range(5):task = Task(long_running_function, i)pool.start(task)
11. 数据持久化
PySide6应用通常需要保存用户设置、缓存或其他数据。
11.1 QSettings
用于存储应用程序设置:
from PySide6.QtCore import QSettings# 创建设置对象
settings = QSettings("MyCompany", "MyApp")# 保存设置
settings.setValue("window/size", window.size())
settings.setValue("window/position", window.pos())
settings.setValue("user/name", "John Doe")
settings.setValue("user/preferences", {"theme": "dark", "language": "en"})# 读取设置
window_size = settings.value("window/size")
user_name = settings.value("user/name", "Guest") # 提供默认值
user_prefs = settings.value("user/preferences", {})# 检查键是否存在
if settings.contains("user/name"):print("User name is set")# 删除设置
settings.remove("user/name")# 清除所有设置
settings.clear()
11.2 SQLite数据库集成
使用内置sqlite3模块或QSqlDatabase:
from PySide6.QtSql import QSqlDatabase, QSqlQuery# 创建连接
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("myapp.db")if not db.open():print("Cannot open database")print(db.lastError().text())return# 创建表
query = QSqlQuery()
query.exec_("""CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,email TEXT UNIQUE)
""")# 插入数据
query.prepare("INSERT INTO users (name, email) VALUES (:name, :email)")
query.bindValue(":name", "John Doe")
query.bindValue(":email", "john@example.com")
query.exec_()# 查询数据
query.exec_("SELECT * FROM users")
while query.next():user_id = query.value(0)name = query.value(1)email = query.value(2)print(f"User: {user_id}, {name}, {email}")# 关闭数据库
db.close()
12. 高级图形与动画
PySide6提供了丰富的图形和动画功能。
12.1 自定义绘图
使用QPainter进行自定义绘图:
from PySide6.QtWidgets import QWidget
from PySide6.QtGui import QPainter, QPen, QBrush
from PySide6.QtCore import Qtclass DrawingWidget(QWidget):def __init__(self):super().__init__()self.setMinimumSize(300, 200)def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.Antialiasing)# 设置画笔(轮廓)pen = QPen(Qt.blue, 2)painter.setPen(pen)# 设置画刷(填充)brush = QBrush(Qt.green)painter.setBrush(brush)# 绘制矩形painter.drawRect(50, 50, 100, 50)# 绘制圆形painter.setPen(QPen(Qt.red, 3))painter.setBrush(QBrush(Qt.yellow))painter.drawEllipse(200, 50, 80, 80)# 绘制文本painter.setPen(Qt.black)painter.drawText(50, 150, "Hello, PySide6!")
12.2 动画效果
使用QPropertyAnimation创建平滑动画:
from PySide6.QtCore import QPropertyAnimation, QEasingCurve, Propertyclass AnimatedButton(QPushButton):def __init__(self, text):super().__init__(text)self._opacity = 1.0def opacity(self):return self._opacitydef setOpacity(self, opacity):self._opacity = opacityself.update()# 创建属性opacity = Property(float, opacity, setOpacity)def paintEvent(self, event):painter = QPainter(self)painter.setOpacity(self._opacity)super().paintEvent(event)# 创建动画
button = AnimatedButton("Animated Button")
animation = QPropertyAnimation(button, b"opacity")
animation.setDuration(1000) # 1秒
animation.setStartValue(1.0)
animation.setEndValue(0.2)
animation.setEasingCurve(QEasingCurve.InOutQuad)
animation.start()
13. 国际化与本地化
使PySide6应用支持多语言。
13.1 使用QTranslator
from PySide6.QtCore import QTranslator, QLocale# 创建翻译器
translator = QTranslator()# 加载翻译文件(根据系统语言)
locale = QLocale.system().name() # 如 "zh_CN"
if translator.load(f"myapp_{locale}", "translations"):app.installTranslator(translator)
13.2 使用tr()进行文本标记
class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle(self.tr("My Application"))label = QLabel(self.tr("Hello, World!"))button = QPushButton(self.tr("Click Me"))# 带复数和上下文的翻译items_count = 5message = self.tr("%n item(s) selected", "", items_count)# 带上下文的翻译context_message = self.tr("Open", "File menu action")
14. 实战项目开发
14.1 应用架构设计
使用模型-视图-控制器(MVC)或模型-视图-视图模型(MVVM)架构:
# 模型 - 处理数据
class TodoModel:def __init__(self):self.items = []def add_item(self, text, due_date=None):self.items.append({"text": text, "completed": False, "due_date": due_date})def complete_item(self, index):if 0 <= index < len(self.items):self.items[index]["completed"] = True# 视图模型 - 连接模型和视图
class TodoViewModel(QObject):item_added = Signal(str, object)item_completed = Signal(int)def __init__(self, model):super().__init__()self.model = modeldef add_item(self, text, due_date=None):self.model.add_item(text, due_date)self.item_added.emit(text, due_date)def complete_item(self, index):self.model.complete_item(index)self.item_completed.emit(index)# 视图 - 用户界面
class TodoView(QMainWindow):def __init__(self, view_model):super().__init__()self.view_model = view_modelself.setupUi()# 连接视图模型的信号self.view_model.item_added.connect(self.add_item_to_ui)self.view_model.item_completed.connect(self.mark_item_completed)
14.2 项目质量保证
- 使用类型提示
- 添加单元测试
- 使用版本控制
- 编写文档
- 进行代码审查
15. 打包与发布
15.1 使用PyInstaller打包
将PySide6应用打包成可执行文件:
# 安装PyInstaller
pip install pyinstaller# 打包应用
pyinstaller --name=MyApp --windowed --onefile main.py# 指定图标和其他资源
pyinstaller --name=MyApp --windowed --onefile --icon=app_icon.ico main.py
15.2 使用cx_Freeze打包
另一个打包选项:
# setup.py
from cx_Freeze import setup, Executablesetup(name="MyApp",version="1.0",description="My PySide6 Application",executables=[Executable("main.py", target_name="MyApp.exe", icon="app_icon.ico")],options={"build_exe": {"packages": ["PySide6"],"include_files": ["icons/", "translations/"]}}
)
运行:
python setup.py build
16. 总结
本文全面介绍了PySide6桌面应用开发,从基础概念、控件使用、布局管理,到高级特性如多线程、动画和国际化。PySide6结合了Qt强大的功能和Python的简洁性,是创建专业级桌面应用的理想选择。
掌握PySide6可以让开发者创建跨平台、功能丰富的应用程序,从简单工具到复杂企业软件。通过持续学习和实践,你可以充分发挥PySide6的潜力,构建出美观、高效且用户友好的桌面应用。
作者:climber1121
链接:https://blog.csdn.net/climber1121
来源:CSDN
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。