scale a QImage in Qt - c++

What the easiest way to draw an image as a background for a QGraphicsRectItem?
I can set the background as follows but I can't scale the image:
QGraphicsRectItem *enemyItem;
QImage *image = new QImage(":/PaperMario.png");
QBrush *brush = new QBrush(*image);
enemyItem = new QGraphicsRectItem();
enemyItem->setBrush(*brush);
enemyItem->setRect(enemy->getXPos()*30,enemy->getYPos()*30,30,30);
scene->addItem(enemyItem);
inside QGraphicsRectItem
void MySquare::setBrush(QColor _color){
color = _color;
color_pressed = _color;
update(); //repaint
}
I have tried it using:
QBrush *brush = new QBrush(*image->scaled(10));
But I'm stuck: doesn't want to compile.
Is this the way to do it?
Edit:
QImage *image = new QImage(":/PaperMario.png");
QImage *scaled_image = new QImage(image->scaled(35,35,Qt::KeepAspectRatio));
QBrush *brush = new QBrush(*scaled_image);
Does does draw it but it still shows the image multiple times in the drawing

when you display your image in a scene (what i would guess) why dont you use QGraphicsScene::addPixmap(QPixmap) and use the QPixmap::fromImage ( const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor ) to get your image into the right format.

*image->scaled(10) does not work on a QImage*. Either you write *image.scaled or (better) image->scaled.

Related

Qt - QPixmap of QLabel don't update after long-running

I use QLabel to show frame captured by camera.
//subThread: capture frame from camera
...
m_videoCapture = new cv::VideoCapture(pipeline, CAP_GSTREAMER);
while(m_videoCapture.isOpened())
{
m_videoCapture->read(m_frame);
if (m_frame)
{
//callback method
m_funcFrame(m_frame, pUser);
}
}
//MainThread
//callback method--show image on QLabel
showImage(cv::Mat& frame)
{
m_image = QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
QPixmap pixmap = QPixmap::fromImage(m_image).copy();
m_pixmap = pixmap.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformat).copy;
ui->m_label->setPixmap(m_pixmap);
}
it work fine but QLabel's pixmap don't update any more on matter how I move camera after 5 or 6 hours. I have to restart my application to let it back to normal.

how to delay drawing

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);
}

Error with drawing point on QLabel

I'm trying to draw on QLabel in Qt like this:
paintscene.h:
class PaintScene : public QWidget
{
Q_OBJECT
public:
PaintScene(QWidget* parent = NULL);
QVector<QLabel*> _layers;
QColor _color;
int _width;
void mousePressEvent(QMouseEvent* event);
private slots:
void updateWidth();
};
paintscene.cpp:
PaintScene::PaintScene(QWidget* parent) : QWidget(parent)
{
_width = 10;
_color = Qt::red;
QLabel* inital = new QLabel(this);
inital->setStyleSheet("QLabel { background-color : white; }");
_layers.push_back(inital);
QGridLayout* layout = new QGridLayout();
layout->addWidget(inital, 1, 1, 1, 1);
this->setLayout(layout);
}
void PaintScene::mousePressEvent(QMouseEvent *event)
{
QImage tmp = _layers.back()->pixmap()->toImage();
QPainter painter(&tmp);
QPen paintpen(_color);
paintpen.setWidth(_width);
painter.setPen(paintpen);
painter.drawPoint(event->x(), event->y());
_layers.back()->setPixmap(QPixmap::fromImage(tmp));
}
The list is needed because I want to implement the work with layers (QLabel - a separate layer).
However, I get an error, the program terminates. The error occurs on the line QImage tmp = _layers.back()->pixmap()->toImage();.
What makes this happen? How can this be fixed? Maybe for a layer to use something different, not QLabel?
#Jeremy Friesner is right about the reason for the error, not having a QPixmap this will be null, in my answer I will show a possible solution
void PaintScene::mousePressEvent(QMouseEvent *event)
{
QLabel *label = _layers.back();
const QPixmap *pix= label->pixmap();
QPixmap pixmap;
if(pix)
pixmap = *pix;
else{
pixmap = QPixmap(label->size());
pixmap.fill(Qt::transparent);
}
QPainter painter(&pixmap);
QPen paintpen(_color);
paintpen.setWidth(_width);
painter.setPen(paintpen);
painter.drawPoint(event->pos());
painter.end();
label->setPixmap(pixmap);
}
From the Qt docs for QLabel::pixmap():
This property holds the label's pixmap
If no pixmap has been set this will return 0.
... so when you do this:
QImage tmp = _layers.back()->pixmap()->toImage();
pixmap() is returning NULL (because the QLabel has never had any QPixmap set on it yet), and then you try to dereference that NULL pointer to call toImage() on it, hence the crash.
To avoid crashing, don't try to create a QImage from a NULL QPixmap pointer.
I suspect you wanted to be calling grab() instead of pixmap() -- grab() will create a QPixmap for you that contains the visual appearance of the QLabel. However, an even better approach would be to avoid messing about with QPixmaps at all; instead, make your own subclass of the QLabel class, and override its paintEvent(QPaintEvent *) method to first call up to QLabel::paintEvent(e) and then use a QPainter to draw the additional point afterwards. That will be easier to implement and also more efficient at runtime.

How to flip a QImage

What is the right way to flip/mirror a QImage? The following snippet does not work.
//allocate buffer
BYTE* pRgb32Buffer = new BYTE[width*height* 4];
//create paint device
QImage img = QImage(pRgb32Buffer , width, height, getStride(width, pixelFormat), QImage::Format_RGB32);
//do some drawing on image (works!)
QPainter painter(&img);
painter.drawText(10, 50, QString("some text"));
//mirrore image (doesn't mirror the orignal buffer!!!)
img = img.mirrored(false,true);
//doesn't work either
//QImage mirrored = img.mirrored();
//img = mirrored;
//mirrored.detach();
A solution using QImage::mirrored():
memmove(pRgb32Buffer, img.mirrored().bits(),img.byteCount());
I have this working code to mirror a QPixmap onto an QImage.
QPixmap* source = //... Getting my pixmap form somewhere...
QImage target(QSize(source->width(), source->height()), QImage::Format_ARGB32);
QPainter painter(&target);
QTransform transf = painter.transform();
transf.scale(-1, 1);
painter.setTransform(transf);
painter.drawPixmap(-source->width(), 0, *source);
The source contains the mirrored pixmap after that code. You should be able to do the same with QImageand the QPainter::drawImage function as alternative.
Optionally you can save the file if you want (make sure you have the imageformats dll's or it won't write):
QImageWriter writer("c:\\theimage.tiff", "tiff");
writer.setCompression(1);
writer.write(target);

C++ Qt QGraphicsView: Remembering the position of scrollbars after reload

I have the following problem with scrollbars in graphics view. My application takes a PDF file and creates (in some way) a QImage out of it. The QImage is then converted to QPixmap, which is used to create a QGraphicsScene and from the QGraphicsScene I create a QGraphicsView. The QGraphicsView is added to the central widget and displayed.
The code looks approximately like this
QImage image;
image = loadImage(path);
QPixmap pixmap;
pixmap.convertFromImage(image);
scene = new QGraphicsScene(this);
scene->addPixmap(pixmap);
view = new QGraphicsView(scene);
textEdit = new QTextEdit(this)
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(view);
layout->addWidget(textEdit);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
In the application, the view gets updated every time the PDF file changes. Now the problem is that the thing, which is in the PDF file, can also change in size and the scrollbars get messed up. I want the scrollbars after update to be in a position, such that I will see the same part of the PDF file as I saw before the update.
Can you give me some advice on how to accomplish this? I've searched for this issue, but nothing has worked in my case so far (I could have also be doing something wrong).
Thank you for your answers!
Before changing the view's contents remember scrollbar positions using view->horizontalScrollBar()->value() and view->verticalScrollBar()->value().
After changing reset previous values using setValue(int).
It's bad that you didn't provide any code that you have tried to use to accomplish this so I can't tell you what were you doing wrong.
Here is an example:
int pos_x = view->horizontalScrollBar()->value();
int pos_y = view->verticalScrollBar()->value();
pixmap_item->setPixmap(QPixmap::fromImage(new_image));
view->horizontalScrollBar()->setValue(pos_x);
view->verticalScrollBar()->setValue(pos_y);
Here pixmap_item is the stored result of scene->addPixmap(pixmap). It has QGraphicsPixmapItem* type.
I've changed the code, but it behaves strangely.
QImage image;
image = loadImage(path);
QPixmap pixmap;
pixmap.convertFromImage(image);
scene = new QGraphicsScene(this);
scene->addPixmap(pixmap);
int hsbv = -1;
int vsbv = -1;
if (view != NULL) {
hsbv = view->horizontalScrollBar()->value();
vsbv = view->verticalScrollBar()->value();
}
view = new QGraphicsView(scene);
textEdit = new QTextEdit(this)
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(view);
layout->addWidget(textEdit);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
view->horizontalScrollBar()->setVealue(hsbv);
view->verticalScrollBar()->setValue(vsbv);
When I print out the view->horizontalScrollBar()->value() at the end, I always get 83 and I get 479 for the view->verticalScrollBar()->value(), which is very wierd, but when I print hsbv and vsbv I get reasonable numbers.