Andreas
Qt
Qtopia
Posted by Andreas
 in Qt, Qtopia
 on Saturday, May 27, 2006 @ 10:51

Have you ever implemented mouse handling in a Qt widget? Either in a custom widget, (that inherits QWidget directly,) or by extending some existing widget, let’s say QLabel, with interaction abilities? For the most simple cases, mouse handling is quite intuitive in Qt: just reimplement a mouse handler function (virtual function, inherited from QWidget), and add your code there. Press followed by release: open dialog. Or press, drag: draw line. What happens if you ignore or accept mouse events? Should you always call the base class’ implementation of these events? Well sometimes, even Qt does expect some tongue-in-cheek from you as a developer.

This one goes out to those who have ever reimplemented mouse event handlers in Qt.

Mouse events in Qt come in four flavors: Press, Move, Release, and DoubleClick (press-release-doubleclick-release, right?); all delivered to your widget as events, via the four event handlers QWidget::mousePressEvent(), QWidget::mouseMoveEvent(), and so on. At least six other events are related to mouse events, but treated separately: Enter and leave events are delivered to the widget when the mouse cursor enters or leaves the widget, as are hover enter and hover leave events (for widgets the set the WA_Hover attribute). There’s the context menu event QContextMenuEvent, which on some platforms is invoked by the right mouse button (press on Unix, release on Windows), and on others it’s a separate button on the keyboard. And the wheel event QWheelEvent, which represents scrolling activity, typically by a scroll ball on your keyboard, or the wheel on your mouse. None of the six latter are mouse events in Qt, so I’ll stick to the four ones that are. :-)

Mouse press events are delivered by the window system to the widget under the cursor, (remember you can also punch holes in a widget by setting a mask,) at the point when any of the mouse buttons is pressed. From that point on, the window system stores this widget as the mouse grabber, that is, the widget that from this point on, and for as long as any mouse button stays pressed, will continue to receive mouse events. If you then move the mouse cursor, (even outside the widget’s area,) that widget will receive mouse move events, mouse release events, and any additional mouse press events. When you release the last mouse button, the mouse grabber widget loses the mouse grab, and no longer receives mouse events. Now, if you want your widget to track mouse movement without clicking, you can enable something called mouse tracking on your widget, in which case Qt will send mouse move events to the widget if there is no current mouse grabber, and the mouse cursor is hovering over the widget. And, of course, you can always grab the mouse explicitly, but that’s dangerous. Anyway. Phew. :-)

All these events can be accepted and ignored, like with any event in Qt. And for events that can propagate, mouse events being the typical example, understanding the differences is quite crucial.

Say you implement your own mouse event handling. If you ignore the mouse press event in mousePressEvent() by calling event->ignore(), it will first be mapped to your widget’s parent geometry (QWidget::mapToParent()), and then sent to the parent widget. In turn, your parent can also ignore the event, in which case the event will be resent to your parent’s parent widget, and so on. This is what we call event propagation; the event is “propagated to parent”. By default, the mouse handlers ignore events. So if you don’t implement mouse handlers, the events will be ignored. However, if you do implement an event handler, the event is by default accepted! So you don’t actually have to call accept() inside any mouse event handler. Howz dat? ;-)

void MyMouseyWidget:mousePressEvent(QMouseEvent *event)
{
    event->accept(); // A no-op, but I will do it anyway!!!
}

(!) Mouse events propagate to the widget’s parent. Pare-ern-nett. Children are, of course, always on top of their parents [sic]. So the event goes to the widget underneith? No!! :-) No no no. For sibling widgets that overlap, you may expect the event to be propagated to the sibling widget underneith, since siblings can be arbitrarily stacked, but now you know better: Qt delivers the event to the parent.

(!!) Despite having ignored the mouse press, your widget is still the mouse grabber. So your widget will receive the mouse move events, even if your parent actually accepted the press. And recall that the default mouse event handlers ignore the events? If you only implement the press and release event handlers, your mouse move events will be ignored. Which means they will propagate to parent. A common cause of crashes in mouse event handling widgets is the arrival of an unexpected event. :-)

So here’s the core of confusion. If you accept mouse press events, you probably want to accept move and release and doubleclick events too. All four handlers, or none of them, need to be reimplemented. Common mistake: ignoring the press event, accepting the release event, and assuming you got a perfectly nice click. ;-) If you have done this, then feel very guilty ;-). Why? Because your parent widget will get a single press, followed by a move move, perhaps, and that can be enough to throw any well-written state machine off guard.

And, btw, you don’t want to start thinking about event filters until you’ve grasped all this. ;-)

Ehem. I guess that’ll be all for now.

PS: A followup blog will describe how this works in GraphicsView. ;-)

PPS: I guess I forgot to say anything about calling the base implementation. No, ignoring an event does not send the event to the base implementation. It only affects event propagation for events that support it. If you want the base implementation (like for a line edit, the interaction implementation), call it. Otherwise, don’t.

PPPS: There’s a WA_ for everything. WA_NoMousePropagation turns off mouse event propagation for a widget.

lorn
Qt
Qtopia
Posted by lorn
 in Qt, Qtopia
 on Thursday, May 25, 2006 @ 02:47

ok, have to rant here. I have worked with Qtopia and friends for several years now, and occasionally see ‘Qtopia’ spelled as “QTopia’. and for some reason, it bothers me seeing this (perhaps pedanticness?).

Please, if you write about ‘Qtopia’, spell it as Q-t-o-p-i-a, there is no capital ‘T’. and if you have written it this way in the past, please edit whatever you wrote and spell it correctly.

Thank you, thats all for now…

Comments Off
harald
Qt
Posted by harald
 in Qt
 on Friday, May 19, 2006 @ 22:39

Qt 4.1.3 was released to the public today. A lot of work went into this release, and I’m happy that it’s out in the wild now.

I was monitoring the incoming bugreports during the day and nearly fainted when reading about a binary compatibility breakage. We have some extensive test-suites to make sure we don’t break backward compatibility and I manually went through a diff of all the headers. Luckily, it turned out that the person had an unhealthy mix of Qt libraries on his system which caused the problem.

The total breakage on Mac OS X 10.3 with gcc 3.3 turned out to be a compiler bug that can be solved by installing the November update of the Xcode tools. A patch to build the MySQL plugin with older MySQL versions was published soon after the issue was reported.

All in all, 4.1.3 seems to be a pretty smooth release.

Simon
KDE
Posted by Simon
 in KDE
 on Sunday, May 14, 2006 @ 19:58

Lorn, it’s pretty simple: KDE needs a build system that supports flexible configure checks since it uses a lot of 3rdparty libraries. An easy and portable way of finding if a library is installed in the propery version on a system, if a header provides the correct prototype of a function, if a compiler supports a certain option.

Unfortunately that is a feature that qmake does not provide currently.

lorn
Qt
Qtopia
KDE
Posted by lorn
 in Qt, Qtopia, KDE
 on Sunday, May 14, 2006 @ 19:10

There’s a new build tool around these days - cmake. Kde4 supposedly uses it, replacing the autotools configuration stuff.
Now I can understand why someone would want to not use autotools - too complicated for many engineers to understand properly, and IMHO a pain in the a**.

Trolltech has a great tool already - qmake. Which is just as powerful in generating Makefiles. It can be used in large projects - namely, both Qt and Qtopia use it. Opie also uses qmake.
I always thought it a bit odd that KDE does not use qmake.

Perhaps someone who knows both cmake and qmake can enlighten me on why any project should choose cmake over qmake?

Comments?

Andreas
Qt
Qtopia
KDE
Posted by Andreas
 in Qt, Qtopia, KDE
 on Saturday, May 13, 2006 @ 11:22

From time to time I hear about one particular annoyance people have with Qt’s painting architecture - that drawing transformed pixmaps is horribly slow. Scaled pixmap drawing, for example; something like this:

We all know the use case: some image editor that needs to zoom in on an image to plot pixels, or perhaps someone is storing all their icons in one huge image file, and only want to draw a small part zoomed up. Well here’s the typical, yet surprisingly troublesome, code for it:

void MyWidget::paintEvent(QPaintEvent *event)
{
  QPainter painter(this);
  painter.scale(1000, 1000); // or more!
  painter.drawPixmap(x, y, pix);
}

(I don’t recommend running this unless you are very prepared to hit Ctrl-C!)

Now, despite the fact that you’ve asked QPainter to draw a really really humongous pixmap, the widget is typically small, so, it feels a bit wrong that your app suddenly eats up all those 2 gigs of RAM and your machine starts swapping like crazy. What’s Qt using all that memory for? QPainter could just clip away the parts it doesn’t need, right? Or - it could be smart enough to read a small chunk from the source pixmap, scale it, and then draw only the visible part? Well it doesn’t, but there’s a pretty good reason for it [*]. The good news is, you can easily get around this limitation by fixing the above code a bit.

void MyWidget::paintEvent(QPaintEvent *event)
{
  QPainter painter(this);
  painter.scale(1000, 1000); // or more!

  QRect exposedRect = painter.matrix().inverted()
                     .mapRect(event->rect())
                     .adjusted(-1, -1, 1, 1);
  // the adjust is to account for half pixels along edges
  painter.drawPixmap(exposedRect, pix, exposedRect);
}

Don’t we all love matrices? (matrixes?..) ;-)

We take the exposed rect from the event (that gives us scroll/expose optimizations for free - no need to draw the whole pixmap if your widget is only partially exposed), and reverse map it with the painter matrix. That gives us the part of the pixmap that has actually been exposed. And for a 1600×1200 widget displaying a 1600×1200 pixmap at 1000×1000 scale, the exposed rect is… well… typically (0, 0, 2, 1). That’s 2 pixels. QPainter doesn’t need 1600000×1200000 pixels of image data to draw 2 pixels. We know that. And QPainter is glad that we’ve told it that. ;-) Passing the exposed rect to QPainter actually allows drawing in near-instantaneous time.

Isn’t it also cool that this simple stuff actually works for any painter transformation? QGraphicsView uses this for its pixmap items, which makes them very snappy at high transformation levels:

One pixmap item at 1:1 scale. The same zoomed in, no difference in speed.

[*] Yes, QPainter does have a clip rect set on the widget’s rect. And when it is transformed, that clip rect is also transformed. In fact, it has all the information available to determine exactly how much (or little) it needs to do. But when it comes down to the basic pixel plotting (or whatever we need to do to accomodate limitations in the underlying rendering system), it’s pretty slow to determine what will eventually become visible in advance, before QPainter starts its painting. QPainter’s clip can, after all, be arbitrarily complex (even a QPainterPath in Qt 4), so masking the original pixmap before the transformation is done is very likely to make all other drawing slow.

Until next time, bye!

Simon
Qt
Posted by Simon
 in Qt
 on Wednesday, May 10, 2006 @ 08:25

One of the features that I like in a programming editor is the ability to have multiple views of the same file. It’s a little known fact that this is very easy to accomplish with QTextEdit. Try out this snippet of code:

#include <QtGui>

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QTextDocument sharedDocument;

    QTextEdit *te1 = new QTextEdit;
    QTextEdit *te2 = new QTextEdit;
    QTextEdit *te3 = new QTextEdit;
    te1->setLineWrapMode(QTextEdit::NoWrap);
    te2->setLineWrapMode(QTextEdit::NoWrap);
    te3->setLineWrapMode(QTextEdit::NoWrap);

    te1->setDocument(&sharedDocument);
    te2->setDocument(&sharedDocument);
    te3->setDocument(&sharedDocument);

    sharedDocument.setPlainText("Edit me! ");

    QMainWindow mw;
    QSplitter *verticalSplitter = new QSplitter(Qt::Vertical);
    QSplitter *horizontalSplitter = new QSplitter;
    horizontalSplitter->addWidget(te1);
    horizontalSplitter->addWidget(te2);

    verticalSplitter->addWidget(horizontalSplitter);
    verticalSplitter->addWidget(te3);

    mw.setCentralWidget(verticalSplitter);
    mw.show();

    return app.exec();
}
Comments Off
harald
Qt
KDE
Posted by harald
 in Qt, KDE
 on Tuesday, May 09, 2006 @ 19:59

Deploying applications on Linux used to be a nightmare. Featuring dozens of distributions and incompatible library versions, the most reasonable thing to do was to open-source your application and hope someone with a clue will make a package for you.

With the inclusion of both Qt 3 and Qt 4 in the Linux Standard Base (linuxbase.org), creating a binary that will run on all major distributions got a lot easier. This is the quick version of what you have to do:

- Download the LSB 3.1 Software Development Kit
- add /opt/lsb/bin to your PATH
- set the LSB_MODULES environment variable to Qt4 if you are using Qt 4
- run qmake followed by make to build your application

You should see that your application is compiled with lsbc++ instead of g++. lsbc++ is a wrapper around gcc that makes sure your binary will work on all LSB certified systems.

If you are not using qmake, make sure to tell your build system to use lsbc++ instead of g++. For the autotools, this can usually be achieved by setting the CC and CXX environment variables.

Note: To build Qt 3 based applications, you need to have Qt 3 installed. To build Qt 4 applications, all you need is the LSB SDK mentioned above.

Please let me know of any positive/negative experience you have with the LSB.

Andreas
Qt
KDE
Labs
Graphics View
Graphics Items
Posted by Andreas
 in Qt, KDE, Labs, Graphics View, Graphics Items
 on Monday, May 01, 2006 @ 09:58

Hi, everyone!

I’m back in Norway now, after having been all over (half) the world, talking about threads and graphics in Qt 4 for the Developer Road Show 2006. Only one stop left now: Hamburg, next week, which is rumored to be a fairly large event with over 70 people signed up. This has been a good experience all in all, the best part of course getting a great deal of face-to-face-with-customer time. And I have to say, more than anything else, our customers are waiting for QCanvas. So that’s what this blog is about.

Have you seen this robot?

Have you ever used QCanvas? It’s a set of classes for visualizing light-weight graphical shapes on a 2D canvas. With collision detection, view transformations (like zooming, rotation, etc.), and support for managing millions of items efficiently, this tool has been used to write everything from charting apps, CAD / vector drawing apps, games (typically board-, platform- or asteriods-like games), and image editors. The classic QCanvas app, however, is the flow chart / UML editor tool. Imagine an API for the editor widget, and you’re pretty close to what QCanvas is. It’s simply amazing to see what people are using QCanvas for. My most common responses to seeing a customer demo are:

“No way, that can’t possibly be based on QCanvas!? QCanvas doesn’t support that, does it!?”

QCanvas doesn’t support what our customers are using it for. Hehe ;-). At the same time, isn’t it amazing how much you can tweak Qt classes to do exactly what you want them to? I must salute Warwick Allison, who wrote the original canvas, for coming up with a design that provides for so many possibilities. :-D

QCanvas was phased out of Qt with Qt 4. The main reason for this was that we were sure to finish the successor, and provide that as a replacement in 4.0. Unfortunately, the impact of finishing the 4.0 release was so significant that we couldn’t make it. So the replacement project, GraphicsView, was postponed, and QCanvas became part of Qt 4’s Qt3Support library. Now, Qt 4.1 was released shortly after, addressing very many 4.0 issues, and adding the missing classes that we couldn’t finish for 4.0. Unfortunately, GraphicsView wasn’t ready yet.

With 4.2 (hopefully available in Q3/Q4 this year), GraphicsView is finally here. GraphicsView is QCanvas’ official successor. This project has taken into account all the applications that we have seen QCanvas being used for, removing most of the things that people had to hack in earlier, and making proper features instead. The heart of the new project is in its new features.

In this example, QGraphicsView is used to visualize thousands of shapes that all have arbitrary transformations, colors and transparency. Click the image to see a 1:1 screenshot.

Let’s do a little rundown of some new features for GraphicsView. Keep in mind that this is a snapshot of the current set of features, and it may change between now and the final release. Scroll to the end of this article to see how you can influence the final touches of GraphicsView before it’s released.

  • Full double-precision coordinate system. Including negative world coordinates.
  • Item interaction support through event propagation.
    • All items can receive key, mouse, focus, and accessibility events.
    • Mouse events use real coordinates.
    • Some features, like selection and moving, are provided by default.
  • Items can now be rotated, scaled, etc., individually.
  • Items can now have a parent, and a list of children. Item positions and transformations are propagated from parent to child.
  • Support for OpenGL.
    • You can render onto a QGLWidget by inserting a new QGLWidget into the view.
    • The items can either draw using QPainter, or using raw GL, with untransformed coordinates.
  • View transformations now keep the center aligned.
  • The chunks in QCanvas are no longer explicit. The scene is automatically indexed, using an axis aligned BSP tree.
  • Animation support. Out with static item velocity, here comes proper animation path support.
  • The text item has as powerful editing capabilities as QTextEdit. Seriously. ;-)
  • Collision detection has been greatly improved.
“What? Who wouldn’t want to edit text rotated 170 degrees and with a 40% skew?” Four views visualizing a scene with 4 million chips. You can navigate the scene in real-time, zoom in to read the text on each chip, or zoom out and see the big picture: a bunch of happy trolls chilling.

In general, one major message is that the API is now so pleasant to work with that everyone will want to use it!

Now if this features list changes, and it probably will, then please don’t sue anyone for it. ;-) No, seriously, Qt 4.2 is still many months away, and we will adapt to the feedback from when the first snapshots are available. QGraphicsView will be available as soon as we start generating snapshots for 4.2. Around that time, we may also release a beta package. When that happens, everyone must run and grab a snapshot, and start writing GraphicsView applications ;-). And let us know what you think. Where? There are several channels available.

  • Post to qt-interest (http://lists.trolltech.com/) - include GraphicsView in the subject.
  • Customers, you can post suggestions through your regular support channels.

Now stay put, and wait for the first snapshots of 4.2 to appear. There’s a lot of excellent stuff coming up in this release.