4

I'm trying to move the icon of a QCheckBox from the left of the label to immediately on the right.

I've checked these posts:

But neither seem to work for a QCheckBox. Actually, the second solution is the best I have so far, but I would like the icon to be just to the right of the label, not aligned all the way to the right of the widget.

Here are my experiments:

from PyQt5 import QtGui
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QStyle, QApplication, QLabel, QHBoxLayout


class CheckBoxWIcon(QCheckBox):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        icon = self.icon()
        icon_size = self.iconSize()
        # remove icon
        self.setIcon(QtGui.QIcon())
        label_icon = QLabel()
        label_icon.setAttribute(Qt.WA_TranslucentBackground)
        label_icon.setAttribute(Qt.WA_TransparentForMouseEvents)
        lay = QHBoxLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(label_icon, alignment=Qt.AlignRight)
        label_icon.setPixmap(icon.pixmap(icon_size))


app = QApplication([])
mw = QWidget()
layout = QVBoxLayout()

test1 = QCheckBox("Default")
test1.setIcon(app.style().standardIcon(QStyle.SP_MediaSkipForward))
test1.setStyleSheet("""QCheckBox { text-align: right; }""")

test2 = QCheckBox("Using style-sheet")
test2.setIcon(app.style().standardIcon(QStyle.SP_MediaSkipForward))
test2.setStyleSheet("""QCheckBox { text-align: left; }""")

test3 = QCheckBox("Using layout direction")
test3.setIcon(app.style().standardIcon(QStyle.SP_MediaSkipForward))
test3.setLayoutDirection(Qt.RightToLeft)

test4 = CheckBoxWIcon("Custom class", icon=QApplication.style().standardIcon(QStyle.SP_MediaSkipForward))

layout.addWidget(test1)
layout.addWidget(test2)
layout.addWidget(test3)
layout.addWidget(test4)
mw.setLayout(layout)

mw.show()
app.exec()

enter image description here

Desired output:

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75

1 Answers1

3

A possible solution is to use a QProxyStyle to modify the painting:

from PyQt5.QtCore import QRect, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QCheckBox, QProxyStyle, QStyle, QWidget


class IconProxyStyle(QProxyStyle):
    def drawControl(self, element, option, painter, widget=None):
        if element == QStyle.CE_CheckBoxLabel:
            offset = 4
            icon = QIcon(option.icon)
            option.icon = QIcon()

            super().drawControl(element, option, painter, widget)

            alignment = self.visualAlignment(
                option.direction, Qt.AlignLeft | Qt.AlignVCenter
            )
            if not self.proxy().styleHint(QStyle.SH_UnderlineShortcut, option, widget):
                alignment |= Qt.TextHideMnemonic
            r = painter.boundingRect(
                option.rect, alignment | Qt.TextShowMnemonic, option.text
            )

            option.rect.setLeft(r.right() + offset)
            option.text = ""
            option.icon = icon

        super().drawControl(element, option, painter, widget)


def main():
    import sys

    app = QApplication(sys.argv)
    app.setStyle("fusion")
    app.setStyle(IconProxyStyle(app.style()))
    button = QCheckBox(
        "Test\nwith\nQProxyStyle",
        icon=QApplication.style().standardIcon(QStyle.SP_MediaSkipForward),
    )
    # button.setStyle(IconProxyStyle(button.style()))

    button.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241