Search code examples
pythonpyqt5qcombobox

How do I set the style of the selected QComboBox element in PyQt5?


how do I add some style for the selected element (in this case for 1)?

For example, how to paint the background of the selected element in red?

Here is the code

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow
import sys


class Ui_MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.comboBox = QtWidgets.QComboBox(self)
        self.comboBox.addItem("1")
        self.comboBox.addItem("2")
        self.comboBox.addItem("3")
        self.comboBox.setStyleSheet('''''')



if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = Ui_MainWindow()
    MainWindow.show()
    sys.exit(app.exec_())

for example, I have the same code, but there I use QWidgets exept QComboBox

from PyQt5 import QtWidgets, QtCore, QtGui


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralwidget = QtWidgets.QWidget()
        self.centralwidget.setObjectName("centralwidget")
        self.setCentralWidget(self.centralwidget)

        mainMenu = self.menuBar()
        mainMenu.setNativeMenuBar(False)
        self.fileMenu = mainMenu.addMenu('Menu')

        action1 = QtWidgets.QWidgetAction(self, objectName="action1")
        self.label1 = QtWidgets.QLabel("Action 1", objectName="label1")
        action1.setDefaultWidget(self.label1);
        action1.setText('Action 1')

        action2 = QtWidgets.QWidgetAction(self, objectName="action2")
        self.label2 = QtWidgets.QLabel("Action 2", objectName="label2")
        action2.setDefaultWidget(self.label2);
        action2.setText('Action 2')

        action3 = QtWidgets.QWidgetAction(self, objectName="action3")
        self.label3 = QtWidgets.QLabel("Action 3", objectName="label3")
        action3.setDefaultWidget(self.label3);
        action3.setText('Action 3')

        self.fileMenu.addAction(action1)
        self.fileMenu.addAction(action2)
        self.fileMenu.addAction(action3)
        self.fileMenu.triggered.connect(self.triggered_action)

        layout = QtWidgets.QHBoxLayout(self.centralwidget)
        layout.addWidget(mainMenu)

        self.triggered_action(action1)

    def triggered_action(self, q):
        objectName = q.objectName()

        self.label1.setStyleSheet("""
            QLabel {background-color: #ABABAB; padding: 10px 12px 10px 12px;}
            QLabel:hover {background-color: #654321;}
        """)
        self.label2.setStyleSheet("""
            QLabel {background-color: #ABABAB; padding: 10px 12px 10px 12px;}
            QLabel:hover{background-color: #654321;}
        """)
        self.label3.setStyleSheet("""
            QLabel {background-color: #ABABAB; padding: 10px 12px 10px 12px;}
            QLabel:hover {background-color: #654321;}
        """)

        if objectName == 'action1':
            self.label1.setStyleSheet("""
                QLabel {background-color: red; padding: 10px 12px 10px 12px;}
                QLabel:hover {background-color: #C10000;}
            """)
            self.fileMenu.setTitle('Action 1')
        elif objectName == 'action2':
            self.label2.setStyleSheet("""
                QLabel { background-color : red; padding: 10px 12px 10px 12px;}
                QLabel:hover { background-color: #C10000;}
            """)
            self.fileMenu.setTitle('Action 2')
        elif objectName == 'action3':
            self.label3.setStyleSheet("""
                QLabel { background-color : red; padding: 10px 12px 10px 12px;}
                QLabel:hover { background-color: #C10000;}
            """)
            self.fileMenu.setTitle('Action 3')


QSS = """
#centralwidget {
    background-color: #18465d;
}

QMenuBar {
    background-color: qlineargradient(
        x1:0, y1:0, x2:0, y2:1,
        stop:0 lightgray, stop:1 darkgray);
    max-height: 35px;
    min-height: 35px;
    min-width: 140px;
    max-width: 140px;
    font: 22px;   
}
QMenuBar::item {
    background-color: #734046;
    color: rgb(255, 255, 255);  
    border-radius: 2px;
}
QMenuBar::item:selected {    
    background-color: rgb(244, 164, 96);  
}
QMenuBar::item:pressed {
    background: rgb(128, 0, 0);
}

QMenu {
    background-color: #ABABAB;   
    border: 1px solid black;
    margin: 2px;
}
QMenu::item {
    background-color: transparent;
    padding: 20px 25px 20px 20px;
}
QMenu::item:selected { 
    background-color: #654321;
    color: rgb(255,255,255);
}

QLabel { 
    background-color: #ABABAB;
    color: rgb(255, 255, 255);
    font: 20px;
    padding: 10px 12px 10px 12px;
} 
QLabel:hover { 
    background-color: #654321;
} 
"""

if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    app.setStyleSheet(QSS)
    w = MainWindow()
    w.resize(400, 400)
    w.show()
    sys.exit(app.exec_())

Now I haven't enough reputation to give any of screenshots I will attach additional material later

-> update

введите сюда описание изображения

in this case, when element number 1 is selected, I need only this element to be painted in red.

To make it more clear, I will give an example from windows enter image description here

you can see that I have selected the lucida console font in windows notepad, and there is a blue vertical line next to it. this means that when an element is already selected, a special style is applied to it

I hope you understand me now


Solution

  • This cannot be achieved with stylesheets, because the selected item in the popup view is not the same as that of the combo, but only the one selected with the keyboard (or by hovering it with the mouse), and does not correspond to the current index of the combo.

    The only way to do so is by using an item delegate, which can be set on the combo and will be used for the popup.

    Note that you obviously need a reference to the combo to get the actual current index, so a possibility (and also good practice) is to create the delegate with the combobox as parent.

    class SelectDelegate(QStyledItemDelegate):
        def initStyleOption(self, opt, index):
            super().initStyleOption(opt, index)
            if self.parent().currentIndex() == index.row():
                opt.backgroundBrush = QBrush(Qt.red)
    
    
    class Test(QWidget):
        def __init__(self):
            super().__init__()
            self.comboBox = QComboBox()
            self.comboBox.setItemDelegate(
                SelectDelegate(self.comboBox))
    
            self.comboBox.addItem("1")
            self.comboBox.addItem("2")
            self.comboBox.addItem("3")
    
            layout = QVBoxLayout(self)
            layout.addWidget(self.comboBox)
            self.resize(150, 60)
    

    Note that you could also use QItemDelegate, which is normally more similar to the default combo popup.

    Unfortunately, this has the drawback that the view will probably not look exactly like a standard one.

    If you're not using a custom model and you don't need background colors for anything else, there is a possible alternative, which to set the BackgroundRole for all items before showing the popup, using a red QBrush for the current index or None for any other:

    class SelectCombo(QComboBox):
        def showPopup(self):
            current = self.currentIndex()
            for i in range(self.count()):
                if i == current:
                    color = QBrush(Qt.red)
                else:
                    color = None
                self.setItemData(i, color, Qt.BackgroundRole)
            super().showPopup()