PDA

View Full Version : QPainter, scale(), and setFont()



c_07
14th December 2007, 22:52
Hello everyone,

I'm having some trouble with QPainter and scaling. My goal is to implement a simple graphing tool, so I subclassed QWidget and paintEvent(). I want to draw a 20x20 traditional coordinate plane, with (0,0) in the center. So far, I've been able to do so with translate(width()/2, height()/2) and scale(-width()/20, -height()/20), but when I try to use drawFont(), the text is huge and upside down. I would like to have drawFont(QPoint(...), ...) to draw text on logical coordinates. Should I use a separate QPainter...??
Browsing the forums, I have also tried inverting the matrix before drawing text, but it didn't work. I'm pretty lost here. Does anyone have any ideas? Thanks!

marcel
14th December 2007, 23:21
Translating the coordinate system should be enough. Why do you scale it? This actually results in a mirrored and scaled coordinate system.

c_07
14th December 2007, 23:44
Thanks, it should be:


painter.scale(width()/20, height()/20);

Because when I draw a line specifying the coordinates (0,-10) to (0,10), for example, I want it to extend the whole height of the widget (for drawing the y axis).

However, I still have the big text problem when I try to label the tickmarks on the axises. Here's a screenshot: http://www.screencast.com/t/9y2mocrO

Thank you for your help.

jacek
15th December 2007, 13:16
Try this:
void paintEvent( QPaintEvent * )
{
QPainter p( this );

p.save();

// set up "traditional" coordinate system
p.setWindow( -10, -10, 20, 20 );
p.scale( 1.0, -1.0 );
QTransform t = p.combinedTransform();

// draw the graph
p.drawLine( 0, 0, 5, 5 );

// revert to the default coordinate system
p.restore();

// draw labels in the Right Place(tm) using t transform
QPoint pt( 7, 7 );
p.drawText( t.map( pt ), "xxx" );
}

Instead of using save()/restore() you can prepare the right QTransform yourself and map all points.

Another solution is to scale the font, but it might not work with all fonts. Also take a look at Qwt (http://qwt.sf.net).

c_07
16th December 2007, 00:12
Thank you Jacek... you made sense of a complicated subject (I think) very quickly :D
I set the anti-aliasing mode to true and it looked even better.
I would use Qwt except that I am trying to learn the in's and out's of QPainter, and also because I am just trying to develop a very simple application to demonstrate L'Hospital's rule for fun (and extra credit:rolleyes:) in my Calculus class.

c_07
16th December 2007, 03:23
And now...

I subclassed mouseEvent() to detect clicks inside the widget, because based on where the mouse click's position, I want to re-center the graph to that point. In the subclass function I passed event->pos() to my painting widget, but I need to transform the physical coordinates from the mouse click into logical coordinates that I can use. I tried map() and that didn't seem to work...?

jacek
16th December 2007, 15:31
event->pos() returns position in widget coordinates, but that t transform maps plot coordinates to widget coordinates, so you need the opposite transformation ("QTransform invT = t.inverted()").

c_07
16th December 2007, 22:23
Thank you again, Jacek, that worked nicely.

c_07
28th December 2007, 23:43
Well, I'm almost there, but I'm scratching my head over this one... :confused:

I've noticed that when I double click on the center (0,0) when I've zoomed out a ways, it re-orients to (1,-2) instead of staying where it should. In fact, at a 20x20 rectangle, the coordinates are always off by that much, but the error margin decreases slightly as the zoom increases. In fact, when I zoom in to about a 4x4 rectangle, the re-centering works perfectly. Next moving to a 6x6 view rect., the x value returned for the dbl-click event is correct, but the y value is off by one. The error margin keeps increasing as the viewing rect is increased (I zoom out on the graph).

Here's the code for the slot that handles the double-click-on-graph event:

void PainterWidget::centerChanged(QPoint center) {
QTransform pInvertTransform = pTransform.inverted();
QPoint mapped_center = pInvertTransform.map(center);
currentCenter = mapped_center;

int newx = mapped_center.x()+(-rectOriginalFunction.width()/2);
int newy = -mapped_center.y()+(-rectOriginalFunction.height()/2);

//Don't change the dimensions of the graph
int samew = rectOriginalFunction.width();
int sameh = rectOriginalFunction.height();

//Clicking on the origin of a 20x20 graph [Rect(-10,-10,20,20)] results in
// the rect (-9,-12,20,20) and a center at (1,-2)
// etc.
setOriginalFunctionRect(QRect(newx,newy,samew,same h));
}

Any ideas? Thanks!

jacek
29th December 2007, 19:13
It might be caused by some rounding errors, since you perform all calculations on integers. Another possibility is that the "active" point of the cursor is in a bit different place than you would expect.

c_07
30th December 2007, 19:25
If rounding errors were indeed the case, then (and that seems to make sense), how could I best circumvent the problem? I can use floats instead of integers, but I can only use QPainter::setWindow() with a QRect, not a QRectF, and this seems to be the underlying hindrance since I set the QTransform with a call to QPainter::combinedTransform() shortly thereafter.

jacek
2nd January 2008, 19:46
but I can only use QPainter::setWindow() with a QRect, not a QRectF
Yes, but you can avoid some of the errors by changing QRectF to QRect in the last step before the call to setWindow().

QTransform uses floats internally, so first I would check whether mapped_center has correct coordinates and whether changing it to QPointF helps.