PyQt5 在更改样式表时使用默认图像图标
PyQt5 use default image icon when changing stylesheet
我正在使用 PyQt5 为 QTreeWigdet 创建自定义样式表。有没有办法使用默认的 RightArrow/LeftArrow 图标,而不是为箭头加载自定义图像?我试过下面的代码无济于事。
AnnotationTree = """
QTreeWidget::branch:selected:closed {
background: rgb(75,75,75);
image: Qt::RightArrow;
}
QTreeWidget::branch:selected:open {
background: rgb(75,75,75);
image: Qt::DownArrow;
}
QTreeWidget::item:selected {
background: rgb(75,75,75);
color: white;
border-left: 2px solid orange;
}
"""
在复杂的小部件上设置样式表(尤其是在使用伪选择器时)会导致通过忽略默认样式来完全覆盖元素的绘制。
Qt 不提供对分支等原始元素的访问,但可以使用样式绘制来绘制元素的像素图,只要已知它们的大小在所有情况下都是固定的即可。
一个可能的解决方案是在启动应用程序时创建所有必需的图标(通过子类化 QApplication)并将它们保存在 SO 临时文件夹中。
class MyWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("""
QTreeWidget::branch:selected:closed {{
background: rgb(75,75,75);
image: url({iconPath}/closed.png);
}}
QTreeWidget::branch:selected:open {{
background: rgb(75,75,75);
image: url({iconPath}/open.png);
}}
QTreeWidget::item:selected {{
background: rgb(255,255,255);
color: white;
border-left: 2px solid orange;
}}
""".format(iconPath=QtWidgets.QApplication.instance().iconPath))
# ...
class Application(QtWidgets.QApplication):
_iconsDone = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tmpDir = QtCore.QDir(QtCore.QStandardPaths.writableLocation(
QtCore.QStandardPaths.TempLocation))
iconDirName = '__qticons'
if not tmpDir.exists(iconDirName):
tmpDir.mkpath(iconDirName)
self.iconPath = tmpDir.absoluteFilePath(iconDirName)
def setStyle(self, style):
if isinstance(style, str):
self.makeIcons(QtWidgets.QStyleFactory.create(style))
else:
self.makeIcons(style)
super().setStyle(style)
def makeIcons(self, style):
iconSize = style.pixelMetric(style.PM_SmallIconSize) - 1
opt = QtWidgets.QStyleOptionViewItem()
opt.palette = style.standardPalette()
opt.rect = QtCore.QRect(0, 0, iconSize, iconSize)
opt.state |= style.State_Enabled
baseState = style.State(opt.state)
iconStates = (
('closed', style.State_Children),
('open', style.State_Children | style.State_Open),
)
for iconName, state in iconStates:
opt.state = baseState | state
pm = QtGui.QPixmap(iconSize, iconSize)
pm.fill(QtCore.Qt.transparent)
qp = QtGui.QPainter(pm)
style.drawPrimitive(style.PE_IndicatorBranch, opt, qp)
qp.end()
pm.save(QtCore.QDir(self.iconPath).absoluteFilePath(
'{}.png'.format(iconName)))
self._iconsDone = True
def exec(self):
if not self._iconsDone:
self.makeIcons(self.style())
self._iconsDone = True
return super().exec()
def exec_(self):
return self.exec()
if __name__ == '__main__':
import sys
app = Application(sys.argv)
w = MyWindow()
w.show()
app.exec_()
但是,有一个问题:您完全依赖当前的绘画风格。在您的样式表中,您使用的是非常暗的背景色,而默认样式通常假定项目视图的背景非常亮(通常是白色)。结果是默认箭头(黑色)可能几乎不可见。例如,这是我系统上的结果:
因此,只要您提供自定义颜色,就不能依赖当前样式(您不知道它是什么)来绘制系统基元,这是 Qt StyleSheets 正确避免使用的另一个原因标准绘画作为后备:如果您想要自定义样式,您必须提供所有内容,包括分支图标。
我正在使用 PyQt5 为 QTreeWigdet 创建自定义样式表。有没有办法使用默认的 RightArrow/LeftArrow 图标,而不是为箭头加载自定义图像?我试过下面的代码无济于事。
AnnotationTree = """
QTreeWidget::branch:selected:closed {
background: rgb(75,75,75);
image: Qt::RightArrow;
}
QTreeWidget::branch:selected:open {
background: rgb(75,75,75);
image: Qt::DownArrow;
}
QTreeWidget::item:selected {
background: rgb(75,75,75);
color: white;
border-left: 2px solid orange;
}
"""
在复杂的小部件上设置样式表(尤其是在使用伪选择器时)会导致通过忽略默认样式来完全覆盖元素的绘制。
Qt 不提供对分支等原始元素的访问,但可以使用样式绘制来绘制元素的像素图,只要已知它们的大小在所有情况下都是固定的即可。
一个可能的解决方案是在启动应用程序时创建所有必需的图标(通过子类化 QApplication)并将它们保存在 SO 临时文件夹中。
class MyWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("""
QTreeWidget::branch:selected:closed {{
background: rgb(75,75,75);
image: url({iconPath}/closed.png);
}}
QTreeWidget::branch:selected:open {{
background: rgb(75,75,75);
image: url({iconPath}/open.png);
}}
QTreeWidget::item:selected {{
background: rgb(255,255,255);
color: white;
border-left: 2px solid orange;
}}
""".format(iconPath=QtWidgets.QApplication.instance().iconPath))
# ...
class Application(QtWidgets.QApplication):
_iconsDone = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tmpDir = QtCore.QDir(QtCore.QStandardPaths.writableLocation(
QtCore.QStandardPaths.TempLocation))
iconDirName = '__qticons'
if not tmpDir.exists(iconDirName):
tmpDir.mkpath(iconDirName)
self.iconPath = tmpDir.absoluteFilePath(iconDirName)
def setStyle(self, style):
if isinstance(style, str):
self.makeIcons(QtWidgets.QStyleFactory.create(style))
else:
self.makeIcons(style)
super().setStyle(style)
def makeIcons(self, style):
iconSize = style.pixelMetric(style.PM_SmallIconSize) - 1
opt = QtWidgets.QStyleOptionViewItem()
opt.palette = style.standardPalette()
opt.rect = QtCore.QRect(0, 0, iconSize, iconSize)
opt.state |= style.State_Enabled
baseState = style.State(opt.state)
iconStates = (
('closed', style.State_Children),
('open', style.State_Children | style.State_Open),
)
for iconName, state in iconStates:
opt.state = baseState | state
pm = QtGui.QPixmap(iconSize, iconSize)
pm.fill(QtCore.Qt.transparent)
qp = QtGui.QPainter(pm)
style.drawPrimitive(style.PE_IndicatorBranch, opt, qp)
qp.end()
pm.save(QtCore.QDir(self.iconPath).absoluteFilePath(
'{}.png'.format(iconName)))
self._iconsDone = True
def exec(self):
if not self._iconsDone:
self.makeIcons(self.style())
self._iconsDone = True
return super().exec()
def exec_(self):
return self.exec()
if __name__ == '__main__':
import sys
app = Application(sys.argv)
w = MyWindow()
w.show()
app.exec_()
但是,有一个问题:您完全依赖当前的绘画风格。在您的样式表中,您使用的是非常暗的背景色,而默认样式通常假定项目视图的背景非常亮(通常是白色)。结果是默认箭头(黑色)可能几乎不可见。例如,这是我系统上的结果:
因此,只要您提供自定义颜色,就不能依赖当前样式(您不知道它是什么)来绘制系统基元,这是 Qt StyleSheets 正确避免使用的另一个原因标准绘画作为后备:如果您想要自定义样式,您必须提供所有内容,包括分支图标。