I have 2 images that I load in QImage and my question is :
How can I overlay these 2 images in the simplest way, to then save it in a QPixmap ?
Here are the following images :
And the awaited result :
(The final image will be used in a QTableView to show the user if it has more of the same potion)
I was able to find the answer on my own after some more research.
Here is my code that I came up with (if you can do it better I would like to see it because I would like to implement any better approach if there is any):
QPixmap base = QPixmap::fromImage(QImage("Your 1st img"));
QPixmap overlay = QPixmap::fromImage(QImage("your 2nd img"));
QPixmap result(base.width(), base.height());
result.fill(Qt::transparent); // force alpha channel
{
QPainter painter(&result);
painter.drawPixmap(0, 0, base);
painter.drawPixmap(0, 0, overlay);
}
QStandardItem *pCombinedItem = new QStandardItem(); //this variable should be in the .h file if you want to conserve it further on.
pCombinedItem->setData(QVariant(result), Qt::DecorationRole);//adding the final img into the StandardItem which we can put then into our table after we put it into a StandardItemModel like so :
model->setItem(1,4,pCombinedItem);
pInventory->setModel(model); //and we can put our model into our tableview
Related
I am trying to implement the same functionality in my widget as it is in cv:: namedWindow.
The goal is to enable zooming and to make the overlay with the grid and the values of pixel's colors directly over the original pixmap. Here is the example: сv picture zoomed:
I inherited the QGraphicsView widget, added to QGraphicsScene the QGraphicsPixmapItem and reimplemented the QWheelEvent so that zooming in and out works correctly now. The problem starts with creating an overlay.
Instead of creating a pack of QGraphicsLineItems and adding them to the scene in order to make the grid, I inherit the QGraphicsRectItem and draw the whole grid on it.
void QGraphicsOverlayItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen pen(Qt::black);
pen.setWidthF(0.02);
painter->setPen(pen);
painter->setBrush(Qt::BrushStyle::NoBrush);
QVector<QLineF> crossLines = createCrossLines();
painter->drawLines(crossLines);
}
This works very fast. But when I try to drawText with the QPainter and set the QFont:: pointSizeF() as small as possible, it works incorrectly (symbols change their size from small to huge during zooming or even disappear at all). Nevertheless, the best result that I get this way is the following:
the QPainter's drawText() result:
QFont font(painter->font());
font.setPointSizeF(0.1);
font.setLetterSpacing(QFont::SpacingType::AbsoluteSpacing,0.01);
painter->setFont(font);
painter->drawText(432,195,"123");
The easiest way is to add to scene a lot of QGraphicsTextItems and scale them to correct size, but it is too slow.
So the question is how can I subscribe the pixel's color value in the QGraphicsScene directly over the QPixmapItem?
I finally watched through the openCV source code and found what I looked for.
The answer for me was the QTransform matrix. OpenCV developers show the image not by using the scene in the QGraphicsView, but actually painting the image directly on the viewport in the paintEvent.
The QTransform matrix is stored in the class and is passed to QPainter in the beginning of the paintEvent.
void DefaultViewPort::paintEvent(QPaintEvent *event)
{
QPainter painter(viewport());
painter.setWorldTransform(param_matrixWorld);
painter.drawImage(QRect(0,0,viewport()->width(),viewport()->height()),image2Draw,QRect(0,0,image2Draw.width(),image2Draw.height()));
If you know the ratio of the image's size to the widget's size, and you also know the scale of QTransform matrix used to paint the image, it is easy to calculate how much area does the single source pixel take on the screen:
qreal ratioX = width() / float(image2Draw.width());
qreal ratioY = height() / float(image2Draw.height());
double pixel_width = qtransform_matrixWorld.m11()*ratioX;
double pixel_height = qtransform_matrixWorld.m11()*ratioY;
If we know the pixel_height, we can just set the QFont::pixelSize like this:
QFont font = painter->font();
font.setPixelSize(pixel_height/5);
painter->setFont(font);
I am trying to make a 3D scatter plot using QtDataVisualizationQ3DScatter and then to take a QPixmap of that and save to file.
Here is my code.
//set up the 3D scatter chart
QtDataVisualization::Q3DScatter *graph = new QtDataVisualization::Q3DScatter();
QWidget *widget = QWidget::createWindowContainer(graph);
QtDataVisualization::QScatter3DSeries *series = new QtDataVisualization::QScatter3DSeries;
//set up the data variable
QtDataVisualization::QScatterDataArray data;
//add data to the data variable
for (int i; i < nodes.size(); i = i + 1)
{
data << QVector3D(x[i], y[i], z[i]);
}
//add the data to series
series->dataProxy()->addItems(data);
//add series to graph
graph->addSeries(series);
//set the graph properties
graph->activeTheme()->setType(QtDataVisualization::Q3DTheme::ThemeArmyBlue);
graph->scene()->activeCamera()->setCameraPreset(QtDataVisualization::Q3DCamera::CameraPresetIsometricRight);
graph->seriesList().at(0)->setMesh(QtDataVisualization::QAbstract3DSeries::MeshPoint);
graph->setShadowQuality(QtDataVisualization::QAbstract3DGraph::ShadowQuality(0));
//widget->show();
const QRect rectangle(0,0,widget->width(),widget->height());
QPixmap test = widget->grab(rectangle);
test.save(QString(workingDirectory) + "/test.png", "PNG");
When I uncomment the "widget->show()", the test.png is just a white square but the widget comes up and shows the full 3DScatter. When I comment out the "widget->show()", test.png is still just a white square. I can't seem to get this to work.
When commenting widget->show(), add a widget->repaint() call to force the widget to be painted/rendered before saving it in the file.
I realise this question is quite old but for anyone who's interested:
QAbstract3DGraph has a function renderToImage. This is inherited by Q3DScatter.
You should be able to grab a qimage of your scene using:
QImage myImage = graph->renderToImage();
and then convert this to a pixmap:
QPixmap p;
p.convertFromImage(myImage);
I'm trying to save a chart to a file, in a QTextDocument in this example :
QTextDocument doc("Frame rate test\n");
QTextCursor cursor(&doc);
cursor.movePosition(QTextCursor::End);
if (getTestFinishedStatus())
{
QPixmap pix = _pFrameRateChart->grab(); //_pFrameRateChart is QChartView
cursor.insertImage(pix.toImage());
}
QTextDocumentWriter docWriter;
docWriter.setFileName("framerate.odf");
docWriter.setFormat("ODF");
docWriter.write(&doc);
The problem is the result isn't same if I'm displaying the chart in an ui.
Here is the result when not displayed :
Here is the result when displayed :
Obviously I would like to have the second result even when I don't add the ChartView to a widget to display it on an ui.
I've tried resizing the QChartView, resizing the QChart, adding the Chart to a temporarly widget and QVBoxLayout then saving it, showing temporarly the QChartView before saving it etc... but didn't managed to get a good result.
I use the following code to render a QGraphivsView on a Pixmap, since QtCharts is based on QGraphivsView, I think this will also work.
Try to render the image instead of trying to grab the pixmap.
void Printer::putProfileImage(QRect profilePlaceholder, QRect viewPort, QPainter *painter, QGraphivsView* profile)
{
int x = profilePlaceholder.x() - viewPort.x();
int y = profilePlaceholder.y() - viewPort.y();
QRect pos(x, y, profilePlaceholder.width(), profilePlaceholder.height());
profile->render(painter, pos);
}
I didn't find any easy way to this, so here's my solution, which is more like a workaround though :
QPixmap ChartView::getChartPixmap()
{
QWidget* w = new QWidget; //creating a temporary widget, which will enable to display the chart
w->resize(REPORT_IMAGE_WIDTH, REPORT_IMAGE_HEIGHT);
QVBoxLayout *vl;
vl = new QVBoxLayout(w);
vl->addWidget(this); //'this' being the QChartView
w->show(); //showing the widget so it is resized and can be grabbed with the correct dimensions
QTest::qWait(500); //we need to wait for a little for the graph to be drawn otherwise you'll still have the same size problem
QPixmap pixmap = w->grab(); //retrieve the pixmap
w->hide(); //hiding the widget
return pixmap;
}
It's working but you'll have a small window opened with the graph for 500 ms.
In my window class, inheriting of QMainWindow, I have a QLabel containing a QPixmap, updated every 20ms.
I want the QLabel, and the QPixmap inside it, to be resized according to the resizing of the window.
I want this Central Widget to take as much space as neccessary but also to be able to resize it down. Even smalled than the original size. But always keeping the ratio.
The actual code :
// in the window constructor
this->setWindowFlags(Qt::Window);
this->resize(500, 300);
this->setCentralWidget(this->label);
// in the updating function
QPixmap output;
output = output.fromImage(Mat2QImage(theImage));
this->label->setPixmap(output);
Now I've tried with :
output.scaled(this->label->x(), this->label->y(), Qt::KeepAspectRatio)
but it doesn't work ...
How can I do that ?
EDIT : I'm using Qt 5.3
QPixmap::scaled is const. Next code doesn't work:
// in the window constructor
this->setCentralWidget(this->label);
// in the updating function
QPixmap output;
output = output.fromImage(Mat2QImage(theImage));
output.scaled( this->label->x(), this->label->y(), Qt::KeepAspectRatio );
this->label->setPixmap(output);
Because output doesn't change. Maybe you need something like this:
// in the window constructor
this->setCentralWidget(this->label);
// in the updating function
QPixmap output;
output = output.fromImage(Mat2QImage(theImage));
output = output.scaled( this->label->x(), this->label->y(), Qt::KeepAspectRatio );
this->label->setPixmap(output);
Try puting your QLabel inside a Layout.
hl = new QHBoxLayout;
hl->addWidget(label);
centralWidget()->setLayout(hl);
check this out: http://qt-project.org/doc/qt-4.8/layout.html
It's my first post here and I would like to say Hello Everyone on stackoverflow :)
OK, welcome already have been and now I specify the problem which I've got. I have one QGraphicsView widget and I want to show two images with some opacity in it, but my code doesn't work and I don't know what is the reason :/
QGraphicsScene *scenaWynikowa = new QGraphicsScene(ui->graphicsViewWynik);
ui->graphicsViewWynik->setScene(scenaWynikowa);
ui->graphicsViewWynik->fitInView(scenaWynikowa->itemsBoundingRect(), Qt::KeepAspectRatio);
//wyświetlenie zdjęcia nr 1
QImage obraz1(s1);
obraz1.scaled(QSize(541,541), Qt::IgnoreAspectRatio, Qt::FastTransformation);
update();
resize(541, 541);
QPixmap mapaPikseli1(n1);
QGraphicsPixmapItem *pixmapItem1 = scenaWynikowa->addPixmap(mapaPikseli1);
QGraphicsOpacityEffect poziomPrzezroczystosci1;
poziomPrzezroczystosci1.setOpacity(0.5);
pixmapItem1->setGraphicsEffect(&poziomPrzezroczystosci1);
//wyświetlenie zdjęcia nr 2
QImage obraz2(s2);
obraz2.scaled(QSize(541,541), Qt::IgnoreAspectRatio, Qt::FastTransformation);
update();
resize(541, 541);
QPixmap mapaPikseli2(n2);
QGraphicsPixmapItem *pixmapItem2 = scenaWynikowa->addPixmap(mapaPikseli2);
QGraphicsOpacityEffect poziomPrzezroczystosci2;
poziomPrzezroczystosci2.setOpacity(0.5);
pixmapItem2->setGraphicsEffect(&poziomPrzezroczystosci2);
pixmapItem2->moveBy(0, 0);
ui->graphicsViewWynik->show();
Sorry for not English Variable's names but it's more convinient for me. If you want I can explain what and why variable has that name :)
Maybe someone finds a mistake in this code and explain to me where's the problem with my code and how to fix it?
edit: It's my new code. When I move position of pix2 on QGraphicsView I can see two images (pix2 under pix1)and it works fine, but images should have opacity level to make a diffusion effect. How should I do it?
The reason it doesn't work is because you are trying to use two different QGraphicsScenes for your QGraphicsView. QGraphicsView can only have one scene.
What you should do, is create only one QGraphicsScene and add your pixmaps there.
QGraphicsScene *scene = new QGraphicsScene(this);
ui->graphicsScene->setScene(scene);
QPixmap pix1(n1);
QGraphicsPixmapItem *pixmapItem1 = scene->addPixmap(pix1);
QPixmap pix2(n2);
QGraphicsPixmapItem *pixmapItem2 = scene->addPixmap(pix2);
pixmapItem2->moveBy(0, pix1.height());
Also your QGraphicsOpacityEffect object is only valid inside the scope you created it in. One way to solve this problem is to allocate it with new.
QGraphicsOpacityEffect *opacity1 = new QGraphicsOpacityEffect;
QGraphicsOpacityEffect *opacity2 = new QGraphicsOpacityEffect;
opacity1->setOpacity(0.5);
opacity2->setOpacity(0.2);
pixmapItem1->setGraphicsEffect(opacity1);
pixmapItem2->setGraphicsEffect(opacity2);
Ok. Thanks #thuga for your help. The problem has been solved. What was wrong? I was setting Opacity twice to two another variables but it was huge mistake. In QGraphicsView we can only declare OpacityEffect variable once, and assign it to multiple variables - just like QGraphicsScene.
Latest version of code (it works fine):
QGraphicsScene *scenaWynikowa = new QGraphicsScene(ui->graphicsViewWynik);
ui->graphicsViewWynik->setScene(scenaWynikowa);
ui->graphicsViewWynik->fitInView(scenaWynikowa->itemsBoundingRect(), Qt::KeepAspectRatio);
QGraphicsOpacityEffect *poziomPrzezroczystosci = new QGraphicsOpacityEffect();
poziomPrzezroczystosci->setOpacity(0.5);
QImage obraz1(s1);
obraz1.scaled(QSize(ui->graphicsViewWynik->width(), ui->graphicsViewWynik->height()), Qt::IgnoreAspectRatio, Qt::FastTransformation);
update();
resize(ui->graphicsViewWynik->width(), ui->graphicsViewWynik->height());
QPixmap mapaPikseli1(n1);
QGraphicsPixmapItem *pixmapItem1 = scenaWynikowa->addPixmap(mapaPikseli1);
QImage obraz2(s2);
obraz2.scaled(QSize(ui->graphicsViewWynik->width(), ui->graphicsViewWynik->height()), Qt::IgnoreAspectRatio, Qt::FastTransformation);
update();
resize(ui->graphicsViewWynik->width(), ui->graphicsViewWynik->height());
QPixmap mapaPikseli2(n2);
QGraphicsPixmapItem *pixmapItem2 = scenaWynikowa->addPixmap(mapaPikseli2);
pixmapItem1->setGraphicsEffect(poziomPrzezroczystosci);
pixmapItem2->setGraphicsEffect(poziomPrzezroczystosci);
pixmapItem2->moveBy(0, 0);
ui->graphicsViewWynik->show();