现在暗模式终于在 Qt 6.5 中出现在 Windows 中,我注意到我的很多图标在深色背景下看起来不太好。所以我想为明暗模式使用不同的图标。而且,为了让事情变得困难,当用户在他的操作系统上切换模式时,图标会改变(它们的外观)。
为了避免到处都是各种小部件
setIcon()
,我想我会继承QIcon
并让它在colorSchemeChanged
信号上改变它的像素图。
class ThemeAwareIcon(QIcon):
def __init__(self, dark_pixmap, light_pixmap, *args):
super().__init__(*args)
self.dark_pm = dark_pixmap
self.light_pm = light_pixmap
self.app = QApplication.instance()
self.app.styleHints().colorSchemeChanged.connect(self.set_appropriate_pixmap)
self.set_appropriate_pixmap()
def set_appropriate_pixmap(self):
current_scheme = self.app.styleHints().colorScheme()
pm = self.dark_pm if current_scheme == Qt.ColorScheme.Dark else self.light_pm
self.addPixmap(pm, QIcon.Mode.Normal, QIcon.State.On)
这几乎按预期工作;像素图根据信号改变。只是更改后的像素图没有显示在设置图标的小部件上。我发现使更改可见的唯一方法是重置小部件上的图标,而这正是我首先要避免的。
那么,我的图标类能否以某种方式获救,或者我想要的是不可能通过这种方式实现的?
经过一番努力,我发现了如何使用
QIconEngine
让图标自己切换。
如果有其他人难以理解什么是
确实如此。它只提供给定大小、模式的像素图, 并向小部件声明图标已打开。仅此而已 较少的。所以如果你继承它,你只需要确保QIconEngine
返回适当的像素图。pixmap()
这是一个非常基本的通用实现。任何信号都可以用来触发开关。
如果在 colorscheme 更改时切换,则无需将信号传递给引擎,因为它是全局可用的。
此示例将像素图生成“卸载”到常规
QIcons
。这可能不是最有效的方法,但在所有情况下您都会获得默认行为。
class SwitchingEngine(QIconEngine):
offload = {}
def __init__(self, file_name, f1, f2, sig):
super().__init__()
self.file_name = file_name
if self.file_name not in self.offload:
self.offload[self.file_name] = {
True: QIcon(f1),
False: QIcon(f2),
}
self.switch = True
sig.connect(lambda: setattr(self, 'switch', not self.switch))
def pixmap(self, size: QSize, mode: QIcon.Mode, state: QIcon.State):
return self.offload[self.file_name][self.switch].pixmap(size, mode, state)
class SwitchingIcon(QIcon):
def __init__(self, file_name, sig):
f1 = f'switch1/{file_name}'
f2 = f'switch2/{file_name}'
engine = SwitchingEngine(file_name, f1, f2, sig)
super().__init__(engine)
作为奖励,有一点“带上你自己的图标文件”测试应用程序。
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QToolButton, QComboBox, QVBoxLayout, QHBoxLayout, QStyleFactory
from PyQt5.QtGui import QIcon, QIconEngine
from PyQt5.QtCore import QSize
class Window(QWidget):
def __init__(self):
super().__init__()
sw = QPushButton('switch')
dis = QPushButton('disable')
but1 = QToolButton()
but2 = QToolButton()
but3 = QToolButton()
icon1 = SwitchingIcon('fn1', sw.clicked)
icon2 = SwitchingIcon('fn2', sw.clicked)
icon3 = SwitchingIcon('fn3', sw.clicked)
but1.setIcon(icon1)
but2.setIcon(icon2)
but3.setIcon(icon3)
sw.clicked.connect(self.update)
dis.clicked.connect(lambda: but1.setEnabled(not but1.isEnabled()))
dis.clicked.connect(lambda: but2.setEnabled(not but2.isEnabled()))
dis.clicked.connect(lambda: but3.setEnabled(not but3.isEnabled()))
cmb = QComboBox()
cmb.addItems(QStyleFactory.keys())
cmb.currentTextChanged.connect(QApplication.instance().setStyle)
tbs = QHBoxLayout()
tbs.addWidget(but1)
tbs.addWidget(but2)
tbs.addWidget(but3)
lay = QVBoxLayout(self)
lay.addWidget(cmb)
lay.addLayout(tbs)
lay.addWidget(sw)
lay.addWidget(dis)
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec())