PyQt5 如何将不同的 QStyles 应用于不同的小部件?

PyQt5 How to apply different QStyles to different widgets?

我正在尝试制作多个具有与 QStyleFactory 提供的内置样式不同样式的小部件,但是当我 运行 我的代码时,它们看起来都一样。我该如何解决这个问题?

from PyQt5 import QtWidgets, QtCore, QtGui
import sys

class Demo(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.container = QtWidgets.QWidget()
        self.setCentralWidget(self.container)
        self.layout = QtWidgets.QVBoxLayout()
        self.container.setLayout(self.layout)
        self.btn = QtWidgets.QPushButton("button")
        self.lw = QtWidgets.QListWidget()
        self.lw.addItems(["one", "two", "three"])
        self.layout.addWidget(self.btn)
        self.layout.addWidget(self.lw)
        self.resize(400, 150)
        self.show()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    widgets = []
    for style_name in QtWidgets.QStyleFactory.keys():
        demo = Demo()
        demo.setWindowTitle(style_name)
        style = QtWidgets.QStyleFactory.create(style_name)
        demo.setStyle(style)
        widgets.append(demo)

    sys.exit(app.exec_())

小部件 上设置 QStyle 的一个重要方面(而不是在整个应用程序上设置)在 QWidget.setStyle() 文档中有报道:

Setting a widget's style has no effect on existing or future child widgets.

所以,发生的事情是您在 QMainWindow 上设置样式,而 children 将始终使用 QApplication 样式。

可以尝试做的是手动设置现有 children:

的样式
 对于 QtWidgets.QStyleFactory.keys() 中的 style_name:
        演示 = 演示 ()
        demo.setWindowTitle(style_name)
        样式 = QtWidgets.QStyleFactory.create(style_name)
        demo.setStyle(样式)
        <b>对于 demo.findChildren 中的 child(QtWidgets.QWidget):
            child.setStyle(风格)</b>
        widgets.append(演示)

无论如何,上述方法有一个缺点:设置样式后创建的任何新children 仍将继承QApplication 样式。避免这种情况的唯一方法是通过(递归地)在 parent 上安装事件过滤器来监视 childEvent(),并相应地设置样式;请注意,您还需要关注 StyleChange 事件。

class ChildEventWatcher(QtCore.QObject):
    def __init__(self, parentWidget):
        super().__init__()
        self.parentWidget = parentWidget
        self.parentWidget.installEventFilter(self)

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.ChildAdded and isinstance(event.child(), QtWidgets.QWidget):
            event.child().installEventFilter(self)
            event.child().setStyle(self.parentWidget.style())
            for child in event.child().findChildren(QtWidgets.QWidget):
                child.installEventFilter(self)
                child.setStyle(self.parentWidget.style())
        elif event.type() == QtCore.QEvent.StyleChange and source == self.parentWidget:
            for child in self.parentWidget.findChildren(QtWidgets.QWidget):
                child.setStyle(self.parentWidget.style())
        return super().eventFilter(source, event)


class Demo(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        # this *must* be created before adding *any* child
        self.childEventWatcher = ChildEventWatcher(self)

        # ...

还要记住文档警告的另一个重要方面:

Warning: This function is particularly useful for demonstration purposes, where you want to show Qt's styling capabilities. Real applications should avoid it and use one consistent GUI style instead.

虽然上面的代码可以满足您的期望,但是在 all child QWidgets 上安装事件过滤器并不是一件好事,尤其是如果您只需要改变样式(这通常应该只做一次,可能在程序开始时)。考虑到有关使用不同样式的警告,我强烈建议您完全按照建议执行此操作:仅用于演示目的 only.

您可以在应用程序上设置。

from PyQt5 import QtWidgets, QtCore, QtGui
import sys


class Demo(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.container = QtWidgets.QWidget()
        self.setCentralWidget(self.container)
        self.layout = QtWidgets.QVBoxLayout()
        self.container.setLayout(self.layout)
        self.btn = QtWidgets.QPushButton("button")
        self.lw = QtWidgets.QListWidget()
        self.lw.addItems(["one", "two", "three"])
        self.layout.addWidget(self.btn)
        self.layout.addWidget(self.lw)
        self.resize(400, 150)
        self.show()

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    win = Demo()
    
    app.setStyle(QtWidgets.QStyleFactory.create("Windows")) # Set style theme on app
    sys.exit(app.exec_())