PyQt6 / PySide6

Prerequisite

Qt 是 C++ 写的图形界面工具,PyQt 和 PySide 都是 Qt 的儿子
PyQt5 和 PyQt6,一家私人公司开发的,用它开发商用软件要给这家公司交钱,不交钱就吃官司(PyQt6 版本是 PyQt5 版本的升级替代者)
PySide2 和 PySide6,分别是基于 C++ 下的 Qt5 和 Qt6 开发的,用它写商用软件是允许的,不会吃官司(PySide6 版本是 PySide2 版本的升级替代者)

总结:PySide6 和 PyQt6 基本类似,只用学 PySide6 就行
PS:我之所以学 Python 版本的 GUI 是因为要完成一个《基于人工智能的图片动漫化》的项目,简单来讲就是 Python UI 界面 + 人工智能 API

入门准备

视频教程为:

参考文档为:

参考项目

针对部分类似目标的成品进行考察(主要参考外观设计结合人工智能 API 的方式

一、跟着教程学习 PySide6

项目介绍

视频:Learn Python GUI Development for Desktop – PySide6 and Qt Tutorial
知识总结:Pyside6 用法、QT designer 用法

下载

QTWidgets 代码

大多数写法如下,这套模板非常简约,下面是 main.py 文件,还有一个控件文件

import sys
from PySide6.QtWidgets import QApplication
# 自定义类
from button_holder import ButtonHolder

app = QApplication(sys.argv)
window = XXX()
window.show() 
app.exec()

PS:大致看一两个控件的演示,就亲自运行文件中的全部代码看效果(不要求记住代码,有个印象即可),然后直接跳到 3:52:00 看下一章节

QtDesigner

制作 ui 文件使用的软件:QtDesigner

编译 ui 文件至 python 文件命令:pyside6-uic widget.ui > ui_widget.py,记得更改文件为 UTF-8
编译资源文件至 python 文件命令:pyside6-rcc resource.qrc -o resource_rc.py

关于 widget 文件(UI 控件)的模板
文件中有六个文件
名称是:main.pywidget.pywidget.uiresource.qrcresource_rc.pyui_widget.py
需要写代码的:主文件(main.py)、控件文件(widget.py
在 QtDesigner 中操作的:UI 控件(widget.ui)、资源文件(resource.qrc

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QWidget
# UI 文件、资源文件
from ui_widget import Ui_Widget
import resource_rc

class Widget(QWidget, Ui_Widget):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.setWindowTitle("User data")

最后

再把项目文件中的演示全都运行一遍即可

二、初步体验实战项目

项目介绍

视频:Python+Pyside/PyQt实现的GUI桌面应用
博客:Python系列-GUI-pyqt(pyside6)
Github 项目:PyDracula - Modern GUI PySide6 / PyQt6
环境:Python 10 + PySide6
目的:在已有的项目上,进行改造和功能实现

项目结构

  • main.py: 主文件
  • main.ui: Qt Designer 工程文件
  • resouces.qrc: Qt Designer 资源文件
  • setup.py: 编译应用程序
  • themes/: 主题文件
  • modules/: GUI 的各个模块
  • modules/app_funtions.py: 应用功能函数
  • modules/app_settings.py: 用于配置用户界面的全局变量。
  • modules/resources_rc.py: 使用命令为 python 编译的 resource.qrc 文件(pyside6-rcc resources.qrc -o resources_rc.py
  • modules/ui_functions.py: 与用户界面 GUI 相关的函数
  • modules/ui_main.py: 与 Qt Designer 导出的用户界面相关的文件
  • images/: 所有图像和图标

初步改造

  • 作者和版本号:modules/ui_main.py 最后两行
  • 软件标题:main.py 的 title 和 description 同时修改(其实有作用的是后者)
  • 图片:images/,记得修改后的文件名不能变
  • 按钮功能禁用:main.pybtnName == "btn_save" 后面删除 print,接上 QMessageBox.information(self, "提示", "该功能暂未实现", QMessageBox.Yes)

全部修改完成后,运行 pyside6-rcc resources.qrc -o resources_rc.py,再将 resources_rc.py 从最外层拷贝到 modules/

实现功能:切换黑白主题

  1. 主题初始化
# 路径冻结,防止打包成 exe 后路径错乱
if getattr(sys, 'frozen', False):
    absPath = os.path.dirname(os.path.abspath(sys.executable))
elif __file__:
    absPath = os.path.dirname(os.path.abspath(__file__))
# 主题初始化为白色
useCustomTheme = True
self.useCustomTheme = useCustomTheme
self.absPath = absPath
themeFile = os.path.abspath(os.path.join(absPath, "themes\py_dracula_light.qss"))
  1. 按钮添加功能
# 新增功能:切换皮肤
widgets.btn_message.clicked.connect(self.buttonClick)
  1. 实现具体切换主题的功能
# 切换主题
if btnName == "btn_message":
    if self.useCustomTheme:
        themeFile = os.path.abspath(os.path.join(self.absPath, "themes\py_dracula_dark.qss"))
        # 跟着原先的代码走
        UIFunctions.theme(self, themeFile, True)
        AppFunctions.setThemeHack(self)
        self.useCustomTheme = False
    else:
        themeFile = os.path.abspath(os.path.join(self.absPath, "themes\py_dracula_light.qss"))
        # 跟着原先的代码走
        UIFunctions.theme(self, themeFile, True)
        AppFunctions.setThemeHack(self)
        self.useCustomTheme = True

实现功能:实时监控(电脑资源,如 cpu、内存)

BUG:点击绘图按钮后,再点击清除,终端会显示 "Can not add series. Series already on the chart.",我认为是 self.chart.addSeries(self.seriesS) 代码的问题,日后待修改

  1. 在 QtDesigner 中创建新按钮和新页面
  • main.ui 一般导入 QtDesigner 后,预览 python 代码,直接粘贴到 ui_main.py(节省了用命令转换)
  • QtDesigner 中按 Ctrl + R 预览

使用的控件是 Push Button 和 Graphics View,记得 Graphics View 要权限提升(QChartView),完成后 Ctrl + s 保存,再回到 main.py 中添加代码

# 新增功能:电脑信息数据分析
widgets.btn_computer.clicked.connect(self.buttonClick)
widgets.computer_info_start.clicked.connect(self.start_computer_info)
widgets.computer_info_clear.clicked.connect(self.clear_computer_info)

# 电脑信息数据分析
if btnName == "btn_computer":
    widgets.stackedWidget.setCurrentWidget(widgets.computer_info) # SET PAGE
    UIFunctions.resetStyle(self, btnName) # RESET ANOTHERS BUTTONS SELECTED
    btn.setStyleSheet(UIFunctions.selectMenu(btn.styleSheet())) # SELECT MENU

    self.seriesS = QLineSeries()
    self.seriesL = QLineSeries()
    self.seriesS.setName("cpu")
    self.seriesL.setName("memory")
  1. 自定义类
# 新增的类
import psutil
import time
from PySide6.QtCharts import QChart, QLineSeries

class NewThread(QThread):
    # 自定义信号声明
    # 使用自定义信号和 UI 主线程通讯,参数是发送信号时附带参数的数据类型,可以是 str,int,list 等
    finishSignal = Signal(str)

    # 带一个参数 t
    def __init__(self, parent=None):
        super(NewThread, self).__init__(parent)

    # run 函数是子线程中的操作,线程启动后开始执行
    if os.path.exists(f"./computer_info.csv"):
        pass
    else:
        with open(r"./computer_info.csv", "w") as f:
            pass
    
    def run(self):
        timer = 0
        while True:
            timer += 1
            cpu_percent = psutil.cpu_percent(interval=1)
            cpu_info = cpu_percent
            virtual_memory = psutil.virtual_memory()
            memory_percent = virtual_memory.percent
            with open(r"./computer_info.csv", "a") as f:
                f.write(f"{timer},{cpu_info}{memory_percent}\n")
            time.sleep(1)
            # 发射自定义信号
            # 通过 emit 函数将参数 i 传递给主线程,触发自定义信号
            self.finishSignal.emit("1")
  1. 自定义函数
def start_computer_info(self):
    """
    开始获取电脑数据
    """
    # 开始分析记录电脑数据,需要持续获取,然后分析
    self.thread1 = NewThread() # 实例化一个线程
    # 将线程 thread 的信号 finishSignal 和 UI 主线程中的槽函数 data_display 进行连接
    self.thread1.finishSignal.connect(self.data_display)
    # 启动线程,执行线程类中的 run 函数
    self.thread1.start()

def data_display(self, str_):
    """
    电脑信息的数据展示
    """
    # 获取已经记录好的数据并展示
    # 设置一个 flag
    with open(r"./computer_info.csv", "r") as f:
        reader = f.readlines()
        reader_last = reader[-1].replace("\n", "").split(",")
        # 横坐标
        col = int(reader_last[0])
        # cpu
        cpu = float(reader_last[1])
        # 内存
        memory = float(reader_last[2])
    
    self.seriesS.append(col, cpu)
    self.seriesL.append(col, memory)
    self.chart = QChart()
    self.chart.setTitle("设备资源图")
    self.chart.addSeries(self.seriesS)
    self.chart.addSeries(self.seriesL)
    self.chart.createDefaultAxes()
    widgets.graphicsView.setChart(self.chart)

def clear_computer_info(self):
    """
    清除设备表格信息
    """
    self.seriesS.clear()
    self.seriesL.clear()
    self.chart.addSeries(self.seriesS)
    self.chart.addSeries(self.seriesL)

实现功能:打开本地文件+打开外部网址+随机切换本地图片

  1. 在 QtDesigner 中创建新按钮
  2. main.py 中添加功能
# 新增功能:打开本地文件
widgets.btn_local.clicked.connect(self.open_file)
# 新增功能:打开网站
widgets.btn_web.clicked.connect(self.open_web)
# 新增功能:切换图片
widgets.btn_pic.clicked.connect(self.change_pic)

def open_file(self):
    import webbrowser
    webbrowser.open("说明书" + ".docx")

def open_web(self):
    import webbrowser
    webbrowser.open("www.baidu.com")

def change_pic(self):
    url_list = [
        "./1.jpg",
        "./2.jpg",
        "./3.jpg",
        "./4.jpg",
        "./5.png"
    ]
    import random
    index = random.randint(0, 4) # 很神奇,能显示 5 张图片
    lb1 = widgets.label # 同页面下的控件
    pix = QPixmap(url_list[index]).scaled(lb1.size(), aspectMode=Qt.KeepAspectRatio)
    lb1.setPixmap(pix)
    lb1.repaint()

打包

  • 下载打包库:pip install pyinstaller
  • 一个 exe(但需要静态资源),缺点是启动慢 pyinstaller -F .\main.py
  • 一个 exe + 多个文件(依然需要静态资源),缺点是文件多 pyinstaller -D .\main.py

完结撒花,全部代码都放在这里

posted @ 2023-02-23 15:14  筱团  阅读(3944)  评论(1编辑  收藏  举报