Qt:如何在保持对齐的前提下使用不同字体绘制文本
Great question! The built-in QPainter::drawText method works perfectly for single-font text alignment, but when you need mixed fonts while preserving that same alignment behavior, you’ve got two solid approaches to choose from. Let’s break them down:
Method 1: Manual Position Calculation (Ideal for Simple Single-Line Text)
This approach gives you full control over every text segment’s position, ensuring the entire block aligns within your target rectangle just like the original drawText method would. Here’s how to implement it:
Step-by-Step Logic
- Split your text into segments, each paired with its desired font.
- Calculate the size of each segment using
QFontMetricsF. - Compute the total width of all segments combined, plus the maximum height across all fonts (to handle vertical alignment correctly).
- Use the original
QTextOptionalignment to calculate the baseline starting position for the entire text block. - Draw each segment sequentially, updating the x-position after each draw to keep text continuous.
Code Example
#include <QPainter> #include <QFontMetricsF> #include <QList> #include <QPair> void drawMultiFontText(QPainter *painter, const QRectF &rect, const QList<QPair<QString, QFont>> &textFragments, const QTextOption &option = QTextOption()) { if (textFragments.isEmpty() || !painter) return; // Calculate metrics for each text segment QList<QFontMetricsF> metricsList; qreal totalWidth = 0; qreal maxHeight = 0; for (const auto &fragment : textFragments) { QFontMetricsF metrics(fragment.second); metricsList.append(metrics); totalWidth += metrics.horizontalAdvance(fragment.first); maxHeight = qMax(maxHeight, metrics.height()); } // Calculate starting baseline position based on alignment qreal baseX = rect.left(); qreal baseY = rect.top(); // Handle horizontal alignment Qt::Alignment hAlign = option.alignment() & Qt::AlignHorizontal_Mask; switch (hAlign) { case Qt::AlignHCenter: baseX = rect.center().x() - totalWidth / 2; break; case Qt::AlignRight: baseX = rect.right() - totalWidth; break; default: // Qt::AlignLeft baseX = rect.left(); break; } // Handle vertical alignment (baseline positioning is key here) Qt::Alignment vAlign = option.alignment() & Qt::AlignVertical_Mask; const auto &firstMetrics = metricsList.first(); switch (vAlign) { case Qt::AlignVCenter: baseY = rect.center().y() + (maxHeight / 2) - firstMetrics.descent(); break; case Qt::AlignBottom: baseY = rect.bottom() - firstMetrics.descent(); break; default: // Qt::AlignTop baseY = rect.top() + firstMetrics.ascent(); break; } // Draw each text segment painter->save(); qreal currentX = baseX; for (int i = 0; i < textFragments.size(); ++i) { const auto &fragment = textFragments[i]; const auto &metrics = metricsList[i]; painter->setFont(fragment.second); painter->drawText(QPointF(currentX, baseY), fragment.first); currentX += metrics.horizontalAdvance(fragment.first); } painter->restore(); }
Usage in paintEvent
void MyWidget::paintEvent(QPaintEvent *) { QPainter painter(this); QRectF drawRect(50, 50, 300, 100); painter.drawRect(drawRect); // Draw rectangle to visualize alignment // Define mixed-font text segments QList<QPair<QString, QFont>> fragments; fragments.append(qMakePair(QString("Hello "), QFont("Arial", 12))); fragments.append(qMakePair(QString("World"), QFont("Times New Roman", 12, QFont::Bold))); // Use center alignment (matches your original use case) QTextOption option(Qt::AlignCenter); drawMultiFontText(&painter, drawRect, fragments, option); }
Method 2: Use QTextDocument (Best for Complex Formatting/Multi-Line Text)
If you need multi-line text, additional formatting (like colors or bold), or just cleaner code, QTextDocument is the way to go. It natively supports HTML-style formatting and honors QTextOption alignment settings.
Code Example
#include <QPainter> #include <QTextDocument> void drawRichTextWithAlignment(QPainter *painter, const QRectF &rect, const QString &richText, const QTextOption &option = QTextOption()) { QTextDocument doc; doc.setHtml(richText); doc.setTextOption(option); doc.setPageSize(rect.size()); painter->save(); painter->translate(rect.topLeft()); doc.drawContents(painter); painter->restore(); }
Usage in paintEvent
void MyWidget::paintEvent(QPaintEvent *) { QPainter painter(this); QRectF drawRect(50, 50, 300, 100); painter.drawRect(drawRect); // Define rich text with mixed fonts QString richText = "<span style='font-family: Arial; font-size: 12pt;'>Hello </span>" "<span style='font-family: Times New Roman; font-size: 12pt; font-weight: bold;'>World</span>"; QTextOption option(Qt::AlignCenter); drawRichTextWithAlignment(&painter, drawRect, richText, option); }
Which Method Should You Choose?
- Manual Calculation: Opt for this if you’re working with simple single-line text, need pixel-perfect control, or want minimal performance overhead.
- QTextDocument: Use this for multi-line text, complex formatting, or when you want to avoid the boilerplate of manual position math.
内容的提问来源于stack exchange,提问作者A. Kviat

