QPushButton icon aligned left with text centered

后端 未结 3 1156
悲&欢浪女
悲&欢浪女 2020-12-17 14:21

In my Qt 5.7.1 application I\'ve some buttons, I want to align button\'s icons to left and centre text, but there is no option in designer to do this.

I can

相关标签:
3条回答
  • 2020-12-17 14:37

    Simply specialize QPushButton and override paintEvent and sizeHint, as proposed by cbuchart here. Then use it as a regular QPushButton.

    MyButton declaration and implementation:

    mybutton.h:

    #pragma once
    
    #include <QPushButton>
    
    class MyButton : public QPushButton
    {
    public:
        explicit MyButton(QWidget* parent = nullptr);
        virtual ~MyButton();
    
        void setPixmap(const QPixmap& pixmap);
    
        virtual QSize sizeHint() const override;
    
    protected:
        virtual void paintEvent(QPaintEvent* e) override;
    
    private:
        QPixmap m_pixmap;
    };
    

    mybutton.cpp:

    #include "mybutton.h"
    
    #include <QPainter>
    
    MyButton::MyButton(QWidget* parent) : QPushButton(parent)
    {
    }
    
    MyButton::~MyButton()
    {
    }
    
    QSize MyButton::sizeHint() const
    {
        const auto parentHint = QPushButton::sizeHint();
        // add margins here if needed
        return QSize(parentHint.width() + m_pixmap.width(), std::max(parentHint.height(), m_pixmap.height()));
    }
    
    void MyButton::setPixmap(const QPixmap& pixmap)
    {
        m_pixmap = pixmap;
    }
    
    void MyButton::paintEvent(QPaintEvent* e)
    {
        QPushButton::paintEvent(e);
    
        if (!m_pixmap.isNull())
        {
            const int y = (height() - m_pixmap.height()) / 2; // add margin if needed
            QPainter painter(this);
            painter.drawPixmap(5, y, m_pixmap); // hardcoded horizontal margin
        }
    }
    

    Exemple of usage:

    Here is an example where "Promote widget" feature in Qt Designer was used to create MyButton from .ui files:

    mainframe.ui:

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>400</width>
        <height>300</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralWidget">
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="MyButton" name="button1">
          <property name="text">
           <string>Button</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="MyButton" name="button2">
          <property name="text">
           <string>Other button</string>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menuBar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>400</width>
         <height>20</height>
        </rect>
       </property>
      </widget>
      <widget class="QToolBar" name="mainToolBar">
       <attribute name="toolBarArea">
        <enum>TopToolBarArea</enum>
       </attribute>
       <attribute name="toolBarBreak">
        <bool>false</bool>
       </attribute>
      </widget>
      <widget class="QStatusBar" name="statusBar"/>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <customwidgets>
      <customwidget>
       <class>MyButton</class>
       <extends>QPushButton</extends>
       <header>mybutton.h</header>
      </customwidget>
     </customwidgets>
     <resources/>
     <connections/>
    </ui>
    

    mainwindow.h:

    #pragma once
    
    #include <QMainWindow>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
    };
    

    mainwindow.cpp:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QStyle>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        QStyle* style = qApp->style();
        // set buttons pixmaps:
        ui->button1->setPixmap( style->standardPixmap(QStyle::SP_ComputerIcon) );
        ui->button2->setPixmap( style->standardPixmap(QStyle::SP_TrashIcon) );
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    

    main.cpp:

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    

    Results in:

    • Compared to docsteer answer, this solution makes it possible to apply the new style to only some buttons of your project. And the code is also smaller.
    • Compared to IGHOR answer, you can still use the button as a regular QPushButton (using QPushButton::setText), you don't need to keep a reference to a mockup QLabel to change button's text.
    0 讨论(0)
  • 2020-12-17 14:50

    To get this level of control you will need to write some code to override the style for your platform. This is best done using a QProxyStyle. In this case we are looking for when the style is asked to draw a CE_PushButtonLabel (the label includes the icon, and they are hard coded in Qt to be aligned together).

    You need to implement a QProxyStyle and override the drawControl() method. The code for the bulk of it is copied directly from the Qt source code for the default drawcontrol method (in qcommonstyle.cpp) - so although it looks lengthy, it is mostly doing what Qt already does. I put extra /****/ markers around the sections I modified. This won't show up in Qt Designer, but will work at runtime.

    Final result (shown on mac, should match platform you're on)

    main.cpp:

    QApplication a(argc, argv);
    a.setStyle(new MyStyle);
    ...
    

    mystyle.h

     class MyStyle : public QProxyStyle
     {
     public:
    
     virtual void drawControl(ControlElement element, const QStyleOption *opt,
                   QPainter *p, const QWidget *widget = Q_NULLPTR) const;
     };
    

    mystyle.cpp

    // Copied from Qt source code..
    static QWindow *qt_getWindow(const QWidget *widget)
    {
        return widget ? widget->window()->windowHandle() : 0;
    }
    
    void MyStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *widget) const
    {
        if(element==CE_PushButtonLabel)
        {
            if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(opt))
            {
                    QRect textRect = button->rect;
                    uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
                    if (!proxy()->styleHint(SH_UnderlineShortcut, button, widget))
                        tf |= Qt::TextHideMnemonic;
    
                    if (!button->icon.isNull()) {
                        QRect iconRect;
                        QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
                        if (mode == QIcon::Normal && button->state & State_HasFocus)
                            mode = QIcon::Active;
                        QIcon::State state = QIcon::Off;
                        if (button->state & State_On)
                            state = QIcon::On;
    
                        QPixmap pixmap = button->icon.pixmap(qt_getWindow(widget), button->iconSize, mode, state);
    
                        int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
                        int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
                        int labelWidth = pixmapWidth;
                        int labelHeight = pixmapHeight;
                        int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint()
                        int textWidth = button->fontMetrics.boundingRect(opt->rect, tf, button->text).width();
                        if (!button->text.isEmpty())
                            labelWidth += (textWidth + iconSpacing);
    
                        /*************************************************************/
                        // Make the icon rectangle always be 10px in from the left edge
                        /*************************************************************/
                        iconRect = QRect(10,
                                         textRect.y() + (textRect.height() - labelHeight) / 2,
                                         pixmapWidth, pixmapHeight);
    
                        iconRect = visualRect(button->direction, textRect, iconRect);
    
                        /***********************************/
                        // Always horizontal align the text
                        /***********************************/
                        tf |= Qt::AlignHCenter;
    
    
                        if (button->state & (State_On | State_Sunken))
                            iconRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget),
                                               proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget));
                        p->drawPixmap(iconRect, pixmap);
                    } else {
                        tf |= Qt::AlignHCenter;
                    }
                    if (button->state & (State_On | State_Sunken))
                        textRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget),
                                     proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget));
    
                    if (button->features & QStyleOptionButton::HasMenu) {
                        int indicatorSize = proxy()->pixelMetric(PM_MenuButtonIndicator, button, widget);
                        if (button->direction == Qt::LeftToRight)
                            textRect = textRect.adjusted(0, 0, -indicatorSize, 0);
                        else
                            textRect = textRect.adjusted(indicatorSize, 0, 0, 0);
                    }
                    proxy()->drawItemText(p, textRect, tf, button->palette, (button->state & State_Enabled),
                                 button->text, QPalette::ButtonText);
                }
                return;
        }
    
        // For all other controls, draw the default
        QProxyStyle::drawControl(element, opt, p, widget);
    }
    
    0 讨论(0)
  • 2020-12-17 14:50

    Less code way without breaking UI style

    pushButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion));
    pushButton->setStyleSheet("text-align:left;");
    pushButton->setLayout(new QGridLayout);
    
    QLabel* textLabel = new QLabel("Hello world!");
    textLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); // or center
    textLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
    
    pushButton->layout()->addWidget(textLabel);
    

    Remember to send setText signals to textLabel instead of pushButton

    0 讨论(0)
提交回复
热议问题