基于`pyside6`开发

文章目录

    • 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 designerpyside6-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类以及QIconQPixmap类的构造函数访问
在这里插入图片描述

实参:$FileName$ -o $FileNameWithoutExtension$.py
工作目录:$FileDir$

以上等同于命令行:(todo_icons.pyj即是转化后可在代码中导包文件)

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)
基于PySide6开发扫雷游戏是指使用PySide6框架来创建一个图形用户界面(GUI)版本的扫雷游戏。PySide6是一个跨平台的GUI工具包,它是Qt for Python的官方实现,提供了一套完整的工具和控件来帮助开发者设计和实现复杂的窗口应用程序。 以下是使用PySide6开发扫雷游戏的一些基本步骤和概念: 1. **安装PySide6**:首先,需要确保已经安装了PySide6。可以通过pip安装: ``` pip install PySide6 ``` 2. **创建主窗口**:在PySide6中,所有的GUI组件都是从QWidget派生的。主窗口可以通过创建一个QMainWindow或QWidget的子类来实现。 3. **设计布局**:使用QGridLayout来布局游戏的网格。QGridLayout允许你把窗口分成行和列的网格,并在每个网格中放置小部件,如按钮,每个按钮对应游戏中的一个格子。 4. **初始化游戏数据**:创建一个二维数组来存储扫雷游戏的状态信息,包括每个格子是否是雷、是否被揭露、周围雷的数量等。 5. **添加逻辑**: - 为每个按钮添加点击事件的处理逻辑,实现揭露格子或者标记雷的功能。 - 当玩家点击一个没有雷的格子时,应该自动揭露周围没有雷的格子(递归揭露),并显示周围雷的数量。 - 如果玩家点击到雷,则游戏结束,揭露所有雷的位置。 - 如果玩家揭露所有非雷格子,则玩家胜利。 6. **添加游戏结束和重置功能**:提供游戏结束时的处理逻辑和重置游戏的功能。 7. **测试和调试**:运行程序,测试游戏的所有功能是否正常工作,并进行调试。 使用PySide6开发扫雷游戏的关键是将游戏逻辑和界面逻辑分离,确保代码的可读性和可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值