PDA

View Full Version : Problem using QwtPlotCanvas::paintCache();



Javier
14th August 2009, 21:49
I wrote a very simple aplication: reads a data from a text file and plots it out. Works fine. Now I want to be able to stream the plot so that the following command runs:
my_simple_application | other_application
Tried the following after the plot is displayed:

application reads data .
plot is created.
.
.
grafScree->show(); // No problem up to here

QwtPlotCanvas grafCanvas(grafScree );
if ( grafCanvas.testPaintAttribute( QwtPlotCanvas::PaintCached ) ) cout << "PaintCached activated" << endl; else cout << "PaintCached not activated" << endl;

QPixmap *pmGraf = grafCanvas.paintCache();
if ( pmGraf->isNull() ) cout << "Pixmap not created."<<endl;
if (pmGraf->isQBitmap() ) cout << "It is a bitmap."<<endl;
cout << pmGraf;

Observations:
a) "PaintCached activated" is reported
b) "Pixmap not created." is reported
c) Allthough the pixmap is not created, something gets streamed out ( ¿garbage?)
The command

./prupixmap | display

where display refers to ImageMagick´s program produces the following error

display: no decode delegate for this image format `/tmp/magick-XXj30Mz2'

///////////////////////////
Using:
Qwt 5.2.0
Qt 4.5.2
Suse Linux 10.1
Dell Optiplex 360
///////////////// Full program

#include <iostream>
using std::cerr;
using std::endl;
using std::cout;
using std::cin;

#include <QApplication>
#include <QString>
#include <QStringList>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <QDateTime>
#include <QPixmap>
#include <QImage>

// Añadidos para incluir Qwt

#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_symbol.h>
#include <qwt_data.h>
//#include <qwt_painter.h>
#include <qcolor.h>
#include <qwt_plot_canvas.h>


// QT_BEGIN_NAMESPACE
// class QRubberBand;
// QT_END_NAMESPACE


int leeVarMet(QString *nArch, QVector<QDateTime> *vDT, QVector<double> *vV );

int leeVarMet(QString &nArch, QVector<QDateTime> *vDT, QVector<double> *vV )
{
QFile aEnt(nArch);

if (!aEnt.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "No se pudo abrir el archivo de entrada" << endl;
return -1;
}
QTextStream entra(&aEnt);

QString linea = entra.readLine(81);
int c =0;

while (!linea.isNull()) {
// cout << qPrintable(linea) << endl;
QStringList lista = linea.split("\t");
bool ok = false;
if (lista.size() == 2) {
QDateTime dtObj = QDateTime::fromString(lista[0], "yyyy-MM-dd HH:mm:ss");
double temp = lista[1].toDouble(&ok);
if ( ok && dtObj.isValid()){
vDT->append(dtObj);
vV->append( temp );
//Problemas con punteros cout << qPrintable( dtObj.toString("yyyy-MM-dd HH:mm:ss")) << " "<< vV[c] << endl;
c++;
} else {
cerr << "Formato de fecha-hora o de punto flotante incorrecto en linea " << c+1 << endl;
return(-2);
}

} else{
cerr << "No hay dos columnas en linea " << c+1 << endl;
return(-3);
}
linea = entra.readLine();
}
aEnt.close();

return 0;
}

int main(int argc, char **argv)
{
QString sN = "pru384.txt";
// QString sN = QString(argv[1]);
QVector<QDateTime> vFechas;
static QVector<double> vVal;

leeVarMet( sN, &vFechas, &vVal );
QApplication app(argc, argv);

QwtPlot *grafScree = new QwtPlot();
grafScree->setTitle("Grafico de Serie de Tiempo");
// insertLegend(new QwtLegend(), QwtPlot::RightLegend);
grafScree->setAxisTitle(QwtPlot::xBottom, "Tiempo");
// grafScree->setAxisScale(QwtPlot::xBottom, 1,2, 0.1);
// grafScree->setAxisMaxMinor(QwtPlot::xBottom,0);
// grafScree->setAxisTitle(QwtPlot::yLeft, "Autovalores");

// Preparando los valores de pru384.txt
int longX = vVal.size();
double *pY = vVal.data();
double *pX = new double[longX];
QDate fRef(2000, 1, 1);
QTime tRef(0, 0, 0);

for (int f = 0; f < longX; f++) {
pX[f] = (double) fRef.daysTo(vFechas[f].date());
pX[f] += tRef.secsTo(vFechas[f].time())/86400.0;
// cout << pX[f]<< " " << pY[f] <<endl;
}

//
QwtPlotCurve *autoVal = new QwtPlotCurve();
autoVal->setData( pX, pY, 384);
QwtSymbol sym;
sym.setStyle(QwtSymbol::Star1);
sym.setSize(4);
sym.setPen(QColor(Qt::red));
sym.setBrush(QColor(Qt::red));
autoVal->setSymbol(sym);
autoVal->setPen(QColor(Qt::black));
autoVal->setStyle(QwtPlotCurve::Lines);
autoVal->attach(grafScree);
grafScree->resize(800,400);
grafScree->show();
// grafScree->replot();

QwtPlotCanvas grafCanvas(grafScree );
if ( grafCanvas.testPaintAttribute( QwtPlotCanvas::PaintCached ) ) cout << "PaintCached activado" << endl; else cout << "PaintCached desactivado" << endl;

// grafCanvas.setPaintAttribute( QwtPlotCanvas::PaintCached, false);

/* fjsoley@supergajo:~/grafiactual/prupixmap> ./prupixmap
PaintCached activado
No se creó el pixmap.*/


QPixmap *pmGraf = grafCanvas.paintCache();
if ( pmGraf->isNull() ) cout << "No se creó el pixmap."<<endl;
if (pmGraf->isQBitmap() ) cout << "Es un bitmap."<<endl;
// QImage imgGraf = pmGraf->toImage();
// pmGraf->save("grafdin384.png", "PNG", -1);
// cout << pmGraf;
// fjsoley@supergajo:~/grafiactual/prupixmap> ./prupixmap | display
// display: no decode delegate for this image format `/tmp/magick-XXj30Mz2'.
//
return app.exec();
}

Javier
18th August 2009, 12:33
Some additional information about paintCache().

If the instruction
QPixmap *pmGraf = grafCanvas.paintCache()
works, is easy to stream the plot. The following code could do it:

***********
<code>
QPixmap *pmGraf = grafCanvas.paintCache();
QImage image = pmGraf->toImage();
QFile outputFile;
if (!outputFile.open(stdout, QIODevice::WriteOnly))
{
cout << "outputFile did not open." <<endl;
}
QDataStream out(&outputFile);
out << image;
</code>
**************
If the output stream is redirected to a file,
my_application >> redirected_file.png
it can not be displayed. The reason is that
the file consists of the image with 4 extra bytes appended at the beginning.
If those 4 extra bytes are eliminated the image displays properly. I read a 16918 bytes
PNG image and streamed it out using a similar code as above . The redirected file was 16943 bytes long and it displayed properly with

tail -c 16939 redirectedfile.png | display

:confused:
In the Qwt documentation I could not find any explanation why paintCache() returns a NULL pixmap. If some one knows please let me know.

Javier
21st August 2009, 23:56
There is really no problem with QwtPlotCanvas::paintCache() .

In my previous post I claimed having trouble using QwtPlotCanvas::paintCache() . Since this function is the only one that returns a QPixmap* in QwtPlotCanvas, I mistakenly assumed that this function is the one that fills the Pixmap with the contents of the plot. Studying the source files one finds that paintCache() only returns a pointer which may be NULL. And that is exactly what I was getting. As it turns out, the contents of the plot are grabbed in the function setPaintAttribute(), allthough the documentation only mentions that it changes the paint atrributes. Tried to use this function with no success, so I grabbed directly the contents.
<code>
const QRect qRC = grafScree->contentsRect();
cerr << "Plot contents rectangle. "<< qRC.x()<< " "<< qRC.y()<< " "<<qRC.width()<<" "<<qRC.height()<<endl;

// Create the PixMap
QPixmap cache = QPixmap(qRC.width(), qRC.height() );

if ( cache.isNull() ) cerr << "Pixmap not created."<<endl; else cerr << "Pixmap created."<<endl;
// Fill the Pixmap with the plot
cache = QPixmap::grabWidget(grafScree,
qRC.x(), qRC.y(), qRC.width(), qRC.height() );
//Also works: QPixmap cache = QPixmap::grabWindow(QApplication::desktop()->winId());
</code>

The whole idea of the exercise was to be able to stream to stdin the plot so that it will be piped to a second application thru stdin. The code to do it is:
<code>
// Change the Pixmap to an image that will be streamed out. (The Pixmap can be streamed out following the same procedure.)
const QImage imSal = cache.toImage();

QFile outputFile;
if (!outputFile.open(stdout, QIODevice::WriteOnly))
{
cerr << "Outputfile not opened" <<endl;
}
QDataStream out(&outputFile);
out << imSal;

</code>
It is important to remember that the output stream consists of the binary image with 4 bytes appended at the beginning. One easy way of getting rid of the first four bytes is using the tail command, For example, with
./streamplot | tail -c 10747 - | display
a 10751 byte stream is trimmed by tail to the last 10747 bytes (the image) and displayed by display .
The attachment contains streamplot.cpp , data file pru384.txt, the PNG image and the project file.:)

Uwe
25th August 2009, 15:24
The cache of the canvas is intended to speed up painting the content of the canvas for situations, where the content hasn't changed ( comparable to the backing store, but tailored for the situation of the Qwt framework ). But the cache is not intended for exporting the plot, what is pretty obvious as you will never see any axes.

Instead you have to use QwtPlot::print what is a much more powerful solution ( even comparable to QPixmap::grabWidget() ) as you can create scalable vector graphics in formats like SVG or PDF.

Uwe