1

I have a basic code for a PyQt5 GUI with two buttons. In it, I want to change the the background colour of one of the buttons. I do this by setting the background-color style sheet attribute for the button. This works, however, under Windows it seems to remove all other style attributes for the button, leaving an unattractive button compared to the standard one, as shown in the image:

GUI in Windows

The same code under Linux does not lose the other button stylings and produces:

enter image description here

where things like the default button rounded corners and hover attributes are kept.

The code is:

import sys

from PyQt5.QtWidgets import QApplication, QGridLayout, QPushButton, QWidget

class Window(QWidget):
    def __init__(self):
        super().__init__()

        self.layout = QGridLayout()

        button1 = QPushButton("A")
        button1.setFixedSize(64, 64)
        button2 = QPushButton("B")
        button2.setFixedSize(64, 64)

        button2.setStyleSheet("background-color: #ff0000")

        self.layout.addWidget(button1, 0, 0)
        self.layout.addWidget(button2, 0, 1)

        self.setLayout(self.layout)
        self.show()


app = QApplication([])

demo = Window()
demo.show()

sys.exit(app.exec())

Is is possible to set the background without losing the other attributes under Windows (11)?

On Windows, I'm running in a conda environment with:

pyqt                      5.12.3
pyqt5-sip                 4.19.18
pyqtchart                 5.12
pyqtwebengine             5.12.1
qt                        5.12.9

and on Linux (Ubuntu 20.04.5 running via WSL2) I'm running in a conda environment with:

pyqt                      5.15.7
pyqt5-sip                 12.11.0
qt-main                   5.15.2
qt-webengine              5.15.9
qtconsole                 5.3.2
Matt Pitkin
  • 3,989
  • 1
  • 18
  • 32
  • 1
    Some widget-styles draw certain elements using pixmaps. If you set a stylesheet property that affects such elements, it may override *all* the normal drawing. In general, there's no way to predict in advance how stylesheet changes will be rendered, since every widget-style may handle the cascading affects somewhat differently. – ekhumoro Jan 10 '23 at 14:13
  • 1
    This seems to answer things for me https://doc.qt.io/qtforpython/overviews/stylesheet-examples.html#customizing-a-qpushbutton-using-the-box-model – Matt Pitkin Jan 10 '23 at 14:42
  • 1
    @MattPitkin remember that style sheets are *overrides* to the default style. If any property setting potentially alters the requirements of the style, it will revert to plain and basic behavior. This also happens in browsers (with obvious differences) for input fields, for which setting certain properties will "clear" the default appearance, so you must then set *all* related properties. For basic widgets you could set the palette instead of the stylesheet (not both!), but remember that it's *not* guaranteed to work (or to work in the same way in all systems). See the documentation of QPalette – musicamante Jan 10 '23 at 19:31

2 Answers2

2

Based on the comments, it seems that if you want to change the style sheet for a QPushButton on Windows you need to also specify all the other style attributes that you want as well. From the documentation here, if shows an example of this (when changing the background colour using a style sheet) and states:

  • We have made a request that cannot be satisfied using the native styles alone (e.g., the Windows Vista theme engine doesn’t let us specify the background color of a button).

  • Therefore, the button is rendered using style sheets.

  • We haven’t specified any values for border-width and border-style , so by default we obtain a 0-pixel wide border of style none.

where the first bullet is particularly relevant.

Update

One thing that has worked, is to explicitly set the overall app's style, so that it doesn't use the "Windows" style. E.g, having:

app = QApplication([])
app.setStyle("Fusion")

allows the background to be correctly set using the code in the question.

Matt Pitkin
  • 3,989
  • 1
  • 18
  • 32
1

This Qt/C++ answer shows the way

Basically, something like:

palette = button.palette()
palette.setColor(QPalette.Button, Qt.GlobalColor.blue)
button.setAutoFillBackground(True)
button.setPalette(palette)

In all my PyQt coding I find I'm unable to alter any stylesheet settings. Doing so means I will no longer be able to conform to the desktop style. Note that the style can change dynamically, such as when the user switches to night mode or swaps from a dark to light theme (which adds more complexities, such as responding by changing custom button and icon backgrounds).

  • Thanks, this still doesn't quite work. Under Linux it works, although you need to remove the `button.setAutoFillBackground(True)`, otherwise, if the button has rounded corners, it fills in the colour outside those rounded corners. However, under Windows it doesn't fill the colour inside the button at all and _only_ fills it in outside the rounded corners. – Matt Pitkin Jan 10 '23 at 20:09
  • In the question I referenced, one response reported that QPalette.Base worked, another QPalette.Window, and even QPalette.Active (which errors for me). The documentation definitely refers to QPalette.Button. I would at least try some of these. Could you take a different tack, for example, use icons? Even on Linux, I find I need to test on gnome, kde and xfce, just like Android, write once, test everywhere and then make compromises. – Michael Hamilton Jan 10 '23 at 20:43
  • 1
    I've now found that if I switch the style sheet for the app to "Fusion" then my original code works. Although as you suggest, this seems to be a trial and error thing and may not work across also OSs and IDEs. – Matt Pitkin Jan 10 '23 at 22:21