Значок QPushButton выравнивается влево с центром в центре

В моем приложении Qt 5.7.1 у меня есть несколько кнопок, я хочу выровнять значки кнопок с левым и центральным текстом, но для этого нет конструктора.

Стандартный альгин

Я могу выровнять значок и текст слева, добавив в таблицу стилей кнопки этот код:

выравнивания текста: слева;

Выровнять влево

Но это не то, чего я хочу достичь. Итак, не могли бы вы рассказать мне: если есть возможность выровнять значок слева и сохранить выравнивание по центру? Благодарим вас за помощь.

Ответ 1

Просто специализируйте QPushButton и переопределите paintEvent и sizeHint, как предложено cbuchart здесь. Затем используйте его как обычный QPushButton.

MyButton декларация и реализация:

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
    }
}

Пример использования:

Вот пример, в котором функция "Продвигать виджет" в Qt Designer использовалась для создания MyButton из .ui файлов:

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();
}

Результаты в:

  • По сравнению с ответом docsteer это решение позволяет применять новый стиль только к некоторым кнопкам вашего проекта. И код также меньше.
  • По сравнению с ответом IGHOR вы все равно можете использовать кнопку в качестве обычного QPushButton (используя QPushButton::setText), вам не нужно сохранять ссылку на макет QLabel, чтобы изменить текст кнопки.

Ответ 2

Чтобы получить этот уровень контроля, вам нужно будет написать код, чтобы переопределить стиль вашей платформы. Это лучше всего сделать с помощью QProxyStyle. В этом случае мы ищем, когда стилю предлагается нарисовать CE_PushButtonLabel (метка содержит значок, и они жестко закодированы в Qt для выравнивания вместе).

Вам нужно реализовать QProxyStyle и переопределить drawControl ( ) метод. Код для большей части его копируется непосредственно из исходного кода Qt для стандартного метода drawcontrol (в qcommonstyle.cpp) - так что, хотя он выглядит длительный, это в основном делает то, что Qt уже делает. Я помещал дополнительные/****/метки вокруг разделов, которые я модифицировал. Это не будет отображаться в Qt Designer, но будет работать во время выполнения.

Конечный результат (отображается на mac, должен соответствовать платформе, на которой вы находитесь)

кнопка с текстом слева

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);
}

Ответ 3

Меньше кода, не нарушая стиль пользовательского интерфейса

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);

Не забудьте отправить сигналы setText в textLabel вместо pushButton