PDA

View Full Version : is it a C++ error or Qt/Qwt related?



jiveaxe
17th November 2007, 14:47
Hi, I'm trying some Qwt programming. I have made a simple program to show my problem: in the following application there are two checkboxes controlling if the respective curves are displayed and a pushbutton to get new data for the curves (here I have simulated the data with rand(), but in my real application they are retrieved from a database). The checkboxes are connected to drawCurves() which attach/detach curves to plot. Now if I click on the button the curves are displayed properly but if i toggle the checkboxes to show/hide a curve all curves gone.


#include <QtGui>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <cstdlib>
#include <ctime>

class Dialog: public QDialog
{
Q_OBJECT

public:
Dialog(QWidget *parent = 0);

public slots:
void getData();
void drawCurves();

private:
QwtPlot *plot;
QwtPlotCurve redCurve;
QwtPlotCurve greenCurve;
QCheckBox *redCurveCheckBox;
QCheckBox *greenCurveCheckBox;
};

Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
srand(time(0));

redCurveCheckBox = new QCheckBox("Red Curve");
redCurveCheckBox->setChecked(true);
greenCurveCheckBox = new QCheckBox("Green Curve");
QPushButton *drawButton = new QPushButton("Draw");

plot = new QwtPlot;

redCurve.setPen(QPen(QColor(Qt::red),2));
redCurve.setStyle(QwtPlotCurve::Lines);
redCurve.setRenderHint(QwtPlotItem::RenderAntialia sed);

greenCurve.setPen(QPen(QColor(Qt::green),2));
greenCurve.setStyle(QwtPlotCurve::Lines);
greenCurve.setRenderHint(QwtPlotItem::RenderAntial iased);

getData();

QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(redCurveCheckBox);
rightLayout->addWidget(greenCurveCheckBox);
rightLayout->addWidget(drawButton);
rightLayout->addStretch();

QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(plot);
mainLayout->addLayout(rightLayout);

setLayout(mainLayout);
resize(640,480);

connect(redCurveCheckBox, SIGNAL(clicked()), this, SLOT(drawCurves()));
connect(greenCurveCheckBox, SIGNAL(clicked()), this, SLOT(drawCurves()));
connect(drawButton, SIGNAL(clicked()), this, SLOT(getData()));
}

void Dialog::getData()
{
int size = 1+rand()%10 + 10;

double redCurveData[size];
double greenCurveData[size];
double xval[size];

for(int i = 0; i < size; i++)
{
redCurveData[i] = 0+rand()%10;
greenCurveData[i] = 0+rand()%10;
xval[i] = i + 1;
}

redCurve.setRawData(xval,redCurveData,size);
greenCurve.setRawData(xval,greenCurveData,size);

plot->setAxisScale(QwtPlot::xBottom, 1, size);
plot->setAxisScale(QwtPlot::yLeft, 0, 10);

drawCurves();
}

void Dialog::drawCurves()
{
if(redCurveCheckBox->isChecked())
redCurve.attach(plot);
else
redCurve.detach();

if(greenCurveCheckBox->isChecked())
greenCurve.attach(plot);
else
greenCurve.detach();

plot->replot();
}

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Dialog *dialog = new Dialog;
dialog->show();
return app.exec();
}

#include "main.moc"


Seems that the data associated to each curve is lost when un/checking: if I output the curves data during drawCurves() with this piece of code:



for(int i = 0; i < 9; i++)
std::cout << redCurve.y(i) << " ";
std::cout << std::endl;

after pushing the button I have, for example, 7 5 3 0 7 2 6 6 1, while un/checking one of the checkbox i have, for example


3.30667e-317 -8.91495e+303 3.90058e-314 2.36344e-310 4.94066e-324 4.94066e-324 4.94066e-324 4.94066e-324 0

Maybe is a c++ error but I don't know where.

Regards

jacek
17th November 2007, 15:30
Qwt docs say:
setRawData is provided for efficiency. It is important to keep the pointers during the lifetime of the underlying QwtCPointerData class.
but you pass pointers to local variables to every call to setRawData().

jiveaxe
17th November 2007, 15:36
Sorry if I ask, but how should I modify the code?

Thanks

mcostalba
17th November 2007, 19:15
Allocate data on the heap.

Put somewhere in your private section:



QVector<double*> samplesPtr;
int samplesSize;


Then modify as follows




void Dialog::getData()
{

samplesSize = 1+rand()%10 + 10;

double* redCurveDataPtr = new double[samplesSize];
double* greenCurveData = new double[samplesSize];
double* xval = new double[samplesSize];

samplesPtr << redCurveDataPtr << greenCurveData << xval;

.......

}



Of course you need to add a Dialog::freeData() to release the memory when no more needed. That's the reason you may want to set samplesPtr and samplesSize as class memeber data.

mcostalba
17th November 2007, 19:32
Interestin enough delete[] works also from copied pointers, it means that you don't need

int samplesSize;

as member data but a local variable will suffice and freeData() became:




void Dialog::freeData()
{
for(int i = 0; i < samplesPtr.size(); i++)
delete[] samplesPtr.at(i);
}

jiveaxe
17th November 2007, 20:18
Thank you, mcostalba, your code works good.

Regards

jiveaxe
17th November 2007, 20:45
One more question: the freeData() function must be called in the destructor only or before each getData()?

Thanks

mcostalba
17th November 2007, 21:48
After



redCurve.setRawData(xval,redCurveData,size);
greenCurve.setRawData(xval,greenCurveData,size);


you don't need the previous samples anymore (it seems) so you can call freeData() after that, of course delaying assign of new pointers until after the free of old data:




void Dialog::getData()
{
int size = 1+rand()%10 + 10;

double* redCurveDataPtr = new double[size];
double* greenCurveData = new double[size];
double* xval = new double[size];

for(int i = 0; i < size; i++)
{
redCurveData[i] = 0+rand()%10;
greenCurveData[i] = 0+rand()%10;
xval[i] = i + 1;
}

redCurve.setRawData(xval,redCurveData,size);
greenCurve.setRawData(xval,greenCurveData,size);

// old data is no more needed now
freeData();

// now we can add newly created samples pointers
samplesPtr << redCurveDataPtr << greenCurveData << xval;

plot->setAxisScale(QwtPlot::xBottom, 1, size);
plot->setAxisScale(QwtPlot::yLeft, 0, 10);

drawCurves();
}

mcostalba
17th November 2007, 21:54
A couple of bugs ;)

Because pointer vector is reused freeData() should be:



void Dialog::freeData()
{
for(int i = 0; i < samplesPtr.size(); i++)
delete[] samplesPtr.at(i);

samplesPtr.clear();
}


And also remember to add a freeData() call in the d'tor to be sure the last samples data set is properly released when closing the dialog.

jiveaxe
18th November 2007, 10:24
Inserting freeData() where you say gives me strange graphs. Removing it works again right.

Regards