首页 > 解决方案 > PyQt5 如何将不同的 QStyles 应用于不同的小部件?

问题描述

我正在尝试使用 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_())

标签: pythonpyqt5qtstylesheetsqstyle

解决方案


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

设置小部件的样式对现有或未来的小部件没有影响。

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

可以尝试做的是手动设置现有孩子的样式:

    for style_name in QtWidgets.QStyleFactory.keys():
        demo = Demo()
        demo.setWindowTitle(style_name)
        style = QtWidgets.QStyleFactory.create(style_name)
        demo.setStyle(style)
        for child in demo.findChildren(QtWidgets.QWidget):
            child.setStyle(style)
        widgets.append(demo)

无论如何,上述方法有一个缺点:设置样式后创建的任何新子级仍将继承QApplication样式。避免这种情况的唯一方法是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)

        # ...

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

警告:此功能对于演示目的特别有用,您想展示 Qt 的样式功能。真正的应用程序应该避免它,而是使用一种一致的 GUI 样式。

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


推荐阅读