The K-Zone: My first Qtopia application -- how to get started with Qtopia development -- part 4

A more complex graphical program

In this section, I will introduce a slightly more complex graphical example. It doesn't do much more than the previous example but it introduces the vital concept of `slots and signals', and also explains how to use the Meta-Object Compiler (MOC).

In the previous example, the application's main window was a simple instance of QMainWindow. There is a limit to what the application can do, because we can't add any new functionality to this pre-defined object. In a real application, the minimum we will have to do is to create a new main window class. In many simple application, this is the only new class that needs to be defined -- the application may consist only of a main window containing prefined user interface objects such as buttons and list boxes.
      In this example, we will define a new class called FrmMain, which is a subclass of QMainWindow. By convention, Qtopia windows that accept data entry are called `forms', usually abbreviated to Frm in class names. This window will have a menu bar, which will contain a `File' menu, which will contain a `Quit' command. Selecting `Quit' will close the application.

Creating the interface for the main window class

Following sensible C++ coding conventions, we will define the new main window class in a separate .cpp file. In This means that we need to define the interface in a separate header file, so that code that uses the new class knows how to refer to it. In fact, we must define the interface in a separate header file, to get the Meta-Object Compiler (see below) to work. I will assume that the new header file will be called FrmMain.h, while the class implementation will go in FrmMain.cpp (later). Here is the header file:
#include <qmainwindow.h>
                                                                                
class FrmMain : public QMainWindow
{
        Q_OBJECT
public:
        FrmMain(QWidget* parent=0, const char* name=0, WFlags fl=0);

public slots:
                                                                                
void    cmdFileQuit();
};
Note that we are defining FrmMain to be a subclass of QMainWindow. The unusual thing about this class definition, if you aren't familiar with Qt development, is the line:
public slots:
The term `slots' is defined in the macro Q_OBJECT, which is why it has to be included. A `slot' is a function that is capable of responding to an event generated elsewhere in the application. I will show later how selecting a command from a menu generates an event, and that event is connected to the `slot'. For now, note that cmdFileQuit() is a function that will be defined in FrmMain.cpp, and which will be invoked in response to a user interface action.

Creating the main window class implementation

As you should be able to see from the interface definition, we need to implement the class constructor, and the method cmdFileQuit(). Here is the code, in FrmMain.cpp.
#include <qpopupmenu.h>
#include <qmenubar.h>
#include "FrmMain.h"
                                                                                
FrmMain::FrmMain(QWidget* parent, const char* name, WFlags fl)
    : QMainWindow(parent, name, fl)
  {
  setCaption("Hello, World!");
                                                                                
  QMenuBar *menubar = this->menuBar();
  QPopupMenu *mnuFile = new QPopupMenu(this, "MenuFile");
  menubar->insertItem("&File", mnuFile);
  mnuFile->insertItem("&Quit", this,
    SLOT(cmdFileQuit()), 0, 1);
  }
                                                                                
void FrmMain::cmdFileQuit()
  {
  this->close();
  }
The constructor creates a menu bar and inserts the menu items into it. The interesting line is this one:
  mnuFile->insertItem("&Quit", this,
    SLOT(cmdFileQuit()), 0, 1);
This line inserts the `Quit' menu item, and links the event generated by the selection of this menu item to the `slot' function cmdFileQuit(). The consequence is that this function will be invoked when the user selects the Quit menu item. The user interface event is a particular type of what Qt calls a `signal'. It is possible for a class to define its own signals, and for other classes to implement slots to receive these signals. This is a very powerful method for decoupling classes from one another, but beyond the scope of this article.

Compiling the main window class

We already know how to do this, from the previous section.
/opt/Embedix/tools/bin/arm-linux-g++ \
-I /opt/Qtopia/sharp/include/ \
-DQWS -fno-rtti -o FrmMain.o -c FrmMain.cpp
This (all being well) produces FrmMain.o from FrmMain.cpp and FrmMain.h.

Creating and compiling the main class

This is essentially the same as in the previous section, but instead of creating an instance of QMainWindow, we must create an instance of FrmMain. So here's the code, in hello3.cpp.
#include <qmainwindow.h>
#include <qpe/qpeapplication.h>
#include "FrmMain.h"
                                                                                
int main(int argc, char** argv)
  {
  QPEApplication app(argc, argv);
  QMainWindow* mainWindow = new FrmMain();
  app.showMainWidget(mainWindow);
  return app.exec();
  }
To compile it:
/opt/Embedix/tools/bin/arm-linux-g++ \
-I /opt/Qtopia/sharp/include/ \
-DQWS -fno-rtti -o hello3.o -c hello3.cpp

The Meta-Object Compiler

At this point we have two object files, hello3.o and FrmMain.o, which should be linkable to produce the complete application. But try it:
/opt/Embedix/tools/bin/arm-linux-g++ \
-L /opt/Qtopia/sharp/lib/ -o hello3  \
hello3.o FrmMain.o -lqpe -lqte -ljpeg -luuid
You'll get a heap of error messages, like this:
FrmMain.o(.text+0x450): undefined reference to 
`FrmMain::QPaintDevice virtual table'
Yeuch! It looks as if something is missing and, indeed, it is. The problem is that the slots-and-signals architecture of Qt needs some infrastructure to make it work. This infrastructure cannot be included in the base classes (such as QMainWindow), because it depends on the way slots and signals are defined. So -- and this is a major departure from most C++ programming models -- it must be generated dynamically. Trolltech provide a tool for this: the Meta-Object Compiler (MOC). The MOC reads header files that describe user interface objects, and generates C++ source files that contain the necessary slots-and-signals plumbing.
      So what we need to do is to run moc on FrmMain.h to produce a C++ file (which I will call FrmMain_moc.cpp), and then this needs to be compiled. Finally, the compiled MOC output needs to be linked with the other object files to produce the application.
      So here we go:
/opt/Qtopia/sharp/bin/moc -o FrmMain_moc.cpp FrmMain.h
/opt/Embedix/tools/bin/arm-linux-g++ \
-I /opt/Qtopia/sharp/include/ -DQWS -fno-rtti \
-o FrmMain_moc.o -c FrmMain_moc.cpp
The first line produces the C++ class line, and the second compiles it.
      So now we can link FrmMain.o, hello3.o, and FrmMain_moc.o.
/opt/Embedix/tools/bin/arm-linux-g++ \
-L /opt/Qtopia/sharp/lib/ -o hello3  \
hello3.o FrmMain.o FrmMain_moc.o \
-lqpe -lqte -ljpeg -luuid
We should end up with the executable hello3, which we can strip, copy, and test as before. Now when you run the application on the PMA430, you should see a menu bar, and when you select File-Quit the application should close.

OK, so the application is not very sophisticated. But it demonstrates everything you need to know to compile, link, and test applications for the PMA430. There is nothing more to it than this. Of course, there is a great deal more to Qtopia than this, but Qtopia itself is well documented, and there are loads of sample applications around.

So, we've got an application, simple as it is, that works. The problem is that we can only run it from the command prompt, which is not ideal for end users. So now we need to know how to package it for distribution. Go to part 5...
©1994-2006 Kevin Boone, all rights reserved