Journal Articles
Browse in : |
All
> Journals
> CVu
> 163
(11)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Going GUI with Qt
Author: Administrator
Date: 05 June 2004 13:16:05 +01:00 or Sat, 05 June 2004 13:16:05 +01:00
Summary:
Welcome to the first installment in a series of articles exploring GUI programming with the Qt C++ toolkit.
Body:
Welcome to the first installment in a series of articles exploring GUI programming with the Qt C++ toolkit. Applications developed with Qt can be run on all the main computing platforms in use today, including Windows 95-XP, Mac OS X, Linux, and most versions of Unix with X11, making C++/Qt applications almost as portable as standard C++ applications. Furthermore, Qt was designed as an object oriented library from its inception, which makes it very clean and pleasant to program with.
We're going to present and explain a very small "Hello" application that gives some of the flavour of Qt programming. The application will display a label, a line editor, and a Quit button, laid out vertically one above the other. The label's text will change whenever the user changes the text in the line editor, and the program will terminate when the user presses the Quit button.
We'll implement the application using three files: main.cpp, window.h, and window.cpp. Let's start with main.cpp which just contains a simple main() function:
#include <qapplication.h> #include "window.h" int main(int argc, char **argv) { QApplication app(argc, argv); Window *window = new Window; app.setMainWidget(window); window->show(); return app.exec(); }
We begin by creating a QApplication object on the stack. Only one of these objects is created in a Qt application; it provides the GUI event loop and access to useful global information such as the desktop's size. Next we create a Window object - this is our custom main window, which we'll look at in a moment. For simple applications like this it is convenient to identify the main window as the application's "main widget"; this ensures that the application will automatically terminate when the main window is closed. Then we show the window on screen, and finally we start off the event loop with the app.exec() call. Most Qt applications have main() functions just as short and simple as the one shown here.
Now we're ready to look at the implementation of our Window subclass, starting with the header.
#ifndef WINDOW_H #define WINDOW_H #include <qdialog.h> class QLabel; class Window : public QDialog { Q_OBJECT public: Window(QWidget *parent=0); private slots: void updateName(const QString &name); private: QLabel *label; }; #endif
The first unusual thing to notice about the header is the Q_OBJECT macro. This signifies that the class has some Qt extensions to C++ which we'll discuss later. The private slots are private methods that can also be called by Qt's signals and slots mechanism. Like most GUI toolkits Qt provides low-level access to events such as mouse presses and releases, and so on. But very often we want to work with higher level concepts: we don't care how a button was pressed (whether by a mouse click, tabbed to then Spacebar, or Alt+underlined letter), we only care that it was pressed. Qt's signals and slots mechanism is a solution to this.
Qt widgets emit "signals" when state changes take place, for example when the text changes in a line edit, or when a new item is selected in a list box. These signals can be "connected" to slots (methods), so that when a signal is emitted any slots it is connected to are called. Widgets that are connected to one another are independent components since they don't need to know anything about each other.
We're now ready to look at window.cpp, starting with the Window constructor.
Window::Window(QWidget *parent) : QDialog(parent) { setCaption("Hello"); label = new QLabel("Hello CVu!", this); QLineEdit *edit = new QLineEdit(this); QPushButton *button = new QPushButton("&Quit", this); QVBoxLayout *vbox = new QVBoxLayout(this, 5, 10); vbox->addWidget(label); vbox->addWidget(edit); vbox->addWidget(button); connect(edit, SIGNAL(textChanged(const QString&)), this, SLOT(updateName(const QString&))); connect(button, SIGNAL(clicked()), this, SLOT(accept())); resize(120, 90); }
In Qt, every widget ("control" in Windows-speak) has a parent. If the parent is 0, the widget is a top-level window. A top-level window has any number of child widgets, nested to any extent. It is very rare to write destructors or to call delete on widgets in Qt, because when a top-level window is removed, it recursively deletes all its children automatically.
We set the widget's caption; for a top-level widget this is the window title. We then create the child widgets we need. In our case we are going to refer to the label in another method, so we've kept a private pointer to it. Since we want the widgets to appear vertically one above the other we create a vertical box layout and add each widget to it. The extra parameters (5, 10), are used to specify the layout's margins and spacing. Qt also provides a horizontal box layout and a grid layout; layouts and widgets can be arbitrarily nested. Qt also supports absolute positioning, but layouts are more convenient since they automatically adapt to the widgets they contain; for example labels will grow to accommodate longer text.
The Qt connect() method takes a QObject (all Qt widgets are QObjects), and associates one of its signals with a slot in another QObject. In our case we've said that whenever the line editor's text is changed our updateName() slot should be called. The QDialog base class provides an accept() slot which is called when the window is closed and accepted, and a reject() slot which is called when the window is closed and rejected, for example when the user clicks the window's X button, or presses Esc. Our second connection tells Qt to call the accept() slot when the Quit button is "clicked" (by whatever means). When the window is accepted (or rejected), because of the setMainWidget() call in main(), the application will terminate.
void Window::updateName(const QString &name) { if(!name.isEmpty()) label->setText("Hello " + name); }
The updateName() slot is trivial. And that completes our GUI application - in just 72 lines of code.
But what about Q_OBJECT, public slots, and other Qt keywords?
The C preprocessor turns them all into pure C++ so the compiler never sees them. The machinery to handle Qt's extensions is generated by the moc (the Meta Object Compiler) which reads your source files and generates some additional source files to implement what you've used. Adding the rules for moc to a makefile isn't very difficult, but the majority of Qt users use Qt project files (.pro files). For this example we would put main.cpp, window.h, and window.cpp in a directory of their own; then we'd run qmake -project to generate the project file, then qmake to generate the makefile, then make as usual.
In later installments we'll explore Qt further, seeing how to create QObject subclasses with our own signals and slots, applications with menus and toolbars, and how to create custom widgets.
Notes:
More fields may be available via dynamicdata ..