Thursday, August 13, 2009

Shrinking Qt widgets to minimum needed size

Shrinking a Qt widgets to the minimum size they can become because of size constraints or child widgets is not a difficult task. In fact, it's simply one call to resize(0, 0) that does just that. Because even resizes by the programmer are bound to the usual constraints, this resizes to the smallest possible size.


But when trying this in practical situations, it might become tricky. Shrinking your widgets means it becomes smaller. A widget (or dialog, or window) usually becomes smaller after removing or changing stuff. You will notice, however, that after removing a widget, a call to resize(0, 0) will not resize your widget to the size it would occupy without the removed widget, but instead it behaves like the widget was still there. This is because the widget is not completely removed until the next processing of the message loop. There are two ways to make certain that the message loop is being processed. One is calling QApplication::processEvents(). The other is using a QTimer::singleShot() and doing the task you want to do in the provided slot. I've tried both in regards to shrinking the widget and noticed that using a singleShot is more flicker-free than calling processEvents(), at least on the Windows platform.


I've created a demo-application to visualize the problem and check which method works better on a certain platform. Here are some screenshots:

Initial window
This is the window after opening the application. You can choose between directly shrinking the widget after removing the last button, using a singleShot() and using processEvents().


Added two buttons
Two buttons have been added, the dialog has automatically grown.


Removed button, shrinked directly
The last button has been removed, resize(0, 0) has been called directly after removing the widget. You can see that the size is not appropriate, the space the button took is still there. Notice the wider gaps between the widgets.


Removed button, shrinked in singleShot
The last button has been removed, resize(0, 0) has been called in a slot that was activated by a QTimer using a singleShot(). The size of the window is correct, no flicker was visible.


Removed button, called processEvents first
The last button has been removed, QApplication::processEvents() has been called before calling resize(0, 0). The size of the window is correct, but there was flicker visible.


You can download the source-code of the demo-appliction here and try it out yourself.

Thursday, July 16, 2009

Background animation for Qt-Widgets

Sometimes you want to load data from a specific datasource (e.g. a database or the internet), but let the user work on with your application while it is loading. When doing this, it is essential that the user can see that data is being loaded and immediately notice when loading has finished.


In my situation it is a table view that gets populated with entries and then validates every entry, which can take quite some time. So I wanted to show a "loading" animation in the background of the table, since this is unintrusive, but still easily notable. I created an animation using ajaxload and saved it as Load.gif in my application's directory.


Enabling GIF-support in Qt


The first hurdle you have to take is getting gif-support in your Qt installation. In order to do so, you need to pass -qt-gif to configure and have the qgif library (libqtgif.so or qtgif.dll) in the subdirectory imageformats in the Qt plug-in directory. I'm not getting into more detail here, since it's not the main topic of this post.


Drawing directly on your widget


Next you want to be able to draw directly on the background of your widget. You do this using the paintEvent() and a QPainter. Here's a very minimal example how to do it:


Header:
#include <QWidget>

class TsTestWidget: public QWidget
{
Q_OBJECT
public:
virtual void paintEvent(QPaintEvent *event);
};

Implementation:
#include "Test.h"

#include <QApplication>
#include <QPainter>

void TsTestWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawRect(10, 10, 50, 50);
}


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

TsTestWidget test;
test.resize(100, 100);
test.show();

app.exec();
}

Result:


Animating the background


The second step is animating this gif. Qt has the QMovie class for this. QMovie is not a widget, it's just managing the animation's frames and timers and provides you the current frame's pixmap. It's frameChanged(int) signal is emitted whenever the next frame should be displayed, usually using a QLabel. In our case we don't use a QLabel to display it, but directly paint it on the widget as we did in the previous step. The tricky part is that paintEvent() is only called when an external event happend that requires repainting of the widget. So we use the frameChanged(int) signal and repaint the widget every time the current frame has changed. Here's a simple example:


Header:
#include <QWidget>
#include <QMovie>

class TsTestWidget: public QWidget
{
Q_OBJECT
public:
TsTestWidget();
virtual void paintEvent(QPaintEvent *event);
private:
QMovie m_movie;
private slots:
void paintNewFrame(int);
};

Implementation:
#include "Test.h"

#include <QApplication>
#include <QPainter>
#include <QPaintEvent>

TsTestWidget::TsTestWidget():
m_movie(qApp->applicationDirPath() + "/Load.gif")
{
connect(
&m_movie,
SIGNAL(frameChanged(int)),
this,
SLOT(paintNewFrame(int)));
m_movie.start();
}

void TsTestWidget::paintEvent(QPaintEvent *event)
{
// First we extract the current frame
QPixmap currentFrame = m_movie.currentPixmap();

QRect frameRect = currentFrame.rect();

// Only redraw when the frame is in the invalidated area
frameRect.moveCenter(rect().center());
if (frameRect.intersects(event->rect()))
{
QPainter painter(this);
painter.drawPixmap(
frameRect.left(),
frameRect.top(),
currentFrame);
}
}

void TsTestWidget::paintNewFrame(int)
{
repaint();
}

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

TsTestWidget test;
test.resize(100, 100);
test.show();

app.exec();
}

Result:


Item views are special


I first tried doing this on a QTableView and failed, frustrated. There is one tricky thing about QAbstractItemViews: You don't paint on the widgets, but on the viewport(), because they're derived from QAbstractScrollArea. Because I didn't immediately realize this, I always got a confusing error message when creating a QPainter in the paintEvent() using the this-pointer. That's because I need to create the painter for the viewport() widget. After finding this out, it became straightforward implementing the animation in the QTableView, just replacing this with viewport().



More re-usable approach


So here we are, having our very own background animated widget, even supporting item views. I could stop this post right here and leave you alone with implementing it into your favorite widgets. But there' still one thing that bugs me: You have to do this for every widget you want an animated background in. When you're using QListView, QTableView and QTreeView in your application, you have to extend all three classes with this functionality, which is error-prone and, bluntly put, lame.


So here's my solution how you can minimize the code you have to write for each new widget-class you want to integrate it into. We create a helper class called "TsBackgroundAnimation". This class does all the work and thanks to multiple inheritance, you just have to derive a class from your Widget and TsBackgroundAnimation, override paintEvent() and call TsBackgroundAnimation's paintAnimation()-function there. There's a a "workaround" we have to make here. Qt does not support deriving from multiple QObject-decendants, hence TsBackgroundAnimation may not derive from QObject. This means that TsBackgroundAnimation can not have slots and it's impossible for it to connect the frameChanged(int)-event to a slot that repaints the widget. So instead of QMovie a TsMovie class is used that provides a frameChanged()-event without the int parameter. That way TsBackgroundAnimation can connect that signal to the slot repaint() of the widget. This made the final implementation a bit larger, so I did not directly paste the sourcecode here. You can download the complete project from here:


BackgroundAnimation.zip

BackgroundAnimation.tar.bz2

And here's a screenshot of the final result:

Tuesday, June 30, 2009

Coolest postcard ever

I received the maybe coolest postcard ever today. It's from a pal I know from IRC (#c++.de on quakenet) who has been living in Madagascar for a few years now. He's planning on moving back to Germany soon, so I asked him to send me a postcard before he leaves Africa.


Here's what he has written (click to enlarge):
Encoded postcard from Madagascar


Have fun decoding it. ;-) (The resulting message is in German, by the way)

Monday, June 8, 2009

Compile-time-array in C++ using templates

We all know that it's possible to use templates for stuff that they were not quite invented for. In this fine example, an arbitrary number is tested for being prime at compile-time: here, see the first screenshot. A friend of mine now came up with the idea to solve this programming contest problem by pre-calculating the needed values using templates and filling a compile-time-array with them. Well, it ended that this was not a feasible solution for that specific task (because of the resulting sourcecode size), but I still wanted to give the compile-time-array a try. And that's what I came up with:

#include <iostream>

struct Null
{
};

template<int V, typename T = Null>
struct Element
{
typedef T Base;
static const int value = V;
};


template<typename Array, int Index>
struct At
{
static const int result =
At<
typename Array::Base,
Index - 1>::result;
};

template<typename Array>
struct At<Array, 0>
{
static const int result = Array::value;
};

int main()
{
using namespace std;
typedef Element<18, Element<23, Element<42> > > Array;
cout << At<Array, 0>::result << endl;
cout << At<Array, 1>::result << endl;
cout << At<Array, 2>::result << endl;
}

Pretty nice I'd say, huh? ;-)

Thanks to mandrill for pointing out that storing the index in Element, as I did in my first approach, is not needed at all.

Tuesday, April 14, 2009

New interesting "chimaera" programming language: Scala

Object oriented programming is currently (or rather: still) state of the art and most commonly found. Functional programming always has been a topic, too, but it never really got the break-through that many people think it deserves. Lisp is one pragmatic example of how object oriented and functional programming can go hand-in-hand, although it's not really clean or even pretty (except for it's S-Expression-based syntax).


There's a relatively new programming language on the field that recently even got attention on a spin-off of Germany's biggest IT news site heise Developer. You can read the german article here or learn more about Scala on it's official website or find news related to the language at scala-blogs.org.


Scala is mixing up object oriented and functional programming. The most important aspects of functional programming (at least to me) are closures and being (optionally) free of side-effects. Scala obviously meets both criteria. It is much more practical than most other functional programming languages because it's based on Java and/or .NET and therefore can use a huge amount of existing third-party libraries out of the box. The most annoying thing about Lisp and, let's say, Haskell, is the absence of existing, maintained libraries to actually do something useful without re-inventing the wheel. Well, there's L Sharp .NET, but well... it's not the real deal and it's not as mature as Scala seems to be. It's even rumored (on Slashdot) that Twitter is moving to Scala.

Friday, March 6, 2009

German community effort to create a good (tm) C++ Tutorial

I have started to gather people to help me write a C++ tutorial that will just blow away everything that's been around. It will be standards compliant, teaching clean, good coding style, fun to read and practical. We put emphasize on making C++ tasty to programming beginners by providing them the necessary tools to easily create graphical output. Our opinion is that tutorials that teach console programming are boring the read too fast, so we've created a "playground" that the reader can use to write graphical programs without first having to learn how to use complex GUI or graphics libraries. We believe that this approach will attract many readers and will make C++ more attractive to beginners (or maybe even advanced programmers that are tired of wading through a console tutorial).


The team currently consists of roughly 6 people from #c++.de on irc.quakenet.org, but I constantly try to recruit new writers. The tutorial's first version and initial draft are all done in German, because the team consists of Germans only and we'd like to keep the barrier to start helping and writing articles as small as possible. We hope that the final product will be completely available in English some time.

Thursday, March 5, 2009

Featured interview at FirebirdNews.org

Marius Popa from FirebirdNews.org interviewed me as a part of the "People around Firebird" series. I had a chance to talk about myself, my projects and my views on topics like Firebird and Qt.

It's an interesting read, don't miss it!