PDA

View Full Version : Printing a QGLWidget



ToddAtWSU
24th October 2006, 21:26
I am trying to print a QGLWidget to a printer. When I do print, I only get the background of my QGLWidget with its grid lines. My QGLWidget is a plot with titles, axis labels, and tick marks as well as data and grid lines. You can see when the grid lines print where the data should be because the grid lines are missing in small spots where the data lies on top of the grid lines. mpPrinter is created in my constructor and mpCentralFrame holds a QHBoxLayout which only holds my QGLWidget. Instead of grabbing the window of the frame I have also tried just grabbing the QGLWidget and have received the exact same results. I have set up my print function to look like this:


void myClass::print( )
{
if( mpPrinter == NULL )
{
fprintf( stderr, "No printer available\n" );
}
QPixmap imageToPrint = QPixmap::grabWindow( mpCentralFrame->winId( ) );
QPainter painter( mpPrinter );
QRect rect = painter.viewport( );
QSize size = imageToPrint.size( );

mpPrinter->setPrintProgram( QString( "lp" ) );
mpPrinter->setColorMode( QPrinter::Color );
size.scale( rect.size( ), Qt::KeepAspectRatio );
painter.setViewport( rect.x( ), rect.y( ), size.width( ), size.height( ) );
painter.setWindow( imageToPrint.rect( ) );
painter.drawImage( 0, 0, imageToPrint.toImage( ) );
}

Any ideas why my printer only seems to grab the first thing that I draw on the QGLWidget which are the grids. I draw the titles and data after drawing the grid lines by setting up their various viewports on the QGLWidget. Thanks for your help!!

wysota
24th October 2006, 22:56
Have you tried using QGLWidget::renderPixmap()?

ToddAtWSU
25th October 2006, 15:09
I tried using renderPixmap but all I get is a black box for my plot. I replaced line 7 in my code from above with:


QPixmap imageToPrint = mpPlot->renderPixmap( );

Any other ideas on how to print my QGLWidget? The reason I tried screen capturing the frame is I tried this a while back and I had multiple QGLWidgets I needed to print on on piece of paper so I couldn't use renderPixmap for that since I needed multiples QGLWidgets. Thanks for your help though!

wysota
25th October 2006, 18:07
If you only get a black box, then you have some error in the GL rendering code. Do you actually use GL calls to do the drawing on the widget?

BTW. I don't see how multiple GL widgets would prevent you from using renderPixmap. Could you explain that?

ToddAtWSU
25th October 2006, 18:41
I get a black box and the white grid lines. I have saved this to a .ps and .jpg files and they show the same thing I see when I print to the actual printer. Unfortunately what I see on my screen is multiple colored data lines, colored titles, colored axis labels, as well as the black background and white grid lines. All of these are done using standard GL calls onto the QGLWidget. So for some reason the renderPixmap, grabWindow, and even QGLWidget::grabFrameBuffer( ) (http://doc.trolltech.com/4.1/qglwidget.html#grabFrameBuffer) doesn't work. They all return the same results of a black box with white grid lines.

FYI I could not use renderPixmap( ) on my previous project because I displayed multiple QGLWidgets on the screen at one time and I needed to print all the different QGLWidgets onto one piece of paper so it was much easier to add them all to a frame and do a grabWindow( ) on the frame so I would capture all the QGLWidgets in one grab instead of grabbing each on individually and trying to figure out how to put them all onto one piece of paper.

But what I currently do for this project is run all my OpenGL code onto one QGLWidget so renderPixmap should work just fine, but for some reason nothing works although everything is drawn on the screen perfectly. Thanks again for your help!

wysota
25th October 2006, 20:25
Is it enough to call initializeGL() and paintGL() to get the desired output? Because that's what renderPixmap() does. Maybe your code needs something additional? Could we see the painting code (you can omit the "middle" part, just leave the beginning and the end).

ToddAtWSU
25th October 2006, 23:16
The way I have my plot set up is it has many children that all draw to the same QGLWidget. I have one child to handle drawing the data lines, one child handles drawing my titles, one for my axis labels and so on and so forth. In the main parent class is where I have my initializeGL( ) and paintGL( ) calls and inside my paintGL( ) call I have a for-loop that goes through each child and calls their respective paint calls. One confusing part is that the grid lines are on of the children so obviously its paint call is being made. I am not in view of the code right now, so I will try to get at it later today or tomorrow.

The weirdest thing that happens with this is I have my data lines draw on top of my grid lines and when I print out the plot and I just get the black background and the white grid lines, I can see the grid lines missing pixels where the data should be printing onto. Say I have a data line that crosses a grid line at y = 100, at x = 50, then at this point, the grid line will not be drawn there because there is supposed to be data drawn there. So it's like it knows there is data there but refuses to print it. I have also sent this out and saved it as a JPEG and post script file instead of wasting so much paper and I keep getting the same results that I do on the paper. Thanks again for your help and I will try to get at that code later.

wysota
25th October 2006, 23:21
What do you mean by a "child"? A separate object? What kind of class is it? Are they set up correctly in initializeGL() and does paintGL() use them? Maybe you need to call QGLContext::makeCurrent() in their paint routines to set a proper GL context (of course you have to fetch the context earlier in initializeGL)?

ToddAtWSU
25th October 2006, 23:43
What I mean by a child in this instance is I have one object (class) that is derived from the QGLWidget class. This QGLWidget is my plot object I then have other objects of different classes that all have a pointer to my QGLWidget object (the plot). So if I want to have grid lines I create a grid line object and allow it access to my plot object so it can draw directly onto the QGLWidget. This grid line object is what I mean when I say a child object. So the data line object and titles object are also examples of what I mean by a child. Sorry I guess child is the wrong word to use, but they all look up to the main plot object like a parent-figure thus being why I called them children. All of these children or sub-objects are all QObjects so they can render onto the QGLWidget. All of these sub-QObjects have a paint function which is called inside the plot object's paintGL function.

I do not know anything about this QGLContext stuff as I have not used it. I might look into this, but I do not know if I need to handle anything with this. I have not retrieved any contexts anywhere but everything draws correctly on the screen. Is there anything else you need to know? What code do you mean you want when you say just the beginning and ending parts? Thanks again!

wysota
26th October 2006, 00:19
I do not know anything about this QGLContext stuff as I have not used it.
You have used it but Qt managed it for you behind the scene.


I might look into this, but I do not know if I need to handle anything with this.
Yes, you do if you want to render to a pixmap.


I have not retrieved any contexts anywhere but everything draws correctly on the screen.
Because you were using a single context (the GLWidget viewport). If you want to render to a pixmap, Qt creates a temporary context for you and uses it to do the rendering. Look at a fragment of QGLWidget::renderPixmap() docs:

This method will create a pixmap and a temporary QGLContext to render on the pixmap. It will then call initializeGL(), resizeGL(), and paintGL() on this context. Finally, the widget's original GL context is restored.


Is there anything else you need to know? What code do you mean you want when you say just the beginning and ending parts? Thanks again!
I think that in this situation and with your design you won't be able to render to a pixmap. It is possible that grabWidget() uses renderPixmap() as well, which would explain why you get the same result. You need to change your design so that a chain of initializeGL() - resizeGL() - paintGL() calls can result in a valid render (without any need to call any additional methods). Your painting subroutines should handle the context switching - it's all explained in the docs (QGLWidget):


Your widget's OpenGL rendering context is made current when paintGL(), resizeGL(), or initializeGL() is called. If you need to call the standard OpenGL API functions from other places (e.g. in your widget's constructor or in your own paint functions), you must call makeCurrent() first.

I don't know how you implemented your paint methods, but it is possible that you're explicitely rendering to the widget's context instead of the pixmap context. That's why I asked you to post a sample painting routine.

ToddAtWSU
26th October 2006, 18:52
I decided to change my plot background to white and now when I save to a file I see everything else. But everything else is drawn in black instead of the color it should be. It appears that this capturing is only working in black and white. I am using a QGLColormap and using glIndexi( ) to state my colors from the colormap. So I am wondering if this is causing the problem as to why the color is not being captured. We are trying to use 8-bit color and I am wondering if this tries to convert it to 24-bit color before printing/saving it and if it loses its color in the translation. I will look more into this, but if you think you have an idea why the color would be getting lost, I would love to hear it. Thanks!

wysota
26th October 2006, 19:39
Colours shouldn't get lost by converting from a more strict to a less strict colour model, so I doubt that's the case.

ToddAtWSU
13th November 2006, 13:30
I took a little break from this printing item but am now back at it. I got to thinking over the weekend, I am using a colormap for my plot. Could this be the reason I am not capturing the colors? The pixmap that I am creating from my QGLWidget does not know how to associate the colors with the colors stored in my Colormap. My colormap is not referenced off a QGLColormap however. So is there a way to set a colormap for my pixmap to help it capture the colors that are really supposed to be there. All I still get is a black and white image. Thanks again for your help!!!

ToddAtWSU
13th November 2006, 14:58
I found that QImage has a color table so I printed out its size and values. It said its color table was size 17 and all of its colors were either black or white. So I am in the process of changing the color table to match my color map. Unfortunately, my colormap is only 15 colors large, and if I try to change the color table to be only 15 colors big, it crashes because of trying to index beyond its range. Do you have any idea what I can do to get it to only use the 15 colors because I have no idea what to make the extra 2 colors, should they be black or white or what? Thanks again for all your help!

fanat9
13th November 2006, 21:25
This code works fine (glScope - QGLWidget)


void MainWindow::print()
{
QPrinter printer;
printer.setOrientation(QPrinter::Landscape);
QPrintDialog *dialog = new QPrintDialog(&printer, this);
if (dialog->exec() != QDialog::Accepted )
return;

QPainter painter(&printer);
QRect rect = painter.viewport();

glScope->refresh();
QImage image = glScope->grabFrameBuffer();

QSize size = image.size();
size.scale(rect.size(), Qt::KeepAspectRatio);
painter.setViewport(rect.x(), rect.y(), size.width()-5, size.height()-5);
painter.setWindow(image.rect());
painter.drawImage(2, 2, image);
}

ToddAtWSU
13th November 2006, 23:06
I am guessing that code works fine in 24-bit color, but I am dealing with 8-bit color which requires a color table for an 8-bit QImage. You are not setting your QImage's color table, so I doubt it will work for me. I will try it out however just to see if by some miracle it does work in 8-bit.

Anyways, I started manipulating the color table to try and figure out which indices to put my colors into. Two colors that are used heavily by my program are Royal Blue and Black. When I create my colormaps, Black is five positions before Royal Blue(ie the 8th and 13th position). I use QImage::pixelIndex (http://doc.trolltech.com/4.1/qimage.html#pixelIndex) to see what index the image is looking for my color at. Royal blue is always 5 indices higher than black. But I reran the same code 14 times and here are my results. Again I am only using 15 colors but for some reason the QImage says it always has more than 15 colors. So here are the results for 14 straight program executions.

Number of Colors in the Color Table : Royal Blue's Index in the Color Table
21 : 18
20 : 17
21 : 18
19 : 16
19 : 16
18 : 15
19 : 15
18 : 15
19 : 15
19 : 16
19 : 15
19 : 15
18 : 15
19 : 15

In the beginning you can see the Royal blue is always at the 3rd last index. Notice the size is based from 1 and the index is based from 0. But on the 7th run, Royal blue is now t he 4th last index. And then it keeps switching between the 3rd and 4th last index. Why does Royal Blue not always stay in one spot when it runs and why does the size of the color table always change when I use the same 15 colors every time? :o Thanks again for all your help and I will try out that other printing method.

ToddAtWSU
14th November 2006, 22:44
I was correct in that code to print does not work for 8-bit color indexed images. I went to more of an OpenGL approach to solve my printing problem. I called glReadPixels to get the color map index values at every single pixel in the QGLWidget. I then found the red, green, and blue components of each pixel and applied this to the corresponding pixel in an empty but allocated QImage. After creating the QImage pixel by pixel I then used the QPainter to paint to my printer or the QImage::save function to save the image out to a JPG file. This works but i a little slow, but is better than any other alternatives I could find. Thanks for all your help through this and sorry I couldn't find a faster, Qt-only solution.