Henrik Hartz
Qt
WebKit
Itemviews
Posted by Henrik Hartz
 in Qt, WebKit, Itemviews
 on Thursday, July 03, 2008 @ 11:03

Hi there. This is my first blog post, so I guess introductions are in order; My name is Henrik Hartz, and work as a Specialist with Product Management in Troll^W Qt Software. In Product Management I do all kinds of stuff, ranging from working with requirements, specifications, product releases, meeting customers, thinking about the future of Qt, the list goes on.. Recently I’ve had the pleasure of focusing on WebKit.

WebKit is a fantastic technology. Being able to stick web content into your application is simply amazing, there’s just about anything out there you can show! And, a lot of people know HTML, so making use of this to add content to your application enables you to (ab)use your Graphic Designer buddies.

But, WebKit is so much more than just a way to show HTML in your app. You can actually do anything a browser can - and much more! Experimenting with WebKit over the last couple of months I’ve made an example that a lot of people seem to be interested in; Using WebKit to host a Google Maps component.

I wanted to show where our offices are located in the world. So, I started with a simple QStandardItemModel that would read in addresses from a txt file. In the constructor, we read in the text file - using the first field of the comma separated line as the display role, and the rest as the address stored in the UserRole;

QStringList addressLines = address.split(",");

QStandardItem *item = new QStandardItem;
this->insertRow(this->rowCount(),item);

item->setData(addressLines.takeFirst(), Qt::DisplayRole);
item->setData(addressLines.join(",").trimmed(),Qt::UserRole);

With this model in place, we simply set it on the list view of our GUI design (yeah, I like using Qt Designer - it’s fast!);

ui.treeView->setModel(new AddressModel(this));

In the GUI we’ve also placed a web view, which is pointed to a web page under our control. This page shows a Google Maps control. So here is the GUI with the address list and QtWebKit component;

GUI design

This doesn’t do much on it’s own - so we promote the QWebView class to a custom component “Map”, defined by map.h in Qt Designer. This component has some additional services that allow us to update the map, specifically;

void geoCode(const QString &address);

What we want is to update the map based on the entries in the list view when they are clicked. We do this in a slot method connected to the clicked signal of my view. Here we can access the data of the item being clicked, extract the address and ask the map component to geo-code it;

void MainWindow::showItem(const QModelIndex &idx)
{
ui.map->geoCode( idx.data( Qt::UserRole).toString() );
}

The geoCode method will use QtWebKit’s capability of calling JavaScript functions in the web environment. Here we’ll use the Google Maps API to get Google to return a coordinate of the specified address in CSV format, using the asynchronous QNetworkAccessManager::get(const QNetworkRequest &request) API;

void Map::geoCode(const QString &address)
{
QString requestStr( tr("http://maps.google.com/maps/geo?q=%1&output=%2&key=%3")
.arg(address)
.arg("csv")
.arg("GOOGLE_MAPS_KEY") );

manager->get( QNetworkRequest(requestStr) );
}

In another method connected to the QNetworkManager::finished(QNetworkReply*) signal, we parse out the coordinate returned in CSV format from Google, and pass it to a method which updates the map component appropriately;

void Map::loadCoordinates()
{
QStringList scriptStr;
scriptStr
< < "var map = new GMap2(document.getElementById(\"map\"));"
< < "var bounds = new GLatLngBounds;"
< < "var markers = [];"
< < "map.setCenter( new GLatLng(0,0),1 );";

int num=-1;
foreach( QPointF point, coordinates ) {
scriptStr < < QString("markers[%1] = new GMarker(new GLatLng(%2, %3));")
.arg(++num)
.arg(point.x())
.arg(point.y());
}

scriptStr
< < "for( var i=0; i<markers.length; ++i ) {"
< < " bounds.extend(markers[i].getPoint());"
< < " map.addOverlay(markers[i]);"
< < "}"
< < "map.setCenter(bounds.getCenter());";

this->page()->mainFrame()->evaluateJavaScript( scriptStr.join("\n") );
}

In the final call, we tell QtWebKit to evaluate the JavaScript we have synthesized, and the web view updates with the appropriate location;

Qt Software Address book

The snippets shown here are simplified to some extent, but you can find the complete source code here. Please remember to put the HTML for the map component on a server you control - and replace the GOOGLE_MAPS_KEY with your own key (you need to register and bind to a domain for it to work) in both map.cpp and index.html.

Enjoy!

sroedal
Qt
Graphics View
Graphics Items
Graphics Dojo
Posted by sroedal
 in Qt, Graphics View, Graphics Items, Graphics Dojo
 on Friday, June 27, 2008 @ 13:47

To breathe some new life into the graphics dojo, here’s an example of how to put widgets on top of an OpenGL scene using QGraphicsView. By leveraging the synergy (tounge in cheek) of the OpenGL module and graphics view’s in 4.4 new widget capabilities, the long lacking feature of putting widgets in OpenGL becomes possible. All that’s needed is to set a QGLWidget as viewport on the graphics view, override QGraphicsScene::drawBackground() to do the OpenGL rendering, and add widgets and other graphics items to the graphics scene as usual. The result can be seen in the screenshot below, which shows a simple obj-model viewer application written in just a couple of hundred lines of code:

Widgets on top of an OpenGL scene

The source code is available by “svn checkout svn://labs.trolltech.com/svn/graphics/dojo/modelviewer”. Have fun!

Morten
Threads
Qt Concurrent
Posted by Morten
 in Threads, Qt Concurrent
 on Friday, June 27, 2008 @ 12:24

The Wide Finder Project is an informal parallel programming competition where the task is to compute web site statistcs from a 218-million line access log. Each entry will be benchmarked on a Sun T2000 with support for 32 hardware threads, giving lots of opportunities for parallel processing.

What makes this really interesting is that the project is not only about performance, but rather about writing code that scales to many CPU cores with as little extra programmer effort as possible. Some results are already in, with OCaml currently in the lead performance-wise.

Each log line looks something like this:

www.example.com - - [17/Jun/2007:21:37:17 -0700] “GET /ongoing/ongoing.atom HTTP/1.1″ 304 - - “-” “Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4

Eager to bring the performance lead back to C++ where it belongs I started out writing my own implementation using QtConcurrent and the other Qt APIs. Briefly explained, the code uses QtConcurrent::mappedReduced to multi-thread the code, and then QByteArray::split() twice to iterate over each word in each line. The current version computes the number of hits for each page.

Results: (parsing 100K lines on an 8-core 2.8 GHz Mac Pro)

1 Thread
real 0m2.283s
user 0m1.141s
sys 0m0.249s

2 Threads:
real 0m1.446s
user 0m1.853s
sys 0m0.271s

1.6X speedup.. not too bad.

4 Threads:
real 0m3.186s
user 0m10.643s
sys 0m0.407s

1 second slower that the single-threaded version.. this does not bode well.

8 Threads:
real 0m7.000s
user 0m46.922s
sys 0m0.724s

Seven seconds! We get a nice linear scaling of the run-time as we increase the number of threads, but unfortunately in the wrong direction. The program us spending a lot of user time doing something though, so let’s run it through Shark and see what’s going on:

spinlock.png

80% in a spin-lock used by malloc/free. But who is calling malloc that much?

spinlock-split.png

Aha.. QByteArray::split(). While being a very convenient API, split() was clearly not designed for heavy parsing like this. Still, I’d like a less catastrophic impact on the run-time when adding threads, even if the program really is calling malloc/free to often. Let’s try with the ptmalloc memory allocator instead:

8 Threads:
real 0m0.908s
user 0m3.784s
sys 0m0.533s

ptmalloc is used in GNU/Linux though the GNU C library and scales much better on multicore systems. The program itself still does not scale beyond 4 threads, but it does not get significantly worse either when adding threads. I guess it’s debatable whether or not this is qualifies as a bug in the Darwin memory allocator, but at least ptmalloc shows that it is possible to do better.

That’s all for now :) For the next installment I’ll try to get better scaling, at the expense of increasing the developer effort.

Sebastian Pipping
Uncategorized
Labs
Documentation
doxygen2qthelp
 in Uncategorized, Labs, Documentation, doxygen2qthelp
 on Friday, June 20, 2008 @ 17:20

Intro

Hello! I have something to show you. I’ve been working on a tool that teams up with Doxygen to produce .qch files (Qt Compressed Help) for use with Qt Assistant from your code documentation - a feature that has been asked for repeatedly since 2003 ([1][2][3]..). In this post I will introduce doxygen2qthelp, the answer to your request.

Download

You can grab the current code from our Subversion repository like this:

$ svn co svn://labs.trolltech.com/svn/documentation/doxygen2qthelp

Building

I should mention doxygen2qthelp requires a Qt version later than 4.4.0, which at the moment means a snapshot of 2008-04-24 or later. If you start building now be sure to configure with -assistant-webkit. More about this later.

Background

Doxygen has been able to produce .chm files (Compressed HTML) from properly documented code. To be precise Doxygen does not create the .chm files itself: instead it produces a bunch of files that are used to instruct the Microsoft® HTML Help Compiler (hhc.exe). These three files contain the table of contens (file index.hhc), the list of keywords appearing in the index (file index.hhk), and a project description (file index.hhp). The former two of these are written in some form of "perverted HTML". What doxygen2qthelp does: take these files, parse through the soup, and create a ready-to-go .qch file for viewing in Qt Assistant. Alternatively, a fine-control .qhp file (Qt Help Project) can be produced.

Usage

Let’s say you were the author of the QWT library and you felt like shipping .qch files of your documentation. What would you do? Let’s first look at a manual approach, and see what we can automate after.

  1. Enable Microsoft® HTML Help output
    First you need to tell Doxygen to generate HTML Help output. To do that you enable GENERATE_HTMLHELP

    GENERATE_HTMLHELP = YES

    in the Doxyfile. Without HHC_LOCATION being set Doxygen will just produced the index.hh* files and not try to call the Help Compiler from Microsoft®. That’s just how we like it, especially on our Linux Machine.

  2. Run Doxygen
    If everything went smoothly we should have both HTML documentation and the index.hh* in doc/html/ .
  3. Run doxygen2qthelp
    Now doxygen2qthelp comes into play. Invoke it from the QWT source folder like this:

    $ doxygen2qthelp --namespace=net.sourceforge.qwt --folder=qwt-5.0.2 doc/html/index.hhp qwt-5.0.2-doc.qch

    Alternatively you could also write this calls information into an .ini file and invoke it like this:

    $ doxygen2qthelp --config qwt.ini

    -- qwt.ini --
    Namespace = net.sourceforge.qwt
    VirtualFolder = qwt-5.0.2
    InputFilename = doc/html/index.hhp
    OutputFilename = qwt-5.0.2-doc.qch

  4. Enjoy
    Open the documentation in Qt Assistant, preferrably a version from Qt 4.4.0 or later configured to use Webkit in Assistant (./configure -assistant-webkit). Without Webkit parts of the Doxygen HTML will look quite messy.

Automation

Wouldn’t it be cool if we could teach Doxygen to call doxygen2qthelp for us? Good news: I wrote a patch against Doxygen 1.5.6 for you (see doxygen_patch folder). Dimitri van Heesch and I planned to integrate that patch upstream for one of the next Doxygen releases.

  1. Apply the patch and rebuild Doxygen
  2. Add a few lines to the Doxyfile:
    ## Paths Relative to the 'html' folder!
    GENERATE_HTMLHELP = YES
    DOXYGEN2QTHELP_LOCATION = doxygen2qthelp
    QTHELP_CONFIG = ../qwt.ini
    QTHELP_FILE = ../qwt-5.0.2-doc.qch
  3. Run Doxygen

That’s it - a single call to Doxygen can now produce .qch files.

Final words

Please report any bugs you might find through the Trolltech Bug tracker. Feedback is also welcome. Thank you!

Thomas Zander
KDE
Bindings Generator
Posted by Thomas Zander
 in KDE, Bindings Generator
 on Wednesday, June 11, 2008 @ 19:00

As many of you know, I’m an OpenDocument fan, I love working on making KOffice rock, which is something I do at every opportunity. The work I do is mostly outside of the KOffice repository nowadays and others are picking up the slack. One really cool development is that I found some people willing to put some corporate funding into making our ODF compliance rock!

If you want to write some software and you want to maintain it for various years the best way to do this is to have regular testing in place to ensure that with further development the features you released in a previous release didn’t break. This is a common practice and the quality of your release is directly linked to the amount of testing you do. In the open source world the many users tend to work as testers, which is a concept that generally works pretty well. A much better solution is to have auto-tests. This is basically a program that does a certain task and checks the outcome for the expected values.

In KOffice we are working on improving out ODF compliance by first writing a test, and then making sure that the feature gets implemented. Before committing all tests ever written are ran and only when all of them (still) pass we can say the new feature is done. It should become obvious to anyone reading this that writing new software like this will create better software that is easier to maintain over time. Especially in open source where new people come and go on a regular basis.

I’m therefor extremely happy to report that Girish Ramakrishnan has so far created and made passing 51 61 OpenDocument Format loading tests. This in effect means we have lots of ODF features we researched what they should do (by reading the ISO specification) and then make sure we actually do that. Now and years from now they will do what they are meant to do.

The features we are testing are, for example, lists. That a document that is meant to have a numbered list with any sort of complex numbering load and show correctly. But sometimes we have some quite different test, things that KOffice never did before. In that case new features have to be added to KOffice. One such feature is the dropcaps. This is a feature that people that write newspaper style documents will love. Let me show a screenshot which explains it best. :)

For this new feature we have yet to add some dialogs to configure this, so to try it out I started OOo and loaded the odt doc in KWord. Which I admit is a pretty cool way to show how far we have come in the interoperability area :D

Update; updated number of passing tests.

gunnar
Uncategorized
Qt
Qt Jambi
WebKit
Qt Concurrent
Graphics View
Patternist
Posted by gunnar
 in Uncategorized, Qt, Qt Jambi, WebKit, Qt Concurrent, Graphics View, Patternist
 on Tuesday, June 10, 2008 @ 09:13

So the time is finally here. Qt 4.4.0 was released a few weeks ago and as promised Qt Jambi is right behind. A lot of effort has gone into this one, in addition to supporting all the new Qt features, like Phonon, Webkit, Widgets in Graphics View, XQuery and Qt Concurrent, we also have a seriously improved deployment system, JDBC support and a compile-time checked signal-slot approach for the paranoid. Its a good time to be a Java developer I tell yah! We already mentioned all the featuers in the Qt Jambi 4.4.0 Preview Blog so we won’t repeat ourselves here… (There is a danger in linking to eskils blog, as it links to others again, which again links to others, which in the end proves to be a fairly complex graph, but then again… we are engineers and like that kind of stuff)

Under the cover we’ve also done quite some work. We also did an overhaul of the garbage collection and memory management subsystem and hopefully ironed out all the bumps and dents. We’ve also done some work on the build system, so that our users that build from source have a bit more substantial buildsystem to work with. Previously it was a complex install document, which has been replaced by a simple ant command which just does it all… I was very happy to see that the deployment system & ANT build scripts works well enough for the webstart to look like a plain, normal webstart app:

<resources>
<j2se version="1.5+"/>
<jar href="qtjambi-examples-4.4.0_01.jar"/>
<jar href="qtjambi-4.4.0_01.jar"/>
</resources>

<resources os="Windows" arch="x86">
<j2se version="1.5+"/>
<jar href="qtjambi-win32-msvc2005-4.4.0_01.jar"/>
</resources>

No magic nativejar or anything like that, just the qtjambi-win32-msvc2005-4.4.0_01.jar in the classpath and that is enough to load it, jpeg and svg plugins and all. The good thing is that the files included in the webstart are produced directly by the ant script with all dependencies etc set up properly… (well… almost properly, it took us an evening last week to get it really working, but now it works properly). Because of the fixes to memory management and deployment Eskil and I got these offical diplomas:

Absolutely last load issue fixed and Last memory managment bug

So, what more is there to say… Try the webstart with its new demos, download the packages and start hacking!

-
The Qt Jambi Team

ariya
Qt
WebKit
Internet
Posted by ariya
 in Qt, WebKit, Internet
 on Monday, June 02, 2008 @ 08:09

For web developers, when it comes to debugging the web applications, tools like Firebug for Firefox, Safari Web Inspector and the recently introduced Opera Dragonfly can be handy and very useful. These kinds of tools offers the possibility to trace the DOM, verify the corresponding (computed) CSS, check network usage and resource loading performance, debug JavaScript, etc.

As for the Web Inspector (which is technically a WebKit feature), it is written in HTML/CSS/JavaScript except for the rather small platform-specific InspectorClient code. As Holger has mentioned before, with QtWebKit it is very easy to bring WebInspector into any QtWebKit-based browser, such as the Demo Browser (shipped with Qt 4.4) or its successor Arora. Since these browsers are available on several platforms, it also means now you can use Web Inspector on Linux (*). Using either the Demo Browser or Arora, just open the menu Tools, Enable Web Inspector and you are ready. After loading any web site, right click on the web page to find the menu item Inspect that will launch the Web Inspector.

The screenshot below gives the unsurprising proof of Web Inspector on Linux (click to enlarge):

Web Inspector on Linux

In the example, I was trying to find out the potential slow-down in SpeedCrunch website. It was easy to spot that the Slideshow JavaScript (slideshow.js) takes around 200 ms. If this particular JavaScript code can be deferred, the web page would have been 200 ms faster.

What about other WebKit-based application? If your application is using QtWebKit, it is almost trivial to enable the Web Inspector. You can use the action available from QWebView::pageAction or trigger it directly using QWebView::triggerPageAction, in both cases passing QWebPage::InspectElement as the action. Easy enough, isn’t it?

Happy inspecting!

(*) Before, it is only possible if you run Safari under Wine

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. :-)

Jens
Uncategorized
Qt
Labs
Styles
Posted by Jens
 in Uncategorized, Qt, Labs, Styles
 on Tuesday, May 13, 2008 @ 20:28

textedit with gtkstyle

Now that 4.4 is out, I finally found the time to kick off some new development and one of my pet projects these days is QGtkStyle. We already have QCleanlooks, icon themes, standard shortcuts and dialog buttons to integrate with GNOME, but to achieve true perfection we need to use the Gtk theme engine directly just like we do on Mac and Windows. QGtkStyle does exactly this, and in addition extends and surpasses QCleanlooks in a lot of other areas as well. All group boxes are now flat style to blend better with GNOME dialogs. Icon theme support has improved, scrollbar buttons are disabled at edges and item view branches now support hover.

spreadsheet example

The style is already available right now as a Qt 4.4 plugin on Labs and given that you can accept the normal disclaimers about using unreleased and experimental code, you can start playing with it immediately.

This is how the Arora browser looks:

clearlooks style

All the above shots were taken using the “Clearlooks” engine. Here are a few alternatives:

Ubuntu Human:
mediaplayer

Mythbuntu:
mediaplayer

And of course the unforgettable Raleigh engine:
mediaplayer