【Python桌面应用开发】PySide6界面设计全方位指南
文章目录
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的主要特点包括:
2.2 PySide6 vs. PyQt6
PySide6和PyQt6都是Qt的Python绑定,功能几乎相同,主要区别在于许可证:
代码方面,两者差异很小,通常只需修改导入语句就能在两者间切换。
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开发环境:
3. Qt 设计原理
3.1 Qt对象模型
Qt对象模型是理解PySide6的基础,它基于以下几个核心概念:
例如,一个基本的QObject派生类:
from PySide6.QtCore import QObject, Signal, Slot
class 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 = value
self.valueChanged.emit(value)
3.2 信号与槽机制
信号与槽是Qt最独特的特性之一,它提供了一种类型安全的回调机制:
信号与槽的连接:
from PySide6.QtWidgets import QApplication, QPushButton
app = 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)通常位于组件的左上角:
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程序的重要部分:
4.3 使用面向对象方法重构
更实用的做法是将应用窗口封装为类:
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget
class 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 Qt
class CounterApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Counter App")
self.setGeometry(100, 100, 300, 200)
# 计数器初始值
self.counter = 0
# 设置UI
self.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 += 1
self.label.setText(f"Count: {self.counter}")
def reset(self):
self.counter = 0
self.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, QKeySequence
class 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 QIcon
class 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, QLabel
class 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, QPushButton
widget = 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, QPushButton
widget = 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, QLineEdit
widget = 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, QComboBox
widget = 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, QSizePolicy
button = 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, QWidget
widget = QWidget()
layout = QHBoxLayout(widget)
# 添加一个按钮靠左
layout.addWidget(QPushButton("Left"))
# 添加可拉伸空间
layout.addStretch(1)
# 添加一个按钮靠右
layout.addWidget(QPushButton("Right"))
# 结果:按钮将分别靠左和靠右,中间有弹性空间
6.6.3 边距和间距
调整布局的边距和间距:
from PySide6.QtWidgets import QVBoxLayout
layout = QVBoxLayout()
# 设置所有边距(左、上、右、下)
layout.setContentsMargins(10, 20, 10, 20)
# 设置控件之间的间距
layout.setSpacing(15)
6.6.4 对齐方式
控制控件在布局中的对齐方式:
from PySide6.QtWidgets import QHBoxLayout, QPushButton
from PySide6.QtCore import Qt
layout = 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 基础概念
信号与槽的基本概念:
from PySide6.QtWidgets import QApplication, QPushButton
app = 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, Signal
class Counter(QObject):
# 定义一个无参数的信号
countChanged = Signal()
# 定义带一个int参数的信号
valueChanged = Signal(int)
# 定义带多个参数的信号
rangeChanged = Signal(int, int)
# 定义带不同类型参数的多个重载
dataChanged = Signal([int], [str])
def __init__(self):
super().__init__()
self._value = 0
def 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 partial
def 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, Slot
class 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 = value
fahrenheit = celsius * 9/5 + 32
kelvin = celsius + 273.15
elif unit == "Fahrenheit":
fahrenheit = value
celsius = (fahrenheit - 32) * 5/9
kelvin = celsius + 273.15
elif unit == "Kelvin":
kelvin = value
celsius = kelvin - 273.15
fahrenheit = celsius * 9/5 + 32
self.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 QPushButton
button = 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, QWidget
app = 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:
@staticmethod
def 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;
}
""")
@staticmethod
def 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":
return
self.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":
return
self.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, QIODevice
app = 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_MainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# 设置UI
self.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 = None
def 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 window
def 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__()
# 设置UI
self.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 time
class 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 = fn
self.args = args
self.kwargs = kwargs
self.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 Qt
class 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, Property
class AnimatedButton(QPushButton):
def __init__(self, text):
super().__init__(text)
self._opacity = 1.0
def opacity(self):
return self._opacity
def setOpacity(self, opacity):
self._opacity = opacity
self.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 = 5
message = 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 = model
def 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_model
self.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, Executable
setup(
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
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。
作者:climber1121