PDA

View Full Version : Problems with QwtPlotRenderer from Qwt 6



mariposa
13th September 2010, 01:51
Hi,

I updated my project to the latest Qwt version from SVN and encountered some problems with the QwtPlotRenderer. I could reproduce them using the included bode example:

When exporting as PDF document, the plot is not correctly painted on the pdf page:
5172

I figured out that this problem is due to the fact, that the printer.setResolution() setting seems to get overwritten by the printer.setPaperSize() function. To solve the problem, I just moved the corresponding line 209 to line 216 of the qwt_plot_renderer.cpp file. The resulting pdf output is correctly painted.



QPrinter printer;
printer.setFullPage( true );
printer.setPaperSize( sizeMM, QPrinter::Millimeter );
printer.setDocName( title );
printer.setOutputFileName( fileName );
printer.setOutputFormat( ( format == "pdf" )
? QPrinter::PdfFormat : QPrinter::PostScriptFormat );
printer.setResolution( resolution );

QPainter painter( &printer );
render( plot, &painter, documentRect );


A further problem is that the exported pictures suffer from a resolution dependent size of the plot elements. If the hardcoded resolution of the rendered images is changed to a higher value, the scales are getting too small compared to the plot canvas. The following jpg image was exported from the bode example with this code in line 216 of the mainwindow.cpp:



renderer.renderDocument(d_plot, fileName, QSizeF(300, 200), 150);


5173

I could not figure out yet how to solve this. I just know that this behaviour did not occur using the print() function from QwtPlot 5.2.

Another problem is concerning the resolution of the produced pdf files if a fitted curve (weeding curve fitter) is used. I use the following code to get my curve fitted:



par->ftspectrumcurve = new QwtPlotCurve();
par->ftspectrumcurve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
par->ftspectrumcurve->attach(par->spectrum_plot);
par->ftspectrumcurve->setCurveFitter(new QwtWeedingCurveFitter(1));
par->ftspectrumcurve->setCurveAttribute(QwtPlotCurve::Fitted, true);


If I now want to export the plot to a pdf document, I use the following code:


QwtPlotRenderer renderer;
renderer.renderDocument(par->spectrum_plot, fileName, QSizeF(200, 150), 300);


No matter what value I use for the resolution, the exported PDF document always has the same file size and the curve only seems to have screen resolution. To get it exported with more points I have to increase the size of the document, but this is, of course, not the way it is intended.

I'd be very thankful for any clues :)

PS: The encountered problems might be platform specific since I only tested it with Mac OS X 10.6.4.

mariposa
30th September 2010, 14:49
I also have problems regarding the mapping of the elements when using custom PlotItems:

In my application I visualize found peaks by labels which are painted on the top part of the plot canvas. Therefore I created a class that inherits QwtPlotItem and I implemented the draw() function accordingly. On screen everything works fine and I like the result very much:

5253

Then I export the plot as graphics file using the following code:



QwtPlotRenderer renderer;
renderer.renderDocument(par->spectrum_plot, fileName, QSizeF(200, 150), 150);


And that is the result:

5254

As you can see, the labels are aligned as if the plot still had screen resolution. The higher resolution of the output image is not taken into account. That's why the labels are displaced.

The problem with the much too small axis persists as well.

Does anyone have an idea how to solve this?

Many thanks in advance :)

Uwe
1st October 2010, 09:42
In my application I visualize found peaks by labels which are painted on the top part of the plot canvas. Therefore I created a class that inherits QwtPlotItem and I implemented the draw() function accordingly
Guess the implementation of your QwtPlotItem doesn't work together with painter transformations, that are enabled when painting to a paint device with a different resolution than the screen resolution.

But without knowing your code it is impossible to say more,

Uwe

Uwe
1st October 2010, 10:22
When exporting as PDF document, the plot is not correctly painted on the pdf page:
And what exactly is wrong with it ?


I figured out that this problem is due to the fact, that the printer.setResolution() setting seems to get overwritten by the printer.setPaperSize() function.
Hm. not here ( Linux/X11, Qt 4.7.0 ) - but all I did is to check if the value of QPrinter::resolution() has been changed after a QPrinter::setPaperSize().

Is this different on the Mac or where did you observe a wrong resolution ?

Uwe

mariposa
1st October 2010, 10:40
I can't paste the whole code for the peak labels since it is quite long. If anyone is interested nonetheless:
https://cutenmr.svn.sourceforge.net/svnroot/cutenmr/trunk/src/peaklabel.cpp

I will instead paste snippets of a minimal example I created. I already expected that it is caused by my (wrong) implementation.

My minimal example is a straight red line that is painted from the top of the canvas to the 0 position of my y-axis. The x-position is "3" in the unit of the x-axis. This is a screenshot of the (expected) result:

5258

I mainly used the code from the cpupiemarker example for implementing my QwtPlotItem:

First, I get the QwtScaleMap from my PlotWidget (which is part of a parameters object that I can easily pass to other objects):



QwtScaleMap xMap = par->spectrum_plot->canvasMap(QwtPlot::xBottom);
QwtScaleMap yMap = par->spectrum_plot->canvasMap(QwtPlot::yLeft);


Then the code for drawing the line:



p->save();
p->setPen(QColor(Qt::red));
p->drawLine(QPointF(xMap.transform(3), 0), QPointF(xMap.transform(3), yMap.transform(0)));
p->restore();


And that's all you can find in my draw() function. Everything else is identical to the cpupiemarker example.

I attach the QwtPlotItem using this code:



peakLabel* pL = new peakLabel(par);
pL->attach(par->spectrum_plot);


And the (unexpected) result of the exported image is:

5262

If you need other parts of the code to evaluate what my mistake is, just ask.

Many thanks :)

mariposa
1st October 2010, 10:52
And what exactly is wrong with it ?



If I open the first pdf file I attached to my first post, I can see that (for example) the whole x axis is outside the page. The plot painted is larger than the page size. But this problem is solved by moving the setResolution line.


Hm. not here ( Linux/X11, Qt 4.7.0 ) - but all I did is to check if the value of QPrinter::resolution() has been changed after a QPrinter::setPaperSize().

Is this different on the Mac or where did you observe a wrong resolution ?


Yes, I observed the wrong resolution on the Mac. I already assumed that this might be a platform specific problem.

Uwe
1st October 2010, 10:56
A further problem is that the exported pictures suffer from a resolution dependent size of the plot elements.
A paper size of 300*200 mm with a resolution of 150dpi means an QImage size of 1772x1181 pixels. When you press 100% in your image viewer you should have text labels in a comparable size to those on screen, otherwise your image viewer will scale the image.

Uwe

mariposa
1st October 2010, 11:24
A paper size of 300*200 mm with a resolution of 150dpi means an QImage size of 1772x1181 pixels. When you press 100% in your image viewer you should have text labels in a comparable size to those on screen, otherwise your image viewer will scale the image.

Yes, that is exactly what I expect from the function. But the exported picture clearly shows much too small axes and labels.
This is the image I already attached:

5263

I created it using the bode example with the modified hard-coded resolution (set to 150 dpi). It is clearly visible that the axes are much smaller than they are on the screen.

I expected that a higher resolution would result in a picture consisting of more pixels (e.g. for publication quality), but with the same relative dimensions of the plot elements. Meaning that the axis labels stay readable even at high resolutions.

But if I set the resolution in the bode example to 600 dpi and display the whole image in my image viewer, the labels are so tiny that they are illegible.

Please bear with me if I totally misunderstood the parameters. :)

Uwe
1st October 2010, 11:47
Another problem is concerning the resolution of the produced pdf files if a fitted curve (weeding curve fitter) is used. ... no matter what value I use for the resolution, the exported PDF document always has the same file size and the curve only seems to have screen resolution.
The curve is fitted and painted using logical coordinates ( = screen resolution ) and translated to "physical" coordinates using painter transformations. Most ( all ? ) sizes in Qwt are in screen coordinates.

If you want to have the tolerance depending the target device resolution you could modify it according to the painter transformation f.e. at the beginning of an overloaded YourCurve::drawCurve().

Uwe

Uwe
1st October 2010, 12:04
But the exported picture clearly shows much too small axes and labels.
This is the image I already attached:
The image has only 10% in size of the image I have created on my box with your parameters. Guess it gets scaled/compressed by the Forum software ?

I expected that a higher resolution would result in a picture consisting of more pixels (e.g. for publication quality), but with the same relative dimensions of the plot elements.
Please recall that a QImage is simply a matrix of rgb values. If you want something scalable you need to use a scalable vector graphics format like PDF or SVG.

Uwe

Uwe
1st October 2010, 12:33
First, I get the QwtScaleMap from my PlotWidget (which is part of a parameters object that I can easily pass to other objects):
Wrong maps - as long as you don't want to paint to the plot canvas widget. Instead you have to use the maps, that are passed as parameters f.e. to QwtPlotItem::draw().

Uwe

mariposa
1st October 2010, 12:51
The image has only 10% in size of the image I have created on my box with your parameters. Guess it gets scaled/compressed by the Forum software ?

Please recall that a QImage is simply a matrix of rgb values. If you want something scalable you need to use a scalable vector graphics format like PDF or SVG.



Yes, the size of the image is decreased by the forum software. But this doesn't matter with respect to the relative size of the plot elements. It is a fact that the axes/labels are getting smaller with increasing resolution. To proove it, I uploaded the original output image to an image hosting service:

http://img80.imageshack.us/img80/5470/bode300.jpg

I just changed line 216 of the mainwindow.cpp from the bode example and entered 300 dpi instead of 85 dpi. It is clearly visible that the axes are so small that they are barely visible if the whole image displayed on screen. Is this the desired behaviour of the function?

In an older version of my application I used the print() function and rendered the plot on a QImage. No matter how large the pixel size of the QImage was, the axes were rendered with the same relative dimension compared to the plot canvas. Meaning a picture of 300*200 pixels looked exactly the same as a picture of 3000*2000 pixels (of course extremely zoomed out to fit the screen). But of course the latter one had a higher resolution.

How can I achieve this result with the rendering implementation of Qwt 6? I just want the user to be able to export high-res pixel images.


The curve is fitted and painted using logical coordinates ( = screen resolution ) and translated to "physical" coordinates using painter transformations. Most ( all ? ) sizes in Qwt are in screen coordinates.

If you want to have the tolerance depending the target device resolution you could modify it according to the painter transformation f.e. at the beginning of an overloaded YourCurve::drawCurve().

Uwe

Many thanks for this hint, I will give it a try :)

mariposa
1st October 2010, 13:00
Wrong maps - as long as you don't want to paint to the plot canvas widget. Instead you have to use the maps, that are passed as parameters f.e. to QwtPlotItem::draw().


Yes, that caused the problem of the displacement :)
I can't believe I missed something that obvious. Thank you very much :)

But the problem with the small axes and labels when using high resolutions persists.

Uwe
1st October 2010, 17:05
It is a fact that the axes/labels are getting smaller with increasing resolution. To proove it, I uploaded the original output image to an image hosting service:
This is an image with a size of 3543x2362 pixels.

When I press 100% in my image viewer ( here gwenview ) my screen is too small to show the plot at once, but - when scrolling - the labels perfectly match to the size of the labels in the plot widget. When I enter "fit to page" the labels are tiny - but this no surprise, when you scale the image heavily down.

Increasing the resolutions leads to larger images. You will have to scroll more, but the labels will keep their size in 100% - and of course the image will be scaled even more in "fit to screen" mode.



I just changed line 216 of the mainwindow.cpp from the bode example and entered 300 dpi instead of 85 dpi. It is clearly visible that the axes are so small that they are barely visible if the whole image displayed on screen. Is this the desired behaviour of the function?
You told the renderer, that you want an image with 300dpi for a size of 30*20 cm. On a device with such metrics (maybe a printer) you will see the labels displayed in a very similar size as on the plot on your screen.

Again: a scalable vector graphics format is what you are looking for - and this is what the new double based render engine of Qwt 6 is made for.

Uwe

mariposa
1st October 2010, 21:07
When I press 100% in my image viewer ( here gwenview ) my screen is too small to show the plot at once, but - when scrolling - the labels perfectly match to the size of the labels in the plot widget.


Yes, this is not my problem. But the height/width (x-axis/y-axis) of the axis in pixels stays the same - no matter what resolution I use. That makes the axis very small on high resolutions. This was not the case using Qwt 5.



Again: a scalable vector graphics format is what you are looking for - and this is what the new double based render engine of Qwt 6 is made for.


I am very sorry to ask this over and over again and probably I am just missing something very basic, but I am not sure whether I explained my intentions in an understandable way.

My application is supposed to support exporting the plot in several graphic formats. On the one hand vector graphic formats (svg and png), and on the other hand pixel graphics (currently png and jpg).

Since not all applications support embedding pdf or svg images (for example Microsoft Word), I need the user to be able to export jpg/png images in a high resolution for printing the plot. I already realized this successfully using Qwt 5 by asking for the desired width/height of the output image in pixels. I created then a QImage with these dimensions and used the print() function to render the plot on that QImage.

The result was an image with the aspect ratio width/height. Even with very high resolutions (lets say 3000*2000 pixels on A5 paper) the axes were very well readable when printed. If always printed on the same paper size, the axes always had the same height/width, no matter what resolution was chosen.

If I now use Qwt 6 and set the resolution to a value so that the resulting image has similar dimensions (3000*2000 for example) I get much too small axes. The image is not usable for printing any more.

I know that SVG/PDF suits the need for printing better, but I want the high-res pixel output to work the way it was possible with Qwt 5.

Please excuse my insistent asking, but I really don't have a clue how to achieve the desired result.

Uwe
2nd October 2010, 00:12
Yes, this is not my problem. But the height/width (x-axis/y-axis) of the axis in pixels stays the same - no matter what resolution I use. That makes the axis very small on high resolutions.
When you are talking about the tick labels read my comments above. If it is about the length of the axis backbones: they are increasing together with the size of the image ( more than 3000 pixels for the x axis in your uploaded image ).



This was not the case using Qwt 5.
Qwt 5 has a method like QwtPlotRenderer::render(), but something like renderDocument with metrics related to the real world simply doesn't exist. So this is obviously wrong.


I am very sorry to ask this over and over again and probably I am just missing something very basic, ...
Obviously.

When you render a plot in a size of 30x20 cm and a resolution of 300dpi to an image it means, that all text labels, symbols etc appear in the same size ( test it with the ruler on your desk) as on the screen, when you display the image on a device with exactly these metrics.

The image itsself only has a width/height in pixels, but this has nothing to do with cm or dpi ( inch !), what are sizes of the real world.

Uwe

Uwe
2nd October 2010, 09:41
When you render a plot in a size of 30x20 cm and a resolution of 300dpi to an image it means, that all text labels, symbols etc appear in the same size ( test it with the ruler on your desk) as on the screen, when you display the image on a device with exactly these metrics.

What's not the case - o.k. now I have understood what you mean.

Uwe

mariposa
2nd October 2010, 09:45
Unfortunately, there is a misunderstanding of my problem.



When you render a plot in a size of 30x20 cm and a resolution of 300dpi to an image it means, that all text labels, symbols etc appear in the same size ( test it with the ruler on your desk) as on the screen, when you display the image on a device with exactly these metrics.


Yes, I absolutely agree. This is exactly the behaviour of the renering function. But this is not the behaviour I need. To illustrate my problem, I exported the plot as pdf file with a paper size of 18*15,32 cm. If I print this, everything is fine because it is a vector graphic. No pixels visible.

Now I used the standard Mac OS X viewing application ("Preview") and saved the pdf file as jpg file. "Preview" (of course) asks for a resolution for the output file, because the page dimensions of the real world need to be converted to pixels. First, I set the value to 72 dpi:

http://img28.imageshack.us/img28/4976/97755256.jpg

If I print this image with the same real world dimensions as the pdf, I get (of course) a pixelated printout. The resolution is too small.

Now I used the very same pdf and saved it with a resolution of 300 dpi as jpg:

http://img638.imageshack.us/img638/2135/300xj.jpg

If I print this image with the same real world dimensions of the pdf, I don't see any pixels, because the resolution is sufficient.

Now I want the user to be able to export the plot as jpg like you can see it in the second link.




Qwt 5 has a method like QwtPlotRenderer::render(), but something like renderDocument with metrics related to the real world simply doesn't exist. So this is obviously wrong.


The image in the second link has a resolution of 2125*1809 pixels. If I rendered the plot to a QImage of the same dimensions using the old print() function, the output looked exactly as in my second link. Now (Qwt 6) the image looks as if I made a screenshot of my application when viewed fullscreen on a monitor with 2125*1809 pixels. And that is definitely not the result I need.

Und nur um ganz sicher zu gehen nochmal auf Deutsch zwischen uns Landsmännern:

Mir geht es nicht darum, dass irgendwelche Achsen zu kurz oder zu lang sind. Und ich weiß auch was dpi sind und dass Pixelgraphiken lediglich eine Höhe und Breite in Pixeln haben und die Auflösung erst dadurch entsteht, dass man es in einer bestimmten Größe darstellt. Ich habe mit der print()-Funktion exzellente Ergebnisse erzielt, habe den Code auf Qwt 6 portiert und bin nun nicht mehr in der Lage das zu reproduzieren.
Ich konnte direkt aus meinem Programm das Bild so exportieren wie du es in meinem zweiten Link siehst. Und das bekomme ich ohne den Umweg über ein externes Programm nicht mehr hin.

Es tut mir wirklich leid, dass ich so penetrant nachfragen muss, aber ich komme der Problemlösung einfach nicht näher.

Uwe
2nd October 2010, 15:57
Ich habe mit der print()-Funktion exzellente Ergebnisse erzielt, habe den Code auf Qwt 6 portiert und bin nun nicht mehr in der Lage das zu reproduzieren.

It's more important to the right thing as to do what Qwt 5 does - nevertheless I modified the bode example in Qwt 5.2 with the following code:


QImage image(3543, 2362, QImage::Format_ARGB32);
image.fill( QColor( Qt::white ).rgb() );
plot->print(image);
image.save( "bode300-52.png" );
The result is the same as with Qwt 6. What exactly was the code, that gave you those excellent results ?

After updating SVN trunk ( painter transformation were killed before ) you could try the following code:


const double scaleFactor = 4.0;
const QSize sz(3543, 2362);

QImage image(sz, QImage::Format_ARGB32);
image.fill( QColor( Qt::white ).rgb() );

QPainter painter(&image);
painter.scale(scaleFactor, scaleFactor);

QwtPlotRenderer renderer;
renderer.render(d_plot, &painter, QRectF( QPointF(0, 0), sz / scaleFactor ) );
image.save( "bode.png" );
Is this what you want ?

Uwe

mariposa
2nd October 2010, 21:05
After updating SVN trunk ( painter transformation were killed before ) you could try the following code:

Is this what you want ?


Many many thanks for your efforts. Unfortunately - even after the SVN update - the exported image is not correctly scaled and the plot is just occupying the upper left quarter.

But I understand how your code works and the effect will probably be very similar to using setDotsPerMeterX/setDotsPerMeterY from QImage.

But this will (as far as I can see it) have the disadvantage that the number of curve points is determined by the size of QRectF( QPointF(0, 0), sz / scaleFactor ). These points are then stretched to the "full" resolution. My tests using setDotsPerMeterX/setDotsPerMeterY resulted in a curve that has visible "steps" and I assume this will also be the case for your proposed solution.

Regarding the code I used successfully with Qwt 5:

When I used Qwt 5 for my project, my code was not yet under revision control and I have overwritten the old parts, therefore I don't have the "original" any more. Until now all efforts to reproduce the "desired" behaviour using Qwt 5 were so far not successful (I know, this is embarrassing). I will go on trying and as soon as I get it working, I will post the code here.

Until I find an acceptable solution for the pixel graphic output I will deactivate these export functions and follow your advice to only offer pdf/svg as graphic format.

Uwe
3rd October 2010, 09:06
Unfortunately - even after the SVN update - the exported image is not correctly scaled and the plot is just occupying the upper left quarter.

Replace the implementation of MainWindow::exportDocument in the bode example with the code I have posted and check the result. Then try to find out what you are doing wrong in your application.


But this will (as far as I can see it) have the disadvantage that the number of curve points is determined by the size of QRectF( QPointF(0, 0), sz / scaleFactor ). These points are then stretched to the "full" resolution.
No, not with the double based render engine of Qwt 6.


My tests using setDotsPerMeterX/setDotsPerMeterY resulted in a curve that has visible "steps" and I assume this will also be the case for your proposed solution.
When painting to a paint device, that is aligning to integer coordinates (like a QWidget or QImage), Qwt manually rounds certain coordinates - instead of letting Qt floor them. Unfortunately this rounding is based on logical coordinates - not respecting the scale factor of the painter. But this is a bug - not a problem in general.

When you modify the implementation of QwtPainter::isAligning like below you might have some effects because of the flooring but it should be very close to what you expect.


bool QwtPainter::isAligning( QPainter *painter )
{
if ( painter && painter->transform().isScaling() )
return false;

...
}

Uwe

PS: Forget about trying to find your code for Qwt5 - you won't succeed. Better try to support the development of Qwt 6

mariposa
3rd October 2010, 13:53
Thank you very much. At first it did not work because (for some reason) after the last SVN co only debug libraries of Qwt were built and therefore the bode example was still linked against the "old" release libraries. But after I modified qwtbuild.pri it worked as expected.

The result looks the way I want it and after modification of the QwtPainter::isAligning function the "steps" are indeed gone.

Unfortunately two issues are remaining:
1) The scaling factor needs to be adjusted according to the desired resolution in order to get reasonably sized plot elements. But it hopefully won't be too difficult for me to find a suitable formula...

2) The text lables in the resulting graphic look a little bit odd:

5264

And the thickness of the lines of the axes (and the grid) doesn't seem to get adjusted. The resulting lines seem to be a little too thin for printing.


Update: I found a way for getting rid of the scaling factor. In my project I successfully use the following code to generate pixel images that can be of any resolution and always look the same when displayed in the same "real world" size:



const double toInch = 1.0 / 25.4; //convert mm to inch

//the user is asked for setWidth and setHeight (intended "real world" size in mm)
const QSizeF size = QSizeF(setWidth, setHeight) * toInch * setResolution;
const QRectF documentRect( 0.0, 0.0, size.width(), size.height() );
const qreal toDPM = 1.0 / 0.0254;

const QRect imageRect = documentRect.toRect();
QImage image( imageRect.size(), QImage::Format_ARGB32 );

//the selected resolution (setResolution) is converted to DotsPerMeter
image.setDotsPerMeterX(setResolution * toDPM);
image.setDotsPerMeterY(setResolution * toDPM);

image.fill(QColor(Qt::white).rgba());
QPainter painter(&image);
QwtPlotRenderer renderer;

renderer.render(par->spectrum_plot, &painter, image.rect());
painter.end();
image.save(fileName, format.toLatin1(), setQuality);



This works so far as intended. Unfortunately, the problem concerning the too thin lines and odd looking texts are still there.

Uwe
3rd October 2010, 14:50
QwtPlotRenderer calculates and sets internally a scale factor depending on the ratio ( logicalDpi ) between plot and target paint device. While my code combines the scale factor assigned in the application code with this ratio, your code changes the ratio itsself. The effect on the internal scale factor is the same.

Concerning the font I recommend to use a different one - mine looks much better. Concerning the lines you should try to assign non-cosmetic pens ( QPen::setCosmetic(bool) ), where you want to have them scaled like the fonts.

For Qwt6.0.0 I will modify QwtPlotRenderer::renderDocument for images and introduce a flag so that the application can explicitely enable/disable the internal rounding of Qwt. Of course the "right" solution is to round to double values, that match the physical resolution of the target device, but I'm afraid it is too late for Qwt 6.0.0.

Uwe

mariposa
6th October 2010, 12:35
Concerning the lines you should try to assign non-cosmetic pens ( QPen::setCosmetic(bool) ), where you want to have them scaled like the fonts.


I've tried to find an easy way to set the pen that is used for drawing the scales, but unfortunately I did not succeed. For the curve and grid it was quite easy (because of the setPen() member function), but I couldn't find something similar for the scales.

Is there an "easy" way, or do I have to subclass QwtScaleDraw and reimplement the drawing functions?

Uwe
6th October 2010, 13:08
QwtScaleDraw explicitely disables cosmetic pens. So I'm afraid you have to overload QwtScaleDraw::drawTick and QwtScaleDraw::drawBackbone to set it again - but I'm not sure if you will see some alignment problems then.

Uwe