五、PyQt6图形用户界面
一、基础概念
PyQt6是Python连接Qt6应用框架的成熟绑定库,作为PyQt5的继任者,它完整支持Qt6的新特性与底层优化。这带来了显著的性能提升(如更快的渲染、更优的内存管理)以及对现代UI设计(高DPI、深色模式、丰富视觉效果)的全面支持,使开发者能构建符合当前标准的应用。
相比PyQt5,PyQt6进行了API清理和现代化(如信号槽语法更简洁、事件处理更统一),移除了过时功能,仅支持Python 3.7+,且商业授权要求更严格。与其他GUI库相比:
- Tkinter:PyQt6提供更现代、功能丰富的组件和视觉效果。
- wxPython:PyQt6通常有更好的跨平台性能和高分屏渲染。
- PySide6:PyQt6文档和社区资源更丰富,但授权限制更严。
- Kivy:PyQt6更专注于提供原生体验的桌面应用开发。
PyQt6受益于Qt6的持续更新,拥有强大的Qt Designer工具和完善的多线程支持。尽管其较新的生态(教程、插件)目前不如PyQt5成熟,但它代表了Qt的未来方向。总的来说,PyQt6是新项目和追求现代特性的首选;PyQt5则在维护旧项目或需要更宽松授权和Python版本兼容性时更合适。
二、环境搭建
深度学习环境部分已经介绍了Miniconda环境,并创建了一个名为deeplearning的conda环境,我们在该环境基础上继续安装PyQt6环境。首先打开Anaconda PowerShell Prompt界面,执行conda activate deeplearning激活环境,之后执行pip install pyqt6 -i https://pypi.tuna.tsinghua.edu.cn/simple安装PyQt6环境。
除了PyQt6基础环境,我们还建议安装PyQt6-Tools,这是一个包含Qt Designer和pyuic6等实用工具的扩展包。其中Qt Designer用于拖拽式设计GUI界面,设计的界面会保存为一个.ui文件;pyuic6将.ui文件转换为.py代码。PyQt6-Tools的安装方法为执行命令pip install pyqt6-tools -i https://pypi.tuna.tsinghua.edu.cn/simple。
安装PyQt6-Tools后,通常需要在Pycharm中配置外部工具,以便更方便地使用Qt Designer和pyuic6。首先进入deeplearning环境的保存路径,搜索designer.exe和pyuic6.exe,记录一下对应的路径,例如,本文的路径如下:
- designer.exe:D:\Data\miniconda\envs\deeplearning\Lib\site-packages\qt6_applications\Qt\bin\designer.exe
- pyuic6.exe:D:\Data\miniconda\envs\deeplearning\Scripts\pyuic6.exe
然后打开Pycharm,依次点击文件-设置-工具-外部工具,点击“+”号添加工具。具体填写内容如下表,表中未提及的选项留空,注意将程序选项中的内容设置为自己的路径。
Qt Designer |
名称:Qt Designer 程序:D:\Data\miniconda\envs\deeplearning\Lib\site-packages\qt6_applications\Qt\bin\designer.exe 工作目录:$ProjectFileDir$ |
pyuic6 |
名称:PyUIC6 程序:D:\Data\miniconda\envs\deeplearning\Scripts\pyuic6.exe 实参:$FileName$ -o $FileNameWithoutExtension$.py 工作目录:$FileDir$ |
三、使用示例
PyQt6的界面开发可以通过纯代码编写和Qt Designer拖拽生成两种方式实现,纯代码编写赋予开发者对界面的完全控制权,适合需要动态调整布局或深度定制的场景,代码结构清晰且便于版本控制,但开发效率较低;Qt Designer拖拽生成通过可视化界面大幅提升开发速度,支持设计师与程序员并行工作,适合快速迭代的大型项目,但需要额外的转换步骤,且可能在复杂逻辑下降低灵活性。初学者建议从纯代码入手,以深入理解PyQt6的组件层级和信号槽机制;熟练后可结合Qt Designer加速开发。对于高度动态或定制化的界面(如运行时布局调整、复杂交互逻辑),纯代码仍是更优选择,因其直接操作对象,避免.ui文件的转换与维护成本。
我们提供了一个示例程序,该程序仅通过一百多行代码,便实现了好看的粒子效果,读者可以运行查看效果,同时验证PyQt6是否安装成功。其中代码细节将在后面的章节介绍,这里仅作为演示内容,方便读者体会PyQt6的强大功能。
import sys
import random
from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget
from PyQt6.QtGui import QPainter, QColor, QPen, QRadialGradient
from PyQt6.QtCore import Qt, QTimer, QPointF, QRectFclass Particle:def __init__(self, x, y):self.pos = QPointF(x, y)self.vel = QPointF(random.uniform(-1.5, 1.5), random.uniform(-1.5, 1.5))self.lifetime = random.randint(30, 120)self.color = QColor(random.randint(150, 255),random.randint(150, 255),random.randint(150, 255),200)self.size = random.randint(4, 10)self.trail = []self.max_trail = 5def update(self, target):self.trail.append(QPointF(self.pos))if len(self.trail) > self.max_trail:self.trail.pop(0)direction = target - self.posdirection /= max(1, QPointF.dotProduct(direction, direction) ** 0.5 * 1.5)self.vel = (self.vel * 0.9 + direction * 0.1)self.vel += QPointF(random.uniform(-0.2, 0.2), random.uniform(-0.2, 0.2))self.pos += self.velself.lifetime -= 1def draw(self, painter):for i, pos in enumerate(self.trail):alpha = min(200, self.lifetime * 2 * (i + 1) / len(self.trail))size = max(1, self.size * (i + 1) / len(self.trail) * 0.7)color = QColor(self.color)color.setAlpha(int(alpha))painter.setBrush(color)painter.setPen(Qt.PenStyle.NoPen)painter.drawEllipse(pos, size, size)alpha = min(255, self.lifetime * 3)self.color.setAlpha(alpha)painter.setBrush(self.color)gradient = QRadialGradient(self.pos, self.size)gradient.setColorAt(0, QColor(255, 255, 255, 180))gradient.setColorAt(1, QColor(self.color.red(), self.color.green(), self.color.blue(), 0))painter.setPen(Qt.PenStyle.NoPen)painter.setBrush(gradient)painter.drawEllipse(self.pos, self.size * 1.5, self.size * 1.5)painter.setBrush(self.color)painter.drawEllipse(self.pos, self.size * 0.7, self.size * 0.7)class ParticleWidget(QWidget):def __init__(self):super(ParticleWidget, self).__init__()self.setWindowTitle("PyQt6 炫酷粒子效果")self.resize(800, 600)self.particles = []self.target = QPointF(400, 300)self.timer = QTimer(self)self.timer.timeout.connect(self.update_particles)self.timer.start(20) # 提高帧率使动画更流畅self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)self.setStyleSheet("background: transparent;")self.bg_particles = self.create_background_particles(50)def create_background_particles(self, count):particles = []for _ in range(count):x = random.randint(0, self.width())y = random.randint(0, self.height())size = random.randint(1, 3)alpha = random.randint(20, 60)particles.append({'pos': QPointF(x, y),'size': size,'color': QColor(180, 180, 220, alpha)})return particlesdef mouseMoveEvent(self, event):self.target = event.position()def update_particles(self):if len(self.particles) < 200:for _ in range(5):offset = QPointF(random.uniform(-30, 30), random.uniform(-30, 30))self.particles.append(Particle(self.target.x() + offset.x(), self.target.y() + offset.y()))for particle in self.particles[:]:particle.update(self.target)if particle.lifetime <= 0:self.particles.remove(particle)for p in self.bg_particles:p['pos'] += QPointF(0.1, 0)if p['pos'].x() > self.width():p['pos'].setX(0)p['pos'].setY(random.randint(0, self.height()))self.update()def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.RenderHint.Antialiasing)gradient = QRadialGradient(self.width() / 2, self.height() / 2, self.width())gradient.setColorAt(0, QColor(10, 5, 20, 220))gradient.setColorAt(1, QColor(30, 15, 40, 220))painter.fillRect(self.rect(), gradient)for p in self.bg_particles:painter.setBrush(p['color'])painter.setPen(Qt.PenStyle.NoPen)painter.drawEllipse(p['pos'], p['size'], p['size'])for particle in self.particles:particle.draw(painter)font = painter.font()font.setPointSize(24)font.setBold(True)painter.setFont(font)text_gradient = QRadialGradient(400, 50, 100)text_gradient.setColorAt(0, Qt.GlobalColor.cyan)text_gradient.setColorAt(1, Qt.GlobalColor.magenta)painter.setPen(QPen(text_gradient, 2))painter.drawText(QRectF(0, 30, self.width(), 50), Qt.AlignmentFlag.AlignCenter, "粒子效果 - 请按住鼠标移动")painter.setPen(QPen(Qt.GlobalColor.white, 1))font.setPointSize(12)painter.setFont(font)painter.drawText(10, self.height() - 20, f"粒子数量: {len(self.particles)}")if __name__ == "__main__":app = QApplication(sys.argv)app.setStyle('Fusion')window = QMainWindow()window.setWindowTitle("PyQt6 炫酷粒子效果")window.setGeometry(300, 300, 800, 600)widget = ParticleWidget()window.setCentralWidget(widget)window.show()sys.exit(app.exec())