0

I would like to change the color or adjust the size of this rectangle behind the open Combobox

I noticed that there is a problem with the increased size of this white background and the offset options compared to when this qss is not used Closed ComboBox

Closed ComboBox

Open ComboBox

Open ComboBox

Here is a code example that recreates this problem:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QVBoxLayout, QWidget


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QComboBox Demo")
        self.setGeometry(100, 100, 300, 200)

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)

        formats = [
            "BMP", "Iris", "PNG", "JPEG", "JPEG 2000", "Targa", "Targa Raw"
        ]

        combo_box = QComboBox()
        combo_box.addItems(formats)
        combo_box.setCurrentIndex(-1)

        layout.addWidget(combo_box)
        layout.addStretch()

        style_sheet = """
        QWidget {
            background-color: #404040;
            color: #e6e6e6;
            margin: 0px;
        }
        QComboBox {
            background-color: #2c2c2c;
            border: 1px solid #404040;
            color: #d0d0d0;
            padding: 4px 6px;
            border-radius: 4px;
            margin: 0px;
        }
        QComboBox:disabled {
            background-color: #323232;
            color: #8a8a8a;
        }
        QComboBox::down-arrow {
            background-color: #2c2c2c;
            image: url('images/QComboBox_down_arrow.png');
        }
        QComboBox::down-arrow:on {
            background-color: #5680c2;
        }
        QComboBox::drop-down {
            background-color: #202020;
            border: none;
            width: 20px;
        }
        QComboBox:on {
            background-color: #5680c2;
            color: #f8f9f9;
        }
        QComboBox QAbstractItemView {
            background-color: #232323;
            border: 1px solid #404040;
            color: #d0d0d0;
            border-radius: 4px;
            margin: 0px;
            padding: 2px;
            min-width: 100px;
            selection-background-color: #5680c2;
        }
        """
        self.setStyleSheet(style_sheet)


if __name__ == "__main__":
    app = QApplication(sys.argv + ['-platform', 'windows:darkmode=1'])
    app.setStyle('Fusion')
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Also this partly corrected when I don't use Fusion style, but also some of the necessary style is lost.

The style is based on hannes delbeke stylesheet: https://github.com/hannesdelbeke/blender-qt-stylesheet/blob/main/blender_stylesheet/blender_stylesheet.qss

0

2 Answers 2

0

The problem is caused by many factors, also involving the style sheets (how they may alter the internal lengths and change painting rules).

The popup of the QComboBox is actually a private QFrame that acts as a container for the item view; even though it has its own layout manager, when the popup is shown, the view is manually positioned with setGeometry() using a QRect that is computed using many factors, including some possible margins.

Unfortunately, it seems like the container inherits in some ways the vertical padding property set on the QComboBox (you can see the difference by setting it to 0), which is probably a bug, even though possibly caused by the attempt to position the popup considering the padding.

In any case, a possible solution could be to subclass QComboBox and override the behavior of showPopup(). The idea is to let Qt show the combo, then grab the view as a bitmap in order to use it as a mask for the container by calling setMask().

This will also allow proper alignment for styles that display the popup on top of the combo (usually by displaying the currently selected item at the same position of the combo).

class Combo(QComboBox):
    def showPopup(self):
        super().showPopup()
        if self.count() <= 0:
            return
        view = self.view()
        container = view.parent()
        if view.height() == container.height():
            return

        frameRect = container.frameGeometry()
        comboRect = self.rect().translated(self.mapToGlobal(QPoint()))
        if (
            frameRect.y() <= comboRect.y()
            and frameRect.bottom() >= comboRect.bottom()
        ):
            # in case the popup is shown on top of the combo, adjust
            # its position so that it matches the correct item alignment
            container.move(container.x(), container.y() - view.y())

        mask = QBitmap(container.size())
        mask.fill(Qt.color0)
        qp = QPainter(mask)
        view.render(qp, view.pos(), flags=QWidget.RenderFlag(0))
        qp.end()
        container.setMask(mask)
Sign up to request clarification or add additional context in comments.

2 Comments

I think using a mask is too much in my case, but thanks for such a deep explanation
The usage of the mask has two benefits: 1. it prevents any unwanted margin possibly caused by the padding; 2. allows proper displaying of the rounded borders, which otherwise will have a black background on the corners (because the popup window is still a full rectangle). Note that there's absolutely nothing wrong with using masking, and it has very little impact on performance: Qt does it on its own in some cases, for instance with tooltips and menus (at least in some styles).
0

I got an acceptable working version, using QApplication.setStyleSheet() in the main (I don't know why it works exactly like that...).

New code:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QVBoxLayout, QWidget


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QComboBox Demo")
        self.setGeometry(100, 100, 300, 200)

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)

        formats = [
            "BMP", "Iris", "PNG", "JPEG", "JPEG 2000", "Targa", "Targa Raw"
        ]

        combo_box = QComboBox()
        combo_box.addItems(formats)
        combo_box.setCurrentIndex(-1)

        layout.addWidget(combo_box)
        layout.addStretch()


if __name__ == "__main__":
    style_sheet = """
                    QWidget {
                        background-color: #404040;
                        color: #e6e6e6;
                        margin: 0px;
                    }
                    QComboBox {
                        background-color: #2c2c2c;
                        border: 1px solid #404040;
                        color: #d0d0d0;
                        padding: 4px 6px;
                        border-radius: 4px;
                        margin: 0px;
                    }
                    QComboBox:disabled {
                        background-color: #323232;
                        color: #8a8a8a;
                    }
                    QComboBox::down-arrow {
                        background-color: #2c2c2c;
                        image: url('images/QComboBox_down_arrow.png');
                    }
                    QComboBox::down-arrow:on {
                        background-color: #5680c2;
                    }
                    QComboBox::drop-down {
                        background-color: #202020;
                        border: none;
                        width: 20px;
                    }
                    QComboBox:on {
                        background-color: #5680c2;
                        color: #f8f9f9;
                    }
                    QComboBox QAbstractItemView {
                        background-color: #232323;
                        border: 1px solid #404040;
                        color: #d0d0d0;
                        border-radius: 4px;
                        margin: 0px;
                        padding: 2px;
                        min-width: 100px;
                        selection-background-color: #5680c2;
                    }
                    """

    app = QApplication(sys.argv + ['-platform', 'windows:darkmode=1'])

    app.setStyleSheet(style_sheet)

    app.setStyle('Fusion')

    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

1 Comment

The reason for which this works when setting the stylesheet for the application is that the popup only partially inherits the stylesheet from the combobox, but it's actually a top level one, therefore it doesn't inherit in full QStyleSheetStyle does some internal checks, and applies some rules differently for specific widgets, including the container of the combo box popup that includes the view: in that case, it only applies some rules, while relying on the default style behavior for others. When setting the QSS for the application, it's used for all widgets instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.