文章目录
- 1. 创建`python`开发环境
- 2. 安装`pyside6`
- 3. `pycharm`设置`pyside6 designer`和`pyside6-uic`
- 4. 开始基础`ui`页面设计
- 5. 保存`ui`文件,使用``uic`工具`导出`python`脚本
- 6. 导出文件不能直接启动,需要添加函数
- 7. 代码开发
- 7.1 添加【当天`Tab`】为例
- 7.2 难点:数据新增后切换到【当天`Tab`】,列表页数据不重新加载更新数据的问题
- 8. `Qss`设置样式
- 8.1 设置页面标签的属性名称:
- 8.2 方式1:加载`style.qss`文件
- 8.3 方式2:组件`setStyleSheet`方法直接样式
- 9. `pyside6-deploy`打包成可执行文件
- 10. `pyside6-rcc`设置`ICON`
- 10.1 `pycharm`设置`pyside6-rcc`(非必须,也可以用命令行)
- 10.2 准备`icon`和`.qrc`文件
- 10.3 代码中使用
只记录本人遇到的问题和解决办法,不展示代码,页面成果展示:
【当天】页面

【新增 】页面
【设置】页面
pyside6
官方文档:https://doc.qt.io/qtforpython-6/gettingstarted.html
1. 创建python
开发环境
# 指定路径创建虚拟环境,与安装目录不是一个盘,使用conda env list只能看到路径,没有名称,虚拟环境切换时使用路径名称
conda create --prefix D:\develop\pyside6\venv python=3.10
# 不同盘会将原环境的删除,重新建立新环境,不适合
conda rename -n 旧名称 新名称
conda rename -p 旧路径 新路径
# 切换虚拟环境
conda activate D:\develop\pyside6\venv
# 删除虚拟环境
conda remove --name python3.10 --all
# conda所有配置项
conda config --all
# windows上pycharm可以使用conda命令(也可以conda init cmd)
conda init powershell
2. 安装pyside6
pip install pyside6
3. pycharm
设置pyside6 designer
和pyside6-uic
【设置setting】 – > 【工具】 --> [外部工具]
pyside6-designer
路径: {虚拟环境名称目录}\Scripts\pyside6-designer.exe
, 用于设计QT UI
基础界面(有C
语言基础也可以直接开整),pycharm 2024.3
设置截图
工作目录:$FileDir$
pyside6-uic
路径: {虚拟环境名称目录}\Scripts\pyside6-uic.exe
,将ui
界面导出成python
脚本
实参:$FileName$ -o $FileNameWithoutExtension$.py
工作目录:$FileDir$
4. 开始基础ui
页面设计
-
pycharm
选取项目目录鼠标右键
-
开始设计
ui
官方文档:https://doc.qt.io/qtforpython-6/tools/pyside-designer.html#pyside6-designer
利用此工具可以直接C
直接设计程序,如果使用工具导出python
代码,需要注意页面中各个组件的名称见名知意,不然的话后续代码中很容易混乱。
5. 保存ui
文件,使用``uic工具
导出python
脚本
pycharm
选取ui
文件文件,【右键】 --> 【选取 pyside6
】–> 【pyside6-uic
】
6. 导出文件不能直接启动,需要添加函数
此处类继承取决于ui
页面定义的主页面是QMainWindow
(若选取main_window
), QWidget
#!/usr/bin/env python
# coding: utf-8
import sys
from PySide6.QtCore import QMetaObject
from PySide6.QtWidgets import QApplication, QMainWindow
from gui_handler.main_gui import UiMainWidget
from todoMenu.utils.constant import VERSION
class MainWidget(QMainWindow):
def __init__(self):
super().__init__()
self.ui = UiMainWidget(self)
self.setup_ui()
def setup_ui(self):
self.resize(605, 555)
self.setObjectName(u"main_window")
self.setWindowTitle("todo清单-{}".format(VERSION))
# self.statusbar = QStatusBar(self)
# self.statusbar.setObjectName(u"statusbar")
# self.setStatusBar(self.statusbar)
self.setCentralWidget(self.ui)
QMetaObject.connectSlotsByName(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWidget()
window.show()
sys.exit(app.exec())
运行:
python main.py
7. 代码开发
7.1 添加【当天Tab
】为例
main_gui.py
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'main_gui.ui'
##
## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QMetaObject, QRect,
Qt, QObject, Signal)
from PySide6.QtWidgets import (QStatusBar, QTabWidget, QWidget, QLabel, QVBoxLayout)
from todoMenu.gui_handler.cur_day_tab_gui import UiCurDayTab
class UpdateTaskListSignalObject(QObject):
"""更新task list信号"""
update_task_list_signal = Signal()
class UiTabWidget(QTabWidget):
"""tab 主页"""
def __init__(self, parent=None):
super().__init__(parent=parent)
self.cur_day_tab_widget = UiCurDayTab()
self.update_task_list_signal_obj = UpdateTaskListSignalObject()
self.setupUi()
def setupUi(self):
self.setObjectName(u"tab_widget")
self.setGeometry(QRect(0, 10, 600, 550))
self.setContextMenuPolicy(Qt.ContextMenuPolicy.DefaultContextMenu)
self.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
self.setStyleSheet(u"QTabBar::tab {text-transform: none;}")
self.setTabPosition(QTabWidget.TabPosition.West)
self.setTabShape(QTabWidget.TabShape.Rounded)
self.setDocumentMode(False)
self.setTabsClosable(False)
# 当天
self.addTab(self.cur_day_tab_widget, "")
# 更新当天task list信号绑定当天页面的更新方法
self.update_task_list_signal_obj.update_task_list_signal.connect(self.cur_day_tab_widget.update_task_list)
self.load_ui_content()
def load_ui_content(self):
"""
加兹tab页标签内容
:return:
"""
self.currentChanged.connect(self.tab_widget_change)
self.setTabText(self.indexOf(self.cur_day_tab_widget), "当天")
def tab_widget_change(self, index):
"""
Tab页更新事件
:param index:
:return:
"""
print(index)
if index == self.indexOf(self.cur_day_tab_widget):
self.update_task_list_signal_obj.update_task_list_signal.emit()
class UiMainWidget(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
# tab页主页面
self.tab_widget = UiTabWidget(self)
def setupUi(self):
pass
cur_day_tab_gui.py
#!/usr/bin/env python
# coding: utf-8
import datetime
from datetime import timedelta
from PySide6.QtCore import Qt, QRect, QObject, Signal, Slot
from PySide6.QtWidgets import QWidget, QVBoxLayout, QCalendarWidget, QHBoxLayout, QLabel, QPlainTextEdit, QProgressBar, \
QScrollArea, QSpacerItem, QSizePolicy
from todoMenu.utils.constant import TaskStatus, DATETIME_FORMAT
from todoMenu.utils.csv_handler import LoadCSVData
from todoMenu.utils.notice_text_ui import AboutNoticeText
class UiTaskItem(QVBoxLayout):
"""每一个task 的ui"""
def __init__(self, parent=None, time_and_task_list_widget=None, task=None):
super().__init__(parent=parent)
self.time_and_task_list_widget = time_and_task_list_widget
self.setup_ui()
self.load_ui_content(task)
def setup_ui(self):
self.setObjectName(u"task_item_layout")
self.setContentsMargins(0, 0, 0, 10)
def load_ui_content(self, task):
"""
加载数据
:param task:
:return:
"""
pass
def set_progress_and_status_by_time(self, create_time, finish_time, task_status):
"""
通过时间获取进度
:param create_time:
:param finish_time:
:param task_status:
:return:
"""
pass
class UiCurDayTab(QWidget):
"""当天 tab"""
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setObjectName(u"curDayTab")
# 日历和task 列表widget
self.time_and_task_list_widget = QWidget(self)
# 构建页面
self.setup_ui()
def setup_ui(self):
# 当天tab ui设置
self.cur_tab_init()
# 日历和提示
self.biuld_calendar_and_notice()
# task list
self.build_task_list_ui()
def cur_tab_init(self):
"""
当天tab页面ui设置
:return:
"""
self.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
def biuld_calendar_and_notice(self):
"""
构建日历和提示
:return:
"""
pass
def build_task_list_ui(self):
"""
构建task 列表
:return:
"""
pass
def load_csv_and_build_task_ui(self):
"""
构建每一个task ui组件
:return:
"""
pass
@Slot()
def update_task_list(self):
"""
用于切换tab后更新页面
:return:
"""
pass
7.2 难点:数据新增后切换到【当天Tab
】,列表页数据不重新加载更新数据的问题
解决办法:使用信号进行跨页面通信。通过定义信号(创建QSignal
对象, 即UpdateTaskListSignalObject
)绑定列表页列表页更新的槽函数(Slot
, 即上面定义的update_task_list
),之后在QTabWidget
组件的currentChanged
事件关联的槽函数tab_widget_change
中进行emit
操作。
8. Qss
设置样式
8.1 设置页面标签的属性名称:
如设置label
属性ObjectName
:
from PySide6.QtWidgets import QLabel
label = QLabel(self)
label.setObjectName(u"label")
设置label
属性class
:
label.setProperty("class", "label_class")
设置label
属性``:
8.2 方式1:加载style.qss
文件
定义style.qss
文件
/* 以属性ObjectName值选择 */
#label {
color: rgb(0, 0, 0);
background-color: rgb(238, 255, 220);
font: 12pt;
}
/* 以属性class值选择 */
.label_class {
...
}
/* 以属性名称和类型选择 */
QWidget#task_widget QLabel {
border:1px solid black;
color: rgb(0, 0, 0);
background-color: rgb(255, 255, 255);
font: 10pt;
}
...
读取文件:
with open("./style.qss", 'r', encoding='UTF-8') as file:
css_data = file.read()
设置样式:
label.setStyleSheet(css_data)
8.3 方式2:组件setStyleSheet
方法直接样式
label.setStyleSheet("border:1px solid black;")
9. pyside6-deploy
打包成可执行文件
官方文档:https://doc.qt.io/qtforpython-6/deployment/deployment-pyside6-deploy.html
- 进入项目目录执行:会在目录下生成打包配置文件
pysidedeploy.spec
pyside6-deploy main.py --name todoMenu
或
直接使用nuitka
打包
python -m nuitka --standalone --plugin-enable=pyside6 main.py
- 执行成功提示:
(D:\develop\pyside6\venv) PS D:\develop\pyside6\todoMenu> pyside6-deploy main.py --name todoMenu
You are not using a virtual environment. pyside6-deploy needs to install a few Python packages for deployment to work seamlessly.
Proceed? [Y/n]yes
D:\develop\pyside6\venv\Lib\site-packages\PySide6\scripts\deploy_lib\config.py:488: RuntimeWarning: [DEPLOY] Unable to find dumpbin. This tool helps to find the Qt module dependencies of the application. Skipping checking for dependencies.
warnings.warn(f"[DEPLOY] Unable to find {self.dependency_reader.lib_reader_name}. This "
[DEPLOY] Executed file created in D:\develop\pyside6\todoMenu\todoMenu.exe
因为安装Nuitka
打包工具且依赖C
编译插件,网络不好会很慢,可以手动下载地址(参考大佬总结:https://zhuanlan.zhihu.com/p/659134472)(个人选择64位)
- 安装
Nuitka
打包工具
pip install Nuitka==1.5.4 -i https://mirrors.aliyun.com/pypi/simple
- 安装
C
编译插件
# 下载之后解压到某个路径,将该路径/bin目录添加到环境变量中
https://github.com/skeeto/w64devkit/releases
# 解压,将解压后的文件(不带目录)拷贝到路径C:\Users\【设备用户名】\AppData\Local\Nuitka\Nuitka\Cache\downloads\ccache\v4.6 没有就创建
https://github.com/ccache/ccache/releases/tag/v4.6
# 解压,将解压后的文件(不带目录)拷贝到路径C:\Users\WCC\AppData\Local\Nuitka\Nuitka\Cache\downloads\depends\x86_64
https://dependencywalker.com/
# 注意版本,解压,将解压后的文件mingw64(含目录)拷贝到C:\Users\WCC\AppData\Local\Nuitka\Nuitka\Cache\downloads\gcc\x86_64\13.2.0-16.0.6-11.0.1-msvcrt-r1
https://winlibs.com/#download-release
- 为啥是
\gcc\x86_64\13.2.0-16.0.6-11.0.1-msvcrt-r1
,因为执行命令后程序默认下载依赖(看提示,如下)在这个目录(懒人模式,就在这个目录就行),
Is it OK to download and put it in 'C:\Users\WCC\AppData\Local\Nuitka\Nuitka\Cache\DOWNLO~1\gcc\x86_64\13.2.0-16.0.6-11.0.1-msvcrt-r1'.
-
onefile
模式,默认该模式只会在当前目录生成可执行文件(内部包含程序执行所有依赖项),执行时会在系统临时目录创建一个文件夹,路径如:
C:\Users\WCC\AppData\Local\Temp\onefile_256_133778016935747292
,包括所有程序可执行资源(关闭界面之后自动删除),好处是放在任意位置都可执行,但是程序配置项需要考虑路径问题
# 执行时获取临时可执行文件文件所在路径 os.path.dirname(__file__) 或者 os.path.dirname(sys.executable) # 程序内获取源exe文件所在路径可使用环境变量的方式
-
standalone
,会在当前目录生成可执行程序与其依赖项文件,可执行文件正常执行需要和其依赖项文件保持一个目录(可使用快捷方式或软链接)
pyside6-deploy main.py --name todoMenu --mode standalone
- 其他运行参数
-f 不需要提示确认
--keep-deployment-files 保留build编译临时文件
--project_dir 指定项目目录
-
报错:
No such file or directory
pyside6-deploy
(6.8.0.2)没有将静态文件如ini
加入到可执行文件,需要编辑pysidedeploy.spec
[nuitka] extra_args = --quiet --noinclude-qt-translations --include-data-dir="文件路径=打包后文件路 --noconsole"
-
程序内区分打包还是开发环境
import sys if hasattr(sys, "frozen"): BASE_DIR = os.path.dirname(sys.executable) else: BASE_DIR = os.path.dirname(os.path.dirname(__file__))
-
不需要调试窗口
extra_args = --quiet --noinclude-qt-translations --windows-console-mode=disable
10. pyside6-rcc
设置ICON
参考资料: Using .qrc Files (pyside6-rcc) - Qt for Python
Windows 10风格的时间和日期符号和图标,格式有PNG、SVG (igoutu.cn)
10.1 pycharm
设置pyside6-rcc
(非必须,也可以用命令行)
【设置setting】 – > 【工具】 --> 【外部工具】
pyside6-rcc
路径: {虚拟环境名称目录}\Scripts\pyside6-rcc.exe
,可以将图片、字体等转化为二进制, 可供QFile
类以及QIcon
和QPixmap
类的构造函数访问
实参:$FileName$ -o $FileNameWithoutExtension$.py
工作目录:$FileDir$
以上等同于命令行:(todo_icons.py
j即是转化后可在代码中导包文件)
pyside6-rcc icons.qrc -o todo_icons.py
10.2 准备icon
和.qrc
文件
注意:icon
文件存放目录和.qrc
文件在同一个目录
- 定义
.qrc
文件,如icons.qrc
:
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>icons/header.png</file>
<file>icons/today.png</file>
<file>icons/add.png</file>
<file>icons/setting.png</file>
<file>icons/about.png</file>
</qresource>
</RCC>
- 执行
pyside6-rcc
执行完成后icons
内源文件可删
10.3 代码中使用
from PySide6.QtGui import QPixmap, QIcon
from PySide6.QtWidgets import QPushButton,QMainWindow
from todo_icons import *
header_icon = QIcon(QPixmap(":/icons/header.png"))
# 如设置应用icon,其中一种方法
QMainWindow().setWindowIcon(header_icon)
# 设置按钮 icon,其中一种方法
QPushButton().setIcon(header_icon)