Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically resize QIcon without calling setSizeIcon()

I'm currently struggling with what should be an easy issue to solve. Many widgets support some sort of QSizePolicy. This includes the QPushbutton. In my case I have multiple buttons in a grid layout all of which have their QSizePolicy for both vertical and horizontal resizing set to expanding. This leads to the nead result of the buttons being resized according to the size of the widget which the grid layout is part of.

The problem comes from the way icons seem to be handled in Qt. QIcon does not have a QSizePolicy property (or at least I was unable to find one in the official documentation of Qt4 about QIcon and QAbstractButton). The only way seems to be using setIconSize() where you can give the maximum size of the icon. In addition one has to manually implement a routine on how the size is to be updated. In this case it would be (abstract writing here) icon.size == button.size-CONSTANT, where CONSTANT is some kind of a predefined factor (>= 0). Predefining various sizes (a list of QIcons) for the selected icon is also possible but still not a good option (read below why).

This seems to be a little bit of an overkill especially since QPushbutton supports QSizePolicy and the developer doesn't have to tinker in this departement at all unless he/she wants some special resizing going on. Also this sort of contradicts the support for SVG files that can be used in QIcon since, as we know, SVG = vector graphics = you can stretch those as much as you like without loss of quality.

Does anyone know an easy way to do that without the need to add additional even handles for resizing, providing a list of scales of the chosen icon or restricting the size to a maximum size?

PS: I have also looked into QPixmap - still the same issues there.

EDIT: I forgot to mention one way I found out how to do what I wanted (the results are however not as pretty as I want them to be) - using the image property in the stylesheet for QPushbutton. This however does not create an icon! If one doesn't really require a real icon and can just use a painted button using this property allows a huge flexibility in terms of resizing especially when using SVG.

like image 231
rbaleksandar Avatar asked Mar 15 '23 05:03

rbaleksandar


2 Answers

Sub-classing QPushButton, as suggested by @Pavel in a comment, seems like a reasonable option to solve your issue. Below I provide a simple example that shows how this can be done in PySide.

import sys
from PySide import QtGui, QtCore

class myContainter(QtGui.QWidget):
    def __init__(self, parent=None):
        super(myContainter, self).__init__(parent)

        icon = QtGui.QIcon('process-stop.png')

        grid = QtGui.QGridLayout()

        for i in range(3):
            button = myPushButton()
            button.setIcon(icon) 

            grid.addWidget(button, i, 0)

            grid.setRowStretch(i, i)

        self.setLayout(grid)


class myPushButton(QtGui.QPushButton):
    def __init__(self, label=None, parent=None):
        super(myPushButton, self).__init__(label, parent)

        self.pad = 4     # padding between the icon and the button frame
        self.minSize = 8 # minimum size of the icon

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
                                       QtGui.QSizePolicy.Expanding)
        self.setSizePolicy(sizePolicy)

    def paintEvent(self, event):

        qp = QtGui.QPainter()
        qp.begin(self)

        #---- get default style ----

        opt = QtGui.QStyleOptionButton()
        self.initStyleOption(opt)

        #---- scale icon to button size ----

        Rect = opt.rect

        h = Rect.height()
        w = Rect.width()
        iconSize = max(min(h, w) - 2 * self.pad, self.minSize)

        opt.iconSize = QtCore.QSize(iconSize, iconSize)

        #---- draw button ----

        self.style().drawControl(QtGui.QStyle.CE_PushButton, opt, qp, self)

        qp.end()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)

    instance = myContainter()  
    instance.show()    

    sys.exit(app.exec_())

Which results in:

enter image description here

The maximum size of the icon is limited by the size of the png used as input in QIcon. If a svg is used as input for the QIcon, the scaling of the icon won't be limited in size. However, svg icons seems not to be supported in Windows7, but they are in Ubuntu.

The code above would need to be expanded if a label is added to the button. Moreover, it would be possible to also scale the font size of the label to the button size if desired.

like image 69
Jean-Sébastien Avatar answered Mar 20 '23 19:03

Jean-Sébastien


To persons who work in C++. Thanks a lot !

pushbuttoniconautoresize.h

#ifndef PUSHBUTTONIMAGEAUTOMATICRESIZE_H
#define PUSHBUTTONIMAGEAUTOMATICRESIZE_H

#include <QPushButton>

class PushButtonIconAutoResize : public QPushButton
{
    Q_OBJECT

public:
    PushButtonIconAutoResize(const QString &text, QWidget *parent=0);
    ~PushButtonIconAutoResize();

private:
    void paintEvent(QPaintEvent *event);

    int pad;
    int minSize;
};

#endif // PUSHBUTTONIMAGEAUTOMATICRESIZE_H

pushbuttoniconautoresize.cpp

#include "pushbuttoniconautoresize.h"

#include <QSize>
#include <QSizePolicy>
#include <QStylePainter>
#include <QStyleOptionButton>
#include <QtGlobal>

PushButtonIconAutoResize::PushButtonIconAutoResize(const QString &text, QWidget *parent)
    : QPushButton(text, parent)
{
    pad = 4;
    minSize = 8;

    this->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
}

PushButtonIconAutoResize::~PushButtonIconAutoResize()
{

}

void PushButtonIconAutoResize::paintEvent( QPaintEvent *event )
{
    QStylePainter painter(this);

    QStyleOptionButton opt;
    this->initStyleOption(&opt);

    QRect r = opt.rect;

    int h = r.height();
    int w = r.width();
    int iconSize = qMax(qMin(h, w) - 2 * this->pad, this->minSize);

    opt.iconSize = QSize(iconSize, iconSize);

    painter.drawControl(QStyle::CE_PushButton, opt);
}
like image 21
Matzora Avatar answered Mar 20 '23 19:03

Matzora