QPushButton图标左对齐,文本居中

时间:2017-05-20 22:19:30

标签: qt alignment leftalign

在我的Qt 5.7.1应用程序中,我有一些按钮,我想将按钮的图标对齐到左侧和中间文本,但设计师没有选择这样做。

Standard algin

我可以通过向代码添加按钮样式表来对齐图标和文本:

  

文本对齐:左;

Align left

但这不是我想要实现的目标。 那么,请你告诉我,如果有任何选项可以将图标左对齐,并保持文本对齐中心?谢谢你的帮助。

3 个答案:

答案 0 :(得分:10)

要获得此级别的控制,您需要编写一些代码来覆盖平台的样式。最好使用QProxyStyle完成此操作。在这种情况下,我们正在寻找何时要求样式绘制CE_PushButtonLabel(标签包含图标,并且它们在Qt中被硬编码以对齐在一起)。

您需要实施QProxyStyle并覆盖drawControl()方法。它的大部分代码直接从Qt源代码中复制为默认的drawcontrol方法(在qcommonstyle.cpp中) - 所以虽然它看起来很冗长,但它主要是做Qt已经做的。我在我修改的部分周围添加了额外的/ **** /标记。这不会出现在Qt Designer中,但会在运行时运行。

最终结果(显示在mac上,应该与您正在使用的平台相匹配)

button with text left

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

答案 1 :(得分:5)

根据cbuchart here的建议,简单地专注QPushButton并覆盖paintEventsizeHint。然后将其用作常规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中的“推广小部件”功能从.ui文件创建MyButton的示例:

<强> 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 :(得分:3)

在不破坏UI风格的情况下减少代码方式

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