【Python桌面应用】PySide6 界面开发完全指南

文章目录

    • 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()

这个示例展示了:

  1. 创建自定义信号(temperatureChanged)
  2. 使用@Slot装饰器定义槽函数
  3. 连接内置控件信号到自定义槽
  4. 连接自定义信号到自定义槽
  5. 在槽函数中处理业务逻辑

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

主要功能区域:

  1. 控件面板:可用的控件和布局
  2. 对象检查器:显示控件层次结构
  3. 属性编辑器:修改选中控件的属性
  4. 信号/槽编辑器:可视化连接信号和槽
  5. 资源浏览器:管理图像等资源
  6. 动作编辑器:创建和管理应用动作

9.2 创建界面

在Qt Designer中设计界面的基本步骤:

  1. 选择模板(如Widget、Main Window、Dialog)
  2. 从控件面板拖放控件到设计区域
  3. 设置布局(右键区域选择布局类型)
  4. 调整控件属性(在属性编辑器中)
  5. 设置控件的对象名(objectName属性)
  6. 保存为.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
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/75986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用 pytorch 从零开始创建大语言模型(三):编码注意力机制

从零开始创建大语言模型&#xff08;Python/pytorch &#xff09;&#xff08;三&#xff09;&#xff1a;编码注意力机制 3 编码注意力机制3.1 建模长序列的问题3.2 使用注意力机制捕捉数据依赖关系3.3 通过自注意力关注输入的不同部分3.3.1 一个没有可训练权重的简化自注意力…

Spring中的IOC及AOP概述

前言 Spring 框架的两大核心设计思想是 IOC&#xff08;控制反转&#xff09; 和 AOP&#xff08;面向切面编程&#xff09;。它们共同解决了代码耦合度高、重复逻辑冗余等问题。 IOC&#xff08;控制反转&#xff09; 1.核心概念 控制反转&#xff08;Inversion of Control…

STM32_HAL开发环境搭建【Keil(MDK-ARM)、STM32F1xx_DFP、 ST-Link、STM32CubeMX】

安装Keil(MDK-ARM)【集成开发环境IDE】 我们会在Keil(MDK-ARM)上去编写代码、编译代码、烧写代码、调试代码。 Keil(MDK-ARM)的安装方法&#xff1a; 教学视频的第02分03秒开始看。 安装过程中请修改一下下面两个路径&#xff0c;避免占用C盘空间。 Core就是Keil(MDK-ARM)的…

python 第三方库 - dotenv读取配置文件

.env 文件是一种用于存储环境变量的配置文件&#xff0c;常用于项目的运行环境设置。环境变量是操作系统层面的一些变量&#xff0c;它们可以被应用程序访问和使用&#xff0c;通常包含敏感信息或特定于环境的配置&#xff0c;如数据库连接信息、API 密钥、调试模式等。 安装p…

用python压缩图片大小

下载库 cmd开命令或者PyCharm执行都行 pip install pillow2. 然后就是代码 from PIL import Imagedef compress_image(input_path, output_path, quality85, max_sizeNone):"""压缩图片大小。参数:- input_path: 输入图片路径- output_path: 输出图片路径- qu…

【自用记录】本地关联GitHub以及遇到的问题

最近终于又想起GitHub&#xff0c;想上传代码和项目到仓库里。 由于很早之前有在本地连接过GitHub&#xff08;但没怎么用&#xff09;&#xff0c;现在需要重新搞起&#xff08;操作忘得差不多&#xff09;。 在看教程实操的过程中遇到了一些小问题&#xff0c;遂记录一下。 前…

在一个scss文件中定义变量,在另一个scss文件中使用

_variables.scss文件 : $line-gradient-init-color: linear-gradient(90deg, #8057ff 0%, #936bff 50%, #b892ff 100%); $line-gradient-hover-color: linear-gradient(90deg, #936bff 0%, #b892ff 50%, #f781ce 100%); $line-gradient-active-color: linear-gradient(90deg, …

从零开始研发GPS接收机连载——19、自制GPS接收机的春运之旅

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 从零开始研发GPS接收机连载——19、自制GPS接收机的春运之旅 许久未曾更新这个系列&#xff0c;并非我平日里对这事儿没了兴致&#xff0c;不再愿意折腾。实则是受限于自身条…

智能驾驶功能LCC车道保持居中

画龙现象就是LCC常见bug LDW车道偏离预警 LKA车道保持 声音其实就是蜂鸣器 有些车是40 有些是60

Java全栈面试宝典:线程机制与Spring依赖注入深度解析

目录 一、Java线程核心机制 &#x1f525; 问题3&#xff1a;start()与run()的底层执行差异 线程启动流程图解 核心差异对照表 代码验证示例 &#x1f525; 问题4&#xff1a;Thread与Runnable的六大维度对比 类关系UML图 最佳实践代码 &#x1f525; 问题5&#xff1…

使用ANTLR4解析Yaml,JSON和Latex

文章目录 ANTLR4基本使用**1. 安装 Java 运行时&#xff08;必需&#xff09;****2. 安装 ANTLR4 命令行工具****方法一&#xff1a;通过包管理器&#xff08;推荐&#xff09;****macOS/Linux (Homebrew)****Windows (Chocolatey)** **方法二&#xff1a;手动安装&#xff08;…

NixVis 开源轻量级 Nginx 日志分析工具

NixVis NixVis 是一款基于 Go 语言开发的、开源轻量级 Nginx 日志分析工具&#xff0c;专为自部署场景设计。它提供直观的数据可视化和全面的统计分析功能&#xff0c;帮助您实时监控网站流量、访问来源和地理分布等关键指标&#xff0c;无需复杂配置即可快速部署使用。 演示…

黑盒测试的等价类划分法(输入数据划分为有效的等价类和无效的等价类)

重点: 有效等价和单个无效等价各取1个即可 1、正向用例:一条尽可能覆盖多条2、逆向用例:每一条数据&#xff0c;都是一条单独用例。 步骤: 1、明确需求 2、确定有效和无效等价 3、根据有效和无效造数据编写用例 3、适用场景 针对:需要有大量数据测试输入&#xff0c; …

Linux Mem -- 通过reserved-memory缩减内存

目录 1. reserved-memory缩减内存 2. 为什么要通过2段512GB预留内存实现该缩减呢&#xff1f; 3. reserved-momery中的no-map属性 4. 预留的的内存是否会被统计到系统MemTotal中&#xff1f; 本文是解决具体的一些思考总结&#xff0c;和Linux内核的reserved-memory机制相关…

多线程—synchronized原理

上篇文章&#xff1a; 多线程—锁策略https://blog.csdn.net/sniper_fandc/article/details/146508232?fromshareblogdetail&sharetypeblogdetail&sharerId146508232&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目录 1 synchronized的锁…

AWS混合云部署实战:打造企业级数字化转型的“黄金架构”

引言 “上云是必然&#xff0c;但全部上云未必是必然。”在数字化转型的深水区&#xff0c;企业面临的核心矛盾日益凸显&#xff1a;如何在享受公有云敏捷性的同时&#xff0c;满足数据主权、低延迟和遗留系统兼容的刚性需求&#xff1f; AWS混合云凭借“云上云下一张网”的独…

进程模型5-0号进程

内核版本架构作者GitHubCSDNLinux-3.0.1armv7-ALux1206 0号进程的作用 在 Linux 中除了 init_task 0号进程&#xff0c;所有的线/进程都是通过 do_fork 函数复制父线/进程创建得到&#xff0c;因为 0号进程产生时没有任何进程可以参照&#xff0c;只能通过静态方式构造进程描述…

计算机二级考前急救(Word篇)

重点题&#xff08;20套&#xff0c;标黄为精选10套&#xff09;&#xff1a;4&#xff0c;15&#xff0c;17&#xff0c;19&#xff0c;21&#xff0c;24&#xff0c;25&#xff0c;27&#xff0c;36&#xff0c;40&#xff0c;12&#xff0c;18&#xff0c;20&#xff0c;22&…

constant(safe-area-inset-bottom)和env(safe-area-inset-bottom)在uniapp中的使用方法解析

在微信小程序中&#xff0c;padding-bottom: constant(safe-area-inset-bottom); 和 padding-bottom: env(safe-area-inset-bottom); 这两个 CSS 属性用于处理 iPhone X 及更高版本设备的安全区域&#xff08;safe area&#xff09;。这些设备的底部有一个“Home Indicator”&a…

十二、Cluster集群

目录 一、集群简介1、现状问题2、集群作用 二、集群结构设计1、集群存储设2、消息通信设计 三、Cluster集群三主三从结构搭建1、redis.conf配置文件可配置项2、配置集群3、链接集群4、命令客户端连接集群并使用 四、集群扩容1、添加节点2、槽位分配3、添加从节点 五、集群缩容1…