I have an issue with pixmaps created for drag events. For drag events of my derived QGraphicsRectItem I create a semi-transparent pixmap from that item.
In the debug build everything looks fine.
But in the release build the drag pixmap has some periodic and random artefacts
here is the code:
QPixmap MyGraphicsRectItem::toPixmap() const
{
QRect r = boundingRect().toRect();
QPixmap pixmap(r.width(), r.height());
QColor dragColor(color);
dragColor.setAlphaF(0.5);
QPainter painter;
painter.begin(&pixmap);
painter.fillRect(pixmap.rect(), dragColor);
painter.setPen(Qt::white);
QFont font("SegoeUI");
font.setBold(true);
painter.setFont(font);
painter.drawText(pixmap.rect(), QString(" ") + textItem->toPlainText());
if (pixItem != nullptr) {
painter.setOpacity(0.5);
painter.drawPixmap(pixItem->pos(), pixItem->pixmap());
}
painter.end();
return pixmap;
}
Could that be a kind of memory issue?
The QPixmap is initialized with uninitialized data. In Debug, this is often set to a fixed pattern, but in Release it is garbage.
You should fill the pixmap with transparent color before using it.
QPixmap::QPixmap(int width, int height)
Constructs a pixmap with the given width and height. If either width or height is zero, a null pixmap is constructed.
Warning: This will create a QPixmap with uninitialized data. Call fill() to fill the pixmap with an appropriate color before drawing onto it with QPainter.
(From Qt Docs)
Related
I want to show the proccess of filling an area line by line. The problem is: even if I create QPixmap from QImage every time I draw a new line (and also delay) and add it to QGraphicsScene in a loop, it doesn't update until the whole thread is finished.
QTest::qWait does the trick. And using QGraphicsPixmapItem for an actual display of the proccess instead of QPixmap itself. I use QPainter initialized from QPixmap and then just reset the pixmap for the pixmap item.
QImage img = QImage(WIDTH, HEIGHT, QImage::Format_RGB32);
img.fill(Qt::white);
QPixmap *pixmapFill = new QPixmap(QPixmap::fromImage(img));
QGraphicsPixmapItem pixmapItem(*pixmapFill);
fillScene->addItem(&pixmapItem);
pixmapItem.setPixmap(*pixmapFill);
// ...
for (auto point : *points) {
QTest::qWait(delay);
painter.drawLine(point.x(), point.y(), xBarrier, point.y());
pixmapItem.setPixmap(*pixmapFill);
}
I am painting in a custom widget to match the "Google-Styled" cards. I have most of the dimensions and font correct however upon painting an image it is always stretched. Here is a reference image and the relevant code. I would like to keep the image at it's default aspect ratio.
Image:
QRect topPortion = QRect(QPoint(0, 0), QSize(width(), (height()/4)*3));
QPainterPath backgroundPath;
backgroundPath.addRect(topPortion);
QPainterPath bottom = getCornerPath().subtracted(backgroundPath);
QRect bottomRect = QRegion(rect()).subtracted(QRegion(topPortion)).boundingRect();
painter.fillPath(getCornerPath(), m_bColor);
painter.fillPath(bottom, m_fColor);
painter.drawPixmap(topPortion, m_image.scaled(topPortion.size(), Qt::KeepAspectRatio, Qt::FastTransformation));//Issue
painter.setPen(QPen(QColor(50, 50, 50)));
painter.setFont(titleFont);
painter.drawText(QPointF(12, topPortion.height()+((bottomRect.height()-fontHeight)/2)+QFontMetrics(titleFont).ascent()), "Add Record");
painter.setFont(subtitleText);
painter.drawText(QPointF(12, topPortion.height()+((bottomRect.height()-fontHeight)/2)+fontHeight), "Add Record");
You're scaling your image with m_image.scaled function, however passing also to painter.drawPixmap function, the topPortion variable, and according to docs:
The pixmap is scaled to fit the rectangle, if both the pixmap and
rectangle size disagree.
So my solution is:
//Your's calculation area
QRect topPortion = QRect(QPoint(0, 0), QSize(width(), (height() / 4) * 3));
QPixmap pixmap = QPixmap(1024, 768); //Random image
pixmap.fill(Qt::red); //Random color
//Scaled size that will be used to set draw aera to QPainter, with aspect ratio preserved
QSize size = pixmap.size().scaled(topPortion.size(), Qt::KeepAspectRatio);
//Draw the pixmap inside the scaled area, with aspect ratio preserved
painter.drawPixmap(topPortion.x(), topPortion.y(), size.width(), size.height(), pixmap);
For Widgets I can call 'raise' which keeps the widget on top of anything else, but this doesn't seem to work for any QPixmap's that are rendered.
How can I ensure that a QPixmap remains on top of anything else?
In my paintEvent function:
QPainter objPainter(this);
if ( strImage.isEmpty() != true ) {
qint16 int16NudgeImageX = mpobjNode->int16GetAttr(clsXML::mscszAttrNudgeImageX)
,int16NudgeImageY = mpobjNode->int16GetAttr(clsXML::mscszAttrNudeImageY);
QPixmap pmImage(":/" + strImage);
QSize szImage = pmImage.size();
QPoint ptImage(rctGeom.center().x() - (szImage.width() / 2) + int16NudgeImageX
,rctGeom.center().y() - (szImage.height() / 2) + int16NudgeImageY);
QRect rctImage(ptImage, szImage);
objPainter.drawPixmap(rctImage, pmImage);
}
A QPixmap is not a widget (no parent, nor layout).
The QPixmap class is an off-screen image representation that can be used as a paint device.
Use a QLabel for use with a parent (and layout)
QLabel* label = new QLabel(parent);
label->setPixmap(pixmap);
If you want to make sure that your QPixmap is painted on top of everything else inside your widget, simply paint it last.
Keep in mind that other widgets may be painted above your widget.
am I using drawPixmap() correctly?
Essentially my goal is to take an tileset image, and replace an individual tile with a custom tile image.
I'm able to get both images to load on the screen, but when I call drawPixmap(), then original image doesn't change at all.
Thanks in advance.
void replaceCustomTile(QPixmap custom, QPixmap target, int whichTile) {
QRect rect(0, 0 + (squareTileSize * whichTile), squareTileSize, squareTileSize);
QRect customRect = custom.rect();
QPainter painter(this);
painter.drawPixmap(rect, target, customRect);
painter.end();
}
EDIT:
This is how replaceCustomTile is called:
QPixmap terrainTiles(":/static/Terrain.png");
QPixmap customTile(":/static/Smiles.png");
replaceCustomTile(customTile, terrainTiles, 0);
To intialize QPainter by this it must be called from the widget paintEvent(QPaintEvent *) if you want to draw it on some widget. So, replaceCustomTile() should be called from the event handler in that case.
To draw some pixmap on top of another pixmap QPainter should be initialized by the target pixmap using QPainter::begin():
QPainter painter;
painter.begin(&target);
painter.drawPixmap(rect, custom);
painter.end();
The above code draws QPixmap custom into given QRect rect over QPixmap target. The target is modified.
How to change opacity of QPixmap?
I've set an image as background actually I want to change its opacity, Here is my code:
Call.h:
private:
QPixmap m_avatar;
Call.cpp:
void Call::resizeEvent(QResizeEvent *e)
{
QPalette pal = palette();
pal.setBrush(backgroundRole(), m_avatar.scaled(e->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
setPalette(pal);
}
I've changed resizeEvent function, but it doesn't change background's opacity.
void Call::resizeEvent(QResizeEvent *e)
{
QPixmap result_avatar(m_avatar.size());
result_avatar.fill(Qt::transparent);
QPainter painter;
painter.setOpacity(0.5);
painter.begin(&result_avatar);
painter.drawPixmap(0, 0, m_avatar);
painter.end();
QPalette pal = palette();
pal.setBrush(backgroundRole(), result_avatar.scaled(e->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
setPalette(pal);
}
Any suggestion?
You are not using local QPainter object. According to QWidget Events:
paintEvent() is called whenever the widget needs to be repainted.
Every widget displaying custom content must implement it. Painting
using a QPainter can only take place in a paintEvent() or a function
called by a paintEvent().
Here it works:
void Call::paintEvent(QPaintEvent *)
{
// create a new object scaled to widget size
QPixmap result_avatar = m_avatar.scaled(size());
QPainter painter(this);
painter.setOpacity(0.5);
// use scaled image or if needed not scaled m_avatar
painter.drawPixmap(0, 0, result_avatar);
}
Update for paiting on pixmap case
If it is needed only to paint with some opacity on a pixmap using QPainter, the opacity must be set only after QPainter activation by QPainter::begin(). So, after changing the order the pixmap result_avatar has two images (one resized with opacity 1 and original pixmap on top with opacity 0.5):
QPainter painter;
painter.begin(&result_avatar);
painter.setOpacity(0.5);
painter.drawPixmap(0, 0, m_avatar);
painter.end()