pythonpyqtpyqt6qmainwindow

When I used a custom Widget as the CentralWidget for QMainWindow in PyQt6, there was a gap between the CentralWidget and the QMainWindow


When I used a custom Widget as the CentralWidget for QMainWindow in PyQt6, there was a gap between the CentralWidget and the QMainWindow? I wonder why that is.Here's my code:

from PyQt6.QtWidgets import QApplication, QWidget, QMainWindow, QLabel, QHBoxLayout
import sys


class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self._layout = QHBoxLayout(self)
        self.label = QLabel("hello")
        self.label.setStyleSheet("QLabel {color: white;}")
        self._layout.addWidget(self.label)
        self.setStyleSheet("background-color: black; color: white;")


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # When using custom widgets, there is a gap
        self.central_widget = MyWidget()

        # When using Qwidget, there is no gap
        # self.central_widget = QWidget()
        # self.central_layout = QHBoxLayout()
        # self.central_layout.addWidget(QLabel("hello"))
        # self.central_widget.setLayout(self.central_layout)

        self.setCentralWidget(self.central_widget)
        # self.central_widget.setStyleSheet("background-color: black; color: white; border: none;")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = MainWindow()
    win.show()
    sys.exit(app.exec())

picture 1

picture 2

I want to know why there is a difference between using a custom widget and using a QWidget, and how to fix it.


Solution

  • You are misinterpreting the result.

    In your custom subclass you are setting the following stylesheet:

    background-color: black; color: white;
    

    This results in the following:

    1. subclassed QWidgets do not paint the background set in style sheets (see the QSS documentation about QWidget), with the only exception of top level widgets (windows);
    2. using generic properties (without proper selector types) will automatically propagate those properties to any child widget;

    What you see is not the background of the widget, it's the background of the label. The gap that you see near the edges is the layout margin.

    The situation is more clear if you specify the alignment on the label; when adding a child to a layout, the layout will try to make the widget occupy as much space available within the "layout cell", but if the alignment is specified it will only use the widget's size hint.
    Try to do the following:

    self._layout.addWidget(self.label, alignment=Qt.AlignCenter)
    

    And you will see this:

    enter image description here

    Theoretically, you could try to nullify the layout margin:

    self._layout.setContentsMargins(0, 0, 0, 0)
    

    In reality, that is not the correct solution, because:

    So, in order to make the widget have its own background you must use proper syntax in the QSS (with selector types) and ensure that the parent widget actually paints its background, which can be achieved either by setting the Qt.WA_StyledBackground attribute, or by implementing the paintEvent().

    class MyWidget(QWidget):
        def __init__(self):
            ...
            self.setStyleSheet("MyWidget { background-color: black;} ")
            self.setAttribute(Qt.WA_StyledBackground)
    

    Alternatively:

    class MyWidget(QWidget):
        def __init__(self):
            ...
            self.setStyleSheet("MyWidget { background-color: black;} ")
    
        def paintEvent(self, event):
            qp = QStylePainter(self)
            opt = QStyleOption()
            opt.initFrom(self)
            qp.drawPrimitive(QStyle.PE_Widget, opt)