0

I'm using radio buttons and checkboxes and I was wondering if there was a way to change the color of the border around the checkmark/radio indications. The reason is because when I switch to dark mode on my layout, the border is no longer visible.

By going to dark mode, I just inverse the foreground and background colors.

class ChannelWindow(QWidget):
    """channel real-time display class (used for both default and custom types)"""
    def __init__(self, channel, this_type, comPort, spot):
        super().__init__()
        self.setGeometry(myWin.pos().x() - 535 + spot, myWin.pos().y() + 31, 520, 775)
        self.setWindowTitle(f"{channel} Data Window")
        self.setWindowIcon(QtGui.QIcon('files/images/ham.ico'))
        self.setStyleSheet(f"color: {foreground}; background-color: {background}")

enter image description here

1 Answers1

0

Setting a basic stylesheet alone is insufficient, unless you are really thorough. Most of the times, its results are inconsistent.

There are two reasons for that:

  1. Qt uses QStyle to draw widgets, compute their sizes, position child elements (like the indicator of a QCheckBox) and properly draw them;
  2. QSS (Qt Style Sheets) are propagating (since they follow the cascading feature of CSS) and require specific selectors for sub-controls of complex widgets; setting style sheet makes Qt use an internal QStyle (QStyleSheetStyle) that sometimes completely overrides the default behavior of the basic style;

Most importantly, complex widgets usually require to write the full specifications of their contents, especially for sub-controls; and using generic properties is always discouraged for such widgets (including doing it for their parents).

There are various solutions to this, starting by using customized dark "themes" (see this post and the related answers). This has some caveats: it's actually a new style, so you might not like it, or would want to use custom style sheets that would create some compatibility issues.

The custom stylesheet solution, though, is quite more complex, as you should specify all sub controls colors, because even if you use class selectors, that won't be enough. For instance, QCheckBox requires that you specify the properties of ::indicator but, as explained above, once you set a property for a sub control, you must set all other properties along with it; in the case of QCheckBox this means that you have to write rules for all states (disabled, checked, unchecked, etc) and provide an image for the check mark symbol.

Other complex widgets (like scroll bars) require carefully written style sheets, otherwise they would look terrible.

You should also consider that some colors might not be compatible with the chosen ones (like the selection color).

A simpler (and, usually, more compliant) solution, is to set the application palette using the color constructor and eventually use carefully written style sheets to override some widget-specific colors if needed.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

Palettes = []

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        self.setStyleSheet('''
            QGroupBox::title {
                color: red;
            }
        ''')

        layout = QVBoxLayout(self)
        darkCheck = QCheckBox('Switch dark mode')
        layout.addWidget(darkCheck)

        # just some random widgets
        group = QGroupBox('Hello!')
        layout.addWidget(group)
        groupLayout = QVBoxLayout(group)
        groupLayout.addWidget(QTextEdit('lorem ipsum ' * 1000))
        combo = QComboBox()
        combo.addItems(('a', 'b', 'c'))
        groupLayout.addWidget(combo)
        groupLayout.addWidget(QLineEdit('line edit'))

        darkCheck.clicked.connect(setDarkMode)


def setDarkMode(dark):
    app = QApplication.instance()
    if not Palettes:
        Palettes.append(app.palette())
        Palettes.append(QPalette(Qt.darkGray))
    app.setPalette(Palettes[dark])
    for window in QApplication.topLevelWidgets():
        old = window.styleSheet()
        if not old:
            window.setStyleSheet(' ')
        window.setStyleSheet(old)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    test = Widget()
    test.show()
    sys.exit(app.exec())

As you can see, I altered the appearance of the group box title, but I specifically used a selector for the subcontrol of its class.

The last part is to ensure that all the top level widgets (and their children) compute again their stylesheets, even if they do not have any.

musicamante
  • 41,230
  • 6
  • 33
  • 58