Help answer threads with 0 replies.
Go Back > Blogs > rainbowsally
User Name


Rate this Entry

GUI coding. The 4 main parts of a typical GUI toolkit

Posted 04-19-2012 at 01:34 PM by rainbowsally
Updated 04-19-2012 at 01:51 PM by rainbowsally (clarification of 'metaobject' purpose in last line)

Let's start slow.

First of all, most of the GUI toolkits including QT4 are set up in the following steps, and in this order.
  1. Create an application, possibly with commandline args. (If not created explicitly this 'application' thing will already exist somewhere.)
  2. Create a main widget with the 'application' thingie as its parent.
  3. Show the main widget.
  4. Run the application event loop.

The 'application' is the main event dispatcher which translates X events to events or "messages" understood by the toolkit's widgets.

It should do some pre-processing in order to make sure only those events that are applicable are passed to the application's children (like seeing if a mouse click occurs in a location inside a widget's boundaries). And the widget may in turn do other translating and/or filtering and sending of messages to its own children.

In any case, the main widget needs some kind of connection to the application in order to receive events (or messages).

We need to 'show' the widget before running the main loop. This 'show' command may only set a flag in the widget telling it to draw itself. When not shown, the widget may completely destroy itself, freeing all memory. There is no universal way to deal with visibility, but the basic concept exists in all the toolkits I've seen so far.

Back to QT4.

Here's the 'main.cpp' code for examples/desktop/screenshot in the QT4 installation in case this is one of the demos and examples actually in a precompiled package for your distro.

#include <QApplication>

#include "screenshot.h"

int main(int argc, char *argv[])
  // Certain standard args like --help may be processed here.  If they are, the 
  // function won't return.  -rs
  QApplication app(argc, argv); 
  // This creates the widget.  It is not explicitly linked to the parent, 
  // class which should be 'app' so we may want to trace this and see how it 
  // gets connected. -rs
  Screenshot screenshot;
  // This either sets flags or actually fills in variables. (Turns out in QT it
  // does both) -rs;
  // app.exec() returns 0 if there are no errors.  It runs the main loop. 
  return app.exec();
Even Windows implements these 4 parts though they may appear to be 6 or 7 parts, the RegisterWindow() call being the 'connection' between the main widget and the 'application' and the main loop being partially visible (which is a nice feature) allowing overrides of default message handling by intercepting certain messages before sending them back to DefWindowProc() (default window proc = parent class vtable, in essence).

In Windows the object (or object header) is the WNDCLASS or WNDCLASSEX and the equivalent of a vtable is the WNDPROC. But who cares about Windows, other than noticing they have a nifty hook that can be used to intercept messages within the main loop -- which we may also want to do with other toolkits.
I did this to FLTK, by the way, which required a complete rewrite of their system to call 'handle' functions through jump vectors which can be set and reset as required.

Works great but this is another strike against FLTK as our model toolkit since that is a MASSIVE rework of the entire system and mostly only applies to FLTK message handling though it is an interesting experiment if you already have FLTK sources.

If you have kde, take kdbg (v >= 5.0) for a spin. Any insight-like debugger will probably do, but I love kdbg.


Here's the mc2.def file for compiling screenshot without qmake, adding debug info you can step through.

Create a new directory for the Makefile and copy the sources into a new subdir named 'src'. (for this example).

## The output file name not including path
OUTNAME = screenshot

## use default dirs

## What COMPILE should do.
COMPILE = g++ -c                # making no assumption about register width here
CFLAGS = -g3                    # debuggable
INCLUDE = -I/usr/include  -I/usr/include/Qt -I/usr/include/QtGui
# as needed add:
# /usr/include/Qt             /usr/include/QtHelp         /usr/include/QtSvg
# /usr/include/Qt3Support     /usr/include/QtMultimedia   /usr/include/QtTest
# /usr/include/QtCore         /usr/include/QtNetwork      /usr/include/QtUiTools
# /usr/include/QtDBus         /usr/include/QtOpenGL       /usr/include/QtWebKit
# /usr/include/QtDeclarative  /usr/include/QtScript       /usr/include/QtXml
# /usr/include/QtDesigner     /usr/include/QtScriptTools  /usr/include/QtXmlPatterns
# /usr/include/QtGui          /usr/include/QtSql

## What LINK should do.
LINK = g++                      # making no assumption about register width here
LDFLAGS = -lQtGui -lQtCore      # must be symlinks named <name>.so
# as needed add: libQtCore -lQt3Support
# , etc...
# Note, however, that if you list a lib it WILL be considered a dependency and
# the app will abort at runtime if the lib is missing from the user's system 
# even it's not really needed.  Therefor, as a rule, do not list libs that aren't 
# really needed.  An exception might be for MULTI types of mc2 builds, where a 
# shotgun spread of libs is usually ok.

# adjust this path for your own system.  Use 'locate, for example, and 
# copy that path if you need to.
LIB = -L/usr/lib

## User Defined Targets

@rm -f $(MAIN)
@rm -f $(OBJ)
@rm -f *~ */*~ */*/*~ */*/*/*~

moc: force
cd src && moc screenshot.h > moc_screenshot.cpp

The moc file needs to be built before it can be automatically added to the build by mc2.

mc2 -update         # create a new makefile after which 'make update' works

# We need to create the metaobject and we'll name it moc_screenshot.cpp
# This only needs to be done once unless the header file changes.
make moc            # create the missing cpp file

# now we can add the new file to the build
make update         # adds the new file
make                # and there you have it.
Creates screenshot with debug info which you can trace, as I did, minus the trips into qwidget.cpp and qapplication.cpp which may require a custom qt4 build that includes debug symbols (i.e., -g3 flag for g++ compilation.)

If you can't get qmake to let you compile a debug version of this or other QT4 apps, d/load mc2 and see if that helps. [You'll need the libLQ d/load to compile it.]

Next let's take a look at how messages are passed internally. In the case of QT4, method calls are also messages or 'signals' of sorts, and they are processed in a case switch called 'slots'.

QT calls this kind of method call case switch metaobjects. This is the equivalent of a Windows WNDPROC but it's all set up automatically, as most late-binding linux toolkits do.

[GTK does, Fox does, however XForms/FLTK and so forth do not even do late-binding of methods to to dynamically created classes, but this should be fairly generic nonetheless.]

Posted in Uncategorized
Views 419 Comments 0
« Prev     Main     Next »
Total Comments 0




All times are GMT -5. The time now is 11:31 AM.

Main Menu
Write for LQ is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration