Andreas
Qt
KDE
Graphics View
Posted by Andreas
 in Qt, KDE, Graphics View
 on Friday, May 30, 2008 @ 13:57

I’m studying how we can add light and shadow to widgets and items. I want to hear what you think :-). So I’ll just throw out my ideas and see what happens.

Light and shadow are special effects that follow and decorate items, and affect how they are rendered, at the same time as they’re a bit different from regular items / subitems, or subwidgets. For both light and shadow effects, there’s sometimes a need to render outside the item’s boundaries and blend into surroundings. For widgets, we then need to do something to break out of the box model… to make that happen. For inside-widget light effects, we’re limited to what we can do inside paintEvent() or inside the paint() function. I don’t know about you, but I think both light and shadow effects should be primary citizens of the scene graph, which essentially means they are also items. Stack-above / always-on-top (overlays) or stack-below / always-below items (underlays?).

Here’s a flash video showing a sample of what I’m working on. This is based on Graphics View.

The scene consists of 150 elliptoid items with shadows, and one light source that’s flying over it. It’s a bit psychedelic; anyone who loves colliding mice will love this. ;-) Haha.

Let’s start with shadows. In 2D, shadows can be pretty simple. Shadows can be thought of as transformed filled outlines of an object with a dark semitransparent fill and possibly fuzzy/blurred edges, stacked below the object itself. It has an offset, and/or an angle. The offset can be fixed for all items, or relative to one or more light sources. Ideally two shadows on top of each other don’t make a darker shadow, rather they blend with each other along the edges. Exact shadows are expensive to do accurately, and in many cases they’re completely pointless because they’re hard to see in the first place; at least the types of shadows we foresee being used in 2D / 2.5D UIs… Extreme shadows are cool but useless, subtle shadows, however, to me, are/can be beautiful. There seems to be a “market” for simple stupid fast shadows (e.g., bounding rect / bounding region based), pretty good medium-speed shadows (e.g., shape based), and custom shadows. And possibly perfect shadows (e.g., based on paint()) but I don’t really think that market is very big :-)).

As for how shadows are stacked, the easiest way is to just say that an item with a shadow always renders the shadow before itself. This works fine for most cases. But as soon as sibling items come close, and one’s shadow renders on top of the other, it starts looking wrong.

Sibling Shadows 1 Sibling Shadows 2
Take 1: Sibling shadow overlaps Take 2: Sibling shadows behind both

It would be nice if I could set a shadow on our favorite “Drag And Drop Robot” without having lower leg shadows cast on the upper legs, or a shadow from the left leg draw on top of the right leg. I can see the need for both, but what would the API look like? And how would the items be stacked? My solution right now is to add a special flag to QGraphicsItem called QGraphicsItem::ItemStacksBehindParent (the child and its children are behind the parent), which is certainly one step on the way.

The other thing is how the shadow is placed. I don’t know about you, but I hate it how some presentation tools rotate the shadow relative to the item when you rotate and item that has a shadow on it. A picture says more than a thousand words:

Shadow follows item transform Shadow transform relative to scene
No :-( Shadow follows item. Yes! Shadow follows scene :-)).

It’s a bit complicated though because you want the shadow to follow the item, but you wants its offset to be done scene-relative. Turns out it’s just that the item’s local transform is prepended to instead of appended to the parent item’s scene transform. So I added a flag for that: QGraphicsItem::ItemBeforeSceneTransform. Btw this is just research, it’s not in Qt snapshots or anything.

Now light effects come in many different shapes and colors. Light can affect shadows, or it can just add a glow to an item. Light is typically represented by an abstract member of the scene, to which you can calculate distance and angle and apply a suitable effect to your item, or the background, and so on. In styling, we usually fake light big-time by just applying light and dark effects to our button bevels, or use pixmaps that look shiny and sparkly. Which, of course, is in many cases much faster than doing “correct lighting”. I recall once Zack Rusin and I were talking crap, I don’t know who brought it up but if the whole UI was a complex detailed 3D scene graph, lighting could come by itself (I think the discussion started with why buttons in RTL mode don’t have shadows cast as if the sun shine from the upper right).

For blend effects it’s also necessary to apply aftereffects that combine the source and destination. That can be done in two ways - either just via an offscreen buffer, or directly onto the destination device / framebuffer / or so. If we want blend effects I think we need some type of shader integration. Let’s not digress though.

So, ball in your court. What are your thoughts about light and shadow?

Andreas
Qt
Graphics View
Posted by Andreas
 in Qt, Graphics View
 on Friday, May 23, 2008 @ 07:47

Here’s another brain dump… I’m working on adding opacity support to QGraphicsItem and possibly QWidget, and have run into a common inside pattern, with a common problem to solve. Figured I’d share it with those interested.

Many of Qt’s properties propagate, some do in different ways, but the general pattern is that you set or modify a property on a widget or an item, and that property has some effect up and/or down its hierarchy. This is a good API pattern. Propagation is most often what you want, and you’re often surprised when a certain property doesn’t propagate (like QWidget::style).

  • Q{Graphics,}Widget::visible - if you explicitly show a widget, its implicitly hidden descendants are shown, explicitly hide a widget, and visible children are all implicitly hidden
  • Q{Graphics,}Widget::enabled - if you explicitly enable a widget, its implicitly disabled descendants are enabled, explicitly disable a widget, and its enabled descendants are implicitly disabled
  • Q{Graphics,}Widget::palette - you set a mask [*] of modified palette entries, the palette is resolved against its ancestors, and propagates to children
  • Q{Graphics,}Widget::font - you set a mask of modified font entries, the font (or rather -request) is resolved against the widget’s ancestors, and also propagates to children
  • Q{Graphics,}Widget::pos - if you move a widget, its children move with it
  • QGraphicsItem::transform - if you transform an item, its children transform

There are plenty of other properties that propagate. How does this actually work on the inside? Let’s take a look at visibility, as an example. QWidget::visible’s setter, QWidget::setVisible() (which is called by show() and hide()), sets local state only, and doesn’t touch other parts of the hierarchy - so the setter is “cheap”. Checking isVisible() is also cheap, but what you get from isVisible() is local state only; determining whether the widget will actually be visible to you when its window is shown, requires some more work. It’s relatively expensive to resolve “true” or “effective” visibility for a widget (compared to the setter anyway). Luckily though, QWidget’s visibility property is cleverly designed so you very seldom need to call show() or hide() at all, and I don’t know of any case where I’ve ever had to check my widget’s “effective” visibility. This is all done inside Qt. And that’s good, since we can do clever stuff to avoid the resolving expense. Because visibility rarely changes, we could even cache it.

For the record, if you want to know your widget’s effective visibility, there’s a handy QWidget::isVisibleTo() [**] that you can use:

if (widget->isVisible()) {
    // Whether this widget's local state is visible. But if the
    // parent is hidden, this widget is also effectively hidden.
}
QWidget *window = widget->window();
if (window->isVisible() && widget->isVisibleTo(window)) {
   // Yup, unless the window is outside your desktop, and unless
   // you've turned off your monitor, or perhaps you don't even
   // have a monitor at all, it should be visible. ;-)
}

OK, so let’s give an example of the opposite, a propagating property with a cheap effective getter and an expensive setter: QWidget::palette. When you set a palette, this palette is first resolved against the parent palette, and then explicitly propagated to all descendants. This operation can be expensive, depending on how deep your descendant hierarchy is. Why is the setter expensive? Because unlike QWidget::visible, which changes quite frequently during the runtime of an application (think how many times you open and close menus, dialogs, switch tabs, show and hide docks, minimize and maximize windows), palettes are very rarely changed at all, and mostly assigned once only, right before the widget is shown for the first time. So every time the widget is drawn and you need to access the palette, it’s very cheap and easy to access.

Expensive assignment, cheap resolve

If you think your property will be assigned only once-ish, and you need it to propagate, you go for the cheap resolve and expensive assignment. Typically this involves storing local state in every single child, so it’s also a memory vs. speed trade-off. This implicitly makes the individual child widget’s resolve step (”effective property resolve”) cheap. Using the example above, when you set a palette with a blue color entry for QPalette::Link, QWidget::setPalette will immediately walk the child tree and resolve all child widgets’ palettes against this new entry. So if the deepest descendant needs to redraw itself (or paint using QWidget::render), it has a fully resolved copy of the palette in its private section.

Laziness can help a lot here; you can speed up the expensive assignment by setting a dirty state on all non-dirty descendants - now the resolve is will only happen on access. The setter is the same order of complexity though, it’s still expensive. There’s still room to be clever :-).

Expensive resolve, cheap assignment

If you think your property will be assigned several times, and relatively rarely used, you go for the cheap assignment and expensive resolve. QWidget::pos, for example. Changing the position of a widget is something you might not do very many times yourself, but you can be sure that the layout system does this very many times for many different widgets in the same form (although only once per widget). Making the setter propagate its state immediately, like QWidget::palette, would mean form resizing would become a quadratic operation (for each widget, set the position and adjust all descendants’ positions). So instead, you set local state only. Now, when the widget is rerendered, Qt walks the tree to calculate each widget’s effective position, but it only needs to do this only once per paint event.

Caching can help here, tremendously in fact. When you resolve the state of a leaf node, you cache all resolved states for its parents all the way up to the ancestor. Now, if you need to resolve another leaf, there’s a fair chance you’ll hit a node that already has a resolved state for you. The complexity is still O(N lg N), but the cut-off makes the common case O(N).

So the question is - which approach to choose for widget opacity? :-)

— Andreas

[*] From the outside, QFont and QPalette look like actual value classes, but a little-known secret is that they actually represent a request set. When you assign a palette or a font to a widget, you’re not actually overriding any other entries than the ones you explicitly set, and even those might be modified to match what’s available. This is why neither QWidget::font nor QWidget::palette have symmetric setters and getters (the getters return the resolved palette).

[**] Qt 3 has QWidget::isVisibleToTLW(), which is obsolete (but present) in Qt 4. This does the same as widget->isVisibleTo(widget->window()) < = which is much clearer and doesn't use ugly abbreviations. :-)

Andreas
Qt
KDE
Labs
Graphics View
Painting
Posted by Andreas
 in Qt, KDE, Labs, Graphics View, Painting
 on Monday, May 12, 2008 @ 21:44

I’ve always had a dream that Qt’s widget system would be based on a powerful 2D, or possibly even 3D, graphics engine, reaping all the benefits and optimizations that make games run fast. The reason is, coming from a 3D graphics background originally (alright, I was 16 at the time), I’ve always been puzzled by how poor application UIs perform, and how constrained they are, compared to the most basic 2D and 3D graphics engines out there. I think there are many reason for why graphics toolkits provide limited capabilities, and performance, and I’ve been studying this, hoping to help find ways for Qt to be better than the rest. If you ask me why oh why, be warned, I will talk all night. ;-)

I think I could get shot for saying this, but IMO widgets are monolithic beasts. Input, painting, clipping, geometry, events and all are almost always packed into just one class. And that class plugs into a framework that works in only one way. It’s hard to change the way a widget clips without introducing rendering artifacts (draw outside and try to update with the rendered region - oops, the dirty region is autoclipped to the widget rect). It’s impossible to know what a widget looks like without calling paintEvent(), which is a virtual function that might do something different every time it’s called. Multithreaded painting is extremely hard. It’s hard to make the widget paint outside paintEvent() in general. Couldn’t the widget just say what it looks like instead?

The main reason it’s like this, I think, is that UI toolkits’ graphics capabilities are just a hurdle in the path to the ultimate goal, which is to pull together UIs with a nice tool, perhaps targeting your favorite language, and with a cool style and the perfect widget ;-). IOW modeled vertically, after the concrete problem to be solved (which is generally speaking a good approach), and perhaps constrained by the capabilities of the primary target platform. I still think we can learn a lot from looking at UIs as a specialization of a 2D graphics scene API, rather than 2D graphics being an extension to a UI toolkit. We need to model our graphics the way that graphics works (both software and hardware), and not cling so much to a particular problem space. Make sense?

Qt provides both a high-level widget API, a low-level graphics API, and a “mid-level” canvas API called Graphics View. Graphics View is an example of loosening up the constraints of the high-level API, without exposing too many low-level problems such as geometry and dirty region handling. In a way it’s more a 2D graphics engine than anything else. It manages surfaces in 2D (or 2.5D, quasi-3D) space. Originally, we meant for it to be different from QWidget. Obviously, it’s a framework that’s meant for something completely different than widgets (vector graphics, charts, maps, IC design, large scrollable scenes, and so on). What we’ve learned, however, is that the two aren’t really that different. Why can’t I have 1000 widgets in a QScrollArea, for example.

So looking back a few years, you’ll see that we’ve made changes to improve QWidget. Without breaking compatibility of course (which is amazing in itself, shows how Qt’s architecture allows for significant internal changes).

Before Graphics View came out, in Qt 4.1, we loosened up one constraint in QWidget by enabling automatic background propagation. Now, widgets no longer had any default background (remember this change? how about QWidget::autoFillBackground oh, OK now you remember ;-)), and we were one step out of the box-model that widgets traditionally represent (btw when I use the term “box-model” I refer to a widget representing an independent rectangular region of actual screen real estate). Then, in Qt 4.2, we introduced delayed widget creation (DWC), which allows a widget to be constructed without an actual window handle. As part of the DWC work Paul, Matthias and a few others did for 4.2, they had to ensure that widgets had enough local state to independently represent what it otherwise had with a window handle, as without. Well, this had a ripple-effect. During the Qt 4.2 and 4.3 maintenance cycles, we discussed the fact that the only widget that really needs a window handle, is the top-level. With Qt 4.4, Bjørn Erik did some tough refactoring work (which some of us, me included, thought wouldn’t really be feasible), and gave birth to what we call Alien Widgets, the invisible behind-the-scenes beauty. Because of this, in Qt 4.4, a window and a widget are different, despite being the same class, in that the window signs a “contract” with the windowing system to register some screen real estate. This is the same now on all platforms.

Still, after this, you could see some strings that pull QWidget down. Painting outside paint event - using QWidget for screen shot captures, for example, required painter redirection. Each widget still constructed its own QPainter inside paintEvent(), and with it a separate paint engine, despite how all painting ended up in the same paint device: the QPixmap backingstore, which was associated with the same top-level window. Now in Qt 4.4, QWidget has a render-function, much like QGraphicsItem has a paint-function, and the window is, for a subtree of QWidgets, essentially the same as a QGraphicsView is for a scene. Puh.

Background propagation. DWC. Alien Widgets. Shared Painter. You see what’s happening? We’re on a mission. :-) We’re closing the gap between QWidget and Graphics View. And we’re not done, there’s still more to come. :-) There are some things that we cannot easily change, like QWidget’s clipping model for one (it’s opposite from Graphics View) [*]. And that Graphics View can’t make windows like QWidget (arguably, this is solvable though). Plus all our widgets are QWidget-based. Embedding the QWidget-based widgets into Graphics View using WoC is cool, but it’s just not good enough for full-blown exploitation…

Feature by feature, I must say the situation today looks surprisingly good. I’m looking forward to the day when I can simply assign a QGLWidget viewport as QWidget’s window. Or when I can load UIs from Qt Designer into Graphics View. Or in Qt 5, maybe the two are the same thing (the latter is usually only mentioned between some specially interested devs in Trolltech social events after consuming large amounts of beer).

That’s enough blabber for one blog post. I just felt like sharing what’s on my mind these days. This is btw all part of the research we’re doing in Development / Trolltech to support next generation UIs.

[*] QWidget is by default clipped to the intersect of its rect() and the localized exposed region before paintEvent() is called. QScrollArea has no explicit clipping features. Because most widgets don’t draw outside their bounds, item-imposed clipping should have been off by default (obviously expose-clipping is unavoidable for viewports that allow partial updates). And scroll areas should instead explicitly clip the widgets that intersect its edges (widgets outside shouldn’t be drawn, you don’t need clipping for that) (most 2D and 3D graphics APIs use subdivision instead, essentially real-time retesselation of the intersecting primitives to avoid clipping altogether). It’s extremely hard to change this in QWidget today without breaking compatibility. QGraphicsItem has the preferred model in place. But all our standard widgets are written using QWidget, not QGraphicsItem/QGraphicsWidget.

Andreas
Qt
KDE
Posted by Andreas
 in Qt, KDE
 on Saturday, April 19, 2008 @ 12:59

Kevin and I are chilling out in the speakers’ room in FISL. Later today we’ll head back to Thiago’s place in Sao Paulo before we start heading home tomorrow. It’s been a fun week, and I’ve learned a lot. I can’t wait to come home and get my hands into code again.

I had the pleasure of meeting some fellows from INDT here, they showed my some amazing stuff they’d done, and I’m bringing home a bunch of impressions for those who’ll be working on improving our UI. Since this research is going to be interesting for so many, Kevin also asked me to submit a proposal for this year’s aKademy. I think I’ll submit one or two proposals and we’ll see how that goes. Maybe one about our UI research, and one about Qt’s widget internals. We’ll see.

Now there are only three legs left of my journey. Sao Paulo, Frankfurt, then Oslo. Travelling north, it’ll first get much warmer, then much colder again ;-). It’s around 12 degrees C at home, and my favorite season (Spring in Norway is quite amazing). Looking forward to coming home :-).

Andreas
Qt
KDE
Posted by Andreas
 in Qt, KDE
 on Monday, April 14, 2008 @ 14:35

Greetings from Helsinki airport. I came back from my visit to Milan (thanks Riccardo for arranging the sprint, it was great to be there!) at around 02:30am this morning. Now I’m waiting for my connection through Frankfurt to go to Brazil.

I chatted with Thiago, who’s there already, and he said it’s a good 28C degrees in Sao Paolo right now. Here I am with my suede leather jacket, long jeans and a sweater, my suitcase has only black t-shirts in it, no sun glasses and no sunscreen, feeling pretty silly. He-he! ;-)

Milano was fun, I think I’ll want to sign up for more of those events in the future. My idle task was optimizations in QGraphicsScene’s sorting and transform code (I’ve managed a 15-30x speed-up so far, will submit changes to main/4.5 some time when I come back, see patch below). There’s still more optimizations to come, I think I’m just in the flow with this stuff now. Fun to help out with the port to using QGraphicsWidget, and also with some work we did to speed up the SVG cache. Sorry that I couldn’t stay for the API review. :-/

I’m bringing some feedback home to our team in Oslo:

* QtSvg could use some optimization work
* It would be very nice if QtSvg covered the entire SVG 1.1 Tiny standard
* I should write a blog or a Qt Quarterly article (or both) about tricks to make graphics fast / like device space pixmap caching for static content.

Now I’ve been in Helsinki, holding a presentation about our ideas for improving widgets, theming, animations and the like. Fun and interesting! And now I’m headed to Brazil to do the same. Wee!

PS: Apply to Qt it you have problems with performance with deeply nested structures in Graphics View: http://www.andreas.hanssen.name/scene-speed.txt (no warranty!)

Andreas
Qt
KDE
Graphics View
Posted by Andreas
 in Qt, KDE, Graphics View
 on Friday, April 04, 2008 @ 06:57

I’m preparing for an interesting trip… Finishing up Qt 4.4.0, as Thiago blogged about yesterday, I’m spending my time planning activites for 4.5 and 4.6. Input from everybody is crucial these days and months ahead; we want to hear first hand from you all what we should focus on. So I’m going on a trip to meet our fellows and users face to face :-). It goes something like this:

Oslo - Prague - Milan - Helsinki - Frankfurt - Sao Paolo - Porto Alegre - Sao Paolo - Frankfurt - Oslo

Thiago pointed out that I’ll be in four countries in just two days. One is a connection, so it doesn’t really count ;-). But I’m not used to traveling, and I prefer economy over business [*] ;-). I enjoy airtime, so it’ll be fine.

I’ll be in Milan for the April Plasma sprint. Hoping for quality time with some of KDE’s Plasma developers. I’m there to look and listen, hopefully I can help out with something. I’ll share some ideas we have for improvements to Graphics View and Qt in general, we’ll see what comes from that. Looking forward to beer in the evenings too of course ;-). Unfortunately (a slight communication error or lack of attention from my side) I’ll be there a day late, arriving the evening of the 11th, and I have to leave the evening of the 13th, so I’ll only be there for two whole days. Still, I’m very much looking forward to this event :-). Got my laptop, prepared to eat chips, drink coffee and coke, and just help out.

After that I’m heading to Helsinki to co-present some of our plans for Qt for a group of representatives of our future benefactors. :-) At first I was intimidated by the upcoming acquisition. Now I’ve learned that there are some really cool people working for Nokia, they’re down-to-earth, they respect the work that we’re doing, and also the methodology and development culture that Trolltech is known for. Arriving at 00:55 that day (ugh!).

Then I’m going to Sao Paolo (Thiago’s home town!), having some pizza, staying over one night and then going to Porto Alegre to attend the FISL (9° Fórum Internacional Software Livre) to present how Qt is evolving to match tomorrow’s UI requirements across all platforms.

Oh and btw, our plans point in the direction of improving stability, quality, and performance of Qt as a whole. My sketchboard is also covered with ideas on how we can evolve the UI (thinking about styling, benefits and limitations our widget model, animation support, and more).

If you have small or large features you’d like to see in Qt, go ahead and open the threads on qt-interest, QtCentre.org and other forums. Get your ideas out, get feedback for them, and let us know about it.

[*] I’ve never flown business class ever before, and I’m proud of it!

Andreas
Qt
KDE
Graphics View
Posted by Andreas
 in Qt, KDE, Graphics View
 on Tuesday, February 05, 2008 @ 22:27

I recently got a new embedded Linux device to decorate my desk. This is like my childhood dream. :-)) I’m incredibly happy to have the opportunity to work with such a range of awesome stuff at Trolltech. And working on embedded devices is like bringing me back to the days when I was hacking graphics demos on a home made 386 SX 33MHz with a CGA screen ;-) (INF). Here’s my new baby:

My Gumstix

It’s running a simple Graphics View demo app. I’m hereby dedicating my Creative Fridays to play around with this device, it’s actually great fun to see how fast you can make it go. And I must say, speaking as an open source developer, it pleases me to know how easy it is for anyone to get hooked up with one of these babies.

First, Brad and I went onto the Gumstix web site and ordered two LCD packs. This is a decent piece of embedded hardware: you get a 600MHz XScale CPU, with a 10/100MBit ethernet plug, USB and serial connectors, and a nice big 4.3″ touchscreen 18bit display, at around $450. Delivery was swift. Now Brad got his device up in no-time, but me being completely new to this game, I needed some help with getting the screws into the right spot. It took us 15 minutes (now that Brad had figured out how to connect the LCD ;-)).

My Gumstix 2

After assembling the device, we booted it up. It gave us the Linux console prompt out-of-the box, and immediately hooked onto the network/DHCP, so I had ssh access right away. Following the instructions on http://docwiki.gumstix.org/Buildroot I quickly got the basic setup for running cross compilation, and I installed some of the basic tools I need to have a decent console (e.g., bash). I installed my build root under ~/gumstix-buildroot, and added the bin dir of the cross compiler to my PATH:

export PATH=$HOME/gumstix-buildroot/build_arm_nofpu/staging_dir/bin:$PATH

Then I configured Qt 4.4 for Embedded Linux (ftp://ftp.trolltech.com/qt/snapshots gives you the free edition, Qtopia Core source packages). Turned out to be pretty straight forward. I created a qt-4.4 directory on my device in my user’s home dir, and configured Qt like this:

$ ./configure -release -embedded arm -no-webkit 
        -prefix /home/ahanssen/qt-4.4 -hostprefix   
        && make

Notice how I skipped webkit, you probably want to skip all the modules you aren’t planning to deploy on the device. Space counts :-).

After this, I copied the lib files onto the device, and copied an example over, and ran it on the host like this:

$ ./collidingmice -qws

That’s it! For Linux developers, working on a Gumstix device like this is really really fun. I strongly encourage more people to throw themselves into this game. These devices also come in boxed versions, with Wifi and GPS expansion boards, audio chipsets, and more.

Next step: I’m planning to get KDE4 and Plasma to run on this device. Oh yes. ;-)

Andreas
Uncategorized
Aggregated
Posted by Andreas
 in Uncategorized, Aggregated
 on Tuesday, February 05, 2008 @ 17:36

On my way home from work today, I somehow found myself in an awkward situation, laughing as I saw an elder man that I don’t know hit the ground with a thump. Didn’t look too serious, but it most certainly hurt, and it really made my day. Hehe! A wonderful sense of euphoria went through my body as I wondered whether or not I should flip a coin into the air towards this unfortunate fellow, for reasons that probably wouldn’t strike your average observer as being glaringly obvious (in contrary, for also-oh-so-unobvious reasons this might have been perceived as a very rude thing to do, but I don’t quite know why).

So it all started when I was humming and strolling through the streets, past the shops near where I live. Sometimes I enjoy looking at all the funny people around me, at the silly signs people put up outside their stores. I decided to take a trip into the indoor mall. As the weather is pretty harsh sometimes in this country (rain/snow/wind) the mall has this double set of automatic glass doors, mounted like the sluice in a shape ship, to make sure cold air doesn’t get in, and warm air doesn’t get out. So anyway. As I close in on these doors from the outside, I suddenly stop up, and realize that the doors open very slowly today. They’re set up at about half speed. Probably because weather has been bad. I think, “That’s funny, hehe”, and then I continue walking in once the doors are completely open.

Please Spare Some Change

Now, as I’m on my way out again after buying a bag of raisin buns, I see two teenage girls approaching from the outside, all poshed up and chatting like teenage girls do, and suddenly I hear a loud “Bang!” as one of the girls has managed to bang her forehaid on the door as she walked in. “Hehe,” I found myself bursting out aloud. Her reaction was to give me, and then the half-open door the evil eye before shouting “Fy F#¤N i H#”#¤e, er det muuulig!?” [*] And so her friend naturally immediately concurred, “Ja fy f#¤n er det mulig.”

This is truly the age where on one side of the street, your average lady is facing a closed door, and blinking her eyes, hoping that that’ll remove this funny obstruction which she hasn’t encountered before. The door, on its side, is presenting its knob as best it can, wishing it could just say “Here!!! it’s right here!! it’s a KNOB!! you can use it to open me!!”. Your average guy walks straight into the shop window (i.e., that glass thing with no handles, next to the door) BANG, and curses the heavens above, rubbing his forehead and rushing away. These people are angry and upset with a wooden board, or a sheet of glass, failing to meet with their absolute requirements of not being in the way. Ah, astonishing.

And so it happens. Within two seconds after the last girl had just caught her breath, the guy, a man whom I do not know, walks BANG right into the same half-open door. The door did not open (with the speed that he was expecting). His predicted trajectory intersected that of the door-which-moveth-too-slow. ;-) This was an underperforming lame excuse for an automatic door. And so, he fell backwards, dropped his two bags, and whammed into the ground, and of course, he too, cursed out loud, and angry.

And I laughed. I’m evil. I’m sorry. I laughed out loud. :-D

[*] The first girl’s outburst translates to “Oh, darn you bad devil from hell, is this not completely stupid, if at all possible?!?”. The other girl’s response would be something like “Yeah, I soo agree darn, you bad devil, it’s quite stupid, and not likely possible at all.” :-D Norwegian teenage cursing is great fun :-D.

Andreas
Qt
KDE
Labs
Graphics View
Painting
Posted by Andreas
 in Qt, KDE, Labs, Graphics View, Painting
 on Tuesday, January 08, 2008 @ 14:54

We’ve gotten reports about how Graphics View’s performance drops in one special (yet quite common) case. It’s the case when the item is fairly thin, flat, non-square, or simply sparse, where QGraphicsItem’s bounding rect generates something close to worst-case updates that cause repaints of areas that really haven’t changed. A very helpful customer showed me a simple testcase that illustrates this problem, which is quite hard to work around, which again effectively makes it a bug. Start the portedcanvas example, and clear the canvas (CTRL-E). Add a few meshes (ALT-M). Now click a node in the top left corner and drag it down towards the bottom right corner.

portedcanvas screenshot

Performance degrades steadily as you get closer and closer to the bottom-right corner, until the example almost grinds to a halt. The example is very busy doing something close to nothing, at least it seems so. It’s redrawing the bounding rectangle of the edges to the node you’re dragging.

The yellow thing you get in debug mode by setting/exporting QT_FLUSH_PAINT=1 reveals what’s going on.

portedcanvas screenshot using bounding rect updates

To finally hammer this bug down, we’ve added an opt-in feature, QGraphicsItem::boundingRegion(), and a configurable granularity. You can tune the granularity of the bounding region of your line / edge / connector items, and with the wave of a wand, QGraphicsView will magically generate an optimal update region for that item.

So with 4.4, you’ll instead get something like this:

portedcanvas screenshot using granular region updates

What you’re seeing is that each of the two connector lines updates itself using a region consisting of 20-30 rectangles, a tunable granularity.

Should be in tonight’s 4.4 snapshots.

Andreas
Qt
KDE
Labs
Graphics View
Posted by Andreas
 in Qt, KDE, Labs, Graphics View
 on Wednesday, December 19, 2007 @ 07:51

Hm, that title sounds funny with no associated context.

We’re implementing popup support in Graphics View. This is the result of an outstanding bug, or rather essential missing functionality, in the Widgets on Canvas project: support for embedded popups. I.e., popups that pop out of widgets that are embedded into a QGraphicsScene. Such as the QComboBox popup list, the QMenus of a QMenuBar, the context menu in QLineEdit. I could go on. Popups are everywhere! (AAaaarggh….) The bug is easy to see. Add a combobox to a scene, then click on it. As of a few days ago, the popup will show up at the right spot. But you’ll soon uncover the problem. It’s not a popup. It’s just a widget. And uh, plain widgets aren’t half way as cool at being a popup as popups are.

#include <qtgui>

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

    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

    QComboBox *box = new QComboBox;
    box->addItem("Mandag"); box->addItem("Tirsdag"); box->addItem("Onsdag");
    box->addItem("Torsdag"); box->addItem("Fredag"); box->addItem("Lørdag");
    box->addItem("Søndag");

    QGraphicsScene scene;
    scene.addWidget(box)->show();

    QGraphicsView view(&scene);
    view.show();

    return app.exec();
}

This is crazy, when you think of it. First we thought QGraphicsWidget would be fine. From a framework POV, it made perfect sense. We add support for layouts and widget-like properties, and you can write your own widgets to put inside a scene. Then some bright fellow said “what about widgets people already spent time writing, do they have to _port_ them to QGraphicsWidget?”. We all knew the answer, which was an undisputable “yes and no”. Yes, you can, and that should be quite easy, but no, you shouldn’t have to. Making basic things easy, and advanced stuff possible, is a central design philosophy that swirls around in our minds more often than what’s good for us. Then some sucker ends up saying what everyone was thinking, (but couldn’t quite grasp so they chose not to say at risk of being held responsible for some major mistake later on,) which sounded something like “….a proxy widget?”.

Embedded popups - strike 1

Why on earth does the popup show up there? Wait, windows use global coordinates… and the popup adjusts to the current screen’s available geometry….

Yeah, a proxy widget. You know, just “plug it in!”. Ehem. Everybody nods and goes “that makes perfect sense, but,…” and then you see everybody thinking like crazy, before one of our brightest people suddenly breaks and tilts his head to the side, a spring popping out of his ear. Everyone wants to say something, everyone knows a proxy is a can of worms, but some silly person goes “hey, how hard can it be?”. And you know what, we really just can’t get enough of those people. ;-) Sometimes I like to think that many of mandkind’s greatest achievements come from people with a sudden unexplained lack-of-insight, inability to foresee the future, and just plain stupid. It’s like building a house. Everyone knows building a house is crazy, it’ll take way too long, cost more than you have, mess up your relationship, but still some people go “Look sweetheart, it’s just €175000 for the house and another €150000 for the lot!”. And hey, you get a house. Which is great ;-). And have more kids, and you know.

So the project starts, and when it reaches its successful end, everyone, (including the stupid entrepreneur,) wonders how on earth this project could succeed.

Hah! Anyway. Popups! Yes.

What’s the big deal with popups? Popups imply explicit and implicit mouse grabbing, event replays, nesting popups (i.e., nesting grabs), grab-on-show, release-on-hide. Window positioning (converting QWidget global screen coordinates to QGraphicsWidget local parent coordinates). Delete/hide/remove/deactivate/unfocus/disable while grabbing? Implicitly? Explicitly? What if you grab while grabbing? Ungrab without grabbing? You grab the mouse and some other widget does the same, then the other releases the grab. Or better yet, releases _your_ grab. Should the scene propagate the grab to the view? [*] It’s one hell of a state machine! I’m losing sleep thinking about cases where somebody’d explicitly release a grab that some other widget has implicitly gained from a simple mouse press. That’s a silly reason to lose sleep ;-).

Of course, it’ll work, that’s almost the annoying part. When it’s done it’ll work just fine. No bells ‘n’ whistles, just a darn popup. It’s just such a can of worms.

Embedded popups

Now that’s more like it!

[*] No, it probably shouldn’t ;-)

Update 2007-12-20: Here’s a patch that implements popup and mouse grab support. It’s a bit broken, but the basic functionality is there.