更新样式表而不失去原有的样式

4

我正在创建具有特定样式的qpushbutton,然后更新样式表以稍后对它们进行着色。 但是,当我这样做时,它会覆盖原始样式。 有没有一种方法可以更新或附加到对象的样式表而不会丢失它或不得不重新定义所有内容?

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_() )
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_() )

1
如果您对所有其他参数都没有头绪,因为它们使用默认调色板,该怎么办? - Guimoute

0
这是一种“hack”的方式来更新样式表,让我们直接看我的代码示例。
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 是用来格式化字符串的。 它的作用类似于“更新”操作,新的样式将覆盖之前定义的相同样式。


0
将QSS字符串转换为字典(在下面的代码中使用MyQSS.str_to_mapping),并修改字典。 将修改后的字典转换回字符串(在下面的代码中使用MyQSS.mapping_to_str)。 使用新字符串调用setStyleSheet(在下面的代码中使用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:
    @classmethod
    def mapping_to_str(cls, mapping: QSSMapping) -> str:
        qss_str = ""
        for selector, declaration in mapping.items():
            if isinstance(declaration, str):
                # str_to_mapping requires every value ends with ";"
                if not declaration.endswith(";"):
                    declaration = f"{declaration};"
                qss_str += f"{selector}: {declaration}\n"
            else:
                qss_str += f"{selector} {{\n"  # }}
                for property, value in declaration.items():
                    # str_to_mapping requires every value ends with ";"
                    if not value.endswith(";"):
                        value = f"{value};"
                    qss_str += f"{property}: {value}\n"
                qss_str += "}\n"
        return qss_str

    @classmethod
    def str_to_mapping(cls, qss_str: str) -> QSSMapping:
        """
        This func requires that in the qss_str
            (1) Every value ends with ";"
                e.g., "font-size: 11pt" ✗
                e.g., "font-size: 11pt;" ✓
            (2) The trailing ";" be attached to the value and separated
                from the subsequent token
                e.g., "font-size: 11pt ;" ✗
                e.g., "font-size: 11pt;" ✓
                e.g., "font-size: 11pt;max-width: 75px;" ✗
                e.g., "font-size: 11pt; max-width: 75px;" ✓
            (3) The trailing ":" of a property be attached to the lhs property and
                separated from the rhs value
                e.g., "font-size :11pt;" ✗
                e.g., "font-size : 11pt;" ✗
                e.g., "font-size: 11pt;" ✓
        This func allows that in the qss_str
            (1) Values have whitespaces
                e.g., "font-family: Noto Sans;" ✓
            (2) Selectors have whitespaces
                e.g., "QDialog QPushButton { color: black; }" ✓
                e.g., "QDialog > QPushButton { color: black; }" ✓
            (3) "{" and "}" are not separated from others
                e.g., "QDialog QPushButton{color: black;}" ✓
        Later value for the same property will override the previous one
        The returned dict will always has a trailing ";" across its values
        """
        selector_declaration_mapping = {}
        cur_selector = None
        cur_property = None
        qss_str = qss_str.replace("{", " { ").replace("}", " } ")
        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 set_value(cls, widget: QWidget, selector: str, property: str, value: str) -> None:
        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;}"
                "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")
                MyQSS.set_value(self.pybutton[str(i)], "*", "background-color", "green")
            else:
                # self.pybutton[str(i)].setStyleSheet("background-color: red")
                MyQSS.set_value(self.pybutton[str(i)], "*", "background-color", "red")


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接