1 概述
通常提到的 PyQt 实际上可以分为 PyQt 和 PySide 两个分支。
PyQt6 和 PySide6 都是基于 Qt 库开发的 Python 绑定,用于构建跨平台的图形用户界面(GUI)应用程序。它们在功能上非常相似,主要区别体现在授权方式、维护团队以及部分 API 设计上。
本文重点介绍通用 PySide 的环境配置、工具配置及使用方法,适用于在线、离线配置,兼容多个常用版本。
- PySide 环境配置 是进行 PySide 开发的第一步,虽然基础但至关重要。
- 在实际开发中,工具的使用 对开发效率影响较大。
- 与 Qt 官方安装包“开箱即用”的体验不同,PySide 相对较新,存在较多版本差异和 bug,因此本文将总结以下内容:
- 常用版本的环境搭建
- 工具配置与使用技巧
- 常见问题与 bug 的解决方案
- 主要版本:
- PySide2:基于 Qt 5(>=5.11);
- PySide6:基于 Qt 6,是当前推荐使用的版本;
1.1 PySide 能做什么?
- 图形用户界面开发:使用 Qt 提供的 QWidget 或 QML 模块构建桌面应用程序界面。
- 跨平台支持:支持 Windows、macOS、Linux 等主流操作系统。
- 多媒体和网络功能:内置模块支持音视频播放、网络通信(如 HTTP 请求、WebSocket)等功能。
- 数据可视化:利用 Qt Charts、QOpenGLWidget 等模块实现图表展示和 3D 渲染。
- 嵌入式系统开发:可用于开发嵌入式设备的 UI,如工业控制面板、车载系统等。
- 与 Web 技术集成:使用 Qt WebEngine 模块可以嵌入网页内容或开发混合型应用。
1.2 PySide 的优点
优点 | 描述 |
---|---|
🌐 跨平台 | 支持 Windows、Linux、macOS,甚至嵌入式系统。 |
📦 功能丰富 | 提供 GUI、网络、数据库、多线程、绘图等一整套模块。 |
🔧 官方支持 | PySide 是 Qt 官方提供的 Python 绑定,更新及时、文档完善。 |
🚀 性能较好 | 基于 C++ 的 Qt 引擎,性能优于 Tkinter 等纯 Python GUI 框架。 |
📐 支持 QML | 可以结合 QML 进行现代 UI 设计,适合动态界面。 |
1.3 PySide 的缺点
缺点 | 描述 |
---|---|
⚖️ 许可证复杂 | 商业用途需要购买 Qt 许可证;开源项目可用 LGPL 协议。 |
📦 包体积较大 | 打包发布时依赖较多,最终程序体积较大。 |
🧠 学习曲线陡峭 | Qt API 复杂,对新手有一定门槛,尤其是布局管理、信号槽机制等。 |
📱 移动端支持有限 | 虽然支持 Android/iOS,但不如原生开发或 Flutter/Kivy 成熟。 |
1.4 示例代码(简单窗口)
from PySide6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout # 导入必要的模块
app = QApplication([]) # 创建一个QApplication实例,这是所有PySide6应用程序的基础
w = QWidget() # 创建主窗口部件(QWidget),它将作为应用程序的主窗口
label = QLabel("Hello, PySide6!", w) # 创建一个标签控件(QLabel),显示文本 "Hello, PySide6!"
w.show() # 显示主窗口
app.exec() # 进入应用程序的主循环,等待用户交互
2 环境准备
2.1 安装必要软件
- Python:从 Python 官方网站 下载并安装。
- PyCharm:从 PyCharm 官方网站 下载并安装。
- Qt(可选):从 Qt 官方下载页面 下载并安装。虽然可以在 Qt Creator 中开发 PyQt 项目,但 PyCharm 对 Python 语法的支持更好。
2.2 修改 pip 源
为避免默认源安装速度过慢,建议使用 阿里云 pip 镜像。按以下步骤操作,如果不存在则创建:
- Windows系统:
C:\Users\用户名\.pip\pip.conf
; - Linux系统:
~/.pip/pip.conf
; - 在
.pip
文件夹中创建一个pip.conf
文件,并添加以下内容:
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
3 PySide2
3.1 环境要求
支持环境
环境 | 版本 |
---|---|
PySide2 | python2.7、3.5<=python<=3.10 |
系统 | win7+、linux、MacOS |
演示环境
环境 | 版本 |
---|---|
PySide2 | PySide2=5.15.2.1 |
python | V3.8.10 |
系统 | windows10 |
pycharm | pycharm-community-2024.3.5.exe |
3.2 配置PySide2
方法1: 命令行安装(在线)
- 使用
pip install pyside2
安装最新版本(网络状况差可能导致安装失败); - 使用
pip install pyside2==5.15.2.1
指定安装版本(网络状况差可能导致安装失败); - 使用
pip install PySide2 --index-url https://mirrors.aliyun.com/pypi/simple
指定临时安装源进行安装; - 使用
pip install https://download.qt.io/official_releases/QtForPython/pyside2/PySide2-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl
指定下载地址进行安装;
方法2: 下载whl包安装(离线)
- 进入Index of /official_releases/QtForPython/pyside2或者PySide2 ·PyPI下载whl包到本地;
- 例如PySide2-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl;
- 然后使用
pip install ./PySide2-5.15.2.1-5.15.2-cp35.cp36.cp37.cp38.cp39.cp310-none-win_amd64.whl
命令进行安装。
方法3: 使用pycharm安装(在线)
打开pycharm,创建一个工程;
选择python软件包,搜索PySide2,如下所示,点击【安装】;
3.3 工具配置
pyside2或者pyside6<6.5的版本,内置工具不多,只有designer、lupdate、rcc、uic等几个;
如果需要用到其他工具,可以单独安装Qt安装包或者高版本的PySide6;
安装完成PySide2后,打开pycharm,【文件】【设置】【工具】【外部工具】;
点击+,如下所示配置
pyside2-designer
工具;配置完成后,点击【工具】【外部工具】就可以打开UI设计器。
使用
pyproject
文件管理工程是一个更加简便的方式,但是只有在PySide6.5以上版本才支持,这里通过自己编写脚本,可以通过pyproject文件对工程进行管理,简化部分操作;在工程顶级路径下创建一个project.py文件;
添加章节6脚本,运行脚本代码可自动执行重复操作,简化开发流程,避免出现基础错误。
4 PySide6
4.1 环境
支持环境
环境 | 版本 |
---|---|
PySide6 | 3.9<=python |
系统 | win10+、linux、MacOS |
支持win7系统 | python3.8 + PySide6.1.3 |
演示环境
环境 | 版本 |
---|---|
PySide6 | PySide6=6.8.3 |
python | V3.13.3 |
系统 | windows10 |
pycharm | pycharm-community-2024.3.5.exe |
4.2 配置PySide6
安装方法1:使用命令行在线安装
使用
pip install PySide6
目录安装默认版本;使用
pip install pyside6==6.8.3
安装指定版本;使用
pip install PySide6 --index-url https://mirrors.aliyun.com/pypi/simple
指定安装源进行安装;使用
pip install https://download.qt.io/official_releases/QtForPython/pyside6-essentials/PySide6_Essentials-6.8.2.1-cp39-abi3-win_amd64.whl
指定下载地址进行安装;使用
pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.4/latest pyside6 --trusted-host download.qt.io
从指定服务器安装。
安装方法2: 使用pycharm安装
打开pycharm,创建一个工程;
选择python软件包,搜索PySide6,如下所示,点击【安装】;
安装方法3: 下载whl安装包,离线安装,对于离线环境或者网络不好pip安装失败的环境可用;
进入Index of /official_releases/QtForPython下载地址或者pypi下载地址;
选择自己需要的版本、系统,例如
PySide6_Essentials-6.8.3-cp39-abi3-win_amd64.whl、shiboken6_generator-6.8.3-cp39-abi3-win_amd64.whl、shiboken6-6.9.0-cp39-abi3-win_amd64.whl
,下载到本地,主要3个包版本要相同;然后执行
pip install ./PySide6_Essentials-6.8.3-cp39-abi3-win_amd64.whl ./shiboken6_generator-6.8.3-cp39-abi3-win_amd64.whl ./shiboken6-6.9.0-cp39-abi3-win_amd64.whl
命令进行安装。
4.3 工具配置
打开【设置】【工具】【外部工具】,点击+;
添加
designer
,设置程序路径和工作目录如下所示;添加
pyside6-project
,用于构建pyside工程;更新翻译文件(ts)
打开翻译文件
pyside6-linguist
4.4 修改pyside6-project lupdate的bug
使用
pyside6-project lupdate
命令可以在 PySide6 中生成翻译使用的 ts 文件,但存在一些问题:- 无法将 ts 文件放在子文件夹下。
- 无法清除已经失效的文本。
这是因为
pyside6-project lupdate
中使用的是 ts 文件名,不包含路径,所以会在工程路径下执行,并且有几个 ts 文件就会执行几次。解决办法:打开
.venv\Lib\site-packages\PySide6\scripts\project.py
文件,在def lupdate(self)
函数中修改为以下代码:def lupdate(self): for sub_project_file in self.project.sub_projects_files: Project(project_file=sub_project_file).lupdate() if not self.project.ts_files: print(f"{self.project.project_file.name}: No .ts file found.", file=sys.stderr) return source_files = self.project.python_files + self.project.ui_files project_dir = self.project.project_file.parent cmd_prefix = [LUPDATE_CMD] + [os.fspath(p.relative_to(project_dir)) for p in source_files] cmd_prefix.append("-ts") ts_files = [] for ts_file in self.project.ts_files: if requires_rebuild(source_files, ts_file): ts_dir = ts_file.parent if not ts_dir.exists(): ts_dir.mkdir(parents=True, exist_ok=True) ts_files.append(ts_file.resolve().as_posix()) if ts_files: cmd = cmd_prefix; cmd.extend(ts_files) cmd.append("-no-obsolete") # 清除失效文本 run_command(cmd, cwd=project_dir)
修改后,运行
pyside6-project.exe lupdate
命令就会将 ts 文件生成到指定路径中,并且只执行一次。(.venv) PS E:\Code\py\Test> pyside6-project.exe lupdate pyside6-lupdate main.py mainwindow.py TableWidget.py mainwindow.ui logwidget.ui control.ui -ts E:/Code/py/Test/Language/Test_zh_CN.ts E:/Code/py/Test/Language/Test_en_US.ts
5 工具使用
5.1 使用pyside6-project构建工程
需要pyside6.5以上版本支持,低版本可以通过自定义脚本支持(见后续章节);
创建一个
名称.pyproject
文件;按以下格式添加需要构建的文件名称,会批量将 ui 文件生成
ui_name.py
文件:{ "files": [ "main.py", "widget.ui", "widget1.ui" ] }
运行
python project.py -o
命令,自动将所有文件加载到.pyproject
工程文件进行管理;鼠标点击【工具】【外部工具】【pyside6-project】,完成工程构建;
将ui、qrc文件生成为py文件;
将
.ts
翻译文件编译为.qm
文件;
注意:pyside6-project需要
tomlkit
库支持。
5.2 创建、打开ui文件
鼠标点击【工具】【外部工具】【pyside6-designer】,打开设计师窗口;
选择创建窗体类型,点击【创建】
创建完成后,按【Ctrl + S】将ui文件保存到工程文件夹中;
创建完成后在pycharm中就可以看见ui文件了,双击ui文件就可以使用设计器打开;
5.3 生成翻译文件
生成pyproject工程文件;
打开
.pyproject
工程文件,添加生成.ts
翻译文件的路径;{ "files": [ "main.py", "res/res.qrc", "res/language/en_US.ts", "res/language/zh_CN.ts", ] }
鼠标点击【工具】【外部工具】【lupdate】,自动将代码中需要翻译的文本输出到
.ts
文件,如果ts文件不存在则会自动创建,如果已存在则会更新;如果是小于PySide6.5版本,则使用自定义脚本命令
python project.py -lupdate
。
5.4 修改ts翻译文件
使用
lupdate
生成.ts
文件;鼠标选中
.ts
文件,右键【外部工具】【pyside6-linguist】,打开Qt语言家;在Qt语言家窗口中可将文本翻译为指定语言。
6 工程管理脚本
输入
python project.py -h
查看脚本使用方法。
"""
这个文件是用来搜索当前路径下所有py、ui、qrc、ts文件,并生成pyproject文件。
不再需要手动谢pyproject文件,只需要运行这个文件即可。
支持自动执行 Qt 用户界面编译器(uic)和 Qt 资源编译器(rcc);
支持自动执行 Qt 语言文件更新工具(lupdate);
"""
import fnmatch
import json
import os
import subprocess
from pathlib import Path # 更安全的路径处理
import importlib.util
import argparse
import sys
def list_files(directory='.', include_patterns=None, exclude_patterns=None, ignore_folders=None):
"""
列出指定目录下所有匹配指定模式的文件
:param directory: 查询目录
:param include_patterns: 搜索的文件,支持通配符
:param exclude_patterns: 需要排除的文件,支持通配符
:param ignore_folders: 需要忽略的文件夹
:return: 匹配的文件列表,JSON 格式,输出为pyproject文件可用格式
"""
if include_patterns is None:
include_patterns = {'*.py', '*.ui', '*.qrc', '*.ts'}
if exclude_patterns is None:
exclude_patterns = set()
if ignore_folders is None:
ignore_folders = set()
files_list = []
for root, dirs, files in os.walk(directory):
# 过滤需要忽略的文件夹
dirs[:] = [d for d in dirs if d not in ignore_folders]
for file in files:
# 检查文件是否匹配需要包含的通配符模式,并且不匹配需要排除的通配符模式
if any(fnmatch.fnmatch(file, pattern) for pattern in include_patterns) and \
not any(fnmatch.fnmatch(file, pattern) for pattern in exclude_patterns):
# 计算相对路径
relative_path = os.path.relpath(os.path.join(root, file), directory)
relative_path = relative_path.replace('\\', '/')
files_list.append(relative_path)
# 返回 JSON 格式的文件列表
return json.dumps({"files": files_list}, indent=4)
def run_uic(project_name=None):
"""
执行 Qt 用户界面编译器(uic)的自动化流程
▼ 典型工作流程:
1. 定位 Qt 用户界面编译器(uic)
2. 加载项目配置文件(JSON格式)
3. 遍历配置文件中的 ui 文件路径
4. 构建 uic 命令行参数
5. 执行命令并处理执行结果,将ui文件编译为py文件
"""
project_file = Path(project_name).resolve()
with open(project_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# ▼ 配置有效性验证
if 'files' not in config or not isinstance(config['files'], list):
raise ValueError("配置文件必须包含 'files' 列表")
ui_files = [Path(f).resolve() for f in config["files"] if f.endswith('.ui')]
for ui_file in ui_files:
filename = os.path.basename(ui_file)
parent_dir = os.path.dirname(ui_file)
out_file = os.path.join(parent_dir, 'ui_' + filename.replace('.ui', '.py'))
if importlib.util.find_spec('PySide6') is not None: # 优先使用 PySide6, 如果找不到则使用 PySide2
cmd_args = ['pyside6-uic', str(ui_file), '-o', out_file]
elif importlib.util.find_spec('PySide2') is not None:
cmd_args = ['pyside2-uic', str(ui_file), '-o', out_file]
result = subprocess.run(
cmd_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
print(f"执行命令: {result.args}")
def run_rcc(project_name=None):
"""
执行 Qt 资源编译器(rcc)的自动化流程
▼ 典型工作流程:
1. 定位 Qt 资源编译器(rcc)
2. 加载项目配置文件(JSON格式)
3. 遍历配置文件中的 qrc 文件路径
4. 构建 rcc 命令行参数
5. 执行命令并处理执行结果,将qrc文件编译为py文件
"""
project_file = Path(project_name).resolve()
with open(project_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# ▼ 配置有效性验证
if 'files' not in config or not isinstance(config['files'], list):
raise ValueError("配置文件必须包含 'files' 列表")
rc_files = [Path(f).resolve() for f in config["files"] if f.endswith('.qrc')]
for rc_file in rc_files:
filename = os.path.basename(rc_file)
parent_dir = os.path.dirname(rc_file)
out_file = os.path.join(parent_dir, 'rc_' + filename.replace('.qrc', '.py'))
if importlib.util.find_spec('PySide6') is not None:
cmd_args = ['pyside6-rcc', str(rc_file), '-o', out_file]
elif importlib.util.find_spec('PySide2') is not None:
cmd_args = ['pyside2-rcc', str(rc_file), '-o', out_file]
result = subprocess.run(
cmd_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
print(f"执行命令: {result.args}")
def run_lupdate(project_name=None):
"""
执行 Qt 语言文件更新工具(lupdate)的自动化流程
▼ 典型工作流程:
1. 定位 Qt 语言更新工具(lupdate)
2. 加载项目配置文件(JSON格式)
3. 构建 lupdate 命令行参数
4. 执行命令并处理执行结果
▼ 配置文件要求:
- 必须包含 'files' 键,值为文件路径列表
- 示例配置:
{
"files": [
"src/*.py",
"translations/zh_CN.ts",
"translations/en_US.ts"
]
}
异常:
ValueError: 当配置文件格式无效时抛出
"""
# 初始化路径对象(使用 pathlib 处理跨平台路径问题)
# language: PySide6 语言更新工具的可执行路径
# project_file: 项目配置文件路径(包含待翻译文件列表)
# ts_files: 生成的翻译文件目标路径列表(中英文版本)
if importlib.util.find_spec('PySide6') is not None:
lupdate = Path('pyside6-lupdate.exe').resolve() # lupdate 工具路径,如果不存在,请手动修改路径
# 构建基础命令参数(使用字符串显式转换保证兼容性)
cmd_args = [str(lupdate), '-no-obsolete'] # -no-obsolete 选项用于排除已过时的翻译
elif importlib.util.find_spec('PySide2') is not None:
lupdate = Path('pyside2-lupdate.exe').resolve() # lupdate 工具路径,如果不存在,请手动修改路径
# 构建基础命令参数(使用字符串显式转换保证兼容性)
cmd_args = [str(lupdate), '-noobsolete']
project_file = Path(project_name).resolve()
try:
# 读取并解析项目配置文件(JSON 格式)
# 将配置中的文件列表添加到命令参数中
with open(project_file, 'r', encoding='utf-8') as f:
config = json.load(f)
# ▼ 配置有效性验证
if 'files' not in config or not isinstance(config['files'], list):
raise ValueError("配置文件必须包含 'files' 列表")
# 过滤掉以 .ts 为后缀的文件
filtered_files = [file for file in config['files'] if not file.endswith('.ts')]
cmd_args.extend(filtered_files) # 直接扩展参数列表
# 从文件列表中筛选出以 .ts 结尾的文件路径
# ts_files: 生成的翻译文件目标路径列表(中英文版本)
ts_files = [Path(f).resolve() for f in config["files"] if f.endswith('.ts')]
# ▼ 防止无翻译文件导致命令错误
if not ts_files:
print("警告:未发现.ts翻译文件")
return
# 添加翻译文件生成参数
# -ts 参数后接转换后的字符串路径列表
cmd_args.extend(['-ts', *map(str, ts_files)])
# 执行外部命令并捕获输出
# 使用 check=True 自动检查非零返回码
# text=True 自动解码输出内容为字符串
result = subprocess.run(
cmd_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
print(f"执行命令: {' '.join(result.args)}")
# ▼ 输出处理策略
if result.stdout:
print("输出日志:\n" + result.stdout)
if result.stderr:
print("错误信息:\n" + result.stderr)
# ▼ 异常处理优先级调整
except FileNotFoundError as e:
print(f"关键文件缺失: {e.filename}")
except json.JSONDecodeError as e:
print(f"Json 解析错误: {project_file}: {e}")
except subprocess.CalledProcessError as e:
print(f"指令运行失败 (code {e.returncode}):\n{e.stderr}")
except ValueError as e:
print(f"配置验证失败: {e}")
def find_pyproject_files(directory='.'):
pyproject_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if fnmatch.fnmatch(file, '*.pyproject'):
pyproject_files.append(os.path.join(root, file))
return pyproject_files
if __name__ == '__main__':
# 1. 创建解析器
parser = argparse.ArgumentParser(description='工程管理')
# 2. 添加参数
parser.add_argument('-o', '--output', action='store_true', help='自动遍历当前路径下所有py、ui、qrc、ts文件,并生成pyproject文件')
parser.add_argument('-b', '--build', action='store_true', help='构建项目,使用pyproject文件生成ui、qrc文件的py文件')
parser.add_argument('-u', '--lupdate', action='store_true', help='执行 Qt 语言文件更新工具(lupdate)')
parser.add_argument('-p', '--pyproject', type=str, help='指定pyproject文件路径,如果不指定则使用当前路径下的pyproject文件')
# 3. 解析参数
args = parser.parse_args()
# 获取pyproject文件路径
pyproject_file = args.pyproject
if not pyproject_file:
pyprojects = find_pyproject_files()
if pyprojects:
pyproject_file = pyprojects[0]
cmd_num = len(sys.argv) - 1
if args.output or cmd_num == 0: # 使用-o 参数或者无参数时,执行输出
# 列出当前路径下所有.py、.ui、.qrc、.ts文件,忽略.temp类型的文件和名为temp的文件夹
ret = list_files(include_patterns={'*.py', '*.ui', '*.qrc', '*.ts'},
exclude_patterns={'__init__.py', 'project.py', 'ui_*', 'rc_*'}, ignore_folders={'.venv'})
if pyproject_file:
with open(pyproject_file, 'w', encoding='utf-8') as project:
project.write(ret)
if args.build:
if pyproject_file:
# 可以选择不执行后面三个函数
run_uic(pyproject_file) # 执行 Qt 用户界面编译器, 将ui文件编译为py文件
run_rcc(pyproject_file) # 执行 Qt 资源编译器, 将qrc文件编译为py文件
if args.lupdate:
run_lupdate(pyproject_file) # 执行 Qt 语言文件更新工具(lupdate)