user23346328
user23346328

Reputation: 23

Pyside6 .qss file causes toggle button to not resize until after clicked

I'm trying to make a toggle button that simply switches from a lock to an unlocked symbol when you click it.

For my main program, I have a .qss file that applies default stylesheets to a bunch of widgets in my program. When I don't use this .qss file, my program works fine. However when I apply the .qss file, the toggle button takes up the size of normal pushbutton until you click the button once.

Example:

Top 4 buttons have been clicked once

As you can see, the top buttons I've clicked once, but the bottom ones are much larger. If I don't use the .qss file, the buttons default to the small size as intended.

I've tried adding self.setstylesheet("") to the Toggle_Button so it doesn't inharent from the stylehseet (it still does)

My togglebutton code looks like this:

class ToggleButton(QPushButton):
    def __init__(self):
        super().__init__()
        #get image paths:
        self.locked = "Locked_Clipart.png"
        self.unlocked = "Un_Locked_Clipart.png"
        # Load the image and set it as the button icon
        self.setIcon(QIcon(self.locked))
        
        # Set the icon and button size to 32x32 pixels
        self.setIconSize(QSize(32, 32))
        self.setFixedSize(32, 32)

        self.setMinimumWidth(32)
        self.setMaximumWidth(32)
        self.adjustSize()
        
        # Optional: remove button border
        self.setStyleSheet("")

        #setup toggle
        self.setCheckable(True)
        self.toggled.connect(self.update_icon)
    
    def update_icon(self, checked):
        if checked == False:
            self.setIcon(QIcon(self.locked))
            
        else:
            self.setIcon(QIcon(self.unlocked))

        # Set the icon and button size to 32x32 pixels again (idk if this is needed)
        self.setIconSize(QSize(32, 32))
        self.setFixedSize(32, 32)

The two images needed can be found here but you can use your own too: Locked Pic unlocked

My basic example can be found here:

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()

        # Layout to display the button
        count = 1
        while count < 10:
            button = ToggleButton()
            layout.addWidget(button)
            count += 1
        
        self.setLayout(layout)


app = QApplication([])
# Apply stylesheet
qss_path = "style.qss"

try:
    with open(qss_path, "r") as file:
        stylesheet = file.read()
except FileNotFoundError:
    print(f"Stylesheet not found at {qss_path}")
    stylesheet = ""

if stylesheet:
    app.setStyleSheet(stylesheet)

window = MainWindow()
window.show()
app.exec()

The .qss file can be found here:

QPushButton {
    background-color: rgb(0, 128, 200);
    color: white;
    border: 1px solid #000000;
    padding: 5px;
    border-radius: 4px;
    min-width: 85px;
    margin: 0px;
    height: 15px
}
QPushButton#Lock_Toggle {
    background-color: #f0f0f0;       /* Light gray background */
    color: black;                    /* Black text */
    border: 1px solid #a9a9a9;       /* Gray border */
    padding: 4px 8px;                /* Padding for button content */
    border-radius: 3px;    
}

QPushButton:hover {
    background-color: rgb(0, 164, 242);
}

QPushButton:pressed {
    background-color: rgb(79, 198, 255);
}

Upvotes: 0

Views: 49

Answers (1)

musicamante
musicamante

Reputation: 48444

Qt style sheets (QSS) are not always consistent in their syntax with property management and their declarations, and one should always be careful in setting object properties in style sheets that may be inconsistent with what done with code.
In this specific case, the min-size property in the style sheet is in complete contrast with the setMinimumWidth() call.

While this may be considered a bug (or at least an inconsistency in behavior), you must consider that the Qt style may take precedence over size hints/restraints of the widget, and styles may update the widget at unexpected times: they may potentially update a property even if it was set by pure code.
When any of that eventually happens is not always foreseeable, and particular care must be taken when setting widget properties in style sheets (see the related notes about settings QObject properties, which is also valid for properties accepted by the common QSS syntax).

There are different ways to work around this, mostly depending on the style sheet, but also considering widget, object and class hierarchy structure.

If you want all QPushButtons to have that minimum width, except from that specific subclass, then you just need to use the class selector (see the QSS syntax docs), so that only QPushButton instances will follow that rule, not its subclasses:

QPushButton {
    /*
        everything else *but* the min-width property
    */
}
.QPushButton {
    min-width: 85px;
}

Another option, as long as you are completely sure that the class explicitly sets a size restraint, is to use a smaller value than what declared anywhere in the code; the simplest solution is to use a negative value:

ToggleButton {
    min-width: -1;
}

Note: the QSS specification is based on the early CSS 2.1, which does not provide "negations" such as the :not() selector.

Alternatively, you could override changeEvent() in the subclass and force again the fixed size in case a StyleChange event type is received, which is what happens whenever a QWidget is "polished" by a style (including receiving or inheriting a style sheet, even though it may not be relevant to it):

class ToggleButton(QPushButton):
    ...
    def changeEvent(self, event):
        super().changeEvent(event)
        if event.type() == event.Type.StyleChange:
            self.setFixedWidth(32)

Finally:

  • calling adjustSize() in a layout-managed widget is inappropriate, since the purpose of a layout manager is to manage the layout (therefore, calling setGeometry() on its widgets, which obviously overrides any previous attempt in calling adjustSize());
  • as the documentation clearly explains, setFixedSize() sets both the minimum and maximum size; calling setMinimumWidth() and setMaximumWidth() with the same width value used for a previous setFixedSize() is pointless;
  • calling setStyleSheet("") within the __init__ of a widget is useless as inappropriate, since the style sheet is empty anyway at that point, and also forces a recursive style computation that is completely unnecessary;

Upvotes: 0

Related Questions