Home > Net >  QComboBox has unexpected behavior when changing stylesheets while Qt.UIEffect.UI_AnimateCombo is set
QComboBox has unexpected behavior when changing stylesheets while Qt.UIEffect.UI_AnimateCombo is set

Time:03-08

My goal was to make a combo box which shows the down arrow only when the mouse is hovering above it. To do that I subclassed QComboBox and reimplemented enterEvent and leaveEvent which would change its stylesheet accordingly. It works just as expected, when to window shows up, down arrow is hidden and when the mouse hovers above combo box, the down arrow shows up. Then, when the mouse clicks on combo box text, the drop down list shows up and the down arrow becomes invisible again (which I can deal with, but if someone has a solution I'm all ears). The main problem is when the mouse clicks on the drop down arrow, instead of just hiding the drop down arrow, as it did when mouse clicked on the text, it also hides the text along with the drop down arrow. I did some testing to find out what's causing the problem and it seems that calling setEffectEnabled(Qt.UIEffect.UI_AnimateCombo, False) on QApplication is the issue, although I have no idea how disabling drop down animation is related to making text disappear when clicking on drop down arrow. Also, if someone has more elegant way of doing what I want, again, I'm all ears :). Here's the example code:

import sys

from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtGui import QEnterEvent
from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QWidget


class TestComboBox(QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.default_stylesheet = ''' 
        QComboBox {
            background-color: rgba(0, 0, 0, 0);
        }
        QComboBox QAbstractItemView {
            background-color: white;
            min-width: 150px;
        }'''

        self.hide_down_arrow_stylesheet = '''QComboBox::down-arrow { \
                                             background-color: rgba(0, 0, 0, 0);}'''

        self.setStyleSheet(self.default_stylesheet   self.hide_down_arrow_stylesheet)

    def enterEvent(self, event: QEnterEvent) -> None:
        self.setStyleSheet(self.default_stylesheet)
        super().enterEvent(event)

    def leaveEvent(self, event: QEvent) -> None:
        self.setStyleSheet(self.default_stylesheet   self.hide_down_arrow_stylesheet)
        super().leaveEvent(event)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.widget = QWidget(self)
        self.setCentralWidget(self.widget)

        self.combobox = TestComboBox(self.widget)
        self.combobox.resize(180, 30)
        self.combobox.insertItem(0, "item 1")
        self.combobox.insertItem(0, "item 2")

        self.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setEffectEnabled(Qt.UIEffect.UI_AnimateCombo, False)
    window = MainWindow()
    sys.exit(app.exec())

CodePudding user response:

There's probably some internal and complex bug related to the fact that you only have set a single sub-control property, but the documentation specifically addresses this aspect:

Note: With complex widgets such as QComboBox and QScrollBar, if one property or sub-control is customized, all the other properties or sub-controls must be customized as well.

I can't remember all required QSS properties for QComboBox, but, in reality, this is not the real problem: the main issue here is the fact that you're not using layout managers. And this is not only a problem for this specific case, but a general aspect that should be never forgotten.
Hardcoded geometries for child widgets should always be avoided, for a lot of reasons that I will not list here.

As long as a layout is set (as you should always do), the problem disappears:

class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        layout = QVBoxLayout(self.widget)
        layout.addWidget(self.combobox)

Note: it's good practice to not call self.show() (or self.setVisible(True)) within the __init__ of a widget.

CodePudding user response:

I managed to fix the issue by reading through the documentation https://doc.qt.io/qt-5/stylesheet-syntax.html#sub-controls. Here's the solution:

import sys

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QComboBox, QWidget, QVBoxLayout


class TestComboBox(QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.default_stylesheet = ''' 
        QComboBox {
            color: black;
            selection-color: black;
            selection-background-color: rgba(0, 0, 0, 0);
            background-color: rgba(0, 0, 0, 0);
        }
        
        QComboBox QAbstractItemView {
            background-color: white;
            min-width: 150px;
        }
        
        QComboBox:!hover::down-arrow {
        color: rgba(0, 0, 0, 0);
        }
        '''

        self.setStyleSheet(self.default_stylesheet)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.widget = QWidget(self)
        self.setCentralWidget(self.widget)

        self.layout = QVBoxLayout(self.widget)

        self.combobox = TestComboBox()
        self.combobox.resize(180, 30)
        self.combobox.insertItem(0, "item 1")
        self.combobox.insertItem(0, "item 2")
        self.combobox.insertItem(0, "item 3")
        self.combobox.insertItem(0, "item 4")
        self.combobox.insertItem(0, "item 5")
        self.layout.addWidget(self.combobox)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setEffectEnabled(Qt.UIEffect.UI_AnimateCombo, False)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
  • Related