How to save QPainter instance - c++

I have a Qt application where I use QPainter to draw on a QPixmap which is then through QLabel displayed on the screen. Mainly it's drawing text and it's doing it several times every second.
I see it inefficient when the QPainter instance is constructing several times per second (including it's settings like font, pen etc.). So my question is, how can I save the instance of QPainter and reuse it later? I've tried this but it doesn't preserve the "settings" of the QPainter:
this->painter = new QPainter(&this->canvas); // this->cavas is a QPixmap
painter->setFont(this->font);
painter->setPen(this->font_color);
//...
painter->begin(&this->canvas);
painter->drawText(0, 0, 20, 20, "test");
painter->end();
Thanks

Related

Extend QPixmap on the right side without modify the content

For a given QPixmap with 200x200 dimension, I want to extend it with 50px (transparent) on the right side, without scaling the existing content.
Is this possible on QPixmap or with any Qt helper class?
Just paint the original pixmap onto a new, extended one...
QPixmap original_pixmap = ...
QPixmap new_pixmap(original_pixmap.width() + 50, original_pixmap.height());
new_pixmap.fill(Qt::transparent);
QPainter painter(new_pixmap);
painter.drawPixmap(0, 0, original_pixmap);

What might I be doing wrong to draw text with QPainter in QT 5 using C++?

In my paintEvent method for a custom widget for a game I'm writing, After calling the various model objects' render() methods to render them to the widget, I am trying to render the "Hi-Score" text. Here's the general code for just the text drawing:
painter.fillRect(event->rect(), QColor(0, 0, 0));
painter.drawImage(QRectF(event->rect().x(), event->rect().y() + 30, 512, 512), getGameBoardImage());
//...rendering other model components
painter.setBrush(QBrush(QColor(255, 255, 255)));
//painter.setFont(getGameFont());
painter.setFont(QFont("Times New Roman", 16, QFont::Bold));
painter.drawText(0, 0, "HI-SCORE");
I 'was' trying to draw the text in a custom font loaded from resource (I found an answer on here for that) but it wouldn't even display, even with a white brush. I thought maybe it was because it was because I didn't 'set' a font, but setting it to Times New Roman doesn't display anything either. What might I be doing wrong? As you can see the background is a black background with the game board painted on top but leaving a small buffer at the top and bottom. Do I need to do something special to display the text? Please don't suggest using a QLabel because I am trying to keep it all in one widget if possible. If I must, I will split the Hi-Score and 1-Ups into 2 other label sets with specialty fonts.
you code look ok, but you are drawing at 0,0 which is the top left corner of the widget canvas AND the text is actually there but not visible...
draw instead at
painter.drawText(margin, y+margin, "HI-SCORE");
where y is the high of the font used to draw the text and margin is you know a little margin border to make it look better something like 5 units
update
you can get the value of the text you are painting doing somthing like
QFont font("times", 25);
QFontMetrics fm(font);
int pixelsW{fm.width("HI-SCORE")};
int pixelsH{fm.height()};

QT combobox with multiple items at the same line

The buttonbox above has multiple items on the same line. It's a program called draftsight, written in QT.
The buttonbox with the red square is what i have, but i need more info on the same line..
Someone know's how to achieve this??
My current code to add 2 items in the same combobox line :
//setup combobox :
QComboBox *colors = new QComboBox;
ui->toolBar_color->addWidget(colors);
colors->setMinimumWidth(150);
//routine to fill the combobox with external data :
colors->clear();
Dialog_color().extern_toolbar_load();
QPixmap pixmap(15,15);
for(int i = 0; i<red_list.size(); i++){
pixmap.fill(QColor (red_list.at(i),green_list.at(i),blue_list.at(i)));
colors->addItem(pixmap, comments.at(i));
}
So in fact i need something like this :
colors->addItem(pixmap, pixmap, pixmap, "text", "text");
Is there a way?
thanks for your response,
To explain the red_list, green_list, blue list. This are the rgb color value's that user has set up in a tablewidget. The tablewidget data is loaded during program start and during program execution if refreshes from a text file which stores the user defined values. The dialog is not finished, but it's working as expected.
I have been thinking also about combining several pixmap to a long pixmap, but it must be a automated way to combine all rgb value's and other value's like linetype etc which each other. So how can i automate combining several pixmap's to one.. Hmm i have to think about this.
How did they do this in draftsight with QT designer? Default Systems will not provide the solution i think.
All input is welcome !
Edit, i found a solution.. Which can combine things automated when the function example is expanded. So far i am satisfied with the solution.
If qt can do a update for this. That would be nice. For the combobox a pressed signal would be nice also instead of using a event filter..
//setup combobox..
QComboBox *test = new QComboBox;
ui->toolBar_layer->addWidget(test);
test->setMinimumWidth(150);
test->setMinimumHeight(20);
test->addItem("test item");
test->setIconSize(QSize(100, 15));
//setup picture template..
QPixmap pixmap(100, 15);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
//contruct a picture based on rgb colors..
QPixmap pixmap_1(15,15);
pixmap_1.fill(QColor (255,0,0));
painter.drawPixmap(0, 0, 15, 15, pixmap_1);
//load pictures from file..
painter.drawPixmap(30, 0, 15, 15, QPixmap(":/icons/edit-paste.svg"));
painter.drawPixmap(60, 0, 15, 15, QPixmap(":/icons/terminal.svg"));
painter.end();
test->addItem(pixmap, "combobox");

Text from QPainter much nicer than from QPainterPath

I want to draw text using QPainter, and I want to use QPainterPath first (because ultimately I want to rotate the text in all sorts of ways). However, I find that the text produced by QPainterPath is much uglier than the text produced by QPainter.
The following code:
void MyWidget::paintEvent(QPaintEvent* /*event*/) {
QFont font;
font.setStyleHint(QFont::Times, QFont::PreferAntialias);
font.setPointSize(30);
QPainter painter;
painter.begin(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(Qt::black);
painter.setFont(font);
painter.drawText(10, 40, "Hello World");
QPainterPath textPath;
textPath.addText(10, 100, font, "Hello world");
painter.drawPath(textPath);
painter.end();
}
produces the following result:
The former is clearly much cleaner and nicer, especially in smaller fonts. What should I do to get the same result from QPainterPath?
I'm producing the above results on a Windows 7 computer, with Qt 5.0.
According to the Qt documentation for adding text to a QPainterPath: -
Adds the given text to this path as a set of closed subpaths created
from the font supplied.
So there is a conversion going on here, which is why it doesn't look the same. If you need to rotate text, you could try rotating the QPainter before rendering and then restoring it afterwards. Alternatively, if you can use QGraphicsView and QGraphicsDisplay instead of just rendering onto the widget, there is the class QGraphicsTextItem which may help.
But overall, it's the conversion to the set of closed subpaths that is responsible for the different output of text quality.
The two fonts don't look the same because you're adding extra contours to you QPainterPath text.
The following piece of code should give good results :
QFont font;
font.setStyleHint(QFont::Times, QFont::PreferAntialias);
font.setPointSize(30);
QPainter painter;
painter.begin(this);
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing);
painter.setFont(font);
// painter text color is modified by setPen()
painter.setPen(Qt::white);
painter.drawText(10, 40, "Hello World 1");
QPainterPath textPath;
textPath.addText(10, 100, font, "Hello World 2");
// painter path text color is modified by setBrush()
painter.setBrush(Qt::white);
// setPen(Qt::white) add extra white contour on text path (what you did)
painter.setPen(Qt::white);
painter.drawPath(textPath);
QPainterPath textPath2;
textPath2.addText(10, 160, font, "Hello World 3");
// painter path text color is modified by setBrush
painter.setBrush(Qt::white);
// setPen(Qt::NoPen) avoid extra contours for QPainter Path
painter.setPen(Qt::NoPen);
painter.drawPath(textPath2);
painter.end();
I admit that QPainterPath text "Hello World 3" is a little bit uglier that QPainterText "Hello World 1", but the result is still better than "Hello World 2"
I would beg to differ with the above answers and suggest that both the addText/drawPath and the drawText approaches do the same thing and that there likely is not material 'conversion'.
As noted by Mehdi-Antoine - using drawText gives a mid sized text weight, while using addText/drawPath with both a Pen and Brush gives a heavy weight, while using addText/drawPath with only the fill gives a light weight.
Note than the QPen value has a width, defaulting to 1, and if you apply this pen to stroke the outline of the text with a width of 1, the text will appear very heavily as demonstrated by Medhi-Antoine's example.
However - you can achieve effectively identical results to drawText by using the addText/drawPath approach simply by adjusting the weight of the Pen utilized. For a font size of 30 points, Arial, setting a Pen width of 0.2 and then painting using the addText/drawPath approach seems to create something identical to the drawText approach.
It seems that the drawText method takes the colour of the Pen, then uses that to do a fill, then apply a stroke at a specific thickness.
If you're going to use the drawPath method, you'll have to provide bot the Fill and the Pen, and importantly, to adjust the width of the pen appropriately.

Drawing a scalable QIcon using QPainter

I want to design a new QIcon and want it to look like a fixed text with a rounded rectangle around it
.-----.
| Phy |
`-----ยด
The icon is supposed to scale without the "pixel-blocks" effect when painting on a QPainter that eventually has a scale transformation applied (for example when I paint into a widget that is part of a QGraphicsView with a scale applied on its scene).
Therefor, I have difficulties knowing how I should paint my QIcon. If I do it in the following way, I will paint a QPixmap that always has a fixed amount of pixels, thus introducing the pixel-blocks effect inevitably when the scale is large enough
void MyWidget::drawIcon(QPainter *painter, QPoint pos) {
QPixmap pixmap = icon.pixmap(QSize(22, 22),
isEnabled() ? QIcon::Normal
: QIcon::Disabled,
isChecked() ? QIcon::On
: QIcon::Off);
painter->drawPixmap(pos, pixmap);
}
What I am looking for is a way similar to how QFont with drawText works. Regardless on how large my scale is, when I draw fonts it always looks sharp and I cannot detect individual pixels.
I imagine that I could tell QPainter to paint my icon into a given pixel rectangle, and QPainter transforms the rectangle itself before letting my QIconEngine::paint render the item into a possibly larger rectangle or pixmap. But I see no way how I could do something like this.
Am I just being stupid and not seeing the obvious solution?
I was indeed completely dump. I can just use QIcon::paint and pass it the rectangle. It will correctly delegate the request to the icon engine.
I do this by creating my icons/images as SVG files, and using QSvgRenderer to paint them onto a QPainter. The required classes are in the SVG module.