【问题标题】:QPushButton icon aligned left with text centeredQPushButton 图标左对齐,文本居中
【发布时间】:2017-10-20 20:10:37
【问题描述】:

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

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

文本对齐:左;

但这不是我想要达到的。 那么,您能否告诉我,如果有任何选项可以将图标左对齐并保持文本居中对齐?谢谢你的帮助。

【问题讨论】:

    标签: qt alignment leftalign


    【解决方案1】:

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

    您需要实现QProxyStyle 并覆盖drawControl() 方法。它的大部分代码是直接从默认drawcontrol方法的Qt源代码中复制的(在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);
    }
    

    【讨论】:

      【解决方案2】:

      在不破坏 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

      【讨论】:

      • 这是一个非常有趣的解决方法。我也喜欢pyQt5 用户如何轻松翻译它。当您不是 C 编码员时,即使只有 mybutton.cpp 真正需要翻译,接受的答案也很难阅读。
      【解决方案3】:

      按照 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 中的“Promote widget”功能从 .ui 文件创建 MyButton 的示例:

      ma​​inframe.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>
      

      ma​​inwindow.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;
      };
      

      ma​​inwindow.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;
      }
      

      ma​​in.cpp:

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

      结果:

      • 与 docsteer answer 相比,此解决方案可以将新样式仅应用于项目的某些按钮。而且代码也更小。
      • 与 IGHOR 答案相比,您仍然可以将按钮用作常规 QPushButton(使用 QPushButton::setText),您无需保留对模型 QLabel 的引用来更改按钮的文本。

      【讨论】:

      • 你好,我正在尝试实现这个,但是在推广小部件之后,没有任何改变:(我应该如何实现这个?
      • 你调用 setPixmap 了吗?是否调用了paintEvent(尝试在此处设置断点)
      • @km2442:添加了一个通过 .ui 使用的示例
      • 明天会在家里检查这个:)
      • 感谢您的回答。你的解决方案是最好的。它不会破坏整个样式表,并且可以应用于选定的 PushButtins :)
      猜你喜欢
      • 2019-10-01
      • 1970-01-01
      • 2019-04-24
      • 1970-01-01
      • 1970-01-01
      • 2017-06-29
      • 1970-01-01
      • 2017-05-21
      • 1970-01-01
      相关资源
      最近更新 更多