Articles / Benefits of the Qt Object M…

Benefits of the Qt Object Model

Qt is known as a cross-platform graphical user interface toolkit. It is that, but it's also a toolkit for dealing with databases, file access, sockets, and much more. This article concerns the Qt object model and why it is an improvement over the classic C++ model.

The Qt Object Model

The classic C++ object model has data and methods which can be either private, protected, or public. When designing a GUI (or any other event-driven task), we want to tie a method implemented by us to a specific event. To do this, we inherit the event-generating class, overload the virtual method, and re-implement it to do what we want it to do. This gives us two problems:

  1. We will have a new class for each action.
  2. If there was any functionality in the original virtual method, we will have to re-implement it or make an explicit call to the original function.

Qt solves this problem by introducing signals and slots. A method can be defined as a slot that is either private, protected, or public. Slots are ordinary methods, but they are listed in the slot table of the meta object. Each class utilizing the Qt object model has an automatically-generated meta object containing type information, inheritance information, and a list of signals, slots, and properties.

A signal is declared as if it were a usual method, but it may not be implemented. Signals cannot be called, but emitted. Let's go back to the event-driven GUI example. Instead of inheriting the event-generating class, we can implement a slot which fits the signal emitted at the interesting event.

A Code Example

Let's demonstrate this by looking at a small example. We will put two buttons in a dialog and let them alter the text of the title bar. We will let the functionality (i.e., the slots) be a part of our dialog, and simply connect the "clicked" signals from the buttons to the appropriate slots.

We begin by looking at the main function that simply initializes the application. It is very simple, and should not pose any problems.

#include "example1dialog.h"

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

  // Create an instance of our dialog
  example1Dialog dialog( 0, 0, TRUE );
  // Set it as the main widget (as we do not have a main window)
  app.setMainWidget(&dialog);

  // Execute
  dialog.exec();

  return 0;
}

Notice that Qt is accompanied by a great documentation set to which you can refer if you want to know any details. It is available online from http://doc.trolltech.com/, and contains reference material, examples, and in-depth tutorials. In this tutorial, I use the free 2.3 version for win32, just to demonstrate that the Windows version is also freely available. The code I present here is fully compatible with any Unix or Mac version of Qt (it is, as I mentioned, a cross-platform toolkit).

Now, we can continue by looking at the class declaration of the example1Dialog class. This is where the Qt object model comes into play.

class example1Dialog : public QDialog
{
  Q_OBJECT

public:
  example1Dialog( QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags f = 0 );

  protected slots:
  void fooClicked( void );
  void barClicked( void );
};

First, notice that we inherit a descendant of QObject. This is necessary; the QObject class holds much of the object model functionality. The first line in the actual body of the class declaration must be Q_OBJECT. This macro has two purposes: It declares the things needed to get the object model running and works as a marker that the meta object compiler triggers on (more about this later).

The slots are declared in an intuitive way. Signals are declared in a similar way, but in the "signals:" section of the declaration.

We continue by looking at the implementation of the slots:

void example1Dialog::fooClicked()
{
  setCaption( "Foo" );
}

void example1Dialog::barClicked()
{
  setCaption( "Bar" );
}

This is pretty straightforward, as slots are simply ordinary methods that can be connected to signals. As these calls are made from inside a descendant of a QDialog, we can set the caption as easily as shown in the code.

We will now show the method which takes care of setting things up, the implementation of the creator of our example1Dialog class. This method creates a layout and buttons, and sets up the connections between the signals and the slots.

example1Dialog::example1Dialog( QWidget* parent, const char* name, bool modal, WFlags f ) : QDialog( parent, name, modal, f )
{
  setCaption( "Click!" );

  // The layout
  QVBoxLayout *vb = new QVBoxLayout( this, 8 );
  vb->setAutoAdd( vb );

  // The buttons
  QPushButton *pbFoo = new QPushButton( "Foo", this );
  QPushButton *pbBar = new QPushButton( "Bar", this );

  // The connections
  connect( pbFoo, SIGNAL(clicked()), this, SLOT(fooClicked()) );
  connect( pbBar, SIGNAL(clicked()), this, SLOT(barClicked()) );
}

The layout is created with a spacing of eight pixel margins between the edges and each widget. The call to setAutoAdd means that all widgets created with example1Dialog as a parent will be automatically inserted into the layout.

The buttons are created with a text and the dialog as the parent. This means that Qt will delete them from memory the moment the dialog is deleted. Thus, we do not need to care about any complex memory management issues. Each parent will take care of its children through the QObject heritage.

The last thing we do is set up the connections. This is where the magic takes place. Here, we connect each of the button's clicked signals to the slots we created. Notice that we can send parameters between signals and slots (almost) without any restrictions. There's no need to declare special POD (plain old data) classes to carry parameters; just emit the signals, and do not bother.

The Meta Object Compiler

If you try to compile the above code, you will run into problems. The tool that is needed is the meta object compiler, the moc. It creates code for signals and the meta object of the class. You might think that this is an "ugly" solution. This is actually the most common objection to using Qt. However, I have never, during three years of usage, run into any serious problems caused by the moc or the object model. If I encounter any problems, they are always easily solved, thanks to good documentation and good debugging support. The biggest benefit, however, is the syntactical clarity that the moc provides. Anyone who knows C++ will be able to read and understand code with signals and slots after just 1-2 minutes of introduction.

The output from the moc will be stored in the file moc_example1dialog.cpp. Simply include this file in your makefile, and you're ready to go!

qmake

Since version 3.0, Qt has shipped with the qmake tool. This completely removes the makefile hell that Qt could lead to in earlier versions. A project file (.pro) is translated into a proper makefile by qmake. For our example, the project file would look something like this:

  SOURCES = main.cpp example1dialog.cpp
  HEADERS = example1dialog.h
  CONFIG += qt want_on release

As you can see, the need to explicitly handle the moc_*.cpp files is removed. Do not let the simplicity of this example fool you; qmake is a powerful tool which can handle pretty much all the things that a makefile can do. Through fine tuning, qmake can even handle platform differences in, for example, third party support libraries.

Conclusions

Qt offers an extended object model which makes it easier to reuse code. The object model introduces new syntax elements and a new compilation stage (the moc). To remove the introduced complexity, Qt also supplies the qmake tool, which makes it easier to build projects.

The syntax of Qt-extended code integrates nicely with C++, and makes the code readable and easy to understand. The extensions are transparent to the programmer, and do not introduce any new things to think about, besides inheriting QObject or any of its descendants and including the Q_OBJECT macro first in the class declaration.

This tutorial does not cover many of the capabilities of the Qt object model or of Qt. It's worth mentioning that the object model introduces supports for named properties and lots of runtime information. By using Qt Scripting for Application (QSA), Qt objects can be scripted in Javascript.

Recent comments

29 Jan 2004 22:40 Avatar norman_b_robinson

Re: Pitfalls ?
% Actually, there has been a GPL version
> of QT available since 2000. See
> the announcement. Both the embedded and
> X11 versions of QT are available under
> the GPL, and the windows port is
> available gratis for non-commercial use.


Actually, it isn't clear that the *windows* port is available for non-commercial use without exception. It seems that if you are on windows *at all* you have to pay, although there has been some discussion that you can freely use the windows port only if the application is GPL licensed.
You'll note the link you provided doesn't mention the windows port at all - and an hour or so on a search engine will find no clear answer ;)

27 Jan 2004 04:58 Avatar janet

Re: I like Qt
I have to agree. I am happy with Qt, too.

janet - zomilla.org (http://www.zomilla.org)

12 Jan 2004 20:42 Avatar gurensan

Re: Pitfalls ?

>
> %
> % %
> % %
> % % Actually, there has been a GPL
> % version
> % % of QT available since 2000. See
> % % the announcement. Both the
> embedded
> % and
> % % X11 versions of QT are available
> % under
> % % the GPL, and the windows port is
> % % available gratis for non-commercial
> % use.
> %
> %
> % Actually that is the pitfall right
> % there. The GPL. There's nothing
> wrong
> % with it, but you need to be aware of
> it.
> % *All* programs that are linked to
> % qt/free must be GPL. Whereas GTK+,
> (or
> % the GTKMM/libsigc++ bindings) are all
> % LGPL which greatly enhances your
> freedom
> % to use the libraries while still
> % mainting the open-source nature of
> the
> % libraries themselves. It's a good
> % compromise between OSS/Freedom issues
> % and proprietary concerns.
> %
> % Michael
> %
>
>
> Since when it it a problem that non-free
> software cannot use a free library?
> Isn't it more right that those who
> charge for their products get charged by
> the developers of the libraries upon
> which their products are built? I do not
> understand this argument. Earlier, Qt
> wasn't free, now it is too free?


I don't get this guy's objection either.

switch (use QT for a commercial app){
case TRUE: get a commercial license;
break;
case FALSE: GPL your program;
break;
case SCARED: use another library;
break;
default: develop your own;
break;
}

It's a pretty easy thing to understand.

(This used to be K&R formatted).

16 Sep 2003 23:16 Avatar e8johan

Re: Aspects

> One of the biggest shortcomings of QT
> compared to the GTK family, is that it
> is not as nice about multiple
> programming language support.


Please check your facts:
http://kdemyths.urbanlizard.com/viewMyth.php?mythID=18

11 Sep 2003 18:22 Avatar bingalls

Aspects
There is another way for functions or messages to be shared amongst diverse classes:
Aspect Oriented Programming.
You can read more at
http://www.prakinf.tu-ilmenau.de/~czarn/aop/


You should be able to pass around a callback, or pointer to a function through an Aspect.
Anyone try comparing Aspects to Signals or Slots? Aspects seem easier, more standard, and it should be easier to create inter-(programming)language portability with Aspects.

One of the biggest shortcomings of QT compared to the GTK family, is that it is not as nice about multiple programming language support.

Screenshot

Project Spotlight

Kigo Video Converter Ultimate for Mac

A tool for converting and editing videos.

Screenshot

Project Spotlight

Kid3

An efficient tagger for MP3, Ogg/Vorbis, and FLAC files.