Applying a Qt style sheet to a QPushButton and a QToolButton

4.8k Views Asked by At

I'm trying to create a widget which will have similar behaviour to a QComboBox, but with different behaviours depending on whether the user clicks on the arrow or elsewhere on the widget. For starters, I'm modelling the widget itself as a wide QPushButton with a QToolButton superimposed on it. Once I have this looking as I want it to, I'll add a QListView to provide the drop-down menu and make it appear whenever the unit presses the QToolButton (but not the QPushButton). My basic test code so far looks like this:

#include <QtWidgets/QApplication>

#include <QDialog>
#include <QPushButton>
#include <QToolButton>
#include <QPushButton>
#include <QGridLayout>

class StylesheetTest : public QDialog
{
    Q_OBJECT
public:
    StylesheetTest(QWidget * parent = nullptr) : QDialog(parent)
    {
        // Create the widgets and layouts
        itsLayout = new QGridLayout;
        itsMainButton = new QPushButton;
        itsDropdownButton = new QToolButton;

        // Place the widgets in the layout:
        itsLayout -> addWidget(itsMainButton, 0, 0, 1, 2);
        itsLayout -> addWidget(itsDropdownButton, 0, 1, 1, 1);
        itsLayout -> setSpacing(0);
        itsLayout -> setColumnStretch(0, 1);
        itsMainButton -> setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
        itsDropdownButton -> setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        itsDropdownButton -> setArrowType(Qt::DownArrow);

        // Formally appoint the layout
        setLayout(itsLayout);

        // Now the stylesheet settings
        setStyleSheet("QToolButton { border: none; } ");

        // Give the button a label
        itsMainButton -> setText("An extraordinarily long label for a button");
    }

private:
    QPushButton *itsMainButton;
    QToolButton *itsDropdownButton;
    QGridLayout *itsLayout;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    StylesheetTest t;
    t.show();
    return a.exec();
}

#include "main.moc"

This gets me most of the way there; the resulting button looks like this:

First attempt

It looks very much like a QComboBox, but I get the independent behaviours of the QPushButton and the QToolButton, just as I want. However, I'd like to make it even better:

  • I'd like to set a bit of a margin on the right-hand side of the QPushButton, so that the text doesn't overlap the arrow
  • I'd like the arrow itself to be a bit smaller

Everything I know about style sheets (which admittedly isn't much) tells me that to achieve this I need something resembling

    // Now the stylesheet settings
    setStyleSheet("QPushButton { padding-right: 20px; }");
    setStyleSheet("QToolButton { border: none; } "
                  "QToolButton::down-arrow { width: 8px; height: 8px; }" );

but in fact the result of this is a mess:

Second attempt - much worse

Further experimentation shows that trying to set the 'padding-right' parameter of the QPushButton to any value at all leads to its vertical height getting squeezed in this way, while all attempts to modify the size of the down-arrow fail. I'm obviously doing something wrong, but I can't see what. Any hints would be gratefully received.

UPDATE

I've decided that a better approach is, rather than using a QToolButton in front of a QPushButton and having manually to implement the QListView behaviour, to put a QPushButton in front of a QComboBox leacing just the latter's arrow exposed. Like this:

#include <QtWidgets/QApplication>

#include <QDialog>
#include <QPushButton>
#include <QComboBox>
#include <QGridLayout>

class StylesheetTest : public QDialog
{
    Q_OBJECT
public:
    StylesheetTest(QWidget * parent = nullptr) : QDialog(parent)
    {
        // Create the widgets and layouts
        itsLayout = new QGridLayout;
        itsMainButton = new QPushButton;
        itsHiddenComboBox = new QComboBox;

        // Place the widgets in the layout:
        itsLayout -> addWidget(itsHiddenComboBox, 0, 0, 1, 3);
        itsLayout -> addWidget(itsMainButton,     0, 0, 1, 2);
        itsLayout -> setSpacing(0);
        itsLayout -> setColumnStretch(0, 1);
        itsLayout -> setColumnMinimumWidth(2, 20);
        itsMainButton -> setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
        itsHiddenComboBox -> setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);

        // Formally appoint the layout
        setLayout(itsLayout);

        // Now the stylesheet settings
        setStyleSheet("QPushButton { border: none; padding-left: 10px; } ");
        itsMainButton -> setText("An extraordinarily long label for a button");
    }

private:
    QPushButton *itsMainButton;
    QComboBox   *itsHiddenComboBox;
    QGridLayout *itsLayout;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    StylesheetTest t;
    t.show();
    return a.exec();
}

#include "main.moc"

The one shortcoming of this solution is that, having eliminated the border from the QPushButton, it no longer responds visibly to being clicked (even though the usual signals are sent). This has been documented elsewhere: see here. As it happens I can cope with this, as my ultimate intention is for pressing the QPushButton to cause a change of the cursor and this will make it obvious enough to the user that the button has been successfully clicked.

0

There are 0 best solutions below