Qt memory leak using QPixmap - c++

I'm getting a strange memory leak somewhere in this code. The method is a SLOT connected to a method in another thread. It does 2 things: 1 it updates a text box with the iteration that that the other thread is on. 2 it updates the image shown on the GUI to the image corresponding to that iteration.
It works great for 10-30 iterations, then blows up. Watching its memory usage in the task manager, I can see that it's steady at first, then each iteration increases the RAM usage by about 10%. What can I do to remove the leak?
Transition::Transition(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Transition)
{
ui->setupUi(this);
this->files = files;
imageObject = new QImage();
scene = new QGraphicsScene(this);
}
Transition::~Transition()
{
delete ui;
delete imageObject;
delete scene;
}
The SLOT in question:
void Transition::onCounterChanged(QString counter){
ui->imageCounter->setText(counter);
foldername = ui ->folderName->toPlainText();
int m = counter.toInt();
QString filename = files[m];
imageObject->load(filename);
image = QPixmap::fromImage(*imageObject);
scene->clear();//THIS FIXES THE LEAK
scene->addPixmap(image);
ui->picDisplay->setScene(scene);
ui->picDisplay->fitInView(image.rect(),Qt::IgnoreAspectRatio);
}

I think you do not simply update your image, but create new pixmap item to the scene with:
void Transition::onCounterChanged(QString counter)
{
[..]
imageObject->load(filename);
image = QPixmap::fromImage(*imageObject);
scene->addPixmap(image); // <----- Adds new pixmap item to the scene
[..]
}
So, after 10-30 iterations you have 10-30 pixmap items on your scene. I think, you have to update existing QGraphicsPixmapItem using QGraphicsPixmapItem::setPixmap() function instead of creating a new one on each iteration.

Related

Qt Charts Bar Plot Resize Crash

Situation:
I wrote a bar plot using QtCharts, QChartView, QChart, QBarSeries, QBarSet, QValueAxis and QCategoryAxis and it works fine.
Problem:
When i resize the window or use QRubberbandon the plot, it crashes.
What i tried:
Every other plot i wrote is not affected by that problem, including the ones using QStackedBarSeries. The Problem also occured in another program i wrote in the same situation.
Code - Head:
int D_Plot::Plot_Stat_Multi_MeanMedian(
QChartView *pChartView,
vector<vector<double> > *vv_SetSta,
bool pl_mean,
bool pl_sd,
bool pl_median,
bool pl_adm,
QString name_title,
QString name_categories,
QString name_y)
Code - Body:
//Chart
QChart *chart = new QChart();
chart->setTitle(name_title);
//Sets
QBarSet set_mean("Mean");
QBarSet set_sdev("Standard Deviation");
QBarSet set_medi("Median");
QBarSet set_aadm("Average Absolute Deviation from Median");
//Categories
QStringList categories;
//Series
QBarSeries *series = new QBarSeries();
for(unsigned int set = 0; set < vv_SetSta->size(); set++)
{
if(pl_mean) set_mean.append((*vv_SetSta)[set][c_STAT_MEAN_ARITMETIC]);
if(pl_sd) set_sdev.append((*vv_SetSta)[set][c_STAT_STAN_DEV_SAMPLE]);
if(pl_median) set_medi.append((*vv_SetSta)[set][c_STAT_MEDIAN]);
if(pl_adm) set_aadm.append((*vv_SetSta)[set][c_STAT_ABS_DEV_MED]);
categories.append(QString::number(set));
}
if(pl_mean) series->append(&set_mean);
if(pl_sd) series->append(&set_sdev);
if(pl_median) series->append(&set_medi);
if(pl_adm) series->append(&set_aadm);
chart->addSeries(series);
//Axis
QBarCategoryAxis *X_axis = new QBarCategoryAxis();
X_axis->append(categories);
X_axis->setTitleText(name_categories);
chart->setAxisX(X_axis, series);
QValueAxis *Y_axis = new QValueAxis();
Y_axis->setTitleText(name_y);
chart->setAxisY(Y_axis, series);
//Showing
pChartView->setChart(chart);
return ER_Okay;
You are creating your QBarSet objects locally (i.e. not via the new operator) and passing a pointer to these sets to the QBarSet::append method which AFAIK takes ownership if the objects pointed to i.e feels obliged to de-allocate them when going out of scope. This is bound to fail with locally allocated objects.

How to update/redraw QChart after data is added to QLineSeries?

I am generating some data that I want to chart using QChart & friends. This is my first time using QChart, and so basically what I did was copy the QLineSeries Example and modify it to my needs. My current code looks like this:
quint64 last=0;
quint64 *lastp=&last;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, series( nullptr )
{
ui->setupUi(this);
QChart *chart = new QChart();
series=new QLineSeries(chart);
chart->legend()->hide();
chart->addSeries(series);
chart->createDefaultAxes();
chart->setTitle("Simple line chart example");
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
setCentralWidget(chartView);
GeneticTask *gTask = new GeneticTask();
connect(gTask, &GeneticTask::point, this, [=](QPointF pt) {
// New point added to series
*series<<pt;
// Limit updates to once per second
quint64 now=QDateTime::currentMSecsSinceEpoch();
if(now-(*lastp)>1000) {
qDebug()<<"UPDATE";
// [A] WHAT TO PUT HERE TO HAVE CHART REDRAW WITH NEW DATA?
*lastp=now;
}
}
);
QThreadPool::globalInstance()->start(gTask);
}
When I run this code I would expect my new data to show up in the graph, but it does not, so my question is: How can I have the chart update to show the new data? In other words, what should I put in the code where the comment reads [A]?
Appending a value to QLineSeries using the operator << or the append method should repaint the graph. If it does not happen form some reason, you could trying calling the repaint method on the QChartView.
Here is some code that will center the data once it is added with a cap of at most once per second:
// Global or class scope or
qreal max=-10000000000;
qreal min=-max;
qreal *maxp=&max;
qreal *minp=&min;
// Same scope as before
connect(gTask, &GeneticTask::point, this, [=](QPointF pt) {
if(pt.y()>*maxp) {
*maxp=pt.y();
}
if(pt.y()<*minp) {
*minp=pt.y();
}
*series<<pt;
quint64 now=QDateTime::currentMSecsSinceEpoch();
if(now-(*lastp)>1000) {
qDebug()<<"UPDATE";
chart->axisX()->setRange(0,series->count());
chart->axisY()->setRange(*minp,*maxp);
*lastp=now;
}
}
);
Little correction to the answer above. Qt Documentation says:
void QWidget::repaint()
Repaints the widget directly by calling
paintEvent() immediately, unless updates are disabled or the widget is
hidden. We suggest only using repaint() if you need an immediate
repaint, for example during animation. In almost all circumstances
update() is better, as it permits Qt to optimize for speed and
minimize flicker.
Warning: If you call repaint() in a function which
may itself be called from paintEvent(), you may get infinite
recursion. The update() function never causes recursion.
As result, QChartView::update() works for me.

QGraphicsScene::clear doesn't change sceneRect

I have a QGraphicsScene "scene" and QGraphicsView "graphicsView".
I have a drawing method. When I need redraw all the graphics, I call this method. Everything is OK. But I realized that scene->clear() doesn't change the sceneRect.
Also I tried:
graphicsView->items().clear();
scene->clear();
graphicsView->viewport()->update();
After that, if I get the sceneRect by
QRectF bound = scene->sceneRect();
qDebug() << bound.width();
qDebug() << bound.height();
I expect the bound.width and bound.height to be '0'. But they aren't. I see the previous values everytime. How to clear sceneRect when I clear the scene itself?
It gives some problems that sceneRect remains the same, while using graphicsView->fitInView() method.I use following code:
QRectF bounds = scene->sceneRect();
bounds.setWidth(bounds.width()*1.007); // to give some margins
bounds.setHeight(bounds.height()); // same as above
graphicsView->fitInView(bounds);
Although I completely cleared the scene and added only one rather small rectangle, the rectangle didn't fit into view because of sceneRect remains too big.
I hope I could explain my problem.
From the Qt Docs (emphasis mine):
This property holds the scene rectangle; the bounding rectangle of the scene
The scene rectangle defines the extent of the scene. It is primarily used by QGraphicsView to determine the view's default scrollable area, and by QGraphicsScene to manage item indexing.
If unset, or if set to a null QRectF, sceneRect() will return the largest bounding rect of all items on the scene since the scene was created (i.e., a rectangle that grows when items are added to or moved in the scene, but never shrinks).
Therefore, the only way to shrink the sceneRect is to use setSceneRect.
The better question is why do you need to set scene rectangle? In case you have a smaller scene don't set it. Instead add items to the scene and fit in view based on items bounding rectangle as in my example below:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsRectItem>
#include <QPointF>
#include <QDebug>
#include <qglobal.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
_scene = new QGraphicsScene(this);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->setScene(_scene);
connect(ui->button, SIGNAL(released()), this, SLOT(_handleRelease()));
}
MainWindow::~MainWindow()
{
delete ui;
}
int MainWindow::_random(int min, int max)
{
return qrand() % ((max + 1) - min) + min;
}
void MainWindow::_handleRelease()
{
_scene->clear();
QGraphicsRectItem* pRect1 = _scene->addRect(0, 0, _random(50,100), _random(50,100));
QGraphicsRectItem* pRect2 = _scene->addRect(0, 0, _random(20,50), _random(20,50));
pRect1->setPos(QPoint(40,40));
pRect2->setPos(QPoint(20,20));
ui->graphicsView->fitInView(_scene->itemsBoundingRect(),Qt::KeepAspectRatio);
}
In case you have a large scene with hundreds of items this approach will be slow because:
If the scene rect is unset, QGraphicsScene will use the bounding area
of all items, as returned by itemsBoundingRect(), as the scene rect.
However, itemsBoundingRect() is a relatively time consuming function,
as it operates by collecting positional information for every item on
the scene. Because of this, you should always set the scene rect when
operating on large scenes.

How to add effects in QTDialog during show/hide?

Is there any way to add effects like dialog coming out to maximum size from tiny size when users clicks to show dialog !
like in iphoto when we request to open a dialog, it comes out in the same way!!!
The code which i'm using is :
fade_effect = new QGraphicsOpacityEffect(this);
this->setGraphicsEffect(fade_effect);
animation = new QPropertyAnimation(fade_effect, "opacity");
animation->setEasingCurve(QEasingCurve::InOutQuad);
animation->setDuration(5000);
animation->setStartValue(1);
animation->setEndValue(0.01);
animation->start(QPropertyAnimation::DeleteWhenStopped);
this->setWindowOpacity(0.5);
//this->hide();
//QDialog::reject();
Its not working in hiding case.
Qt Animation Framework gives you a lot of tools to create animation effects. Here is a sample how you can achieve your aim with QPropetyAnimation :
void YourWindowClass::showEvent(QShowEvent* e)
{
//create animation for "geometry" property
QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");
//duration in ms
animation->setDuration(500);
//starting geometry
QRect startRect(900,500,100,100);
//final geometry
QRect endRect(750,350,400,400);
animation->setStartValue(startRect);
animation->setEndValue(endRect);
//starts animation which will be deleted when finished
animation->start(QAbstractAnimation::DeleteWhenStopped);
}

Spacing between widgets in QHBoxLayout

I'm trying to create a GUI with QtCreator. For this GUI, I need to display several images with different sizes next to each other. These images should be touching each other.
I use a QWidget with a QHBoxLayout, where I add the labels (with different sizes) containing the images.
According to related questions, I should use setSpacing and setContentsMargin to remove these spaces, but that won't work; I tried several times.
Here's the code:
QWidget *widget = new QWidget(ui->tagcloud);
QHBoxLayout * l = new QHBoxLayout(widget);
ui->tagcloud->setWidget(widget);
for(int i=0;i<list.size();++i)
{
QLabel *lab = new QLabel;
QPixmap pic((list[i].imgPath).c_str()); //This fetches the image
int sizeChange = 50 + (2*list[i].percent); //Calculates the size of the image
lab->setFixedSize(QSize(sizeChange, sizeChange));
lab->setPixmap(pic);
lab->setScaledContents(true);
l->addWidget(lab);
l->setSpacing(0);
}
However, when I run this, the spacing remains the same (i.e. definitely not zero).
If I add more labels to the layout, the spacing seems to get smaller.
Can anyone explain or help me? Thanks!
Setting spacing to 0 and adding stretch before and after works for me :
l->addStretch();
for(int i = 0; i < list.size(); ++i)
{
QLabel *lab = new QLabel;
QPixmap pic((list[i].imgPath).c_str()); //This fetches the image
int sizeChange = 50 + (2*list[i].percent); //Calculates the size of the image
lab->setFixedSize(QSize(sizeChange, sizeChange));
lab->setPixmap(pic);
lab->setScaledContents(true);
l->addWidget(lab);
}
l->addStretch();
l->setSpacing(0);
Also this works I think
l->setSizeConstraint(QLayout::SetMaximumSize);