简体   繁体   中英

PyQt5 How to apply different QStyles to different widgets?

I am trying to make multiple widgets with different styles from the built in styles provided by QStyleFactory, but when I run my code they all look the same. How can I fix this?

在此处输入图像描述

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_())

An important aspect of setting a QStyle on widgets (instead of setting it on the whole application) is reported in the QWidget.setStyle() documentation:

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

So, what's happening is that you're setting the style on the QMainWindow only , while the children will always use the QApplication style.

What you could try to do is to manually set the style for the existing children:

    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)

In any case, the above approach has a drawback: any new children created after setting the style will still inherit the QApplication style. The only way to avoid this is to watch for childEvent() by (recursively) installing an event filter on the parent, and set the styles accordingly; note that you need to watch for StyleChange events too.

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)

        # ...

Also remember another important aspect the documentation warns about:

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.

While the above code will do what you're expecting, installing an event filter on all child QWidgets is not a good thing to do, especially if you only need to do the style change (which is something that should normally be done just once, possibly at the start of the program). Considering the warning about using different styles, I highly suggest you to do this exactly as suggested: for demonstration purposes only .

You can set it on the application.

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_())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM