Qt C++ How to Scale Text to Width w/QPainter - c++

I am looking for an effective way to use the drawText method of QPainter according to a QRect. For example if I were to have a QRect as the origin with a size of 50x50: QRect(0, 0, 50, 50); and wanted to fit text so it fits from left to right. Here are a few of the examples that I currently have, but to no extent can I get them to move fluently.
Example 1:
font.setStyleHint(QFont::AnyStyle, QFont::PreferAntialias);
painter.setRenderHint(QPainter::TextAntialiasing);
QStyleOptionGraphicsItem option;
qreal lod = option.levelOfDetailFromTransform(painter.worldTransform());
QRectF r = textRect;
QFont f = font;
qreal aspectRatio = painter.fontMetrics().lineSpacing() / painter.fontMetrics().averageCharWidth();
int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (m_text.length() * 3)) * aspectRatio;
f.setPixelSize(pixelsize);
int flags = Qt::AlignCenter | Qt::TextDontClip | Qt::TextWordWrap;
if ((pixelsize * lod) < 13)
flags |= Qt::TextWrapAnywhere;
QFontMetricsF fm(f);
QRectF tbr = fm.boundingRect(r, flags, m_text);
pixelsize = f.pixelSize() * qMin(r.width() * 0.65 / tbr.width(), r.height() * 0.65 / tbr.height());
f.setPixelSize(pixelsize);
painter.setFont(f);
painter.drawText(r, flags, m_text);
However this leaves me with jittery text that is not smooth.
Example 2:
float factor = (rectangle.width() / painter.fontMetrics().width(m_text))/2;
if ((factor < 1) || (factor > 1.25))
{
font.setPointSizeF(font.pointSizeF()*factor);
painter.setFont(font);
}
QPainterPath textPath;
textPath.addText(textRect.topLeft(), font, m_text);
painter.drawPath(textPath);
Example 3:
QImage image(size(), QImage::Format_ARGB32_Premultiplied);
{
QPainter p(&image);
p.setRenderHint(QPainter::TextAntialiasing);
QFont font;
font.setFamily("Roboto medium");
font.setPointSize((double)scalar/10);
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
p.setPen(Qt::black);
p.setFont(font);
p.drawText(rect(), Qt::AlignLeft , m_text);
}
painter.drawImage(rectangle, image);
Honestly I am using these method to make circular buttons that can either have text, text+icon, or just an icon. I would like them to be scalable. Any method that can do as such would be greatly appreciated. Thank you very much.

Related

How can I modify my Qt PaintEvent code to scale a displayed Pixmap relative to the mouse pointer

I have a (so far) fairly simple QWidget (size of say 300 * 300) that I am using to display a large QPixmap (e.g 5184 * 3456). The key parts of the code read as follows:
void DSSImageWidget::resizeEvent(QResizeEvent* e)
{
QSize sz = e->size();
qreal hScale = (qreal)sz.width() / (m_pixmap.width() + 4);
qreal vScale = (qreal)sz.height() / (m_pixmap.height() + 4);
m_scale = std::min(hScale, vScale);
update();
Inherited::resizeEvent(e);
}
void DSSImageWidget::paintEvent(QPaintEvent* event)
{
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
//QPointF whereScaled = m_where / (m_zoom * m_scale);
//qDebug() << "m_where:" << m_where.x() << m_where.y();
//qDebug() << whereScaled.x() << " " << whereScaled.y();
//painter.translate(-m_where);
painter.scale(m_zoom*m_scale, m_zoom*m_scale);
//painter.translate(m_where);
painter.drawPixmap(QPointF(0.0, 0.0), m_pixmap);
painter.setPen(QPen(QColor(255, 0, 0, alpha), 0.25, Qt::SolidLine, Qt::FlatCap, Qt::BevelJoin));
painter.setBrush(Qt::NoBrush);
painter.drawRect(QRectF(0, 0, m_pixmap.width(), m_pixmap.height()).adjusted(-2, -2, 2, 2));
painter.end();
}
void DSSImageWidget::wheelEvent(QWheelEvent* e)
{
qreal degrees = -e->angleDelta().y() / 8.0;
//
// If zooming in and zoom factor is currently 1.0
// then remember mouse location
//
if ((degrees > 0) && (m_zoom == 1.0))
{
m_where = e->position();
}
qreal steps = degrees / 60.0;
qreal factor = m_zoom * std::pow(1.125, steps);
m_zoom = std::clamp(factor, 1.0, 5.0);
update();
Inherited::wheelEvent(e);
}
This scales the pixmap about the window origin which is a good start, but it's not what I want. I want it to scale the pixmap so that the part of the image under the mouse pointer remains where it is and the image expands around that point.
I've played all sorts of tunes with the painter.translate() calls and the location I use to actually draw the pixmap, but I've so far failed with flying colours :).
Please could someone who knows how this stuff actually works put me out of my misery and tell me how I achieve my objective here?
Thank you
After quite some grappling with the code I've finally solved this. Given that it wasn't dead simple, I'm posting the relevant portions of the code here in the hope it will assist someone else.
From the header:
typedef QWidget
Inherited;
private:
bool initialised;
qreal m_scale, m_zoom;
QPointF m_origin;
QPixmap & m_pixmap;
QPointF m_pointInPixmap;
inline bool mouseOverImage(QPointF loc)
{
qreal x = loc.x(), y = loc.y(), ox = m_origin.x(), oy = m_origin.y();
return (
(x >= ox) &&
(x <= ox + (m_pixmap.width() * m_scale)) &&
(y >= oy) &&
(y <= oy + (m_pixmap.height() * m_scale)));
};
And the source code:
DSSImageWidget::DSSImageWidget(QPixmap& p, QWidget* parent)
: QWidget(parent),
initialised(false),
m_scale(1.0),
m_zoom(1.0),
m_origin(0.0, 0.0),
m_pixmap(p),
m_pointInPixmap((m_pixmap.width() / 2), (m_pixmap.height() / 2))
{
setAttribute(Qt::WA_MouseTracking);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
void DSSImageWidget::resizeEvent(QResizeEvent* e)
{
QSize sz = e->size();
qreal pixWidth = m_pixmap.width();
qreal pixHeight = m_pixmap.height();
qreal hScale = (qreal)sz.width() / pixWidth;
qreal vScale = (qreal)sz.height() / pixHeight;
m_scale = std::min(hScale, vScale);
qreal xoffset = 0.0, yoffset = 0.0;
if ((pixWidth * m_scale) < sz.width())
{
xoffset = (sz.width() - (pixWidth * m_scale)) / 2.0;
}
if ((pixHeight * m_scale) < sz.height())
{
yoffset = (sz.height() - (pixHeight * m_scale)) / 2.0;
}
m_origin = QPointF(xoffset, yoffset);
update();
Inherited::resizeEvent(e);
}
void DSSImageWidget::paintEvent(QPaintEvent* event)
{
QPainter painter;
qDebug() << "pointInPixmap: " << m_pointInPixmap.x() << m_pointInPixmap.y();
//
// Now calcualate the rectangle we're interested in
//
qreal width = m_pixmap.width();
qreal height = m_pixmap.height();
qreal x = m_pointInPixmap.x();
qreal y = m_pointInPixmap.y();
QRectF sourceRect(
x - (x / m_zoom),
y - (y / m_zoom),
width / m_zoom,
height / m_zoom
);
qDebug() << "sourceRect: " << sourceRect.x() << sourceRect.y() << sourceRect.width() << sourceRect.height();
//
// Now calculate the rectangle that is the intersection of this rectangle and the pixmap's rectangle.
//
sourceRect &= m_pixmap.rect();
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.translate(m_origin);
painter.scale(m_zoom*m_scale, m_zoom*m_scale);
painter.translate(-m_origin);
//painter.drawPixmap(QPointF(0.0, 0.0), m_pixmap, sourceRect);
painter.drawPixmap(m_origin, m_pixmap, sourceRect);
painter.end();
}
#if QT_CONFIG(wheelevent)
void DSSImageWidget::wheelEvent(QWheelEvent* e)
{
qreal degrees = -e->angleDelta().y() / 8.0;
//
// If zooming in and zoom factor is currently 1.0
// then remember mouse location
//
if ((degrees > 0) && (m_zoom == 1.0))
{
QPointF mouseLocation = e->position();
if (mouseOverImage(mouseLocation))
{
m_pointInPixmap = QPointF((mouseLocation-m_origin) / m_scale);
}
else
{
m_pointInPixmap = QPointF((m_pixmap.width() / 2), (m_pixmap.height() / 2));
}
}
qreal steps = degrees / 60.0;
qreal factor = m_zoom * std::pow(1.125, steps);
m_zoom = std::clamp(factor, 1.0, 5.0);
if (degrees < 0 && m_zoom == 1.0)
{
m_pointInPixmap = QPointF((m_pixmap.width() / 2), (m_pixmap.height() / 2));
}
update();
Inherited::wheelEvent(e);
}
#endif
All the best, David

How to draw triangle and rhombus shapes using QT

I need to draw triangle shape and rhombus shapes like this image.In this code which design triangle shape (figure 1) but I need to add this shape to text "TRI" . And I also need to implement this code to design rhombus shape like (figure 2). please help me to solve this.
Figure 1
void MainWindow::on_btnTri_clicked()
{
QPen redPen(Qt::black);
redPen.setWidth(2);
QRectF rect = QRectF(0, 0, 200, 200);
QPainterPath path;
path.moveTo(rect.left() + (rect.width() / 2), rect.top());
path.lineTo(rect.bottomLeft());
path.lineTo(rect.bottomRight());
path.lineTo(rect.left() + (rect.width() / 2), rect.top());
QGraphicsPathItem* item = ui->graphicsView->scene()->addPath(path, redPen);
item->setFlag(QGraphicsItem::ItemIsMovable, true);
item->setFlag(QGraphicsItem::ItemIsSelectable,true);
}
Figure 2 I use this code to design figure 2 But which cannot pass parameters to change there size,My figure 1 designed code I can able pass two parameters to QRectF(0, 0, para1, para2); this for change triangle's size.so I need to change this code to do the same thing using QPainterPath or any other way.
void MainWindow::on_btnRomb_clicked()
{
QPolygonF romb;
romb.append(QPointF(20,40));
romb.append(QPointF(0,20));
romb.append(QPointF(20,0));
romb.append(QPointF(40, 20));
QGraphicsPolygonItem* itemR = ui->graphicsView->scene()->addPolygon(romb);
itemR->setFlag(QGraphicsItem::ItemIsMovable);
}
you must use the addText() method of QPainterPath, to place it in the center you must calculate the width and height of the text for it QFontMetrics is used:
QPen redPen(Qt::black);
redPen.setWidth(2);
QRectF rect(0, 0, 200, 200);
QPainterPath path;
path.moveTo(rect.left() + (rect.width() / 2), rect.top());
path.lineTo(rect.bottomLeft());
path.lineTo(rect.bottomRight());
path.lineTo(rect.left() + (rect.width() / 2), rect.top());
path.moveTo(rect.center());
QFont font("Times", 20, QFont::Bold);
QFontMetrics fm(font);
QString text = "TRI";
QSize size = fm.size(Qt::TextSingleLine, text);
path.addText(rect.center()+ QPointF(-size.width()*0.5, size.height()*0.5), font, text);
QGraphicsPathItem *item = ui->graphicsView->scene()->addPath(path, redPen);
item->setFlag(QGraphicsItem::ItemIsMovable, true);
item->setFlag(QGraphicsItem::ItemIsSelectable,true);
For the case of the diamond you should only get the midpoints of each vertex:
QPainterPath path;
QRectF rect(0, 0 , 100, 100);
path.moveTo(rect.center().x(), rect.top());
path.lineTo(rect.right(), rect.center().y());
path.lineTo(rect.center().x(), rect.bottom());
path.lineTo(rect.left(), rect.center().y());
path.lineTo(rect.center().x(), rect.top());
QGraphicsPathItem* itemR = ui->graphicsView->scene()->addPath(path);
itemR->setFlag(QGraphicsItem::ItemIsMovable);

QPainter not drawing text properly

I am not able to draw path using QPainter drawText() method
QString Path = "/home/User"
QPainter *painter
QRectF centerRect = QRectF (textX,centerY + m_bounds.height()*0.15, fm.width(Path),fm.height());
painter->drawText(centerRect,Path,QTextOption(Qt::AlignCenter | Qt::AlignHCenter ));
In windows its drawing the text but on ubunthu Linux its showing only /home
QFontMetrics fm = painter->fontMetrics();
qreal centerY = ( m_bounds.height() - fm.height() -10 )/2;
qreal textX = 8;
QRectF centerRect = QRectF (textX,centerY + m_bounds.height()*0.15, fm.width(m_folderPath),fm.height());
painter->drawText(centerRect,m_folderPath,QTextOption(Qt::AlignCenter | Qt::AlignHCenter ));
fm.width(m_folderPath)
shows differnet value for /home/user and /homeuser4

Is it possible to make a pdf with different page size in Qt?

If the size of the page is same, then no problem, but I need the page size should be the size of Image(which is variable),
void DgJpgToPdf::convertToPdf( const QStringList& theImagePathList, const QString& theOutputFileName )
{
QPrinter printer;
printer.setOrientation(QPrinter::Portrait);
printer.setOutputFormat( QPrinter::PdfFormat );
printer.setOutputFileName( theOutputFileName );
QPainter painter;
painter.begin( &printer );
//printer.newPage();
foreach( QString fileName, theImagePathList ){
QImage imgObj( fileName );
printer.setPaperSize( QSizeF(imgObj.width(), imgObj.height()), QPrinter::DevicePixel );
painter.drawImage( 0, 0, imgObj );
printer.newPage();
}
painter.end();
}
You may wish to use QPdfWriter rather than QPrinter. You'll have to pick a PPI for the image, but if you want to save at 96 PPI, you could do something like
QPdfWriter pdfWriter(theOutputFileName);
pdfWriter.setTitle(theDocumentTitle);
pdfWriter.setCreater(yourApplicationName);
const qreal horizontalMarginMM = 2.0; // 2mm margin on each side
const qreal verticalMarginMM = 2.0;
QPagedDevice::Margins margins;
margins.left = margins.right = horizontalMarginMM;
margins.top = margins.bottom = verticalMarginMM;
pdfWriter.setMargins(margins);
bool newPage = false;
QPainter painter;
painter.begin(&pdfWriter);
foreach (const QString& fileName, theImagePathList) {
QImage imageObj(fileName);
if (newPage)
pdfWriter.newPage();
newPage = true;
qreal pageWidthMM = imgObj.width() / 96.0 * 25.4 + 2*horizontalMarginMM;
qreal pageHeightMM = imgObj.height() / 96.0 * 25.4 + 2*verticalMarginMM;
imgObj.setDotsPerMeterX(96.0 / 25.4 * 1000.0);
imgObj.setDotsPerMeterY(96.0 / 25.4 * 1000.0);
pdfWriter.setPageSizeMM(QSizeF(pageWidthMM, pageHeightMM));
painter.drawImage(0, 0, imageObj);
}
painter.end();

How to draw text surrounding circle?

Using QT, how would I go about taking user-supplied input (text) and drawing the font in such a way that it "follows" a circular path?
I really know nothing at all about QT but if I understood your question right, I found the solution with a simple google search. Code is below and here is the source link:
http://developer.qt.nokia.com/faq/answer/how_do_i_make_text_follow_the_line_curve_and_angle_of_the_qpainterpath
#include <QtGui>
#include <cmath>
class Widget : public QWidget
{
public:
Widget ()
: QWidget() { }
private:
void paintEvent ( QPaintEvent *)
{
QString hw("hello world");
int drawWidth = width() / 100;
QPainter painter(this);
QPen pen = painter.pen();
pen.setWidth(drawWidth);
pen.setColor(Qt::darkGreen);
painter.setPen(pen);
QPainterPath path(QPointF(0.0, 0.0));
QPointF c1(width()*0.2,height()*0.8);
QPointF c2(width()*0.8,height()*0.2);
path.cubicTo(c1,c2,QPointF(width(),height()));
//draw the bezier curve
painter.drawPath(path);
//Make the painter ready to draw chars
QFont font = painter.font();
font.setPixelSize(drawWidth*2);
painter.setFont(font);
pen.setColor(Qt::red);
painter.setPen(pen);
qreal percentIncrease = (qreal) 1/(hw.size()+1);
qreal percent = 0;
for ( int i = 0; i < hw.size(); i++ ) {
percent += percentIncrease;
QPointF point = path.pointAtPercent(percent);
qreal angle = path.angleAtPercent(percent);
qreal rad =qreal(0.017453292519943295769)*angle; // PI/180
// From the documentation:
/**
QTransform transforms a point in the plane to another point using the following formulas:
x' = m11*x + m21*y + dx
y' = m22*y + m12*x + dy
**/
// So the idea is to find the "new position of the character
// After we apply the world rotation.
// Then translate the painter back to the original position.
qreal sina = std::sin(rad);
qreal cosa = std::cos(rad);
// Finding the delta for the penwidth
// Don't divide by 2 because some space would be nice
qreal deltaPenX = cosa * pen.width();
qreal deltaPenY = sina * pen.width();
// Finding new posision after rotation
qreal newX = (cosa * point.x()) - (sina * point.y());
qreal newY = (cosa * point.y()) + (sina * point.x());
// Getting the delta distance
qreal deltaX = newX - point.x();
qreal deltaY = newY - point.y();
// Applying the rotation with the translation.
QTransform tran(cosa,sina,-sina,cosa,-deltaX + deltaPenX,-deltaY - deltaPenY);
painter.setWorldTransform(tran);
painter.drawText(point,QString(hw[i]));
}
}
};
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Widget widget;
widget.show();
return app.exec();
}