Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add an icon to a QGroupBox title?

Tags:

c++

qt

Is it possible to add an icon (programmatically, via QtCreator, with or without a stylesheet) to the QGroupBox title?

I want to place a mark close to the title of some of the QGroupBoxes in my GUI. The best place would be to the right of the title, but any other reasonable position (to the left of the title, or in the top right corner of the widget) could be also valid.

The only way I could think of is creating a QLabel with an icon and visually placing it on top of the QGroupBox, in the desired position. But I find this an ugly solution.

Edit

I know there is a way of setting the indicator used for checkable QGroupBoxes, as any image, both for checked and unchecked states, using stylesheets. But then the QGroupBox must be checkable, etc. I want to add this mark (a small image) independently to the checkability of the QGroupBox.

like image 719
J C Gonzalez Avatar asked Dec 14 '22 14:12

J C Gonzalez


1 Answers

Background

Unfortunatelly, QGroupBox does not have a setIcon method, as QPushButton does.

A possible solution could be to make QGroupBox checkable using QGroupBox::setCheckable and set a custom image as an indicator using a stylesheet like this:

QGroupBox::indicator:unchecked {
    image: url(:/images/checkbox_unchecked.png);
}

However, this will make the whole group box disabled, when the checkbox is unchecked.

Solution

Having this background in mind, I would suggest you a fairly complex, but also a pretty flexible solution, consisting of the following steps:

  1. Subclass QStyleOptionGroupBox and declare additional variables as public class attributes:

    QPixmap pixmap;
    int offset;
    int leftSpace;
    int rightSpace;
    

    These will be used for passing the decoration pixmap, as well as the placement settings, to the style.

  2. Subclass QCommonStyle and reimplement QCommonStyle::drawComplexControl like this:

    if (const StyleOptionDecoratedGroupBox *groupBox = qstyleoption_cast<const StyleOptionDecoratedGroupBox *>(opt)) {
        QRect textRect = proxy()->subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, widget);
        QRect checkBoxRect = proxy()->subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, widget);
        int decorationOffset = 0;
        int pixmapRectWidth = 0;
        int pixmapHeight = 0;
        int textOffset = 0;
    
        if (!groupBox->pixmap.isNull()) {
            decorationOffset = groupBox->offset;
            pixmapRectWidth = groupBox->leftSpace
                            + groupBox->pixmap.width()
                            + groupBox->rightSpace;
            pixmapHeight = groupBox->pixmap.height();
            textOffset = decorationOffset + pixmapRectWidth;
        }
    
        textRect.adjust(textOffset, 0, textOffset, 0);
    
        // Draw frame
        if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
            QStyleOptionFrame frame;
            frame.QStyleOption::operator=(*groupBox);
            frame.features = groupBox->features;
            frame.lineWidth = groupBox->lineWidth;
            frame.midLineWidth = groupBox->midLineWidth;
            frame.rect = proxy()->subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, widget);
            p->save();
            QRegion region(groupBox->rect);
            if (!groupBox->text.isEmpty()) {
                bool ltr = groupBox->direction == Qt::LeftToRight;
                QRect finalRect;
                if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
                    finalRect = checkBoxRect.united(textRect);
                    finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
                } else {
                    finalRect = textRect;
                }
                region -= finalRect;
            }
            p->setClipRegion(region);
            proxy()->drawPrimitive(PE_FrameGroupBox, &frame, p, widget);
            p->restore();
        }
    
        // Draw icon
        if (!groupBox->pixmap.isNull()) {
            p->fillRect(decorationOffset, 0, pixmapRectWidth, pixmapHeight, opt->palette.window().color());
            proxy()->drawItemPixmap(p, QRect(decorationOffset, 0, pixmapRectWidth, pixmapHeight),
                                    Qt::AlignCenter, groupBox->pixmap);
        }
    
        // Draw title
        if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
            QColor textColor = groupBox->textColor;
            if (textColor.isValid())
                p->setPen(textColor);
            int alignment = int(groupBox->textAlignment);
            if (!proxy()->styleHint(QStyle::SH_UnderlineShortcut, opt, widget))
                alignment |= Qt::TextHideMnemonic;
            proxy()->drawItemText(p, textRect,  Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
                                  groupBox->palette, groupBox->state & State_Enabled, groupBox->text,
                                  textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
            if (groupBox->state & State_HasFocus) {
                QStyleOptionFocusRect fropt;
                fropt.QStyleOption::operator=(*groupBox);
                fropt.rect = textRect;
                proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
            }
        }
    } else {
        QCommonStyle::drawComplexControl(cc, opt, p, widget);
    }
    
  3. Subclass QGroupBox and reimplement QGroupBox::paintEvent like this:

    CustomStyle style;
    QPainter painter(this);
    StyleOptionDecoratedGroupBox option;
    
    initStyleOption(&option);
    option.pixmap = m_pixmap;
    option.offset = m_offset;
    option.leftSpace = m_leftSpace;
    option.rightSpace = m_rightSpace;
    
    style.drawComplexControl(QStyle::CC_GroupBox, &option, &painter, this);
    

Note: This solution does not work well when the group box is checkable.

Example

The solution requires a lot of work, but fortunatelly I have created the necessary classes for you, as well as a complete example of how to use them in your own projects. The code is available on GitHub.

Result

The provided example produces the following result:

Window with a decorated group box and radio buttons

like image 56
scopchanov Avatar answered Dec 25 '22 22:12

scopchanov