Draw a Circle Qt C++

Drawing circular text in Qt

published at sixteen.02.2018 twenty:17 past Jens Weller
Save to Instapaper Pocket

For a few weeks at present, I use Friday afternoon to practice some coding. As often managing Meeting C++ has go a not-code activeness, information technology feels good to take a spot in the week where I focus on "what could I code today?". Today I focused on drawing circular text in Qt, which mostly consisted of writing epitome code, in that location is nevertheless lots to tweak if you'd wanted to employ this in product.

I'll simply need this, to create a svg version of the r/cpp_review logo with Qt. Lets dive in.

So when you google this, you'll notice this stackoverflow reply, which really shows how to draw text on a bezier curve. Cool! So I adopted that solution for drawing text around a circle, and refactored the code into a office, with this signature:

void drawCircularText(QPainter& painter,const QPen& pen,const QFont& font,const QString& text,int x, int y, int diameter,qreal percentfactor = 0.5,qreal start_in_percent=0)

Curt view on the arguments:

  • painter - the painter knowing on which context to describe.
  • pen - the Pen which you want to depict with
  • font - since its text, lets also have that
  • text - what ends up circular
  • x,y - center of the circle
  • bore - how big would you like to accept your circumvolve?
  • qreal - its a Qt typedef for double/float.
    • percentfactor - this determines a factor past which each letter is separated on the circle, the original just spreads it on 360°, what is not what everyone needs
    • start_in_percent - every bit the original code uses percent, this is where to beginning on the circle between 0-ane.

Not much has changed from the original lawmaking, as mentioned the path at present uses addEllipse(10,y,diameter,diameter) to have a circular layout for the text. The only important change is these two tweaks:

qreal percentIncrease = (qreal) ane.0/text.size()*percentfactor;  for ( int i = 0; i < text.size(); i++ ) {     start_in_percent += percentIncrease;     if(start_in_percent > 1.0)     {         qDebug() << "start_in_percent is over one.0:" << start_in_percent;         start_in_percent -= one.0;     }     QPointF point = path.pointAtPercent(start_in_percent);     qreal angle = path.angleAtPercent(start_in_percent);   // Clockwise is negative      painter.salve();     // Movement the virtual origin to the point on the curve     painter.translate(indicate);     // Rotate to match the angle of the curve     // Clockwise is positive so we negate the bending from in a higher place     painter.rotate(-angle);     // Draw a line width above the origin to motion the text above the line     // and let Qt do the transformations     painter.drawText(QPoint(0, -pen.width()),QString(text[i]));     painter.restore(); }

Multiplying with percentfactor makes it possible to fine tune the spreading of the letter accross the circle. As one keeps adding to start_in_percent, I check if it goes over 1.0, to arrange the value. This prevents that zip is drawn, when the text is too long for the circumvolve. After this the actual drawing happens.

This works very well, only what if you want to take your text counter clock wise?

Counter clock wise - the difficult manner

So, the net knows really nothing near this, at least in relation to Qt. Which also motivated me to web log about this, as hereafter generations might simply find this commodity over google...

Information technology turns out there are two solutions, a hard one, and an like shooting fish in a barrel one. The piece of cake one is a scrap tricky, as information technology feels so natural, in one case you've seen it, but without knowing every particular of the Qt APIs, its easily overlooked. Which happened to me, so behold my first, non very well working solution:

Reverse the text string (std::reverse) and then merely describe with drawText! Oh, well, that doesn't work, as the messages are nonetheless in the wrong direction. Just they are already in the right position. All I'd need is to flip them. Why non describe them on a QImage, and and so just flip that image, so that the letter magically ends upwards correct? I'yard not sure if the painter API would offer something similar without drawing showtime on an image, but lets beginning cheque this solution out, before nosotros go farther.

First, it seemed not to work, every bit the painted images but were really trashy. Can ane utilize more so 1 painter in parallel in the aforementioned thread? But then I noticed an interesting pattern, the beginning image was virtually right, except that the non drawn part of the QImage independent trash seemingly. More than interesting, it seemed equally that at runtime, the same QImage would be used to depict the whole text on. The farther images all just had more piled letters on elevation of each other, until i could merely run across a blob of lines and curves. So information technology seems that the optimizer - at least I blame the optimizer for this - was like, hey thats a really expensive operation to always allocate a new prototype in this loop, lets merely simply always reuse that i! Then I refactored that lawmaking into a function:

QImage drawTextOnPixmap(const QString& text,QFont font,int size) {     QImage pixmap(size,size,QImage::Format_ARGB32);     QPainter pmp(&pixmap);     pmp.setRenderHint(QPainter::Antialiasing);     pmp.setFont(font);     pmp.setBackground(QBrush(Qt::white));     pmp.fillRect(pixmap.rect(),QBrush(Qt::black));     pmp.setPen(Qt::white);     pmp.drawText(pixmap.rect(),Qt::AlignCenter,text);     return pixmap;//.scaled(size,size,Qt::KeepAspectRatio,Qt::SmoothTransformation); }

That was super easy! Just, NRVO is now doing, what previously seemed the optimizer to do: we're ever cartoon on the same image! Which isn't and then bad, as that way some allocations are saved. A fillRect each time makes certain the whole image gets redrawn. While this works, and draws text counter clock wise, its a hack, and has big problem:

blog/circulartext_bad.png

Do yous see the antialiasing? While the painter happily draws the text in a practiced quality, the image is getting rotated into identify and the result of the actual text shown is not very pretty. Also, I'm non sure what std::reverse would practice to unicode text...

Counter clock wise text - the easy fashion

While the first solution "worked", it was articulate that its not a good solution, and not even bringing in the needed upshot. There needs to exist a solution, which works with only using the Qt API. And at that place is, and its so easy. QPainter offers lots of drawXYZ fellow member functions, and so it would not be and then surprising if drawCircularText existed. But at that place is no such affair, no drawRoundedText, or whatsoever other interface in QPainter offer this. Thats why the to a higher place solution for circular text is the actual working ane I'm enlightened of so far. And its quite easy to tweak it to produce round text in anti-clockwise direction:

path = path.toReversed();

With this, the QPainterPath now flows in the counter-clock wise direction, and magically the text is anti-clock wise! One picayune departure there is though: while the clock wise text is on the out side of the circle, the anti-clock wise text is on the within. This time its a trivial ameliorate:

blog/circulartext.png

One problem, which tin be stock-still hands, persists: the spacing of the letters currently does not accost the width of the letter of the alphabet in the font. QFontMetrics::width makes this quite easy.

Bring together the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!

childerswitend.blogspot.com

Source: https://meetingcpp.com/blog/items/Drawing-circular-text-in-Qt.html

0 Response to "Draw a Circle Qt C++"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel