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.
.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.
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.
/opt/Embedix/tools/bin/arm-linux-g++ \ -I /opt/Qtopia/sharp/include/ \ -DQWS -fno-rtti -o FrmMain.o -c FrmMain.cppThis (all being well) produces
FrmMain.o
from FrmMain.cpp and FrmMain.h.
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
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 -luuidYou'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.
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.
/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.cppThe first line produces the C++ class line, and the second compiles it.
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 -luuidWe 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