Journal Articles

CVu Journal Vol 16, #4 - Aug 2004 + Programming Topics
Browse in : All > Journals > CVu > 164 (12)
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: Creating Standard GUI Applications

Author: Administrator

Date: 03 August 2004 13:16:06 +01:00 or Tue, 03 August 2004 13:16:06 +01:00

Summary: 

In this second installment of our series on GUI programming with the Qt C++ toolkit, we're going to see how to create a standard GUI application, with a menu, toolbar, status bar, and a central area.

Body: 

In this second installment of our series on GUI programming with the Qt C++ toolkit, we're going to see how to create a standard GUI application, with a menu, toolbar, status bar, and a central area. The application is a simple image viewer that can display images in any of the formats that your installed version of Qt supports.

The main() function, in main.cpp, is straightforward:

#include <qapplication.h>
#include "viewer.h"

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  Viewer *viewer = new Viewer;
  viewer->show();
  viewer->connect(&app,
                  SIGNAL(lastWindowClosed()),
                  &app,
                  SLOT(quit()));
  return app.exec();
}

We create a QApplication object and then the form which we immediately show. The "signal-slot" connection ensures that when the last top-level window (in this case the only window) is closed, the application will terminate. Finally we start the event loop and wait for user interactions.

Qt provides the QMainWindow class as the base class for main windows, so we will subclass this and specialise it to our needs. Here's the definition which we've put in viewer.h:

#ifndef VIEWER_H
#define VIEWER_H

#include <qmainwindow.h>

class QAction;
class QLabel;

class Viewer : public QMainWindow {
  Q_OBJECT

public:
  Viewer(QWidget *parent = 0);

private slots:
  void openFile();

private:
  QAction *openFileAction;
  QAction *quitAction;

  QLabel *imageLabel;
  QString fileName;
};

#endif

We need the Q_OBJECT macro because we are using Qt's "signals and slots" mechanism in our subclass. We will connect the "open file" action (File|Open) to the openFile() slot. We'll explain the rest as we describe the implementation in viewer.cpp, piece by piece, starting with the includes.

#include <qaction.h>
#include <qfiledialog.h>

#include <qimage.h>
#include <qlabel.h>
#include <qmenubar.h>
#include <qpopupmenu.h>
#include <qstatusbar.h>
#include <qtoolbar.h>
#include "viewer.h"
#include "icon.xpm"
#include "openfile.xpm"

We include all the Qt classes we need, viewer.h, and also two XPM files. The XPM file format is an image format that is also valid C++. XPM images are easy to find on the Internet, and Linux distributions come with lots of them.

The constructor for a main window application is usually concerned with the creation of "actions": these are are "activated'' when the user clicks their associated menu option or toolbar button, or types their keyboard shortcut. Main windows usually have very simple layouts since they often contain either a single widget or an MDI (multiple document interface) workspace.

Viewer::Viewer(QWidget *parent)
               : QMainWindow(parent),
                 fileName(".") {
  imageLabel = new QLabel(this);
  imageLabel->setAlignment(AlignCenter);
  setCentralWidget(imageLabel);

  openFileAction =
        new QAction(QPixmap(openfile_xpm),
                    "&Open...",
                    CTRL+Key_O, this);
  connect(openFileAction,
          SIGNAL(activated()), 
          this,
          SLOT(openFile()));
  quitAction = new QAction("&Quit",
                           CTRL+Key_Q,
                           this);
  connect(quitAction, SIGNAL(activated()), 
          this, SLOT(close()));

  QPopupMenu *fileMenu = new QPopupMenu(this);
  openFileAction->addTo(fileMenu);
  quitAction->addTo(fileMenu);
  menuBar()->insertItem("&File", fileMenu);

  QToolBar *fileTools =
            new QToolBar("File Tools", this);
  openFileAction->addTo(fileTools);
  setCaption("Image Viewer");
  setIcon(QPixmap(viewer_xpm));

  statusBar()->message("Ready");
}

We begin by creating a label widget that will be used to display the image. We then create two actions, the first "open file'', has an icon (openfile.xpm) and a shortcut of Ctrl+O. We connect the action to our custom openFile() slot (implemented shortly). The "quit" action is connected to the built-in close() slot. After the actions are created we want to give them a visual representation in the user interface. We create a new menu and add the "open file" action, and the "quit" action to it. We also create a toolbar and add the "open file" action to it. Qt automatically keeps menus and toolbars in sync.

Finally we set the application's caption and icon, and display "Ready" on the status bar. The first time we call menuBar() and statusBar(), Qt creates them; this ensures they are only created if they're actually used.

When the user invokes the "open file" action (by choosing File|Open, by clicking the "open" toolbar button, or by pressing Ctrl+O), the openFile() slot is called.

void Viewer::openFile() {
  QStringList formats =
                    QImage::inputFormatList();
  QString filters = "Images (*."
                     + formats.join(" *.")
                              .lower() + ")";
  QString newFileName =
      QFileDialog::getOpenFileName(fileName,
                               filters, this);
  if(!newFileName.isEmpty()) {
    fileName = newFileName;
    QPixmap pixmap(fileName);


    imageLabel->setPixmap(pixmap);
    statusBar()->message(QString("%1 %2x%3")
                  .arg(fileName)
                  .arg(pixmap.width())
                  .arg(pixmap.height()));
  }
}

Qt can provide a list of the image formats it can read, and we use this to create a file filter. For example, the filter might look like this, "Images (*.bmp *.gif *.jpeg *.pbm *.pgm *.png *.ppm *.xbm *.xpm)". We use one of QFileDialog's static convenience functions to get an image file name, and unless the user clicks Cancel (in which case getOpenFileName() returns an empty string), we load the image and put some information in the status bar. If we didn't need to access the image after reading it we could have simply used:

imageLabel->setPixmap(QPixmap(fileName));

To build and run the application, save the files in a directory of their own (e.g. viewer), change to that directory, and run the following commands:

qmake -project
qmake viewer.pro
make

The first command creates a project file, the second creates a makefile based on the project file, and the third builds the application. If you use Visual Studio, use nmake instead of make.

In the previous article we saw how to create a dialog and lay out widgets inside it. Now we've seen how to create a main window application. In the next installment we'll combine this knowledge to create an application that can interact with the user through a dialog, and later on we'll see how to create custom widgets with any look and behaviour we want.

Notes: 

More fields may be available via dynamicdata ..