一. 前言
这段时间一直在尝试做一个 Python 桌面端的项目 ,到了现在基本上已经可以发布了 。
陆陆续续折腾了1-2个月了 ,算是把 Python 桌面应用的点都体验到了,这里整理出来 ,用于后续的其它尝试。
二. 环境准备
2.1 搭建 Python 开发环境
- 简易版的就是本地安装 Python ,然后在代码工具里面使用即可
- 但是如果是多版本多应用的开发 ,最好使用虚拟环境
这里主要记录虚拟环境的用法 ,Python 虚拟开发环境是一种隔离的环境 ,允许开发者 为每个项目独立安装特定的 Python 版本和依赖包,避免全局依赖冲突和版本问题。
虚拟环境一般常见有几种 : Python 内置 venv / virtualenv / pipenv / conda.
conda 虚拟环境的安装使用
- S1 : 首先进入 Download Now | Anaconda 下载工具
- S2 : 正常流程安装 Anaconda (
安装完成界面最后两项不用选) - S3 : 配置环境变量 ,主要有四个 (当前安装路径下)
- D:[当前安装路径下]\anaconda
- D:[当前安装路径下]\anaconda\Scripts
- D:[当前安装路径下]\anaconda\Library\mingw-w64\bin
- D:[当前安装路径下]\anaconda\Library\bin
- S4 : conda配置python环境
- 查询安装结果 : conda --version
- 查询详情 :conda info
- 创建环境 : conda create --name simple python=3.11.9
- 查看环境 : conda info --envs
- 激活环境 : conda activate simple
- 查询当前依赖 : conda list
- 退出环境 : conda deactivate
- 删除所有依赖 : conda remove -n simple --all
- S5 : Python 应用使用 虚拟环境
- (其他的工具怎么用可以百度 ,这里主要看 VSCode)
- VSCode 中 CTRL+ P 👉 select interpreter
- 选择对应的环境即可
三. 功能组件
3.1 安装组件
项目环境准备好了后 ,就要开始安装组件了 ,我个人项目中主要使用了如下组件 :
- python 桌面应用组件套 :
- PyQt6 :Python 里面很好用的桌面库 ,支持 GPLv3 开源协议 ,支持商业许可
- PyQt6-WebEngine : 用于在桌面组件里面添加网页
- PyQt6-Frameless-Window : 第三方库,提供无边框窗口的实现
- PyQt6-Fluent-Widgets : 基于微软 Fluent Design 设计语言的 PyQt6 界面组件库
- PyQt6-tools : 用于构建项目 (
最大支持 Python 3.11)
- DAS 层相关套装 :
- peewee : 轻量级的 Python ORM(对象关系映射)库
- dbutils : 一组工具模块,用于处理与数据库相关的常见任务 ,连接池等
- SQLite : 一个轻量级的嵌入式关系型数据库管理系统 , Python 自带
- 其他组件 :
-
Tushare : 量化工具,基本上常见的股票接口都有,部分接口免费
-
cachetools : 缓存工具
-
matplotlib : 2D 数据可视化库,用于创建图表、图形和绘图
-
pystand : 打包组件
-
// 先进入 Conda 环境进行操作
conda init
conda activate antMonitor
// 出现如下说明环境进入成功 : (可能需要先推出 Terminal)
(antMonitor) PS D:\code\python\ant-monitor>
// 检测一下当前依赖 :
conda list
// 安装依赖 (这里可能存在版本依赖问题 ,所以为了更好的效果 ,我指定了版本)
pip install PyQt6==6.7.1
pip install PyQt6-Qt6==6.7.2
pip install PyQt6-WebEngine==6.7.0
pip install PyQt6-WebEngine-Qt6==6.7.3
pip install PyQt6-Frameless-Window==0.4.3
pip install PyQt6-Fluent-Widgets -i https://pypi.org/simple/
pip install matplotlib
pip install peewee
pip install dbutils
pip install tushare
pip install schedule
pip install cachetools
pip install pystand
// 如果存在依赖冲突 ,可以删除依赖
pip uninstall PyQt6
pip uninstall pyqt6-qt6
pip uninstall PyQt6-WebEngine
pip install --force-reinstall pyqt6==6.7.1
- 查询当前的依赖版本 : pip list
3.2 PyQT 配置文件准备
如果多 PyQT 的各项功能不清楚的 ,可以先看看这篇 :
静态文件配置 - resources.qrc:
<RCC>
<qresource prefix="/images">
<file>views/common/images/logo.png</file>
</qresource>
<qresource prefix="/page">
<file>views/qss/dark/gallery_interface.qss</file>
<file>views/qss/dark/home_interface.qss</file>
<file>views/qss/dark/icon_interface.qss</file>
<file>views/qss/dark/link_card.qss</file>
<file>views/qss/dark/sample_card.qss</file>
<file>views/qss/dark/setting_interface.qss</file>
<file>views/qss/dark/view_interface.qss</file>
<file>views/qss/dark/navigation_view_interface.qss</file>
<file>views/qss/light/gallery_interface.qss</file>
<file>views/qss/light/home_interface.qss</file>
<file>views/qss/light/icon_interface.qss</file>
<file>views/qss/light/link_card.qss</file>
<file>views/qss/light/sample_card.qss</file>
<file>views/qss/light/setting_interface.qss</file>
<file>views/qss/light/view_interface.qss</file>
<file>views/qss/light/navigation_view_interface.qss</file>
</qresource>
</RCC>
- 这里进行一次手动编译 ,由于 PyQT6 本身没有编译工具 ,这里需要手动改下(把下面的地方改成 PyQT6 即可)
# 手动生成 resources_rc 文件
rcc -g python -o resources_rc.py resources.qrc
四. 代码流程
4.1 基础组件
- QFluentWidgets : 基于 C++ Qt/PyQt/PySide 的 Fluent Design 风格组件库
- PyQT6 : Python 桌面端组件
关联文章 :
4.2 项目结构
├─common : 公共模块
│ ├───enum : 业务中使用的枚举
│ └───utils
├─config
├─logs
├─service : service 层逻辑
│ ├───das :数据交互层
├─views
│ ├───common
│ │ ├──html : WebEngine 中使用的页面
│ │ └──images : 业务中使用的图片
│ ├───qss
│ │ ├──dark : 暗色主题
│ │ └──light : 白色主题
- 个人主业是 Java ,所以 构建的项目难免带有一些 Java 的风格
4.3 代码细节
主入口 : main.py + app.py
from qfluentwidgets import (
NavigationItemPosition,
FluentWindow,
SubtitleLabel,
setFont,
SplashScreen,
SystemThemeListener,
)
from qfluentwidgets import FluentIcon as FIF
from PyQt6.QtWidgets import (
QApplication,
QHBoxLayout,
QFrame,
)
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import QSize
from views.common_setting_view import CommonSettingView
from views.demo_chat_view import DemoChatView
from views.base_gallery_view import GalleryInterface
from config.log_config import AntLogger
from views.common.signal_bus import signalBus
from views.common.translator import Translator
from views.common.config import ZH_SUPPORT_URL, EN_SUPPORT_URL, cfg
from views.demo_common_view import DemoCommonView
class Widget(QFrame):
def __init__(self, text: str, parent=None):
super().__init__(parent=parent)
# 创建一个带副标题样式的标签,显示传入的文本
self.label = SubtitleLabel(text, self)
self.hBoxLayout = QHBoxLayout(self) # 使用水平布局管理器
# 设置标签字体大小
setFont(self.label, 24)
# 将标签添加到水平布局,并设置伸缩因子为 1
self.hBoxLayout.addWidget(self.label, 1)
# 必须给子界面设置全局唯一的对象名,以便后续查找
self.setObjectName(text.replace(" ", "-"))
class MainWindow(FluentWindow):
"""
主窗口类,继承自 FluentWindow。
实现了应用的主界面,包括导航栏、多个子页面以及主题监听器等功能。
"""
def __init__(self):
super().__init__()
self.initWindow() # 初始化窗口属性
# 创建系统主题监听器,用于检测和响应主题变化
self.themeListener = SystemThemeListener(self)
# 初始化各个子页面
"""
核心修改点一 : 这里添加你的页面
"""
self.homeView = DemoCommonView(self)
self.chatView = DemoChatView(self)
self.settingView = CommonSettingView(self)
# 启用亚克力效果
self.navigationInterface.setAcrylicEnabled(True)
self.connectSignalToSlot() # 连接信号与槽
# 初始化导航栏及其内容
self.initNavigation()
# 结束启动画面
self.splashScreen.finish()
# 开始监听系统主题变化
self.themeListener.start()
def initNavigation(self):
"""初始化导航栏"""
pos = NavigationItemPosition.SCROLL # 设置导航项位置为可滚动区域
"""
核心修改点二 : 把页面添加到导航栏
"""
# 添加首页视图
self.addSubInterface(self.homeView, FIF.SPEED_HIGH, "首页", pos)
# 添加聊天演示视图
self.addSubInterface(self.chatView, FIF.CAFE, "表格原理Demo", pos)
# 在导航栏中添加分隔符
self.navigationInterface.addSeparator()
# 添加设置视图
self.addSubInterface(
self.settingView,
FIF.SETTING,
self.tr("Settings"),
NavigationItemPosition.BOTTOM, # 设置为固定在导航栏底部
)
def initWindow(self):
"""初始化窗口属性"""
self.resize(1200, 1000) # 设置窗口大小
self.setMinimumWidth(1000) # 设置窗口最小宽度
self.setWindowIcon(
QIcon(":/images/views/common/images/logo.png")
) # 设置窗口图标
self.setWindowTitle("Demo-桌面端小程序基础框架") # 设置窗口标题
# 根据配置启用 Mica 效果(Windows 专属的模糊效果)
self.setMicaEffectEnabled(cfg.get(cfg.micaEnabled))
# 创建启动画面
self.splashScreen = SplashScreen(self.windowIcon(), self)
self.splashScreen.setIconSize(QSize(106, 106)) # 设置启动画面图标大小
self.splashScreen.raise_() # 将启动画面置于最前
# 窗口居中显示
desktop = QApplication.screens()[0].availableGeometry()
w, h = desktop.width(), desktop.height()
self.move(w // 2 - self.width() // 2, h // 2 - self.height() // 2)
self.show() # 显示窗口
QApplication.processEvents() # 强制刷新界面
def resizeEvent(self, e):
"""处理窗口大小变化事件"""
super().resizeEvent(e)
if hasattr(self, "splashScreen"): # 如果启动画面存在,则调整其大小
self.splashScreen.resize(self.size())
def closeEvent(self, e):
"""处理窗口关闭事件"""
self.themeListener.terminate() # 停止主题监听器
self.themeListener.deleteLater() # 删除监听器对象
super().closeEvent(e)
def connectSignalToSlot(self):
"""连接信号与槽"""
# 连接信号:启用/禁用 Mica 效果
signalBus.micaEnableChanged.connect(self.setMicaEffectEnabled)
# 连接信号:切换到示例卡片
signalBus.switchToSampleCard.connect(self.switchToSample)
def switchToSample(self, routeKey, index, data):
"""
切换到示例界面,并滚动到指定的卡片。
参数:
- routeKey: 子界面的唯一标识符(用于查找目标界面)
- index: 要滚动到的卡片索引
- data: 需要传递给目标界面的附加数据
"""
# 找到当前窗口中所有基于 GalleryInterface 的子界面
interfaces = self.findChildren(GalleryInterface)
for w in interfaces:
if w.objectName() == routeKey: # 检查对象名是否匹配
w.setExchangeData(data) # 设置需要传递的数据
self.stackedWidget.setCurrentWidget(w, False) # 切换到目标界面
w.scrollToCard(index) # 滚动到指定索引的卡片
核心类 :抽象框架类 : template_page_view.py
这个类是整个脚手架中最核心的类,通过这个类可以快速的构建各种内部组件:
- 使用方式如下 :
# 添加一个 Table
self.addTableTemplate("当前股票", self.getStockTableInfo, self.getBaseParam, 1)
# 添加一个 EChat
self.addEchatTemplate(
"大盘风险", "demo_chat", self.exchangeMainStockRisk, self.getBaseParam, 1
)
五.结果展示
六. 项目地址
- 注意是 Demo-001-SimpleFramework 分支哦!!!
总结
这段时间总算是把这个搞完了 ,这一套弄会了, 后面再写类似的基本上就不需要单独写什么了。
-
代码里面很多都是 GPT 帮忙写的 ,所以质量还在优化中,仅供学习和快速上手。
-
另外个人开源的组件也在最后测试阶段了, 欢迎关注 ❗❗
-
打包使用的是
PyStand,东西比较多 ,准备单独放一篇,过两天就发!!
最后的最后 ❤️❤️❤️👇👇👇
- 👈 欢迎关注 ,超200篇优质文章,未来持续高质量输出 🎉🎉
- 🔥🔥🔥 系列文章集合,高并发,源码应有尽有 👍👍
- 走过路过不要错过 ,知识无价还不收钱 ❗❗