ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PyQt5入门指南:从环境搭建到界面开发与打包部署

PyQt5入门指南:从环境搭建到界面开发与打包部署 1. 从命令行到窗口为什么我们需要PyQt5如果你是从Python的print(“Hello World”)开始编程的那么你大概率已经习惯了那个黑底白字的命令行窗口。它简洁、高效是处理数据和逻辑的绝佳场所。但当你需要把程序交给一个不懂代码的同事、朋友或者想为自己打造一个顺手的小工具时命令行界面就显得不那么友好了。想象一下你需要一个批量重命名图片的工具难道要每次都让用户去修改配置文件然后在终端里输入一长串命令吗这显然不现实。这时图形用户界面GUI就登场了。它用按钮、文本框、下拉菜单这些直观的元素把复杂的操作封装起来让用户通过点击和输入就能完成工作。在Python的世界里GUI库的选择不少比如自带的tkinter轻量级的PySimpleGUI还有wxPython、Kivy等等。但当你需要一个功能强大、界面美观、跨平台且能与现代操作系统深度集成的解决方案时PyQt5几乎是一个绕不开的选择。PyQt5是Qt应用程序框架的Python绑定。Qt本身是一个用C编写的、极其成熟的跨平台框架被广泛应用于工业软件如Autodesk Maya、嵌入式设备界面乃至汽车的中控系统。PyQt5让你能用Python的语法调用Qt的全部能力。这意味着你可以轻松创建出带有复杂表格、精美图表、多线程任务管理、甚至支持OpenGL 3D渲染的专业级桌面应用。网络上那些看起来像用C#或C写的“桌面软件”很多背后都是Python和PyQt5的功劳。我最初接触PyQt5是为了给实验室的数据处理流程做一个前端界面。当时用Matplotlib画完图还需要手动调整样式、保存非常繁琐。用PyQt5做了一个带预览和批量导出功能的小工具后效率提升了不止十倍。更重要的是它让不熟悉Python的团队成员也能轻松使用我们开发的算法。这就是GUI编程的魅力它连接了代码的逻辑世界和用户的交互世界。2. 环境搭建避开安装路上的那些“坑”万事开头难PyQt5的安装对于新手来说可能就是第一个挑战。它不像pip install requests那样一帆风顺经常会遇到各种依赖和版本问题。下面我结合自己的踩坑经历给你梳理一条最稳妥的路径。2.1 核心包安装选对渠道是关键最直接的方式是使用pip安装。打开你的终端Windows的CMD或PowerShellmacOS/Linux的Terminal输入以下命令pip install PyQt5这个命令会安装PyQt5的核心模块。但是仅仅这样安装你可能会发现缺少一些重要的组件比如Qt Designer一个可视化的界面设计工具和PyQt5-tools包含将Qt Designer生成的.ui文件转换为Python代码的工具。因此更推荐的做法是同时安装pyqt5-toolspip install PyQt5 PyQt5-tools注意在某些系统或网络环境下直接使用pip安装Qt相关的包可能会因为需要从源码编译而失败尤其是缺少C编译环境时。一个更稳定、更省心的方案是使用清华大学、阿里云等国内镜像源加速下载预编译好的wheel包pip install PyQt5 PyQt5-tools -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后如何验证不要急着写复杂代码。打开Python交互环境在终端输入python执行下面两行import PyQt5 print(PyQt5.__version__)如果没有报错并且打印出版本号比如5.15.9那么恭喜你核心安装成功了。2.2 配置开发环境让工具触手可及安装好包只是第一步配置好开发环境才能事半功倍。这里主要涉及两个关键工具的位置定位。1. Qt Designer (designer.exe)这是一个“所见即所得”的界面设计器。你可以在里面拖拽按钮、文本框等控件来设计窗口布局它会生成一个后缀为.ui的XML格式文件。安装pyqt5-tools后这个工具就在你的Python环境目录下。如何找到它一个通用的方法是Windows: 通常在你的Python安装目录\Lib\site-packages\qt5_applications\Qt\bin\designer.exe。更简单的方法是在文件资源管理器中搜索designer.exe范围限定在你的Python安装目录。macOS/Linux: 路径可能类似/usr/local/bin/designer或藏在Python的site-packages目录里。在终端输入which designer或find ~ -name “designer” 2/dev/null来查找。我个人的习惯是找到这个designer.exe后右键“发送到桌面快捷方式”或者将其路径添加到系统的环境变量PATH中这样在终端直接输入designer就能打开了。2. UI文件转换工具 (pyuic5)Qt Designer生成的.ui文件不能被Python直接识别需要转换成.py文件。这个转换工具就是pyuic5。安装pyqt5-tools后它应该已经作为脚本被安装到了你的Python的ScriptsWindows或binmacOS/Linux目录下。这个目录通常会自动加入PATH所以你可以在终端直接使用它。验证一下打开终端注意不是Python交互环境输入pyuic5 --version如果显示版本信息说明配置成功。2.3 集成到IDE以VSCode为例为了提高效率我们通常希望在IDE里就能完成设计、转换、编码的全流程。这里以VSCode为例。首先你可以在VSCode中安装Python和Qt相关的插件比如“Python”和“Qt for Python”。但这还不够方便。更实用的方法是配置任务Tasks或者使用终端面板。我常用的一个快速转换.ui文件的方法是在VSCode中打开包含.ui文件的目录然后直接使用终端pyuic5 -x my_window.ui -o my_window.py参数解释-x: 表示在生成的Python代码中添加一个简单的if __name__ “__main__”:块使其可以直接运行。my_window.ui: 你的UI文件。-o my_window.py: 指定输出的Python文件名。为了更懒一点你可以写一个简单的批处理脚本Windows或Shell脚本macOS/Linux一键转换当前目录下所有.ui文件。或者在VSCode的settings.json中配置一个自定义命令。不过对于初学者先掌握手动命令的方式更有利于理解整个过程。3. 第一个PyQt5程序理解“信号与槽”这个核心魔法环境配好了让我们从一个最经典的“Hello World”开始但这次要带点PyQt5的特色。我们将创建一个带按钮的窗口点击按钮标签文字会改变。通过这个例子你会接触到PyQt5最核心的概念信号与槽。3.1 纯代码构建理解每一个部件我们先不用Qt Designer完全用代码来写这样你能清楚地知道每一个控件是如何被创建和组织的。import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QVBoxLayout class MyFirstWindow(QWidget): def __init__(self): super().__init__() # 初始化父类QWidget self.initUI() def initUI(self): # 创建一个标签显示初始文本 self.label QLabel(‘Hello World!‘, self) # 创建一个按钮 self.button QPushButton(‘点击我‘, self) # 创建一个垂直布局管理器 layout QVBoxLayout() layout.addWidget(self.label) # 将标签加入布局 layout.addWidget(self.button) # 将按钮加入布局 self.setLayout(layout) # 为窗口设置这个布局 # 连接信号与槽当按钮被点击clicked就调用on_button_clicked方法 self.button.clicked.connect(self.on_button_clicked) # 设置窗口属性 self.setGeometry(300, 300, 300, 200) # (x, y, width, height) self.setWindowTitle(‘我的第一个PyQt5程序‘) self.show() def on_button_clicked(self): # 这个函数就是“槽”当信号触发时被调用 self.label.setText(‘你好PyQt5‘) if __name__ ‘__main__‘: app QApplication(sys.argv) # 每个PyQt5应用都必须创建一个QApplication实例 window MyFirstWindow() # 创建我们的窗口实例 sys.exit(app.exec_()) # 进入应用的主循环直到窗口关闭逐行解析导入模块QApplication管理应用的生命周期QWidget是所有窗口部件的基类QPushButton和QLabel是具体的控件QVBoxLayout是垂直布局管理器。创建应用和窗口app QApplication(sys.argv)是固定写法。我们的MyFirstWindow类继承自QWidget所以它本身就是一个窗口。布局管理这是GUI编程中极其重要的一环。如果不使用布局Layout控件会堆叠在左上角且窗口缩放时控件位置不变非常丑。QVBoxLayout垂直布局让控件从上到下依次排列。同理还有QHBoxLayout水平布局和更复杂的QGridLayout网格布局。信号与槽这是Qt的基石也是理解PyQt5事件处理的关键。信号Signal由对象如按钮在特定事件发生时发出。比如QPushButton有一个clicked信号当按钮被点击时就会发出。槽Slot就是一个普通的Python函数或方法用来响应信号。连接Connect使用.connect()方法将某个信号的发射绑定到某个槽函数的执行。self.button.clicked.connect(self.on_button_clicked)就实现了“点击按钮 → 改变标签文字”的逻辑。这种机制的好处是解耦。按钮只管发出“我被点了”的信号它完全不知道也不关心谁会处理这个信号。处理逻辑槽函数可以定义在任何一个地方。这比传统GUI库里需要写一堆if event.type MOUSE_CLICK:这样的回调函数要清晰和灵活得多。3.2 使用Qt Designer可视化设计提速对于复杂的界面纯手写布局代码会很繁琐。这时就该Qt Designer出场了。打开Qt Designer你会看到一个空白窗口和左侧丰富的控件面板。从左侧拖一个Label和一个Push Button到中间的窗体上。在右侧的属性编辑器里可以修改它们的objectName在代码里引用的名字如labelHello、btnClick和text属性显示的文字。使用顶部的布局工具如“垂直布局”按钮或者右键窗体选择“布局”快速排列控件。保存文件例如hello_designer.ui。现在你得到了一个.ui文件。它用XML描述了界面的结构。我们需要用pyuic5把它转换成Python代码pyuic5 -x hello_designer.ui -o ui_hello.py打开生成的ui_hello.py你会看到一个名为Ui_MainWindow的类里面有一个setupUi方法。这个类负责创建和设置所有控件。我们不能直接修改这个文件因为下次转换会被覆盖标准的做法是继承它。创建一个新的主程序文件main.pyimport sys from PyQt5.QtWidgets import QApplication, QMainWindow from ui_hello import Ui_MainWindow # 导入生成的界面类 class MainWindow(QMainWindow): def __init__(self): super().__init__() # 创建UI对象并设置到当前窗口 self.ui Ui_MainWindow() self.ui.setupUi(self) # 现在可以通过 self.ui 访问所有控件了 # 例如连接按钮信号 self.ui.btnClick.clicked.connect(self.on_button_clicked) def on_button_clicked(self): self.ui.labelHello.setText(“你好来自Designer”) if __name__ ‘__main__‘: app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec_())这种方法分离了界面描述.ui文件和业务逻辑main.py是更工程化、更易维护的做法。当界面需要调整时只需在Designer里修改并重新转换逻辑代码基本不受影响。4. 布局与控件进阶打造规整且专业的界面一个软件给人的第一印象就是界面。杂乱无章的控件堆放是业余的体现而规整、有呼吸感的布局则显得专业。PyQt5提供了强大的布局管理系统你必须掌握它。4.1 深入理解布局管理器布局管理器Layout负责自动排列其内部的子控件Widget。当窗口大小改变时布局管理器会自动调整子控件的大小和位置。这是实现响应式界面的基础。1. 基础布局QVBoxLayout/QHBoxLayout垂直/水平排列简单直接。可以嵌套使用例如先创建一个水平布局放两个按钮再把这个水平布局作为一个整体放入一个垂直布局中。QGridLayout网格布局功能最强大。你可以将控件放入特定的行和列还可以设置跨行跨列rowSpan,columnSpan。非常适合表单标签在左输入框在右或者仪表盘式的界面。2. 尺寸策略sizePolicy与伸缩因子stretch这是控制控件在布局中如何伸缩的精细工具。尺寸策略每个控件都有水平和垂直两个方向的尺寸策略。例如QPushButton的默认策略通常是Fixed固定大小或Minimum最小尺寸而QTextEdit或QTableView通常是Expanding尽可能扩展以填充空间。你可以在代码中通过setSizePolicy修改或在Designer的属性编辑器中设置。伸缩因子在布局中添加控件时可以指定一个伸缩因子。因子越大在空间分配时占的比例就越大。例如在一个水平布局中有两个文本框第一个的伸缩因子为1第二个为2那么当窗口变宽时第二个文本框获得的额外宽度将是第一个的两倍。实操技巧在Qt Designer中练习布局的最好方法就是随意拖入几个控件然后尝试不同的布局方式并不断拖动窗口边框观察控件是如何自适应变化的。同时多使用“水平 spacer”和“垂直 spacer”弹簧来填充空白区域让界面元素不会全部挤在一起。4.2 常用核心控件解析PyQt5的控件库非常丰富这里挑几个最常用且功能强大的介绍一下。1.QLineEdit与QTextEdit单行与多行文本输入QLineEdit用于输入用户名、搜索关键词等短文本。除了基本的文本获取text()它支持密码模式setEchoMode、输入验证器QValidator如限制只能输入数字、以及 placeholder text提示文字。QTextEdit一个功能完整的富文本编辑器。可以设置字体、颜色、插入图片、获取HTML格式内容等。它同时也是纯文本编辑器。通过setPlainText()和toPlainText()来处理纯文本setHtml()和toHtml()处理富文本。2.QComboBox与QListWidget下拉列表与列表框QComboBox节省空间的下拉选择框。通过addItem()添加选项currentText()或currentIndex()获取当前选择。可以设置为可编辑setEditable(True)允许用户输入新值。QListWidget显示一个列表支持单选、多选。它与QComboBox的主要区别是列表项始终可见。适合选项不多且需要常显的场景。你可以方便地添加、删除、排序列表项。3.QTableWidget表格数据展示之王这是处理结构化数据的利器。你可以把它想象成一个可编辑的Excel表格。核心操作设置行数列数setRowCount(),setColumnCount()设置水平/垂直表头setHorizontalHeaderLabels()填充数据setItem(row, col, QTableWidgetItem(“内容”))。进阶功能支持单元格的格式设置字体、背景色、对齐方式嵌入其他控件如在下拉框单元格里放一个QComboBox排序、筛选等。对于超大数据集更高效的是QTableView搭配自定义的数据模型QAbstractTableModel但QTableWidget在数据量不大时更简单易用。4.QMessageBox与用户对话弹窗是必不可少的交互手段。QMessageBox提供了几种标准对话框# 信息提示 QMessageBox.information(self, “标题”, “操作成功”) # 警告 QMessageBox.warning(self, “警告”, “文件将被覆盖是否继续“) # 错误 QMessageBox.critical(self, “错误”, “无法打开文件”) # 提问是/否 reply QMessageBox.question(self, “确认”, “确定要删除吗“, QMessageBox.Yes | QMessageBox.No) if reply QMessageBox.Yes: # 执行删除操作 pass合理使用不同类型的弹窗能极大地提升用户体验。5. 事件处理与多线程让界面保持“流畅”的秘密GUI应用是事件驱动的。用户的每一次点击、按键系统的每一个定时信号都是一个事件。PyQt5的主线程也叫GUI线程负责处理这些事件和更新界面。这里有一个黄金法则永远不要在GUI线程中执行耗时操作。5.1 为什么界面会“卡死”试想一下你在界面上点击了一个“处理数据”的按钮槽函数里直接写了一个需要运行10秒的复杂计算或者一个网络请求。在这10秒内GUI线程被这个计算任务完全占用它无法处理任何新的事件比如重绘窗口、响应鼠标移动。结果就是窗口变成白色未响应按钮按下去弹不起来用户体验极差。5.2 解决方案多线程与QThread正确的做法是将耗时任务放到另一个线程工作线程中去执行。PyQt5提供了QThread类来方便地进行多线程编程。但直接使用Python标准的threading模块与PyQt5控件交互是危险的因为Qt的对象通常不是线程安全的。QThread提供了安全的线程间通信机制。下面是一个典型的工作线程模式import sys, time from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout, QLabel, QProgressBar) from PyQt5.QtCore import QThread, pyqtSignal # 1. 定义一个工作线程类继承自QThread class WorkerThread(QThread): # 2. 定义信号。信号是线程间通信的桥梁。 progress_updated pyqtSignal(int) # 用于更新进度条传递一个整数 task_finished pyqtSignal(str) # 任务完成时发出传递一个字符串消息 def run(self): 线程的主执行函数重写run方法 for i in range(1, 101): time.sleep(0.05) # 模拟耗时操作 # 3. 通过信号发送数据到主线程 self.progress_updated.emit(i) self.task_finished.emit(“数据处理完成“) class MainWindow(QWidget): def __init__(self): super().__init__() self.initUI() self.worker None # 用于保存工作线程实例 def initUI(self): layout QVBoxLayout() self.btn_start QPushButton(“开始耗时任务“) self.label_status QLabel(“准备就绪“) self.progress_bar QProgressBar() layout.addWidget(self.btn_start) layout.addWidget(self.progress_bar) layout.addWidget(self.label_status) self.setLayout(layout) self.btn_start.clicked.connect(self.start_long_task) def start_long_task(self): # 禁用按钮防止重复点击 self.btn_start.setEnabled(False) self.label_status.setText(“任务进行中...“) self.progress_bar.setValue(0) # 4. 创建并启动工作线程 self.worker WorkerThread() # 5. 连接工作线程的信号到主线程的槽函数 self.worker.progress_updated.connect(self.update_progress) self.worker.task_finished.connect(self.on_task_finished) # 线程结束时自动清理重要 self.worker.finished.connect(self.worker.deleteLater) self.worker.start() # 6. 在主线程中更新UI的槽函数 def update_progress(self, value): self.progress_bar.setValue(value) def on_task_finished(self, message): self.label_status.setText(message) self.btn_start.setEnabled(True) # 重新启用按钮 self.worker None # 清空引用 if __name__ ‘__main__‘: app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec_())关键点解析pyqtSignal用于定义信号。必须作为类的属性在类作用域内定义。括号内指定信号传递的数据类型。emit()在工作线程的run方法中通过信号.emit(数据)来发射信号。连接与执行在主线程中将工作线程的信号连接到主线程的槽函数。这些槽函数如update_progress是安全的因为它们是在主线程上下文中被调用的。线程生命周期管理通过finished信号连接deleteLater()确保线程对象在结束后被安全清理避免内存泄漏。使用这种模式耗时任务在后台运行同时通过信号不断向主线程报告进度主线程即时更新进度条和状态标签界面始终保持流畅响应。5.3 其他事件处理键盘与鼠标除了按钮点击我们经常需要处理键盘和鼠标事件。这可以通过重写控件的事件处理器event handler来实现。from PyQt5.QtCore import Qt class MyWidget(QWidget): def keyPressEvent(self, event): 处理键盘按下事件 if event.key() Qt.Key_Return or event.key() Qt.Key_Enter: print(“回车键被按下“) # 可以在这里触发某个操作如提交表单 elif event.key() Qt.Key_Escape: self.close() # 按ESC关闭窗口 else: super().keyPressEvent(event) # 调用父类处理其他按键 def mousePressEvent(self, event): 处理鼠标按下事件 if event.button() Qt.LeftButton: print(f“左键在 ({event.x()}, {event.y()}) 被点击“)通过重写这些方法你可以实现丰富的交互逻辑比如自定义控件的拖动、快捷键支持等。6. 数据与界面的绑定Model/View架构初探当你的数据变得复杂比如从一个数据库或API获取了大量数据要在表格或列表中显示时直接操作QTableWidget或QListWidget的每一项item会非常低效且代码臃肿。这时就该引入Qt强大的Model/View模型/视图架构了。6.1 理解Model/View这个架构的核心思想是分离数据与显示。模型Model负责管理数据。它知道数据有多少行、多少列每个位置的数据是什么。它不关心数据如何被显示。视图View负责显示数据。它从模型获取数据并以表格、列表、树形等方式呈现给用户。它不关心数据的存储细节。委托Delegate负责渲染视图中的每个数据项。默认情况下视图使用简单的文本委托。你可以自定义委托来显示复选框、进度条、颜色选择器等复杂控件。这种分离带来了巨大的好处效率对于海量数据视图只请求当前可见部分的数据无需一次性加载所有项。一致性同一个模型可以同时提供给多个不同的视图如一个表格视图和一个列表视图修改模型数据所有视图自动同步更新。灵活性可以轻松更换数据源只需换一个模型而视图代码几乎不用改动。6.2 使用QStandardItemModel快速上手对于初学者QStandardItemModel是一个易于使用的通用模型。它可以用来构建树形、列表和表格数据。import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QTreeView, QListView, QSplitter from PyQt5.QtGui import QStandardItemModel, QStandardItem class MainWindow(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 1. 创建数据模型 model QStandardItemModel(4, 3) # 4行3列 model.setHorizontalHeaderLabels([‘姓名‘, ‘年龄‘, ‘部门‘]) # 2. 填充数据 data [ (‘张三‘, ‘28‘, ‘研发部‘), (‘李四‘, ‘35‘, ‘市场部‘), (‘王五‘, ‘22‘, ‘实习生‘), (‘赵六‘, ‘40‘, ‘管理层‘) ] for row, (name, age, dept) in enumerate(data): model.setItem(row, 0, QStandardItem(name)) model.setItem(row, 1, QStandardItem(age)) model.setItem(row, 2, QStandardItem(dept)) # 3. 创建不同的视图并设置同一个模型 table_view QTableView() list_view QListView() tree_view QTreeView() table_view.setModel(model) # 注意QListView默认显示第一列我们可以设置一个代理来显示多列信息略复杂 # 这里简单地将第一列作为列表显示 list_view.setModel(model) list_view.setModelColumn(0) # 显示第0列姓名 tree_view.setModel(model) # 4. 使用分割器同时显示多个视图 splitter QSplitter() splitter.addWidget(table_view) splitter.addWidget(list_view) splitter.addWidget(tree_view) self.setCentralWidget(splitter) self.setWindowTitle(‘Model/View 演示‘) self.resize(800, 400) if __name__ ‘__main__‘: app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec_())运行这个程序你会看到一个窗口被分割成三部分分别用表格、列表和树形显示同一份数据。在表格视图中修改某个单元格的内容列表和树形视图会立即同步变化因为它们共享同一个模型。6.3 自定义模型连接真实数据源QStandardItemModel适合内存中的数据。如果你的数据来自SQL数据库、网络API或大型文件你需要继承QAbstractTableModel用于表格、QAbstractListModel用于列表等基类实现几个关键方法来创建自定义模型。这涉及rowCount(),columnCount(),data(),setData(),headerData()等方法的实现。虽然代码量稍多但它给了你完全的控制权并能实现懒加载等高级特性是开发数据密集型应用的必经之路。7. 样式表QSS让你的界面焕然一新默认的PyQt5控件样式是操作系统原生风格虽然经典但可能缺乏个性。使用Qt样式表QSS你可以像写CSS装饰网页一样轻松地美化你的PyQt5应用。7.1 QSS基础语法QSS的语法几乎和CSS一样。你可以为控件类型、对象名objectName、类等设置样式。# 在代码中为整个应用设置样式 app.setStyleSheet(”” QPushButton { background-color: #4CAF50; /* 绿色背景 */ border: none; color: white; padding: 10px 24px; text-align: center; font-size: 16px; border-radius: 8px; } QPushButton:hover { background-color: #45a049; /* 鼠标悬停时变深绿 */ } QPushButton:pressed { background-color: #3d8b40; /* 按下时更深 */ } QLineEdit { padding: 5px; border: 2px solid #ccc; border-radius: 4px; } QLineEdit:focus { border-color: #66afe9; /* 获得焦点时边框变蓝 */ } “””)选择器类型QPushButton应用于所有QPushButton实例。#btnSubmit应用于objectName为btnSubmit的控件在Designer或代码中设置setObjectName(“btnSubmit”)。.warning应用于所有property中包含warning的控件通过setProperty(“class”, “warning”)设置。QDialog QPushButton应用于所有在QDialog内部的QPushButton后代选择器。7.2 使用外部QSS文件将样式写在代码里不利于维护。更好的做法是使用单独的.qss文件。创建一个style.qss文件写入你的所有样式规则。在程序启动时加载它def load_stylesheet(filename): with open(filename, ‘r‘, encoding‘utf-8‘) as f: return f.read() app.setStyleSheet(load_stylesheet(‘style.qss‘))7.3 注意事项与技巧子控件选择器有些复杂控件由多个子部件组成。例如QComboBox的下拉箭头是一个子控件可以用QComboBox::drop-down来单独设置其样式。状态伪类除了:hover,:pressed,:focus还有:checked用于复选框、单选按钮、:disabled禁用状态、:selected选中状态等。优先级更具体的选择器优先级更高。例如#myButton的样式会覆盖QPushButton的样式。图片资源可以使用url()语法设置背景图片但需要注意图片路径问题。为了打包方便通常建议将图片资源用Qt的资源系统.qrc文件进行管理然后在QSS中使用url(:/images/button.png)这样的路径。美化界面是一个无底洞适度即可。保持界面清晰、一致、符合操作逻辑比炫酷的效果更重要。对于商业应用一套精心设计的QSS能极大提升软件的专业感和品牌形象。8. 打包与部署从脚本到独立可执行文件开发完成的程序最终要交付给用户使用。你不可能要求每个用户都安装Python、PyQt5以及一堆依赖库。打包就是将你的Python脚本、依赖库和解释器一起封装成一个或几个可以直接双击运行的独立文件的过程。8.1 打包工具选择PyInstaller在众多打包工具中如cx_Freeze, py2exe, NuitkaPyInstaller因其简单易用、跨平台Windows, macOS, Linux而最受欢迎。安装PyInstallerpip install pyinstaller8.2 基础打包命令与常见问题最基本的打包命令在你的项目主文件目录下执行pyinstaller -F -w your_script.py参数解释-F打包成单个可执行文件--onefile。如果不加此参数会生成一个包含很多依赖文件的文件夹。-w运行时不显示命令行窗口--windowed。如果你的程序是纯GUI没有控制台输出一定要加这个参数。如果需要看打印信息调试则不要加。your_script.py你的程序入口文件。执行后会在当前目录生成dist文件夹里面就是打包好的可执行文件。然而直接这样打包PyQt5程序很可能会遇到问题找不到模块如果程序里用了PyQt5.QtWebEngineWidgets用于内嵌浏览器等子模块PyInstaller可能无法自动分析到。需要在打包时通过--hidden-import手动指定。图标不显示/样式丢失如果程序使用了外部资源如图片、QSS文件、.ui文件PyInstaller不会自动把它们打包进去。程序运行时会在相对路径下寻找这些文件如果找不到就会出错。文件过大一个简单的“Hello World”程序打包后可能就有几十MB因为PyInstaller把整个Python解释器和PyQt5库都打包进去了。这是正常现象。8.3 进阶打包配置使用Spec文件对于复杂的项目推荐使用Spec文件进行定制化打包。首先生成一个默认的Spec文件pyinstaller your_script.py这会生成your_script.spec。然后修改这个文件# -*- mode: python ; coding: utf-8 -*- a Analysis( [‘your_script.py‘], pathex[], binaries[], datas[], # 这里需要添加数据文件 hiddenimports[‘PyQt5.QtWebEngineWidgets‘], # 添加隐藏导入 hookspath[], hooksconfig{}, runtime_hooks[], excludes[], noarchiveFalse, ) # ... 其他部分保持不变 # 在 datas 列表中添加资源文件格式为 (源路径, 打包后的虚拟路径) # 例如将当前目录下的 ‘images‘ 文件夹和 ‘style.qss‘ 文件打包进去 datas [(‘images‘, ‘images‘), (‘style.qss‘, ‘.‘)], # 修改图标仅Windows和macOS有效 # exe EXE(pyz, a.scripts, ... , icon‘my_icon.ico‘)修改完Spec文件后使用Spec文件进行打包而不是直接指定Python脚本pyinstaller your_script.spec8.4 在代码中处理资源路径打包后程序的运行目录可能发生变化。为了能正确找到资源文件如图片、QSS在代码中不能使用硬编码的绝对或相对路径。一个可靠的方法是使用sys._MEIPASS属性这个属性只在PyInstaller打包后的运行时环境中存在它指向临时解压资源文件的目录。import sys import os def resource_path(relative_path): 获取资源的绝对路径。在开发环境和打包后均有效 try: # PyInstaller创建的临时文件夹路径 base_path sys._MEIPASS except AttributeError: # 正常开发环境 base_path os.path.abspath(“.“) return os.path.join(base_path, relative_path) # 使用示例 stylesheet_path resource_path(“style.qss“) icon_path resource_path(“images/icon.png“)在Spec文件的datas中你需要确保这些资源文件被正确包含并且resource_path函数能根据sys._MEIPASS找到它们。打包是一个需要耐心调试的过程。建议先不加-F参数打包成文件夹运行测试无误后再尝试打包成单个文件。对于最终发布提供安装程序如使用Inno Setup for Windows制作安装包是更专业的做法。
返回列表