更新样式表而不丢失原始样式

问题描述 投票:0回答:3

我正在创建具有某种样式的 qpushbuttons,然后更新样式表以稍后为它们着色。但是,当我这样做时,它会覆盖原始样式。有没有办法可以更新或附加到对象的样式表而不会丢失它或必须再次重新定义所有内容?

import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout,QPushButton, QMenu, QApplication, QWidget, QInputDialog, QLineEdit
from PyQt5.QtCore import QSize, QCoreApplication, Qt, QLine, QPoint
from PyQt5.QtGui import QPainter, QPen, QFont,QPainter, QPainterPath, QPixmap


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setMinimumSize(QSize(400, 400))   

        color = QPushButton('color', self)
        color.clicked.connect(self.color)
        color.resize(100,115)
        color.move(0, 100)

        buttons= ['button1','button2','button3']
        self.pybutton = {}
        x=0
        t = 0
        for i in buttons:
            t = t + 100
            x+=1
            self.pybutton[str(x)] = QPushButton(i, self) 
            self.pybutton[str(x)].setObjectName('btn' + str(x))
            self.pybutton[str(x)].resize(100,100)
            self.pybutton[str(x)].move(400-int(t),100)
            self.pybutton[str(x)].setStyleSheet('QPushButton {max-width: 75px;text-align:center;padding-left: 20px; max-height: 60px; font-size: 20px;}')
        self.statusBar()
    def status(self):
        sender = self.sender()
        print('PyQt5 button click')
        self.statusBar().showMessage(sender.text() + ' was pressed')

    def color(self):
        for i in self.pybutton:
            self.pybutton[str(i)].objectName()
            if self.pybutton[str(i)].objectName() == 'btn1':
                self.pybutton[str(i)].setStyleSheet("background-color: green")
            else:
                self.pybutton[str(i)].setStyleSheet("background-color: red")    

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit( app.exec_() )
python pyqt pyqt5 qtstylesheets
3个回答
4
投票

一个可能的解决方案是使用

styleSheet()
方法设置样式表,然后分析文本并根据需要进行修改,但这可能非常困难。另一个更好的解决方案是使用 QSS 属性选择器 并通过修改属性来选择颜色:

import sys
from PyQt5.QtWidgets import QMainWindow,QPushButton, QApplication
from PyQt5.QtCore import QSize


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setMinimumSize(QSize(400, 400))   

        color = QPushButton('color', self)
        color.clicked.connect(self.color)
        color.resize(100,115)
        color.move(0, 100)

        buttons= ['button1','button2','button3']
        self.pybutton = {}

        qss = """
        QPushButton{
            max-width: 75px;
            text-align:center;
            padding-left: 20px; 
            max-height: 60px; 
            font-size: 20px;
        }
        QPushButton[color = "0"]{
            background-color: green;
        }
        QPushButton[color = "1"]{
            background-color: red;
        }
        """

        for i, text in enumerate(buttons):
            btn = QPushButton(text, self) 
            btn.setObjectName('btn{}'.format(i))
            btn.setGeometry(300-i*100, 100, 100,100)
            btn.setStyleSheet(qss)
            self.pybutton[str(i)] = btn

    def color(self):
        for i, btn in self.pybutton.items():
            if btn.objectName() == 'btn1':
                btn.setProperty("color", "0")
            else:
                btn.setProperty("color", "1") 
            btn.style().polish(btn)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit( app.exec_() )

0
投票

这是更新样式表的“黑客”方式,让我们直接看我的代码示例。

class ButtonStyle:
    def __init__(self: QPushButton):
        self.setMinimumSize(self.sizeHint())

    @property
    def style_sheet(self) -> str:
        return self.styleSheet() + '\n'

    def set_bg_color(self, color: str):
        # name or hex str
        style = self.style_sheet \
                + """
                QPushButton#%s {
                    background-color: %s;
                }
                """ % (self.objectName(), color)

        self.setStyleSheet(style)

    def set_hover_bg_color(self, color: str):
        style = self.style_sheet \
                + """
                QPushButton#%s:hover{
                    background-color: %s;
                } 
                """ % (self.objectName(), color)

        self.setStyleSheet(style)

    def set_pressed_bg_color(self, color: str):
        style = self.style_sheet \
                + """
                QPushButton#%s:pressed {
                    background-color: %s;
                }
                """ % (self.objectName(), color)
        self.setStyleSheet(style)

    def set_border_radius(self, px: int):
        """set px radius
        """
        style = self.style_sheet \
                + """
                QPushButton#%s {
                    border-radius: %spx;
                }""" % (self.objectName(), px)
        self.setStyleSheet(style)


class Button(QPushButton, ButtonStyle):
    def __init__(self, *args, **kwargs):
        super(Button, self).__init__(*args, **kwargs)
        self.set_default_object_name()

    def set_default_object_name(self):
        """set default object name to :Class: Name
        """
        self.setObjectName(self.__class__.__name__)


class RedButton(Button):
    def __init__(self, *args, **kwargs):
        super(RedButton, self).__init__(*args, **kwargs)
        self.init_style()

    def init_style(self):
        self.set_bg_color('#f44336')
        self.set_hover_bg_color('#e57373')
        self.set_pressed_bg_color('#ffcdd2')
        self.set_border_radius(8)
        print(self.styleSheet())


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.widget = QWidget(self)
        self.setWindowTitle('Button Split Style')
        v_layout = QVBoxLayout()
        self.button = Button('NORMAL')
        self.red_button = RedButton('RED')
        v_layout.addStretch(1)
        v_layout.addWidget(self.button)
        v_layout.addWidget(self.red_button)
        v_layout.addStretch(1)

        self.widget.setLayout(v_layout)
        self.resize(500, 500)
        self.setCentralWidget(self.widget)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

\n
是来自at 样式str。 它的工作原理类似于“更新”操作,新样式将覆盖之前定义的相同样式。


0
投票

我也有同样的问题,这是我经过一番努力后设法做到的:

  1. 将QSS字符串转换为字典(以下代码中的
    MyQSS.str_to_mapping
    )并修改字典
  2. 将修改后的字典转换回字符串(以下代码中的
    MyQSS.mapping_to_str
  3. 使用新字符串调用
    setSytleSheet
    (以下代码中的
    MyQSS.set_value
import re
import sys
from typing import Dict, Union

from PyQt5 import QtWidgets
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import (
    QMainWindow,
    QPushButton,
    QWidget,
)

QSSMapping = Dict[str, Union[str, Dict[str, str]]]


class MyQSS:
    # This regex is used in str_to_mapping to make sure ":" and ";" are
    # attached to the lhs token and separated from the rhs token.
    # e.g., "color :red ;font-size:11pt;" should be "color: red; font-size: 11pt;"
    # We need this conversion because the str_to_mapping uses the ending ":"
    # and ";" to distinct property token and value token from others
    property_value_indicator_re = re.compile(r"\s*([:;])")

    @classmethod
    def str_to_mapping(cls, qss_str: str) -> QSSMapping:
        # This func requires that in the qss_str every value ends with ";",
        #  e.g., font-size: 11pt;
        # Values can have whitespaces
        #  e.g., font-family: "Noto Sans";
        # Selectors can have whitespaces
        #  e.g., QDialog QPushButton {color: black;}
                 # QDialog > QPushButton {color: black}
        # Later value for the same property will override the previous value
        selector_declaration_mapping = {}
        cur_selector = None
        cur_property = None
        qss_str = qss_str.replace("{", " { ").replace("}", " } ")
        qss_str = cls.property_value_indicator_re.sub(r"\1 ", qss_str)
        token_gen = iter(qss_str.split())
        while (token := next(token_gen, None)) is not None:
            if token == "{":
                pass
            elif token == "}":
                cur_selector = None
            elif token.endswith(":"):
                cur_property = token.rstrip(":")
            elif cur_property is not None:
                if cur_selector is None:
                    value_prev = selector_declaration_mapping.get(cur_property, "")
                    value = f"{value_prev} {token}" if not value_prev.endswith(";") else token
                    selector_declaration_mapping[cur_property] = value.lstrip()
                else:
                    if cur_selector not in selector_declaration_mapping:
                        selector_declaration_mapping[cur_selector] = {}
                    value_prev = selector_declaration_mapping[cur_selector].get(cur_property, "")
                    value = f"{value_prev} {token}" if not value_prev.endswith(";") else token
                    selector_declaration_mapping[cur_selector][cur_property] = value.lstrip()
                if token.endswith(";"):
                    cur_property = None
            else:
                cur_selector = f"{cur_selector} {token}" if cur_selector is not None else token
        return selector_declaration_mapping

    @classmethod
    def mapping_to_str(cls, mapping: QSSMapping) -> str:
        qss_str = ""
        for selector, declaration in mapping.items():
            if isinstance(declaration, str):
                qss_str += f"{selector}: {declaration}\n"
                continue
            qss_str += f"{selector} {{\n"  # }}
            for property, value in declaration.items():
                qss_str += f"{property}: {value}\n"
            qss_str += "}\n"
        return qss_str

    @classmethod
    def set_value(cls, widget: QWidget, selector: str, property: str, value: str) -> None:
        # str_to_mapping requires that value ends with ";"
        if not value.endswith(";"):
            value = f"{value};"

        qss_str = widget.styleSheet()
        selector_declaration_mapping: QSSMapping = cls.str_to_mapping(qss_str)
        property_value_mapping = selector_declaration_mapping.get(selector, {})
        if isinstance(property_value_mapping, str):
            raise ValueError(f"{selector} is not a valid selector")
        property_value_mapping[property] = value
        selector_declaration_mapping[selector] = property_value_mapping

        widget.setStyleSheet(cls.mapping_to_str(selector_declaration_mapping))


class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setMinimumSize(QSize(400, 400))

        color = QPushButton("color", self)
        color.clicked.connect(self.color)
        color.resize(100, 115)
        color.move(0, 100)

        buttons = ["button1", "button2", "button3"]
        self.pybutton = {}
        x = 0
        t = 0
        for i in buttons:
            t = t + 100
            x += 1
            self.pybutton[str(x)] = QPushButton(i, self)
            self.pybutton[str(x)].setObjectName("btn" + str(x))
            self.pybutton[str(x)].resize(100, 100)
            self.pybutton[str(x)].move(400 - int(t), 100)
            self.pybutton[str(x)].setStyleSheet(
                "QPushButton {max-width: 75px;text-align:center;padding-left: 20px; max-height: 60px; font-size: 20px;}"
            )
        self.statusBar()

    def status(self):
        sender = self.sender()
        print("PyQt5 button click")
        self.statusBar().showMessage(sender.text() + " was pressed")

    def color(self):
        for i in self.pybutton:
            self.pybutton[str(i)].objectName()
            if self.pybutton[str(i)].objectName() == "btn1":
                MyQSS.set_value(self.pybutton[str(i)], "*", "background-color", "green")
                # self.pybutton[str(i)].setStyleSheet("background-color: green")
            else:
                MyQSS.set_value(self.pybutton[str(i)], "*", "background-color", "red")
                # self.pybutton[str(i)].setStyleSheet("background-color: red")


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())
© www.soinside.com 2019 - 2024. All rights reserved.