Getting started with Qt and ArcGIS Engine


Summary
Qt is a multiplatform C++ graphical user interface (GUI) toolkit created and maintained by Nokia. The variety of Qt widgets and their resources, which you have available to you as a programmer, are not discussed here since this is not a complete Qt resource. However, this topic provides a place to start figuring out the Qt-specific bits of the C++ application programming interface (API) and samples.

Six steps of Qt programming

When writing a Qt program, the following are the six steps that need to be done:
  1. Initialize Qt
  2. Create the widgets
  3. Place the widgets
  4. Implement event listening and callback functions for widgets
  5. Show the widgets
  6. Begin the event handling loop
To illustrate each of these steps, you will create a simple "Hello world" Qt application.

Qt "Hello World" application

This application will contain a single button in the main application window. This example will also implement an event handler using slots and signals, which are a unique central feature to Qt (see Step 4).
Start a new file that will be your program and call it Hello.cpp.
To create this Qt application, you will need to include the Qt header files <qapplication.h>, the header file <qpushbutton.h> to access the QPushButton class and, in this case, <qobject.h> is also needed in to access QObject, which will be used for event handling. The following code example is the initial contents of Hello.cpp:
[Qt C++]
#include <qobject.h>
#include <qapplication.h>
#include <qpushbutton.h>
int main(int argc, char **argv)
{
  return 0;
}

Initialize Qt

There has to be exactly one QApplication object in every application that uses Qt. Notice that you need to pass argc and argv to the QApplication constructor for it to initialize properly. See the following code example:
[Qt C++]
int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  return 0;
}
In ArcGIS Engine applications, you must also use AoInitialize, placing the call before the creation of the QApplication object and before any ArcObjects usage.

Create the widgets

With Qt initialized, you can create the button widget using the QPushButton class. In the following code example, the button is set up to display the text "Hello world!" and be a window of its own (because the constructor specifies 0 for the parent window):
[Qt C++]
int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  QPushButton hello("Hello world!", 0);
  return 0;
}

Place the widgets

To show the widgets, you need to give them attributes, such as size, location, and so on. The button is set up to be 100 pixels wide and 30 pixels high (plus the window system frame). In this case you don't care about the button's position, and you will accept the default value, then attach the widget to the main application with setMainWidget(). setMainWidget sets the push button as the main widget for the application. See the following code example:
[Qt C++]
int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QPushButton hello("Hello world!", 0);
  hello.resize(100, 30);
  app.setMainWidget(&hello);
  return 0;
}

Implement event listening for widgets using signals and slots

You now have a button, but for that to be useful, you must hook it to some functionality. Qt widgets are attached to behavior at certain events through a system of signals and slots. The signal and slot mechanism is a central feature of Qt, and probably the part that differs most from other toolkits. You can connect a widget's signals to its own or another's slots after it is created. See the following code example:
[Qt C++]
int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QPushButton hello("Hello world!", 0);
  hello.resize(100, 30);
  QObject::connect(&hello, SIGNAL(clicked()), &app, SLOT(quit()));
  app.setMainWidget(&hello);
  return 0;
}
These parameters have the following roles:
  • &hello—Widget that will give off the signal you are watching for.
  • SIGNAL(clicked())—Signal given off by clicking the button widget.
  • &app—Widget whose slot responds to the signal.
  • SLOT(quit())—Method to be called when the signal is received.

Slots

For the event-driven signals and slots system to work, you must select an existing slot from a QObject-derived class or create your own. In this example, the button's clicked signal is connected to the quit slot of the QApplication class. When the button is pressed, the application exits.
If you choose to define your own slots, you will need to create your own class inheriting QObject. The code in your class will need to resemble the following code example:
[Qt C++]
class EventClass: public QObject
{
    public slots: void myEvent()
    {
         /* event code here */
    }
}
This class definition will have to reside in a separate source file and pre-compiled using Qt's moc compiler. For more information on this subject, see Nokia's Qt documentation.

Show the widgets

The Qt widgets are not shown when you create them. You must call show() to make it visible. See the following code example:
[Qt C++]
int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QPushButton hello("Hello world!", 0);
  hello.resize(100, 30);
  QObject::connect(&hello, SIGNAL(clicked()), &app, SLOT(quit()));
  app.setMainWidget(&hello);
  hello.show();
  return 0;
}

Begin the event handling loop

Finally, return 0 is replaced by the return value of the application object's exec() method. The exec() function is where the application enters the main event loop and waits until exit() is called or the main widget is destroyed, and returns the value that was set to exit(). It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets. See the following code example:
[Qt C++]
int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  QPushButton hello("Hello world!", 0);
  hello.resize(100, 30);
  QObject::connect(&hello, SIGNAL(clicked()), &app, SLOT(quit()));
  app.setMainWidget(&hello);
  hello.show();
  int ret = app.exec();
  /* Uninitialization code can be used here. */
  return ret;
}
For ArcGIS Engine C++ programming, you must call AoExit before returning. You must also call AoUninitialize before shutting down the application with AoExit.

Setting a custom cursor

If you want to use a mouse cursor other than the default for the control widget, you will need to use the setCuror() method and the Win32 function LoadCursor. For example, if you want to show an hourglass cursor when the mouse pointer is over a control, you might use the following code example:
[Qt C++]
QAxCtl mapControl(AoPROGID_MapControl);
mapControl.setCursor(LoadCursor(_Module->GetModuleInstance(), IDC_WAIT));
To revert to the default cursor for the control, pass it zero to setCursor. See the following code example:
[Qt C++]
mapControl.setCursor(0);

Trying it out

To compile your Qt program, you will need to link only against the Qt libraries. If you are programming on Solaris, you will compile with the Sun Workshop (Forte) and if you are programming on Linux, you will use GCC. See the following:
  • Solaris—CC Hello.cpp -o Hello -I${QtDIR}/include -L${QtDIR}/lib -lQt
  • Linux—g++ Hello.cpp -o Hello -I${QtDIR}/include -L${QtDIR}/lib -lQt
Run the program:
  • ./Hello
When you run it, you will see a small window filled with a single button, and on it you can read the famous words, "Hello World!"
Now that you have a feeling for Qt programming, look at the C++ samples for Qt, and see these steps applied there.

Focus problems

Sometimes, you might find that the controls hosted within the code>QAxCtl widgets will not release the keyboard focus when you click another widget, such as a text box. If this is a problem, use the QAxApplication class in place of QApplication. This is a special class that watches for mouse button clicks and forces focus away from the control widget, if necessary.


See Also:

Nokia's Qt Web site
Nokia's Qt online reference documentation