Как выровнять QPainter drawText вокруг точки, а не прямоугольника?
Я хочу установить выравнивание текста, используя одну точку в качестве координаты, а не прямоугольник.
Насколько я понимаю, QPainter::drawText
позволяет установить выравнивание текста только тогда, когда я передаю координаты как прямоугольник.
Как установить выравнивание текста, если я хочу выровнять текст относительно точки, а не в прямоугольнике?
Ответы
Ответ 1
Когда вы передаете исходную точку для рисования текста, вы эффективно рисуете текст на большом прямоугольнике, который имеет нижний левый угол в данной точке. Так что вам нужно всего лишь предложить подходящий "бесконечный" прямоугольник на основе начальной точки и выбранного выравнивания:
![screenshot]()
// https://github.com/KubaO/stackoverflown/tree/master/questions/alignments-24831484
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
void drawText(QPainter & painter, qreal x, qreal y, Qt::Alignment flags,
const QString & text, QRectF * boundingRect = 0)
{
const qreal size = 32767.0;
QPointF corner(x, y - size);
if (flags & Qt::AlignHCenter) corner.rx() -= size/2.0;
else if (flags & Qt::AlignRight) corner.rx() -= size;
if (flags & Qt::AlignVCenter) corner.ry() += size/2.0;
else if (flags & Qt::AlignTop) corner.ry() += size;
else flags |= Qt::AlignBottom;
QRectF rect{corner.x(), corner.y(), size, size};
painter.drawText(rect, flags, text, boundingRect);
}
void drawText(QPainter & painter, const QPointF & point, Qt::Alignment flags,
const QString & text, QRectF * boundingRect = {})
{
drawText(painter, point.x(), point.y(), flags, text, boundingRect);
}
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QLabel label;
QPicture pic;
pic.setBoundingRect({-100, -100, 200, 200});
QPainter p(&pic);
const QPointF pt;
p.drawEllipse(pt, 3, 3);
p.setFont({"Helvetica", 40});
p.setPen({128, 0, 0, 128});
drawText(p, pt, Qt::AlignBottom, "_LB");
drawText(p, pt, Qt::AlignVCenter, "_LC");
drawText(p, pt, Qt::AlignTop, "_LT");
p.setPen({0, 128, 0, 128});
drawText(p, pt, Qt::AlignBottom | Qt::AlignHCenter, "MB");
drawText(p, pt, Qt::AlignVCenter | Qt::AlignHCenter, "MC");
drawText(p, pt, Qt::AlignTop | Qt::AlignHCenter, "MT");
p.setPen({0, 0, 128, 128});
drawText(p, pt, Qt::AlignBottom | Qt::AlignRight, "RB_");
drawText(p, pt, Qt::AlignVCenter | Qt::AlignRight, "RC_");
drawText(p, pt, Qt::AlignTop | Qt::AlignRight, "RT_");
p.end();
label.setPicture(pic);
label.show();
return a.exec();
}
Ответ 2
Я изменил существующий ответ (fooobar.com/info/494964/...), чтобы сделать его более самодокументируемым и добавить поддержку "выравнивания базовой линии", если вы не указали вертикальное выравнивание.
- Переменные, начинающиеся с
d
являются дельта-векторами. Другие переменные являются абсолютными координатами. - Я не уверен, почему изначально был выбран 32767.0, и я не проверял, меняет ли скорость скорость изменения.
- Я кеширую QFontMetrics.descent(), потому что не знаю, медленно он или нет.
(К сожалению, если я попытаюсь добавить код после маркированного списка, форматирование будет испорчено.)
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
class BaselinePainter: public QPainter {
private:
int calcDescent() const {
QFontMetrics fontMetrics(this->font());
return fontMetrics.descent();
}
int descent = calcDescent();
public:
using QPainter::QPainter;
void setFont(const QFont &f) {
QPainter::setFont(f);
this->descent = calcDescent();
}
void drawTextAligned(qreal x, qreal y, Qt::Alignment align,
const QString & text, QRectF * boundingRect = 0)
{
const qreal dDown = 32767.0;
const qreal dRight = dDown;
qreal left = x;
qreal top = y;
if (align & Qt::AlignHCenter) {
left -= dRight/2.0;
}
else if (align & Qt::AlignRight) {
left -= dRight;
}
if (align & Qt::AlignTop) {
// do nothing
}
else if (align & Qt::AlignVCenter) {
top -= dDown/2.0;
}
else if (align & Qt::AlignBottom) {
top -= dDown;
}
else {
// Emulate baseline alignment (AKA calling drawText() with a point).
// https://code.woboq.org/qt5/qtbase/src/gui/painting/qpainter.cpp.html
// Qt drawText(rect) has a simple "no-shaping" mode (undocumented Qt::TextBypassShaping, will be removed in Qt 6)
// and a complex "glyph-script-shaping" mode.
// My code will only be using drawText() for ASCII characters.
// Each codepath computes font descent differently.
// The simple mode probably constructs one QFontEngine per call, to compute descent.
// The complex mode does weird things.
int dDescentDown = this->descent;
align |= Qt::AlignBottom;
top -= dDown;
top += dDescentDown;
}
QRectF rect{left, top, dRight, dDown};
this->drawText(rect, align, text, boundingRect);
}
void drawTextAligned(const QPointF & point, Qt::Alignment align,
const QString & text, QRectF * boundingRect = {})
{
drawTextAligned(point.x(), point.y(), align, text, boundingRect);
}
};
int main(int argc, char *argv[])
{
QApplication a{argc, argv};
QLabel label;
QPicture pic;
pic.setBoundingRect({-100, -100, 200, 200});
BaselinePainter p(&pic);
const QPointF pt;
p.drawEllipse(pt, 3, 3);
p.setFont({"Helvetica", 16});
p.setPen({128, 0, 0, 128});
p.drawTextAligned(pt, Qt::AlignBottom, "aagg");
p.drawTextAligned(pt, 0, "ga");
p.drawTextAligned(pt, Qt::AlignVCenter, "·");
p.drawTextAligned(pt, Qt::AlignTop, "↑");
p.setPen({0, 128, 0, 128});
p.drawTextAligned(pt, Qt::AlignBottom | Qt::AlignHCenter, "aagg");
p.drawTextAligned(pt, Qt::AlignHCenter, "ga");
p.drawTextAligned(pt, Qt::AlignVCenter | Qt::AlignHCenter, "·");
p.drawTextAligned(pt, Qt::AlignTop | Qt::AlignHCenter, "↑");
p.setPen({0, 0, 128, 128});
p.drawTextAligned(pt, Qt::AlignBottom | Qt::AlignRight, "↘");
p.drawTextAligned(pt, Qt::AlignVCenter | Qt::AlignRight, "→");
p.drawTextAligned(pt, Qt::AlignTop | Qt::AlignRight, "↗");
p.end();
label.setPicture(pic);
label.show();
return a.exec();
}