我正在创建具有某种样式的 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_() )
一个可能的解决方案是使用
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_() )
这是更新样式表的“黑客”方式,让我们直接看我的代码示例。
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。
它的工作原理类似于“更新”操作,新样式将覆盖之前定义的相同样式。
我也有同样的问题,这是我经过一番努力后设法做到的:
MyQSS.str_to_mapping
)并修改字典MyQSS.mapping_to_str
)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_())