Minimum size/width of a QPushButton that is created from code
Asked Answered
C

4

13

I created 2 rows of push buttons, each row is inside a QHBoxLayout. enter image description here

I create the buttons in the code:

static const char* buttonText = "23456789TJQKA";
for (int ii = 0; buttonText[ii]; ii++)
{
    QPushButton* pushButton = new QPushButton(this);
    pushButton->setText(QString(buttonText[ii]));
    ui->horizontalLayout_1->addWidget(pushButton);
}
for (int ii = 0; buttonText[ii]; ii++)
{
    QPushButton* pushButton = new QPushButton(this);
    pushButton->setText(QString(buttonText[ii]));
    ui->horizontalLayout_2->addWidget(pushButton);
}

The problem is that they can't shrink (when the user resizes the dialog) beyond that size, even though their text would fit in a much smaller width. If I create the buttons manually in the resource editor instead of in the code, they can have smaller width than that.

Conn answered 10/7, 2011 at 3:33 Comment(3)
That worked! Thanks. If you post this as answer I can select it as the accepted answer. But why did resizing work for QToolButton but not for QPushButton?Conn
QToolButton and QPushButton have different default "styles". For instance, a QPushButton has a minimum width of approximately 6em. Meaning, the width of 6 characters (in the font that you are using). QToolButton, because it is generally used for images, or "...", things like that, has a much smaller minimum width - like 2em. QPushButton, also usually has more left/right margins. If you start playing with style-sheets this is something you can tinker with.Mcclelland
you may also use setMinimumSize(QSize&) to define the minimum extents.Bespeak
B
11

This happens because the minimumSizeHint of the QPushButton does not allow the QLayout to resize it :

The default implementation of minimumSizeHint() returns an invalid size if there is no layout for this widget, and returns the layout's minimum size otherwise. Most built-in widgets reimplement minimumSizeHint().

QLayout will never resize a widget to a size smaller than the minimum size hint unless minimumSize() is set or the size policy is set to QSizePolicy::Ignore. If minimumSize() is set, the minimum size hint will be ignored.

The simple solution is to set the minimum width explicitly:

static const char* buttonText = "23456789TJQKA";
for (int ii = 0; buttonText[ii]; ii++)
{
   QPushButton* pushButton = new QPushButton(this);
   pushButton->setMinimumWidth(5);
   pushButton->setText(QString(buttonText[ii]));
   ui->horizontalLayout_1->addWidget(pushButton);
}
for (int ii = 0; buttonText[ii]; ii++)
{
   QPushButton* pushButton = new QPushButton(this);
   pushButton->setMinimumWidth(5);
   pushButton->setText(QString(buttonText[ii]));
   ui->horizontalLayout_2->addWidget(pushButton);
}
Bourque answered 16/11, 2011 at 20:42 Comment(0)
S
5

As pnezis wrote, you probably want to override the default minimum size calculated by the button. Here's a way you can do it while avoiding to choose an arbitrary size that might not work when conditions vary (different font or font size, UI style, etc):

QWidget* parent = /* some widget */
auto button = new QPushButton(QLatin1String("X"), parent);
auto textSize = button->fontMetrics().size(Qt::TextShowMnemonic, button->text());
QStyleOptionButton opt;
opt.initFrom(button);
opt.rect.setSize(textSize);
button->setMinimumSize(
  button->style()->sizeFromContents(QStyle::CT_PushButton,
                                    &opt,
                                    textSize,
                                    button));

The above was adapted and simplified from QPushButton's own code. You may want to look at the source of QPushButton::sizeHint for all the details.

Salcido answered 21/10, 2013 at 18:56 Comment(0)
C
1

setMaximumWidth works for me. sample code is in pyqt, but it should translate directly to C++ without any problems.

from PyQt4 import QtGui

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        layout = QtGui.QHBoxLayout()
        texts = [":)",
                 "&Short",
                 "&Longer",
                 "&Different && text",
                 "More && text",
                 "Even longer button text", ]
        for text in texts:
            btn = QtGui.QPushButton(text)
            double = text.count('&&')
            text = text.replace('&', '') + ('&' * double)
            width = btn.fontMetrics().boundingRect(text).width() + 7
            btn.setMaximumWidth(width)
            layout.addWidget(btn)
        self.setLayout(layout)

if __name__ == '__main__':
    import sys

    app = QtGui.QApplication(sys.argv)
    mainWin = Window()
    mainWin.show()
    sys.exit(app.exec_())
Catenary answered 11/3, 2014 at 3:16 Comment(0)
H
1

This minimum button width comes from your QStyle, e.g. 80 pixel if you use "fusion" as style:

You can remove it by a QProxyStyle that overrides this implementation:

#include <QApplication>
#include <QProxyStyle>
#include <QStyleOptionButton>

class CustomStyle: public QProxyStyle {
public:
    using QProxyStyle::QProxyStyle;

protected:
    auto sizeFromContents(
        ContentsType const type,
        QStyleOption const* option,
        QSize const& size,
        QWidget const* widget
    ) const -> QSize override {
        if (type == CT_PushButton) {
            auto newSize = QCommonStyle::sizeFromContents(type, option, size, widget);
            if (auto const btn = qstyleoption_cast<QStyleOptionButton const*>(option)) {
                if (!btn->icon.isNull() && btn->iconSize.height() > 16) {
                    newSize -= QSize(0, 2);
                }
            }
            return newSize;
        }

        return QProxyStyle::sizeFromContents(type, option, size, widget);
    }
};

Set this custom style for your application and your QPushButton's will shrink until they match the text width.

#include <QBoxLayout>
#include <QPushButton>
#include <ranges>

int main(int argc, char** argv) {
    QApplication app(argc, argv);

    // Set your new custom style
    app.setStyle(new CustomStyle()); // or e.g. CustomStyle("fusion")

    auto const layout = new QHBoxLayout;
    for (auto const i: std::ranges::views::iota(0, 19)) {
        layout->addWidget(new QPushButton(QString::number(i)));
    }

    auto widget = QWidget{};
    widget.setLayout(layout);
    widget.show();

    return app.exec();
}

Screenshot of the window

Hundredth answered 1/2 at 19:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.