【问题标题】:Making a custom button in Qt5 with paintEvent使用paintEvent在Qt5中制作自定义按钮
【发布时间】:2021-08-19 23:27:24
【问题描述】:

我想制作一个在每个平台上看起来都一样的自定义按钮,而不是继承 QPushButton 或 QAbstractButton 而是 QWidget。这个按钮应该包含一个像素图作为图标和一个文本加上一个可选的字幕。到目前为止,我已经完成了这项工作,但问题是小部件的宽度。小部件本身被剪裁了一些宽度。

我的问题是,当我用画家画了很多东西时,父小部件如何知道小部件的新尺寸是多少?新大小可以是任何内容,具体取决于文本或像素图。

编辑 1

尺寸提示给出了所需尺寸的估计值

QSize Button::sizeHint() const {
    QFont nameFont(font());
    QFont descriptionFont(font());
    QFontMetrics nameFontMetric(nameFont);
    QFontMetrics descriptionFontMetric(descriptionFont);
    nameFont.setBold(true);

    QSize result(0, 0);
    result.setWidth(getPixmap().width() + 5
            + nameFontMetric.horizontalAdvance(' ')
            + qMax(descriptionFontMetric.boundingRect(m_ptr->description).width(), nameFontMetric.boundingRect(m_ptr->name).width()));
    result.setHeight(qMax(getPixmap().height(),
            nameFontMetric.boundingRect(m_ptr->name).height() + descriptionFontMetric.boundingRect(m_ptr->description).height()));

    return result;
}

paint方法是从paintEvent方法中调用的,在该方法中使用QPainter(this)创建一个painter

void Button::paint(QPainter *painter)
{
    QRect drawingRect;

    QFont nameFont(painter->font());
    QFont descriptionFont(painter->font());
    nameFont.setBold(true);
    descriptionFont.setPointSize(nameFont.pointSize() / 1.5);

    QFontMetrics nameFontMetric(nameFont, painter->device());
    QFontMetrics descriptionFontMetric(descriptionFont, painter->device());

    auto nameFontRectInitial = nameFontMetric.boundingRect(m_ptr->name + " ");
    auto nameFontRect = nameFontMetric.boundingRect(nameFontRectInitial, 0, m_ptr->name + " ");

    auto descriptionFontRectInitial = descriptionFontMetric.boundingRect(m_ptr->description + " ");
    auto descriptionFontRect = descriptionFontMetric.boundingRect(descriptionFontRectInitial, 0, m_ptr->description + " ");

    // Get rect of widget so far
    auto r = rect();

    auto labelRect       = r;
    auto descriptionRect = r;

    labelRect       = labelRect.translated(getPixmap().width() + 5, 0);
    descriptionRect = descriptionRect.translated(getPixmap().width() + 5, 0);

    labelRect.setSize(nameFontRect.size());
    descriptionRect.setSize(descriptionFontRect.size());

    descriptionRect.translate(0, labelRect.height());

    //setFixedSize((labelRect | descriptionRect).size());
    drawingRect = labelRect | descriptionRect | getPixmap().rect();
    drawingRect.adjust(0,0,5,0);
    resize(drawingRect.size());

    // Draw background
    painter->fillRect(drawingRect, palette().color(QPalette::Button));

    // Draw name
    painter->setFont(nameFont);
    painter->setPen(palette().color(QPalette::Text));
    painter->drawText(labelRect, Qt::AlignTop | Qt::TextSingleLine, m_ptr->name);

    // Draw description
    painter->setFont(descriptionFont);
    painter->setPen(palette().color(QPalette::Text));
    painter->drawText(descriptionRect, Qt::AlignTop, m_ptr->description);

    // Draw border
    painter->setPen(palette().color(QPalette::Midlight));
    painter->drawRect(drawingRect.adjusted(0, 0, -1, -1));

    painter->drawPixmap(m_ptr->pixmapOrigin, m_ptr->pixmap);

到目前为止,我使用这种方法可以正常工作,但另一个问题是在我想要拉伸该小部件的布局中调整大小,以便一行中的所有小部件具有相同的宽度。好像不是这样,我不知道该怎么做。

编辑 2:

我通过使用 updateGeometry() 来告诉布局系统尺寸提示发生了变化(这就是我理解文档的方式)。这样做的缺点是我必须在内部保持大小并检查它是否在paintEvent内部发生了变化。

void Button::paint(QPainter *painter)
{
    QRect drawingRect;

    QFont nameFont(painter->font());
    QFont descriptionFont(painter->font());
    nameFont.setBold(true);
    descriptionFont.setPointSize(nameFont.pointSize() / 1.5);

    QFontMetrics nameFontMetric(nameFont, painter->device());
    QFontMetrics descriptionFontMetric(descriptionFont, painter->device());

    auto nameFontRectInitial = nameFontMetric.boundingRect(m_ptr->name + " ");
    auto nameFontRect = nameFontMetric.boundingRect(nameFontRectInitial, 0, m_ptr->name + " ");

    auto descriptionFontRectInitial = descriptionFontMetric.boundingRect(m_ptr->description + " ");
    auto descriptionFontRect = descriptionFontMetric.boundingRect(descriptionFontRectInitial, 0, m_ptr->description + " ");

    // Get rect of widget so far
    auto r = rect();

    auto labelRect       = r;
    auto descriptionRect = r;

    labelRect       = labelRect.translated(getPixmap().width() + 5, 0);
    descriptionRect = descriptionRect.translated(getPixmap().width() + 5, 0);

    labelRect.setSize(nameFontRect.size());
    descriptionRect.setSize(descriptionFontRect.size());

    descriptionRect.translate(0, labelRect.height());

    drawingRect = labelRect | descriptionRect | getPixmap().rect();
    drawingRect.adjust(0,0,5,0);

    if(drawingRect.width() != m_ptr->w || drawingRect.height() != m_ptr->h) {
        m_ptr->w = drawingRect.width();
        m_ptr->h = drawingRect.height();
        updateGeometry();
    }

    // Draw background
    painter->fillRect(r, palette().color(QPalette::Button));

    // Draw name
    painter->setFont(nameFont);
    painter->setPen(palette().color(QPalette::Text));
    painter->drawText(labelRect, Qt::AlignTop | Qt::TextSingleLine, m_ptr->name);

    // Draw description
    painter->setFont(descriptionFont);
    painter->setPen(palette().color(QPalette::Text));
    painter->drawText(descriptionRect, Qt::AlignTop, m_ptr->description);

}

【问题讨论】:

标签: c++ qt user-interface qt5


【解决方案1】:

布局使用sizeHint 来了解小部件需要多少空间,并根据拉伸因子分配所有额外空间,但只要您的按钮返回不同的sizeHints(取决于它们包含的文本)即使具有相同的拉伸因素它们最终具有不同的宽度。因此,为同一行中的按钮实现相同宽度的最简单方法是让它们返回相同的sizeHint

例如,您可以创建帮助函数makeSameWidth,它计算按钮组的最大按钮宽度,并强制它们在sizeHint 中返回此宽度。您可以在将布局应用到小部件后应用该功能。


void makeSameWidth(QList<Button*> buttons, QLayout* layout) {
  int width = 0;
  for(Button* button: buttons) {
      width = qMax(button.sizeHint().width(), width);
  }
  for(Button* button: buttons) {
      button->setSizeHintWidth(width);
  }
  layout->invalidate();
}

class Button {
...

   Button (...) : m_width(-1) ...

   void setSizeHintWidth(int width) {
      m_width = width;
   }

   int m_width;
}

QSize Button::sizeHint() const {
   ...
   int width;
   if (m_width < 0) {
     width = getPixmap().width() + 5
            + nameFontMetric.horizontalAdvance(' ')
            + qMax(descriptionFontMetric.boundingRect(m_ptr->description).width(), nameFontMetric.boundingRect(m_ptr->name).width());
   } else {
     width = m_width;
   }
   int height = qMax(getPixmap().height(),
            nameFontMetric.boundingRect(m_ptr->name).height() + descriptionFontMetric.boundingRect(m_ptr->description).height());
   return QSizeHint(width, heigth);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    makeSameWidth({ui->button1, ui->button2, ui->button3}, ui->buttonsLayout);
    ...
}

【讨论】:

  • 感谢您的回复。到目前为止,我通过使用 updateGeometry() 完成了这项工作。我不知道这是否是 Qt 应该被告知小部件大小变化的预期方式。我认为 makeSameWidth 需要我知道每个按钮,但这是我想要避免的。
猜你喜欢
  • 2016-09-23
  • 1970-01-01
  • 2010-11-23
  • 1970-01-01
  • 2018-09-06
  • 1970-01-01
  • 1970-01-01
  • 2022-11-29
  • 2019-01-21
相关资源
最近更新 更多