Problems with merging multiple QPixmaps - c++

I'm developing a painter-like application and i want my application to have multiple layers for different drawings. For that purpose i have an array, that contains QPixmaps with transparent background and i have a function that merges two QPixmaps (draws one on another). It's done like so:
void MeasuresWidget::MergePixmaps(QPixmap source, QPixmap target)//draw source on target
{
QPainter painter(&target);
painter.drawPixmap(target.rect(),source,source.rect());
painter.end();
imageLabel->setPixmap(target);
}
I'm for 100% sure that array of pixmaps (AllLayers array) contains all the drawing i want. Now i want to consistently merge all the drawings with the original image. Here's how i'm trying to achieve this:
void MeasuresWidget::on_actionAct_triggered()
{
ForMerging = &OriginalImage;
for(int i=0;i<5;i++)
MergePixmaps(AllLayers[i], *ForMerging);
}
where ForMerging is temporary QPixmap object for, well, merging, and OriginalImage is, undoubtedly, QPixMap, that contains original image.
Again, i'm 100% sure, that all layers contain it's image on transparent background. The problem i'm facing is that in result original image is merged only with the last drawing, i.e. with AllLayers[4]. If i make i to run from 0 to 2(not including), for example, the result will be original image merged only with AllLayers[1]. I've struggled with this problem for a period of time and have no idea what might be wrong so i'm seeking for any help possible.

Try merge all QPixmap at the same call to MergePixmaps. For this change your Source variable at MergePixmap function to your AllLayers object like this:
void MeasuresWidget::MergePixmaps(AllLayers *source, QPixmap target)//draw source on target
{
QPainter painter(&target);
for(int i = 0; i < source->lenght();i++){
painter.drawPixmap(target.rect(),source->at(i),source->at(i).rect());
}
painter.end();
imageLabel->setPixmap(target);
}

Related

Qt | QPixmap scaling incorrectly

I have a QPixmap and I set it to resize according to ratio as the window is resized. When the images is first loaded in, it is good and clear for that ratio, but as I resize the image, it distorts all of it.
It manages to go from this:
to this:
Relevant Code:
void MainWindow::resizeEvent(QResizeEvent *)
{
QPixmap pix = ui->labelImage->pixmap()->scaled(ui->labelImage->size(),
Qt::KeepAspectRatio);
ui->labelImage->setPixmap(pix);
}
You're reading the current pixmap from the widget, scaling it, and then writing the scaled pixmap to the widget. Consider what happens if the widget becomes very small and is then resized to something much larger -- you'll get a lot of artifacts due to the scaling transformation used.
I think a better approach would be to store the original full size image in a member of your MainWindow class, say...
QPixmap m_original_pixmap;
Then use a scaled version of that in your resizeEvent member...
void MainWindow::resizeEvent(QResizeEvent *)
{
QPixmap pix = m_original_pixmap.scaled(ui->labelImage->size(), Qt::KeepAspectRatio);
ui->labelImage->setPixmap(pix);
}
Not sure if that will clear everything up but should go some way to removing some of the artifacts.
As a side note, if you're concerned about image quality you might consider specifying Qt::SmoothTransformation as the pixmap transformation mode in the scaling operation.

What is the best way to draw plots in Qt?

I am using Qt to draw a spectrogram like this:
.
I also want to be able to select regions of the graph and edit them, as well as scrolling and zooming around.
I am considering QGraphicsView class but not sure about its performance. As I know, the objects in QGraphicsView are stored individually and drawing a huge amount of dots might impact performance.
What Qt classes should I use to achieve this?
Definitely do not use the QGraphicsItem for every dot/mark. Good approach would be to generate a QPixmap representing your spectrogram and than place this pixmap into the QGraphicsScene as a single item (QGraphicsPixmapItem can be used for that).
To draw on the QPixmap use the QPainter. Maybe a little example will be useful:
const int spectrWidth = 1000;
const int spectrHeight = 500;
QPixmap spectrPixmap(spectrWidth, spectrHeight);
QPainter p(&spectrPixmap);
for (int ir = 0; ir < spectrHeight; ir++)
{
for (int ic = 0; ic < spectrWidth; ic++)
{
double data = getDataForCoordinates(ic, ir); // your function
QColor color = getColorForData(data); // your function
p.setPen(color);
p.drawPoint(ic, ir);
}
}
The getDataForCoordinates() and getColorForData() are just example functions demonstrating how it could work. You have probably different way how to obtain data and it's color.
EDIT
But if you do not need a zoom/pan functionality than even easier would be just to paint directly on the QWidget in the QWidget::paintEvent() and not using the QGraphicsView/QGraphicScene at all.
I would suggest you to use either QCustomPlot or Qwt for this purpose.
Both have very good documentation.
If you choose to work with Qwt take a look at QwtPlotSpectrogram Class.
QCustomPlot is external library, if you are looking for something native to QT then have a look at QPainter class.

Qt - Image merged multiple times gets smaller, includes a "border"

I'm having trouble with Qt.
I'm writing an app that takes a PNG file from database, and allows you to apply multiple layers (each representing a different set of multiple-choice elements (also from database) for different areas of the base PNG), and displays the result within QGraphicsView Widget. However, when I merge the original image with an element, the original gets smaller and gets a "border" around it.
Examples:
Original image - http://i.imgur.com/6kI2M2y.png (sans black area)
The black element is another PNG, in which there's the element and it's surrounded by transparency.
Result after few merges - http://i.imgur.com/wx8UmmK.png
What the hell? The QGraphicsView is 520x520 and doesn't touch any other Widget. I have to get the app to be able to merge the contents of QGraphicsView multiple times without any kind of movement/resizing, as there will be lots and lots of undecided clicking between various elements and layers.
The app works like this: I have two dynamicly generated arrays of buttons with images taken from database, let's say the Base list and Elements list. When I click a button in the Base list, it's loaded into the main QGraphicsView. Now I am supposed to be able to add elements onto that loaded image by clicking buttons on the other list. I do that by getting the content of QGraphicsView widget and drawing the element using a QPainter.
void MainWindow::on_pushButton_clicked() //placeholder for merging testing
{
QPixmap element("C:\\Users\\Petersaber\\Desktop\\warstwa1.png");
QPixmap base = QPixmap::grabWidget(ui->QGraphicsView);
base= oryginal.scaled(QSize(520,520),Qt::KeepAspectRatio, Qt::SmoothTransformation); //resized to QGV
element= element.scaled(QSize(520,520),Qt::KeepAspectRatio, Qt::SmoothTransformation); //resized for QGV
QPainter painter(&base);
painter.drawPixmap(0,0,element);
//the error happens somewhere later from this point
QGraphicsScene* sceneElement = new QGraphicsScene();
QGraphicsPixmapItem* dodany = new QGraphicsPixmapItem(base);
sceneElement->addItem(dodany);
ui->QGraphicsView->setScene(sceneElement);
ui->QGraphicsView->fitInView(sceneElement->sceneRect(),Qt::KeepAspectRatio);
ui->QGraphicsView->show();
}
My guess is that for some reason it takes more than just what's INSIDE of the QGraphicsView, it also takes the 1-2 pixels around it, but why? How to prevent that? I have no idea. I'm a total newbie to Qt, I've only started two days ago.
edit: I am wrong. That's not how it works. The error happens AFTER the merging. Wtf?
It happens regardless of what size the merged images are. After every merge, two white pixels and 1px border always appear between the merged image and the QGraphicsView edge.
edit: I've discovered that the 2px margin is hard-coded into QGraphicsView::fitInView(). I have no idea how to work around that. I've found pieces of code, such as this,
QRectF removeMargin11945(const QRectF& sceneRect, const QSize& viewerSize){
const int bugMargin = 2;
const double mx = sceneRect.width()/viewerSize.width()*bugMargin;
const double my = sceneRect.height()/viewerSize.height()*bugMargin;
return sceneRect.adjusted(mx, my, -mx, -my);
}
, but I don't know how to implement that

Real-time save in Qt

I am creating an app in Qt, similar to scribble (given in sample app). Purpose is to let user draw freehand and once finished, upload these drawing to net. As of now I am saving the drawing as PNG image every 5 seconds, so that the loss of data in an event of unexpected shut-down is minimum (I kept 5 sec to minimize write operations; Real real-time would be highly desirable).
But the problem is, I am saving the entire page as an image every 5 seconds, where the new data added may be few pixels. I was wondering if I could write ONLY the new pixels added into the disk; there is no constraint that I should use PNG while saving; I can convert the data to PNG at the end when user says he is finished.
The piece of code for saving very basic;
void SaveData(const QString &fileName, const char *fileFormat, QImage image)
{
mutex.lock();
QImage visibleImage = image;
if (visibleImage.save(fileName, fileFormat, 50))
{
system("sync");
mutex.unlock();
return true;
} else {
mutex.unlock();
return false;
}
}
I just wonder if REAL real-time save as the pixels getting added if possible..!
Thanks in advance
DK
I suggest you to use tiles to save the image. Split the canvas to many e.g. 64x64 rectangles. And save each rectangle into separate file. When something is changed, you need to rewrite only few small files instead of rewriting the whole picture.
Also there is another dangerous thing in your code. When you run QImage::save, it most likely will erase file contents and write new contents. If the system was shutted down between there two actions, your file will became empty. So it's important to write new contents to a temporary file and then move it to the proper location. Keeping several old version of a file also can be useful. Who knows how the file system will react on the shutdown.
You could maybe use a memory mapped file, something like:
QFile file("rawimage.dat");
file.open(QIODevice::ReadWrite);
// Make sure there is enough memory for the image
quint32 width = 16;
quint32 height = 16;
quint32 bpp32 = 4;
qint64 file_size = width * height * bpp32;
file.resize(file_size);
uchar* mem = file.map(0, file_size);
// make a QImage that uses the file as memory
QImage img(mem, 16, 16, QImage::Format_ARGB32);
// Do some drawing in the image
img.fill(0);
// finished with the file
file.unmap(mem);
file.close();
You will need to check that it actually flushes to disk correctly - I haven't tested this. Ideally on Windows you'd want to be able to call 'FlushViewOfFile' on the memory mapped handle to ensure that modified pages are written to disk. It doesn't look like there is a way of calling this in Qt so you might need to do something operating system specific here to ensure that the disk image is consistent when you want it to be.
You could create a list of QPainterPath objects of the drawn items, which are then rendered to the QImage. You'd need to change the mouse events to do the following: -
Mouse Down : create a new QPainterPath (painterPath) and call painterPath->moveTo
Mouse Move : call painterPath->LineTo
Mouse Up : Store the QPainterPath in a list.
In the paint event, you then pass each new QPainterPath to be drawn
To back up, every n seconds, open up a file and append a stream of the new QPainterPaths since the last time the list was saved.
To restore, open the file, stream them back in and draw them on to the Image.
This can be optimised to check for new items and not to bother saving if none exist. In addition, rather than being time based, you could maintain a number of points that are created in the QPainterPath and only save when it exceeds a certain number.
Note that if you do go down this route, you may also want to store Painter settings with each QPainterPath, if the user can also change things such as pen colour, width etc.
Other advantages come with using QPainterpath - for example, the user could open up one image and then a second, choosing to have it drawn on top of the first.
If you want real-time saving then I suggest you use an uncompressed bitmap format. Changing pixels would be as simple as seeking inside the file to the x-y co-ordinates, usually calculated as
file.seek(y * lineWidth + x * pixelDataSize);
file.write(pixelData);

How to show part of an image using QT?

So, this is my problem: I have this very big image, and I want to show only a specific part of it. After the user pressing a specific key I want the image to move, showing another part of it. The transition from one part of the image to another have to be smooth, animated.
I tried using a QLabel to show the image but it always shows the center of the image, and I do not really know how to make the animation. What would you guys suggest?
Interesting question. Here is something I just tested and seems to work.
Add a QGraphicsView with dimensions the dimensions of the part of the image you want to display, eg 100x100. Create a QGraphicsScene and add it to the view:
QGraphicsScene* pScene = new QGraphicsScene(this);
ui->graphicsView->setScene(pScene);
Now add your image into the scene. In my case I has an image in my resource file. The trick is to set the sceneRect to the position you want to display. I wanted to display an 100x100 part of the image starting from 0,300 :
pItem = pScene->addPixmap(QPixmap::fromImage(QImage(":/photos/image")));
pScene->setSceneRect(0,300,100,100);
In order to test the smooth moving I added a button which when clicked is triggering a slot called move. This slot simply updates the sceneRect. In my simple example I just move the image 100 pixels right. In a real world scenario you could also move it diagonally or vertically and check the image limits.
void move()
{
for (unsigned i=currentX; i<currentX + 100; i++)
{
ui->graphicsView->scene()->setSceneRect(i,300,100,100);
qApp->processEvents();
}
currentX += 100;
}
Notice the currentX variable. It is nothing more than the last image position. Also we must call the processEvents in order to "see" the image moving smoothly.
You could use QPixmap::copy( int x, int y, int width, int height ) to copy a region of the image and display that.
Few options:
Try using a Q3CanvasSprite (within in a Q3Canvas). It is designed more for splitting one image into multiple ones but not for animating between them. You could try abusing it and declaring (say) 100 frames (10 per digit, which would be used as animation steps) or just use the move() method.
Try QGraphicsPixmapItem::setOffset() (within a QGraphicsScene). This might be overkill as QGraphicsScene is made for large number of images).
I'm not sure, but maybe this can be done with QStateMachine and QAbstractAnimation.