PySide6从0开始学习的笔记(二十) qdarkstyle的深入应用

所有的操作均在下载到本地的QDarkStyleSheet-master项目下进行:

项目地址:https://github.com/ColinDuquesnoy/QDarkStyleSheet/tree/master

我放在了网盘:https://pan.baidu.com/share/init?surl=OP5e_ejyB5Brbao3VNSx7Q 提取码: 2jxr


一、扩充色系

qdarkstyle的colorsystem.py中的所有颜色:

python 复制代码
class Gray:
    B0 = '#000000'
    B10 = '#19232D'
    B20 = '#293544'
    B30 = '#37414F'
    B40 = '#455364'
    B50 = '#54687A'
    B60 = '#60798B'
    B70 = '#788D9C'
    B80 = '#9DA9B5'
    B90 = '#ACB1B6'
    B100 = '#B4B8BC'
    B110 = '#C0C4C8'
    B120 = '#D2D5D8'
    B130 = '#DFE1E2'
    B140 = '#FAFAFA'
    B150 = '#FFFFFF'


class Blue:
    B0 = '#000000'
    B10 = '#062647'
    B20 = '#26486B'
    B30 = '#375A7F'
    B40 = '#346792'
    B50 = '#1A72BB'
    B60 = '#057DCE'
    B70 = '#259AE9'
    B80 = '#37AEFE'
    B90 = '#73C7FF'
    B100 = '#9FCBFF'
    B110 = '#C2DFFA'
    B120 = '#CEE8FF'
    B130 = '#DAEDFF'
    B140 = '#F5FAFF'
    B150 = '#FFFFFF'

qdarkstyle的配色系统只有灰色和蓝色两种色系共32个颜色,这显然是不够用的。在原有基础上,增加红、绿、黄、橙、紫色系,作为补充:

扩充过的colorsystem.py

python 复制代码
# colorsystem.py is the full list of colors that can be used to easily create themes.  B10 = '#19232D'


class Gray:
    B0 = '#000000'
    B10 = '#19232D'
    B20 = '#293544'
    B30 = '#37414F'
    B40 = '#455364'
    B50 = '#54687A'
    B60 = '#60798B'
    B70 = '#788D9C'
    B80 = '#9DA9B5'
    B90 = '#ACB1B6'
    B100 = '#B4B8BC'
    B110 = '#C0C4C8'
    B120 = '#D2D5D8'
    B130 = '#DFE1E2'
    B140 = '#FAFAFA'
    B150 = '#FFFFFF'


class Blue:
    B0 = '#000000'
    B10 = '#062647'
    B20 = '#26486B'
    B30 = '#375A7F'
    B40 = '#346792'
    B50 = '#1A72BB'
    B60 = '#057DCE'
    B70 = '#259AE9'
    B80 = '#37AEFE'
    B90 = '#73C7FF'
    B100 = '#9FCBFF'
    B110 = '#C2DFFA'
    B120 = '#CEE8FF'
    B130 = '#DAEDFF'
    B140 = '#F5FAFF'
    B150 = '#FFFFFF'

class Green:
    B0 = '#000000'
    B10 = '#062D06'
    B20 = '#064826'
    B30 = '#065A37'
    B40 = '#066734'
    B50 = '#1ABB1A'
    B60 = '#05CE05'
    B70 = '#25E925'
    B80 = '#37FE37'
    B90 = '#73FF73'
    B100 = '#9FFF9F'
    B110 = '#C2FAC2'
    B120 = '#CEFFCE'
    B130 = '#DAFFDA'
    B140 = '#F5FFF5'
    B150 = '#FFFFFF'

class Red:
    B0 = '#000000'
    B10 = '#2D0606'
    B20 = '#480626'
    B30 = '#5A0637'
    B40 = '#670634'
    B50 = '#BB1A1A'
    B60 = '#CE0505'
    B70 = '#E92525'
    B80 = '#FE3737'
    B90 = '#FF7373'
    B100 = '#FF9F9F'
    B110 = '#FAC2C2'
    B120 = '#FFCECE'
    B130 = '#FFDADA'
    B140 = '#FFF5F5'
    B150 = '#FFFFFF'

class Purple:
    B0 = '#000000'
    B10 = '#1A082C'
    B20 = '#2D1047'
    B30 = '#3F1862'
    B40 = '#51207D'
    B50 = '#632898'
    B60 = '#7530B3'
    B70 = '#8740CC'
    B80 = '#9950E6'
    B90 = '#AB68F0'
    B100 = '#BD80F5'
    B110 = '#CF98FA'
    B120 = '#E1B0FC'
    B130 = '#EFC8FD'
    B140 = '#F7E0FE'
    B150 = '#FFFFFF'

class Yellow:
    B0 = '#000000'
    B10 = '#2D2D06'
    B20 = '#484826'
    B30 = '#5A5A37'
    B40 = '#676734'
    B50 = '#BBBB1A'
    B60 = '#CECE05'
    B70 = '#E9E925'
    B80 = '#FEFE37'
    B90 = '#FFFF73'
    B100 = '#FFFF9F'
    B110 = '#FAFAC2'
    B120 = '#FFFFCE'
    B130 = '#FFFFDA'
    B140 = '#FFFFF5'
    B150 = '#FFFFFF'

class Orange:
    B0 = '#000000'
    B10 = '#2D1906'
    B20 = '#482606'
    B30 = '#5A3706'
    B40 = '#673406'
    B50 = '#BB5A1A'
    B60 = '#CE7005'
    B70 = '#E99A25'
    B80 = '#FEB037'
    B90 = '#FFC273'
    B100 = '#FFD09F'
    B110 = '#FAE0C2'
    B120 = '#FFE6CE'
    B130 = '#FFEDDA'
    B140 = '#FFF8F5'
    B150 = '#FFFFFF'

class Cyan:
    B0 = '#000000'
    B10 = '#062D2D'
    B20 = '#064848'
    B30 = '#065A5A'
    B40 = '#066767'
    B50 = '#1ABBBB'
    B60 = '#05CECE'
    B70 = '#25E9E9'
    B80 = '#37FEFE'
    B90 = '#73FFFF'
    B100 = '#9FFFFF'
    B110 = '#C2FAFA'
    B120 = '#CEFFFF'
    B130 = '#DAFFFF'
    B140 = '#F5FFFF'
    B150 = '#FFFFFF'

class Pink:
    B0 = '#000000'
    B10 = '#2D0619'
    B20 = '#480630'
    B30 = '#5A0642'
    B40 = '#67064A'
    B50 = '#BB1A7F'
    B60 = '#CE0590'
    B70 = '#E925A8'
    B80 = '#FE37C1'
    B90 = '#FF73D0'
    B100 = '#FF9FDD'
    B110 = '#FAC2E8'
    B120 = '#FFCEF0'
    B130 = '#FFDAF5'
    B140 = '#FFF5FC'
    B150 = '#FFFFFF'

class Brown:
    B0 = '#000000'
    B10 = '#261208'
    B20 = '#3D2010'
    B30 = '#532E18'
    B40 = '#693C20'
    B50 = '#804828'
    B60 = '#995A33'
    B70 = '#B36C3E'
    B80 = '#CC804A'
    B90 = '#E6995C'
    B100 = '#F0B37D'
    B110 = '#F5C799'
    B120 = '#F9D6B3'
    B130 = '#FBE2C7'
    B140 = '#FDF0DA'
    B150 = '#FFFFFF'

ALL = [Gray, Blue, Green, Red, Purple, Yellow, Orange, Cyan, Pink, Brown]

展示和验证:

python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QHBoxLayout
from qdarkstyle.colorsystem import ALL


def get_colors(colorClass):
        colors = lambda: [colorClass.B0, colorClass.B10, colorClass.B20, colorClass.B30, colorClass.B40, colorClass.B50,
                          colorClass.B60, colorClass.B70, colorClass.B80, colorClass.B90, colorClass.B100,
                          colorClass.B110, colorClass.B120, colorClass.B130, colorClass.B140, colorClass.B150]
        return colors()

def hex_to_rgb(hex_color):
    """将十六进制颜色(如#1A72BB)转换为RGB元组 (r, g, b)"""
    # 去除#号,统一转为大写(避免大小写问题)
    hex_color = hex_color.lstrip('#').upper()
    # 校验格式(6位十六进制)
    if len(hex_color) != 6:
        raise ValueError("颜色格式错误,需为6位十六进制(如#1A72BB)")
    # 拆分红、绿、蓝通道并转十进制
    r = int(hex_color[0:2], 16)
    g = int(hex_color[2:4], 16)
    b = int(hex_color[4:6], 16)
    return [r, g, b]

def invert_color(hex_color):
    """将十六进制颜色(如#1A72BB)反转为相反的颜色"""
    # 将十六进制颜色转为RGB元组
    colors = hex_to_rgb(hex_color)
    for i, color in enumerate(colors):
        if 90 < color < 160:
            colors[i] = 200
        else:
            colors[i] = 255 - color
    return colors


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = QWidget()
    layout = QHBoxLayout(w)
    w.setLayout(layout)


    def add_color_label(layout, colorClass):
        color_list = get_colors(colorClass)
        layout1 = QVBoxLayout(w)
        color_name= colorClass.__name__
        label = QLabel(f"{color_name}")
        layout1.addWidget(label)

        for color in color_list:
            c = invert_color(color)
            label = QLabel(f"{color}")
            label.setStyleSheet(f"background-color: {color};color:rgb({c[0]},{c[1]},{c[2]});")
            label.setFixedSize(120, 40)
            layout1.addWidget(label)
            layout.addLayout(layout1)
    for color in ALL:
        add_color_label(layout, color)

    w.show()
    sys.exit(app.exec())

二、创建一个自定义的主题并离线调用

1. QDarkStyleSheet-master/qdarkstyle目录下,新建文件夹/custom:

2. /custom下创建/rc文件夹,和__init__.py、palette.py文件:

init.py内容为空:

python 复制代码
# -*- coding: utf-8 -*-

调色板文件,palette.py:(在这里把dark的蓝色系改成了新增的绿色系)

python 复制代码
# -*- coding: utf-8 -*-

"""QDarkStyle default dark palette."""

# Local imports
from qdarkstyle.colorsystem import Blue, Gray, Green
from qdarkstyle.palette import Palette


class CustomPalettee(Palette):
    """Dark palette variables."""

    # Identifier
    ID = 'custom'

    # Color
    COLOR_BACKGROUND_1 = Gray.B10
    COLOR_BACKGROUND_2 = Gray.B20
    COLOR_BACKGROUND_3 = Gray.B30
    COLOR_BACKGROUND_4 = Gray.B40
    COLOR_BACKGROUND_5 = Gray.B50
    COLOR_BACKGROUND_6 = Gray.B60

    COLOR_TEXT_1 = Gray.B130
    COLOR_TEXT_2 = Gray.B110
    COLOR_TEXT_3 = Gray.B90
    COLOR_TEXT_4 = Gray.B80
    
    # 改成了绿色系
    COLOR_ACCENT_1 = Green.B20
    COLOR_ACCENT_2 = Green.B40
    COLOR_ACCENT_3 = Green.B50
    COLOR_ACCENT_4 = Green.B70
    COLOR_ACCENT_5 = Green.B80

    # Color for disabled elements
    COLOR_DISABLED = Gray.B70

    OPACITY_TOOLTIP = 230

3.QDarkStyleSheet-master/docs/images目录下新建文件夹/custom:

4. 复制QDarkStyleSheet-master/docs/images/dark/palette.svg到刚创建的/custom目录下:

5. 复制qdarkstyle/dark/main.scss到/qdarkstyle/custom

6. 修改QDarkStyleSheet-master/qdarkstyle/utils/main.py:

  • 新增:

新增:

新增:

新增:

修改后的完整代码:

python 复制代码
# -*- coding: utf-8 -*-
"""Script to process QRC files (convert .qrc to _rc.py and .rcc).

The script will attempt to compile the qrc file using the following tools:

    - `pyside6-rcc` for PySide6 and QtPy (Python) (Official)
    - There is no specific rcc compiler for PyQt6, use `pyside6-rcc` (Python)
    - `pyrcc5` for PyQt5 (Python)
    - `pyside2-rcc` for PySide2 (Python)
    - `rcc` for Qt5/Qt6 (C++)

Delete the compiled files that you don't want to use manually after
running this script.

Links to understand those tools:

    - `pyside6-rcc`: https://doc.qt.io/qtforpython/tutorials/basictutorial/qrcfiles.html (Official)
    - `pyrcc5`: http://pyqt.sourceforge.net/Docs/PyQt5/resources.html#pyrcc5
    - `pyside2-rcc: https://doc.qt.io/qtforpython/overviews/resources.html (Documentation Incomplete)
    - `rcc` on Qt6: https://doc.qt.io/qt-6/resources.html
    - `rcc` on Qt5: http://doc.qt.io/qt-5/rcc.html

"""

# Standard library imports
import argparse
import logging
import sys

from PySide6.QtWidgets import QApplication
# Third party imports
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer

# Local imports
from qdarkstyle import PACKAGE_PATH
from qdarkstyle.dark.palette import DarkPalette
from qdarkstyle.light.palette import LightPalette
from qdarkstyle.custom.palette import CustomPalette
from qdarkstyle.utils import process_palette

_logger = logging.getLogger(__name__)


class QSSFileHandler(FileSystemEventHandler):
    """QSS File observer."""

    def __init__(self, parser_args):
        """QSS File observer."""
        super(QSSFileHandler, self).__init__()
        self.args = parser_args

    def on_modified(self, event):
        """Handle file system events."""
        if event.src_path.endswith('.qss'):
            # TODO: needs implementation for new palettes
            process_palette(compile_for=self.args.create)
            print('\n')


def main():
    # 新增
    if not QApplication.instance():
        app = QApplication(sys.argv)
    """Process QRC files."""
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('--qrc_dir',
                        default=None,
                        type=str,
                        help="QRC file directory, relative to current directory.",)
    parser.add_argument('--create',
                        default='qtpy',
                        choices=['pyqt5', 'pyqt6', 'pyside2', 'pyside6', 'qtpy', 'pyqtgraph', 'qt', 'qt5', 'all'],
                        type=str,
                        help="Choose which one would be generated.")
    parser.add_argument('--watch', '-w',
                        action='store_true',
                        help="Watch for file changes.")
    parser.add_argument('--custom', '-c',
                        action='store_true',
                        help="Process custom palette", )

    args = parser.parse_args()

    if args.watch:
        path = PACKAGE_PATH
        observer = Observer()
        handler = QSSFileHandler(parser_args=args)
        observer.schedule(handler, path, recursive=True)
        try:
            print('\nWatching QSS file for changes...\nPress Ctrl+C to exit\n')
            observer.start()
        except KeyboardInterrupt:
            observer.stop()
        observer.join()
    elif args.custom:
        print(f"args.custom: {args.custom}")
        process_palette(palette=CustomPalette, compile_for=args.create)
    else:
        for palette in [DarkPalette, LightPalette]:
            process_palette(palette=palette, compile_for=args.create)
    # 新增
    sys.exit(app.quit())


if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    sys.exit(main())

7. 修改QDarkStyleSheet-master/qdarkstyle/utils/images.py:

注释掉两个_ = QApplication([]):

8. QDarkStyleSheet-master/根目录下创建process_qrc.py:

python 复制代码
import subprocess
import os

# 1. 配置关键参数(根据实际情况修改)
conda_python_path = r"C:\Users\DY\.conda\envs\pyside\python.exe"  # conda环境的python路径
qrc_path = r"E:\QDarkStyleSheet-master\qdarkstyle\utils\__main__.py"  # __main__.py的绝对路径
working_dir = r"E:\QDarkStyleSheet-master"  # qdarkstyle根目录(必须!否则模块导入失败)
shell = True  # Windows建议设为True,Linux/Mac可设为False

# 2. 构造命令(可选:添加--create参数,比如pyside6)
cmd = [
    conda_python_path,  # conda的python解释器
    qrc_path,           # 要运行的__main__.py
    '--create', 'pyside6',  # 传递给__main__.py的参数(可选),本项是选择QT的版本
    '--custom'  # 是否使用自定义的调色板
]

# 3. 执行命令(指定工作目录+conda环境)
try:
    # cwd=working_dir:确保运行时的工作目录是qdarkstyle根目录,模块能正确导入
    subprocess.call(cmd, shell=shell, cwd=working_dir)
except Exception as e:
    print(f"执行失败:{e}")

运行process_qrc.py,在QDarkStyleSheet-masterqdarkstyle/custom下会生成两个文件:

pyside6_customstyle_rc.py

customstyle.qss

把这两个文件拷贝到别的项目内即可离线调用。

demo:

新建项目,输入:

python 复制代码
import sys

from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QCheckBox, QComboBox
import pyside6_customstyle_rc  # 导入图像资源


if __name__ == '__main__':
    app = QApplication(sys.argv)
    with open('customstyle.qss', 'r', encoding='utf-8') as f:
        stylesheet = f.read()

    form = QWidget()
    form.setWindowTitle("PySide6 自定义样式")
    form.setGeometry(500, 400, 300, 200)
    app.setStyleSheet(stylesheet)
    btn1 = QPushButton("按钮")
    checkBox = QCheckBox("复选框")
    comboBox = QComboBox()
    comboBox.addItems(["选项1", "选项2", "选项3"])

    layout = QVBoxLayout(form)
    layout.addWidget(btn1)
    layout.addWidget(checkBox)
    layout.addWidget(comboBox)

    form.show()

    sys.exit(app.exec_())

**注:**由于pyside6_customstyle_rc.py和customstyle.qss都使用了pyside6可以读出的格式,所以,离线使用的项目可以不用安装qdarkstyle。


三、深度定制修改的点位

1、使用与默认不同的图像资源

在目录QDarkStyleSheet-master/qdarkstyle/svg下,存储着有可能用到的所有图像资源,可以是公司logo,也可以是背景图片等,当然,默认只有一些零件图标(比如下拉选择框的下拉箭头)和分割线之类的,格式是.svg,你可以修改和增减这些图片文件。

2、怎样改变部件的显示特性

打开QDarkStyleSheet-master/qdarkstyle/qss/_styles.scss

其内容为如下格式:

这个文件是所有的主题共用的。

以QRadioButton为例,它在_styles.scss中的定义为:

python 复制代码
QRadioButton {
    background-color: $COLOR_BACKGROUND_1;
    color: $COLOR_TEXT_1;
    spacing: 4px;
    padding-top: 4px;
    padding-bottom: 4px;
    border: none;
    outline: none;

    &:focus {
        border: none;
    }

    &:disabled {
        background-color: $COLOR_BACKGROUND_1;
        color: $COLOR_DISABLED;
        border: none;
        outline: none;
    }

    QWidget {
        background-color: $COLOR_BACKGROUND_1;
        color: $COLOR_TEXT_1;
        spacing: 0px;
        padding: 0px;
        outline: none;
        border: none;
    }

    &::indicator {
        border: none;
        outline: none;
        margin-left: 2px;
        height: 14px;
        width: 14px;

        &:unchecked {
            image: url($PATH_RESOURCES + '/' + $ID + '/rc/radio_unchecked.png');

            &:hover,
            &:focus,
            &:pressed {
                border: none;
                outline: none;
                image: url($PATH_RESOURCES + '/' + $ID + '/rc/radio_unchecked_focus.png');
            }

            &:disabled {
                image: url($PATH_RESOURCES + '/' + $ID + '/rc/radio_unchecked_disabled.png');
            }
        }

        &:checked {
            border: none;
            outline: none;
            image: url($PATH_RESOURCES + '/' + $ID + '/rc/radio_checked.png');

            &:hover,
            &:focus,
            &:pressed {
                border: none;
                outline: none;
                image: url($PATH_RESOURCES + '/' + $ID + '/rc/radio_checked_focus.png');
            }

            &:disabled {
                outline: none;
                image: url($PATH_RESOURCES + '/' + $ID + '/rc/radio_checked_disabled.png');
            }
        }
    }
}

通过修改上面的代码,就可以把各个部件的特性连接到不同的颜色变量,还可以增加新的部件和设定。比如,在上述代码中的这一行,定义了背景色:

python 复制代码
background-color: $COLOR_BACKGROUND_1;

COLOR_BACKGROUND_1对应的变量就是当前主题中的调色板文件中定义的颜色,如果当前主题是custom,它的专属调色板文件:QDarkStyleSheet-master/qdarkstyle/custom/palette.py中COLOR_BACKGROUND_1就对应了一个颜色:Gray.B10

复制代码
    而Gray.B10,则是在QDarkStyleSheet-master/qdarkstyle/colorsystem.py中定义的,它是所有主题共用的:

按需修改以上文件,并执行前面创建的process_qrc.py,就可以得到深度定制的.qss和_rc.py文件并离线使用。


**另注:**项目原版的utils包,我一直跑不通,所以才拆解它的代码,做了以上修改才跑通,如果有高手知道怎样使用原版的utils创建自定义.qss和_rc.py文件,望不吝赐教。

相关推荐
网络风云2 小时前
Flask 的 Docker 部署指南
python·docker·flask
世转神风-2 小时前
qt-通信协议基础-double转成QbyteArray-小端系统
开发语言·qt
飞天小蜈蚣2 小时前
django的模板渲染、for循环标签、继承模板
数据库·python·django
飞Link2 小时前
【Anaconda】Linux(CentOS7)下安装Anaconda教程
linux·运维·python
web3.08889992 小时前
小红书笔记评论API接口详情展示
开发语言·笔记·python
手抄二进制2 小时前
使用Anaconda创建python环境并链接到Jupyter
开发语言·python·jupyter
知凡D2 小时前
python脚本打包成exe后,对其引用的日历库实时更新-动态化加载模块
python·测试工具
森林里的小老鼠2 小时前
关于林草碳储量计算几点体会
笔记
d111111111d3 小时前
STM32中USART和UART的区别是什么?
笔记·stm32·单片机·嵌入式硬件·学习