Chapter 4. A Guided Tour Through the Simple Widgets

Qt is a rich library and as a beginner, you could easily find yourself reinventing the wheel when a Qt class that meets your needs already exists. This is why we present widgets that are already available with their most useful methods in this chapter and in Chapter 5. We also provide guidelines that specify when to use a certain widget.

This widget tour has two parts. This chapter presents so-called simple widgets, such as push buttons and labels. The next chapter contains the predefined dialog boxes that Qt provides for common tasks such as opening files, as well as the building blocks for defining your own dialogs. Some widgets with a more specific task, such as those used for displaying database data, are described in other chapters to stay with their underlying classes.

For most of the widgets, we show screenshots depicting them in both Windows and Motif styles because these styles are used most frequently. In other styles like the SGI style, the widgets may look somewhat different. To ensure that you can find them easily in the reference, most screenshots are taken directly from the Qt reference documentation, with the kind permission of Trolltech AS.

For your convenience, Table 4-1 contains all widgets that ship with Version 3 of Qt. They are listed alphabetically, each with a short description and a reference to the section of this book where they are explained further.

Table 4-1. The widget classes in Qt 3
WidgetDescriptionSection
QButton

A common base class for button-like widgets

See Section 4.3.

QButtonGroup

Organizes QButton widgets in a group

See Section 4.8.2.

QCanvasView

A widget that displays a canvas with items on it

See Section 9.7 in Chapter 9.

QCheckBox

A checkbox with a label—either a text or a pixmap

See Section 4.3.

QComboBox

A combined button and pop-up list

See Section 4.4.2.

QDataBrowser

A form-like editable view of database records

See Chapter 19.

QTable

A spreadsheet-like table widget for database data

See Chapter 19.

QDataView

A form-like noneditable view of database records

See Chapter 19.

QDateEdit

A widget for entering date values

See Section 4.5.4.

QDateTimeEdit

A widget for entering date and time values

See Section 4.5.4.

QDesktopWidget

A widget that represents the desktop on which your application runs

QDial

A potential meter-like control for entry of numerical data

See Section 4.5.2.

QDockArea

A container for dockable widgets

See Section 4.12.5.

QFrame

The base class of widgets that have an (optional) frame

See Section 4.8.1.

QGLWidget

A widget for displaying OpenGL graphics

See Section 24.1 in Chapter 24 .

QGrid

A widget that lays its children out in a grid and has no visual appearance of its own

See Chapter 6.

QGridView

A general base class for tabular data

See Section 4.17.

QGroupBox

A group box frame with a title

See Section 4.8.2.

QHBox

A widget that lays its children out in a row and has no visual appearance of its own

See Chapter 6.

QHButtonGroup

A button group that automatically arranges its children in a row

See Section 4.8.2.

QHeader

A header for tabular data displays

See Section 4.17.

QHGroupBox

A group box that automatically arranges its children in a row

See Section 4.8.2.

QIconView

Icons in a grid, optionally labelled

See Section 4.16.

QLabel

Displays a static text, a pixmap, or an animation

See Section 4.11.1.

QLCDNumber

Displays a number with LCD-like digits

See Section 4.11.2.

QLineEdit

A simple line editor for inputting text

See Section 4.10.

QListBox

A single-column list of items that can be scrolled

See Section 4.4.1.

QListView

Implements a list/tree view

See Section 4.15.

QMainWindow

A typical application window with a menu bar, some toolbars, and a status bar

See Section 4.12.1.

QMenuBar

A horizontal menu bar

See Section 4.7.

QNPWidget

A widget used for writing Netscape plug-ins

See Section 24.2 in Chapter 24.

QPopupMenu

A pop-up menu widget

See Section 4.7.

QProgressBar

A horizontal progress bar

See Section 4.13.

QPushButton

A push button with a text or pixmap label

See Section 4.3.

QRadioButton

A radio button with a label—either text or a pixmap

See Section 4.3.

QScrollBar

A vertical or horizontal scrollbar

See Section 4.6.

QScrollView

Provides a view of contents, as well as scrollbars to move through the view

See Section 4.14.

QSizeGrip

A handle for resizing a window

See Section 4.8.5.

QSlider

A vertical or horizontal slider

See Section 4.5.1.

QSpinBox

A spin box widget, sometimes called an up-down widget, a little arrows widget, or a spin button

See Section 4.5.3.

QSplitter

A splitter widget for distributing the space between two widgets

See Section 4.8.3.

QStatusBar

A horizontal bar suitable for presenting status messages

See Section 4.12.3.

QTabBar

The bar with the tabs in a tab window—useful for writing your own tab-like widgets

See Section 4.9.

QTable

A spreadsheet-like table widget

See Section 4.17.

QTabWidget

The bar with the tabs in a tab window as well as the window itself—useful for writing your own tab-like widgets

See Section 4.9.

QTextBrowser

Displays stylized text and provides features for hyperlinking

See Section 4.18.

QTextEdit

An editor for inputting text, even with different text properties

See Section 4.10.

QTextView

Displays stylized text

See Section 4.18.

QTimeEdit

A widget for entering time values

See Section 4.5.4.

QToolBar

A simple toolbar

See Section 4.12.2.

QToolButton

A push button whose appearance has been tailored for use in a QToolBar

See Section 4.12.2.

QToolTip

Provides tiny information windows about other widgets

See Section 4.12.6.

QVBox

A widget that lays its children out in a column and has no visual appearance of its own

See Chapter 6.

QVButtonGroup

A button group that arranges its children in a column

See Section 4.8.2.

QVGroupBox

A group box that arranges its children in a column

See Section 4.8.2.

QXtWidget

A widget for integrating Xt widgets into Qt applications

See Section 24.3 in Chapter 24.

QWhatsThis

Provides information windows about other widgets that are larger than those created by QToolTip

See Section 4.12.6.

QWidgetStack

Shows one of its children at a time

See Section 4.8.4.

QWorkspace

Enables the use of the MDI paradigm: subwindows in a parent window that can be independently moved, resized, and managed

See Section 4.12.4.

General Widget Parameters

All widgets in this chapter serve as building blocks for your application; most of them are commonly found in dialogs. Of course, these widgets all have the general properties of Qt widgets. You can, for example, set their fonts and colors. See the reference documentation for QWidget for these methods. The method setEnabled(), which accepts a bool parameter for influencing whether a widget is available for user interaction, is very useful. Enabling and disabling widgets, depending on the situation, is very important for creating good user interfaces. Disabled widgets are displayed “grayed out.” All too often, you see programs in which you can choose some settings, and then you are told “Sorry, this choice is not possible.” Worse, you may be offered choices that have absolutely no effect. These choices waste the user’s time. Remember this when you design your application—your users will thank you for it.

Other settings that apply to all widgets are the font, which is set with setFont(), and the palette for drawing, which is set with setPalette() (you can learn more about palettes in Section 9.3.3 in Chapter 9).

Note the uniformity of the constructors of widgets in Qt. Most widgets have a constructor with three parameters: a QWidget* that specifies the parent, a name (see Chapter 21 to find out how this is used), and widget flags, which only apply to top-level widgets and are rarely needed even for them. You can create a top-level widget without passing any parameters to the constructor because all three parameters usually default to 0. You can create any other widget simply by passing the parent.

Some widgets also provide additional overloaded constructors. These constructors provide parameters for setting additional initial properties. For example, you can pass the label for the push button as a parameter to the constructor of the class QPushButton. Any additional parameters usually come before the standard ones.

Widget Styles

Qt uses GUI emulation, so it can use widgets with a Windows style in Unix and widgets with a Motif style in Windows—or you can use a different style altogether. You could even mix widgets with different styles in your application, but doing so is not recommended.

There are several ways to set the style of widgets:

  • You can set the style for an individual widget by calling setStyle() on the widget object and passing a pointer to an object of a class that inherits from QStyle. The two most frequently used subclasses are QWindowsStyle and QMotifStyle, but there is also QCDEStyle (which emulates the look and feel of the Common Desktop Environment in a way that’s similar to QMotifStyle), QMotifPlusStyle (a Motif-like style with better bevelling and highlighting buttons when the mouse rolls over them), QSGIStyle (a Motif-like style that looks like systems running the Unix-variant Irix from Silicon Graphics), and QPlatinumStyle (which emulates the look of the standard style of the Java Swing toolkit). Qt/Embedded also includes QCompactStyle, which is similar to QWindowsStyle, but which occupies less screen real estate (useful on embedded devices with limited screen resolutions). Finally, in the Qt version of MacOS X, QAquaStyle emulates the default Aqua style used in MacOS X. If you build Qt on Windows XP, you can configure it to use an additional QXPStyle.

    As noted before, setting a certain style for an individual widget is not recommended.

  • You can set the default for all widgets by calling the static method QApplication::setStyle(). You should call this method before any widget is created or repaint all the widgets afterwards. This repainting can be done with the following code:

    // set the default 
    QApplication::setStyle( new QWindowsStyle ); // or any other style you like
    // get a list of all top-level widgets of the application 
    QWidgetList* widgetlist = QApplication::topLevelWidgets(); 
    
    // create an iterator over the list 
    QWidgetListIt widgetlist_it( *widgetlist ); 
    
    // do the following for each top-level widget 
    while( widgetlist_it.current() )
    { 
      // take one widget from the list 
      QWidget* widget = widgetlist_it.current(); 
    
      // advance the iterator to the next element 
      ++widgetlist_it; 
    
      // apply the new style to this widget 
      widget->setStyle( newstyle ); 
    
      // get a list of all descendants of this widget that are widgets 
      // themselves 
      QObjectList* objectlist = widget->queryList( "QWidget", 0, 0, true ); 
    
      // create an iterator over this list 
      QObjectListIt objectlist_it( *objectlist ); 
    
      // apply the new style to all descendant widgets 
      while ( objectlist_it.current() )
      {
        ++objectlist_it; // advance iterator to next element,
                         // the current one is already changed
        QWidget *child = (QWidget *)( objectlist_it.current() ); 
        child->setStyle( newstyle ); 
      } 
      delete objectlist; 
    } 
    
    delete widgetlist;

    You might be surprised about the two delete calls that are here when no new call is seen. That’s because Qt has allocated these objects itself.

  • The last (and often the most advisable) option is that you can set the widget style on the command line. Of course, this only works if none of the other two methods is used. You can call any Qt program with command-line switches such as -style=windows, -style=platinum, or -style=motif.

Properties

Objects of all classes that inherit from QObject can have properties, but properties are mostly used in connection with widgets. A property is nothing more than a name for a certain setting that a widget has, together with a method used to set this setting, a method used to query it, and other features. Properties are defined with the macro Q_PROPERTY. For most things you can set in a widget, there is a corresponding property, and instead of calling the set method directly, you could also use setProperty(). Instead of calling:

myWidget->setDefault( true );

you could also call:

myWidget->setProperty( "default", true );

This code is more cumbersome to write and less readable, so you will rarely use it. Properties are meant for development tools that need to know what can be set in a widget like Qt Designer.

Buttons

Buttons are probably the most common GUI element. You can group them according to how they are used: push buttons yield some kind of action such as closing a dialog box or invoking a program-specific function. On the other hand, radio buttons and checkboxes, which are collectively known as option buttons, are used to select options rather than to invoke actions. With push buttons, this is usually not possible because something happens as soon as they are clicked.[16] There is also a visual difference: push buttons appear “pressed in” only while clicked, while option buttons change appearance when clicked and keep that appearance.

Push Buttons

Push buttons are represented by the class QPushButton (see Figure 4-1) in Qt. These buttons yield some kind of action, such as closing a dialog box or invoking a program-specific function. They can be labeled with either text or pixmaps, using the method setText() or setPixmap(). A text string can also be passed in the constructor. You usually connect a slot to the signal clicked(), which is emitted when the mouse is pressed and released again over the button.

The signals pressed() and released() are also available, but you should be careful with them, since you are probably on your way to designing a hard-to-use GUI if you use them. There is also a signal called toggled(), which is only emitted in a special mode of a QPushButton object (we will discuss this signal later).

The push button in Windows and Motif style (both look the same)
Figure 4-1. The push button in Windows and Motif style (both look the same)

A push button that resides in a dialog can become the default push button. This push button is “clicked” when the user presses the Enter key. Of course, there can only be one default button in a dialog box. Make a push button the default button by calling setDefault() on it. A related method is setAutoDefault(), which makes a button an autodefault button. Unlike the default button, there can be more than one autodefault button. An autodefault button becomes the new default button when it gets the keyboard focus, and it loses this property when the keyboard focus moves to a different widget.

It is good practice to always have a default button to facilitate use of your dialog box with the keyboard only.

Menu buttons are a special case of push buttons. These buttons display a menu when clicked. Of course, menu buttons still emit the same signals, but they are rarely used; instead, the signals connected to the menu items are typically used because we are only interested in a menu button if it displays the menu. Make a push button a menu button by calling QPushButton::setPopup() and passing a pointer to a QPopupMenu. The menu that is pointed at is the menu that will be opened when the button is clicked.

Radio Buttons and Checkboxes

Option buttons are represented in Qt by the classes QCheckBox (see Figure 4-2) and QRadioButton (see Figure 4-3). These buttons differ mainly in how the choices for the user are restricted. Checkboxes are used for “many of many” choices; any number of checkboxes in a group, including none, can be checked. Radio buttons are for “one of many” choices, so exactly one radio button in a group can be checked. Both styles of buttons have special appearances that reflect their choice model, for example, in MS Windows, checkboxes are square and radio buttons are circular. In MS Windows, your users would be confused if you used square buttons that have radio button behavior.

Checkboxes
Figure 4-2. Checkboxes
Radio buttons
Figure 4-3. Radio buttons

As a programmer, you should know whether radio buttons and checkboxes are checked. You find this information with the methods QCheckBox::isChecked() and QRadioButton::isChecked(). When you want to set or unset an option button programmatically (for example, when setting a default value), use QCheckBox::setChecked() or QRadioButton::setChecked(). These methods accept a bool parameter that indicates whether the button should be set or unset.

Like push buttons, option buttons can have text or a pixmap as a label. Unlike push buttons, they are not shown in the button itself, but close to it. Clicking on the text has the same effect as clicking on the button.

Normally, you just check for the values of the option buttons in a dialog box when the dialog box has been closed with OK. Sometimes, however, you may want to react immediately to user interaction. This could be the case when setting or unsetting an option button influences the availability of other buttons. In this case, you can connect to the signals clicked(), released(), pressed(), and toggled(), just as you do with push buttons.

To achieve “one of many behavior” with radio buttons, you have to do more work than simply create them. Your application must know which buttons form a group because there could be two or more groups of radio buttons, each with exactly one button checked.

You can group your radio buttons with a QButtonGroup object. This should also be used to visually identify your buttons as a group, but for now we will just talk about enforcing radio button behavior. You have two options for using QButtonGroup. You can make your radio buttons children of a QButtonGroup object, which automatically enforces radio button behavior:

QButtonGroup* bgroup = new QButtonGroup( "Heading", parent ); 
QRadioButton* radio1 = new QRadioButton( "Choice1", bgroup ); 
QRadioButton* radio2 = new QRadioButton( "Choice2", bgroup ); 
QRadioButton* radio3 = new QRadioButton( "Choice3", bgroup );

The other approach is to insert the radio buttons into a button group by calling QButtonGroup::insert(). Note how the parents differ:

QButtonGroup* bgroup = new QButtonGroup( "Heading", parent ); 
QRadioButton* radio1 = new QRadioButton( "Choice1", parent ); 
bgroup->insert( radio1 ); 
QRadioButton* radio2 = new QRadioButton( "Choice2", parent ); 
bgroup->insert( radio2 ); 
QRadioButton* radio3 = new QRadioButton( "Choice3", parent ); 
bgroup->insert( radio3 );

Sometimes you want a button that looks like a push button but acts like an option button. An example would be a dialog box in a word processor that lets you choose the alignment (flush left, flush right, centered, or justified) of a paragraph. The four choices could be depicted by icons, and it would be graphically more attractive (and more obvious to the user) if the icons themselves could be clicked.

To achieve this functionality, use QPushButton objects and make them toggle buttons with setToggleButton( true ). You can then also connect the signal toggled() to your slot.

While radio buttons can only be on or off, checkboxes can have three different states: on, off, and “not changed.” To use this feature, call QCheckBox::setTristate( true ) first, and then set the checkbox into “no change” mode by calling QCheckBox::setNoChange(). The typical use for this “not changed” option is for font styles in, for example, word processors: if you select an area of text, some text in this area might be italic and some text might not be. In this case, you would set the checkbox into “not changed” mode, which could also be interpreted as “both.” If the user does not do anything with the checkbox, nothing is changed with respect to italicization. However, once the user clicks the checkbox, the whole area will either be made italic or nonitalic. Note that it is not possible to bring the checkbox back into “not changed” mode from the user interface (at least not directly by manipulating the checkbox). Changing the mode can only be done programmatically.

Selection Widgets

This section discusses widgets that let the user choose one (or several) items from a given selection. These widgets include combo boxes and list boxes, which are mainly used for selecting text items, but can also present graphical data to the user.

List Boxes

List boxes are represented by the class QListBox (see Figure 4-4) in Qt. List boxes let the user select one or more entries from a list of items. If there are more items than can fit into the screen space allocated for the list box, the user can use scrollbars to look through the items.

List boxes
Figure 4-4. List boxes

There are three techniques used to insert items into a list box. The most general is the method QListBox::insertItem(), which exists in three variants used to insert strings, pixmaps, or items of type QListBoxItem. The latter can be used to define items different from text and pixmaps, but it is most commonly used to define items that can show text and a pixmap at the same time. If you want to insert several strings in a row and already have them in a QStringList (see Chapter 8), you can use insertStringList(). You can then sort the items in the box according to the texts of the items by using the sort() method.

After inserting entries, you need to be informed about the entries that have been selected. If the list box allows selection of only one entry at a time, (for example, the default), you can simply ask for the currently selected item with QListBox::currentItem(). This yields a zero-based position number, which you can pass to QListBox::text() or QListBox::pixmap() to retrieve the text or pixmap at that position.

If you switched your list box to multiselection mode by calling QListBox::setMultiSelection( true ), things are more difficult. You can loop over all the entries and ask each of them if the entries are selected, for example:

for( uint i = 0; i < listbox->count(); i++ ) 
{ 
    if( listbox->isSelected( i ) 
        // item i (whose text is listbox->text( i ) or whose 
        // pixmap is listbox->pixmap( i )) is selected 
}

You can also be notified when an item is selected. When an item is highlighted with a single mouseclick, the signals highlighted( int ) and highlighted( const QString& ) are emitted. When an item is selected by double-clicking it or by pressing the Enter key, the signals selected( int ) and selected( const QString& ) are emitted. Variants with an int parameter pass the position number, while the variants with the const QString& parameter pass the text of the item.

In multiselection list boxes, the signal selectionChanged() can be useful. When several items are selected in a row by dragging the mouse over them, selectionChanged() is emitted only once.

There are also a couple of signals at a slightly lower level. They pass at least a pointer to the QListBoxItem if all you put in the list box were text and pixmaps and you did not use any QListBoxItems (Qt will create these for each item anyway). These signals report mouse actions on the items. The available signals are clicked(), pressed(), doubleClicked(), returnPressed(), rightButtonClicked(), rightButtonPressed(), mouseButtonPressed(), and mouseButtonClicked() in various incarnations.

QListBox contains many methods for customizing its operation. Please refer to the reference documentation for more details.

Combo Boxes

A combo box is an alternative to a single-selection list box. Its main advantage is the lower amount of screen real estate that it occupies. When not in use, a combo box shows only the currently selected item with a downward arrow. When the user clicks on the combo box, a list pops up, from which the user can choose an item. After the user has chosen an item by clicking on it, the combo box folds together again and shows the new selection.

Combo boxes are represented by the class QComboBox (see Figure 4-5 and Figure 4-6) in Qt. They can contain text and pixmaps, but no user-defined item types like those contained by list boxes. You can have read-only and read-write combo boxes. Read-write combo boxes allow users to insert new items at runtime. These combo boxes have a text-entry field that displays the currently selected item and can also be used for entering new text. Whether the user is allowed to do so, and where new items are inserted in the list, is governed by an “insertion policy” and set with QComboBox::setInsertionPolicy(). See the reference documentation for the available policies.

Read-write combo boxes
Figure 4-5. Read-write combo boxes
Read-only combo boxes
Figure 4-6. Read-only combo boxes

Note that if you use read-write combo boxes, you cannot have pixmaps as items since it is not possible to edit the pixmaps in a text-entry field. When you try to insert a pixmap as an item, nothing will happen.

If you use Motif-like styles, you can choose between old-style combo boxes (read-only) and new-style combo boxes. The old ones look like those found in Motif 1 programs; the new ones look like those in Motif 2 programs. Windows-like styles have only one style of combo boxes. The constructor determines whether an old-style or a new-style combo box is created. If you create your combo boxes with:

QComboBox* combo = new QComboBox( parent );

you get an old-style combo box. If the first parameter is a Boolean value, a new-style combo box is created. This Boolean value determines whether or not the combo box will be read-write:

QComboBox* combo1 = new QComboBox( true, parent ); // read-write 
QComboBox* combo2 = new QComboBox( false, parent ); // read-only

The interface used for inserting items into combo boxes is very similar to the insertion interface for list boxes: you can either insert your text or pixmap items with insertItem(), or you can insert whole string lists in the form of QStringList objects with insertStringList().

As with list boxes, you can ask the combo box for the currently selected item with QComboBox::currentItem(). You can also be notified when an item has been highlighted (the mouse is over the item but still pressed) or selected (the mouse has been released over an item) with the signals highlighted() and activated(). These signals come in an int- and a const QString&- version, as with QListBox.

When you use read-write combo boxes, you may want to check user input for validity before accepting it as a new item in the combo box. Checking user input can be done with validators, which are represented in Qt by the class QValidator. We will talk about this class in the “Validating User Input” section in Chapter 10.

Like list boxes, combo boxes have a lot of methods with which you can customize their appearance and behavior. You should at least skim the reference documentation to see what is in store for you.

There sometimes is confusion about whether to use a list box or a combo box. Choosing the wrong box for a given situation can lead to an awkward user interface. Here are some guidelines, but remember, there is no absolute truth:

  • When you need nonstandard items (items that are neither text nor a pixmap), you have to use a list box.

  • When you want to allow the user to add new items at runtime, you have to use a read-write combo box or provide extra widgets for adding and removing items.

  • When screen real estate is scarce and dialog boxes are already crowded, use a combo box. On the other hand, think about whether your dialog box is really well designed. Dialog boxes with too many options are incomprehensible to casual users. Consider splitting up the dialog box.

  • When you want to permit the user to choose more than one item, you must use a list box.

  • When you have just a few fixed items to choose from, use neither a combo box nor a list box, but a group of radio buttons or checkboxes.

Widgets for Bounded-Range Input

The slider, dial, and spin box widgets allow you to choose a numerical value from a given set. With a slider or a dial, the minimum and maximum values are fixed. A spin box need not be bounded. Finally, three widgets are used to enter date and time values.

Sliders

While it is not always obvious whether you should use a list box or a combo box, when you should use a slider is almost always obvious. In Qt, sliders are represented by the class QSlider (see Figure 4-7). A slider is a longish widget with a knob that you can drag to choose a numeric value from a predefined range. The position of the knob is proportional to the value with respect to the range. Early versions of Qt did not have a slider, which is why some programmers still use a scrollbar to achieve the same effect. Don’t use a scrollbar—your users will become confused.

Sliders
Figure 4-7. Sliders

Sliders have several parameters. The minimum value and the maximum value define the range from which the value can be selected; the step width determines how much the knob moves when you click in the slider trough instead of dragging the knob; and the initial value determines the initial position of the knob. Finally, the orientation says whether the slider should be arranged horizontally or vertically. Since these values usually don’t change over the lifetime of a slider, they should be set in the constructor:

QSlider* slider = new QSlider( 0, 200, 
                               5, 100, 
                               QSlider::Horizontal, 
                               parent, "myslider" );

This code example creates a slider with which you can choose an integer between 0 and 200. Clicking in the trough decreases or increases the slider value by 5. The initial value is 100, and the slider is positioned horizontally.

QSlider inherits from QRangeControl, which allows methods such as setRange(), setSteps(), and setValue() to change these parameters. QSlider adds setOrientation(). Remember that changing this visual parameter after the slider has been shown can be very irritating to the user.

If the slider is long, it can be useful to provide visual clues as to where a certain value might be on the slider “scale.” These clues are tickmarks, which you can add to a slider by calling setTickmarks(). You can choose whether you want the tickmarks to be above, below, or on both sides of the slider, or whether you want none at all. There is no general rule about when to use tickmarks, but we suggest you use them only when the slider is longer than 200 pixels or so to avoid visual overload.

To retrieve the slider’s set value, you can call either QSlider::value(), or you can be notified when the slider value changes. The signal sliderMoved( int ) is emitted whenever the knob is moved so much that a new value is chosen. The parameter contains the new value. If you want to be notified only when the user is done selecting a value (i.e., when the user has released the knob), connect to the signal valueChanged( int ). When you set setTracking( true ), valueChanged( int ) behaves just like sliderMoved( int ). If you are interested in knowing when the user has pressed or released the knob, you can also connect to the signals sliderPressed() or sliderReleased(), but you will rarely need to do so.

It’s interesting and useful to connect a QSlider object to a QLCDNumber object. You will learn about QLCDNumber later in this chapter, but for now you only need to know that it shows a value with a predefined number of digits in the form of the seven-segment LCD displays. Such a display can be used to represent the value chosen with the slider:

QSlider slider = new QSlider( 0, 9, 1, 1, QSlider::Horizontal, parent ); 
QLCDNumber number = new QLCDNumber ( 1, parent ); 
QObject::connect( slider, SIGNAL( sliderMoved( int ) ), 
                  number, SLOT( display( int ) ) );

This combination is so useful that you will probably see more combinations of sliders with QLCDNumber objects than single sliders. For a full example, see the section “Another Example for Signals and Slots” in Chapter 2.

Dials

Dials (see Figure 4-8) are very similar to sliders. They are a recent addition to Qt, and most people could probably just get along with using sliders exclusively. There are two differences between a slider and a dial. First, the slider is long, while the dial is square; second, a dial can “wrap around.” If the user has reached the maximum value and tries to increase the value further, he will end up with the minimum value again and can continue to select from there. Whether this is useful or not depends on the application; in most situations, this wrapping around is probably not what the user expects, but in some rare cases, such as when selecting a direction, it might be just what you need.

Dials
Figure 4-8. Dials

If you do not need the wrapping around, then whether you pick a slider or a dial is mostly a question of dialog box layout. You can use whatever looks nicer in your dialog box. Some users think that sliders are easier to operate, however. Also, there are some patterns from real life that are more connected to either a slider or a dial. Take a stereo for example: the volume knob is usually round like a dial and does not wrap around, fortunately. The various controls for the equalizer are usually sliders, however. If you write a front end to a sound card, you might want to follow these patterns; doing so will make it easier for your user to recognize the functionality of each control.

As for the programming interface, QDial, which implements dials in Qt, has almost the same interface as QSlider so you can read about most methods there. One method that QSlider does not have, of course, is setWrapping( bool ), which specifies whether the dial should wrap around or not. By default, wrapping is off. When wrapping is off, the dial will not be a complete circle, but it will leave out a segment at the bottom to indicate that a “barrier” is present.

Two methods control the dial’s appearance: setNotchesVisible( bool ) specifies whether small lines should indicate the various positions and the method setNotchTarget( double ) specifies a size for these lines. Depending on the size of the dial and its other parameters, QDial will sometimes have to use a slightly different value.

QDial has the same signals as QSlider, but they are of course not named sliderMoved(), etc., but dialMoved(), etc.

Spin Boxes

Spin Boxes (see Figure 4-9), implemented by the class QSpinBox, consist of a text-entry field and two arrow buttons—one for increasing the numerical value in the text field and one for decreasing it. This combination is convenient for entering integer values because the UI element can be operated easily with the mouse. But if doing so becomes cumbersome (for example, the value desired is far away from the current value), the keyboard can be used as well.

Spin boxes in Windows and Motif style (both look the same)
Figure 4-9. Spin boxes in Windows and Motif style (both look the same)

Note that without any extra precautions, this is not a foolproof method for inputting integer numbers in a given range because the user can enter any text whatsoever into the widget. You can use the methods described in the “Validating User Input” section to make sure that the users enter only integer numbers in the desired range.

The most obvious methods are setValue() and value() for setting and retrieving the current value, but there are more. You can change the display in various ways. For example, you can set a prefix and a suffix that are displayed in front of and behind the value in question, respectively. This is done with setPrefix() and setSuffix(). A related method that also influences the display, but not the value itself, is setSpecialValueText().

Say that you want to let the user choose a font size, but that you also want to provide some kind of “default value,” which could mean “take the font from the previous paragraph.” You can have a value like 0, which is an impossible value for the font size as a marker for the font, but it would be difficult to explain this to the user. This is where setSpecialValueText() comes in. With this method, you set text that is shown, instead of the actual value, when the smallest possible value has been chosen. In our example, this could be <default>.

Two more methods to change the value programmatically are stepUp() and stepDown(), which simulate clicking the up arrow and the down arrow buttons, respectively.

Normally, when the user clicks on one of the arrow buttons and the minimum or maximum value that is set together with setRange() is reached, the value does not change any more. But if you have called setWrapping( true ), the values wrap around and start at the maximum or minimum value again.

For the buttons that let the user increase or decrease the selected value, there are two different styles. By default, a small up and a small down arrow are shown, but you can change this arrow to plus and minus signs by calling QSpinBox::setButtonSymbols( PlusMinus ).

Finally, you will find two signals, both with the name valueChanged(). One has an integer parameter and the other has a const QString& parameter. By connecting to these signals, your program can be informed whenever the value of the spin box changes. By choosing the right signal, you can either get just the numerical value or the whole string in the input field, including the prefix and suffix, if any exist.

Widgets for Entering Date and Time

A particular case of bounded range input is the input of date and time values. It is not so much the bounding of the range that is interesting here (even though there are boundaries, like the fact that the month number in a date value must be between 1 and 12), but rather the format. Qt provides three widgets for entering date and time values: QDateEdit (see Figure 4-10) is for entering dates, QTimeEdit (see Figure 4-11) is for entering time values, and QDateTimeEdit (see Figure 4-12) obviously is the simple concatenation of the two. The way these three widgets are used is very similar; QDateEdit works on a QDate object, QTimeEdit on a QTime object, and QDateTimeEdit on a QDateTime object. You can construct all three widgets with the date or time value in question, but be informed about changes with respective valueChanged(...) signals and specify—at least for QDateEdit and QTimeEdit—minimum and maximum values that are permissible. All three also have an auto-advance feature that is turned on by calling setAutoAdvance( true ) and that moves the focus to the next entry section when one is completed.

A date edit field in Windows and Motif style (both look the same except for the highlighting color)
Figure 4-10. A date edit field in Windows and Motif style (both look the same except for the highlighting color)
A time edit field in Windows and Motif style (both look the same except for the highlighting color)
Figure 4-11. A time edit field in Windows and Motif style (both look the same except for the highlighting color)
A date/time edit field in Windows and Motif style (both look the same except for the highlighting color)
Figure 4-12. A date/time edit field in Windows and Motif style (both look the same except for the highlighting color)

These three widgets are pretty simple—they are not much more than a couple of spin boxes in a row. However, third-party components that provide more fancy input (like calendar input) of date and time values are available.

Scrollbars

Like buttons, scrollbars are intuitively understood by most users. You can either use them directly by creating objects of type QScrollBar (see Figure 4-13), or use them by means of QScrollView. Usually, you’ll do the latter because it relieves you from all the geometry management related to scrollbars that are attached to an application window and because it supports automatic scrollbar policies.

Scrollbars
Figure 4-13. Scrollbars

Like sliders, scrollbars have a minimum value, a maximum value, a current value, and an orientation. There is a “line step size,” which is the amount the scrollbar knob moves when you click on one of the buttons at the end of the scrollbar. The “page step size” is the amount the knob moves when you click in the trough of the scrollbar.

It is most convenient to set these values when creating the scrollbar:

QScrollBar* scrollbar = new QScrollBar( 0, /* minimum value */ 
                                        500, /* maximum value */ 
                                        1, /* line step value */ 
                                        10, /* page step value */ 
                                        0, /* initial value */ 
                                        QScrollBar::Vertical, /* orientation */ 
                                        parent
                                        );

but you can also set the values with setRange(), setSteps(), setValue(), and setOrientation(). The latter accepts the values QScrollBar::Vertical and QScrollBar::Horizontal.

To use the scrollbar, you must know when it is operated by the user, no matter whether the user operates it with the knob, by clicking on the arrows, or by clicking in the trough. Your application can be notified of any changes by a plethora of signals. The most important are sliderMoved( int ) and valueChanged( int ). With these signals, the parameter contains the new scrollbar value. The sliderMoved( int ) signal is emitted whenever the value changes, no matter whether the user drags the knob or clicks on some part of the slider. The valueChanged( int ) signal is emitted only when the user clicks on the scrollbar or drags the slider and releases the knob again, unless setTracking( bool ) has been called before. In this case, valueChanged( int ) behaves just like sliderMoved( int ).

Four other signals are emitted when the slider value changes by a line step or a page step: nextPage(), nextLine(), prevPage(), and prevLine(). The last two remaining signals, sliderPressed() and sliderReleased(), are probably less useful. They are emitted when the user has started or quit operating the knob. You could use these signals when you have to prepare your application window for scrolling.

The most common use for scrollbars is moving the view that an application window provides on the application data. There is usually a horizontal scrollbar below the application window and a vertical scrollbar to the right of the application window. Sometimes the scrollbars are only shown on demand—for example, only when the representation of the application data is larger than the visible area. You could arrange the scrollbars yourself, but it is much easier to use the class QScrollView. We will cover the use of QScrollView later in this chapter.

Menu-Related Widgets

To program menus, Qt provides the four classes QPopupMenu (see Figure 4-14), QMenuBar (see Figure 4-15), QMenuData, and QCustomMenuItem.

A pop-up menu in Windows and Motif style (both look the same)
Figure 4-14. A pop-up menu in Windows and Motif style (both look the same)
A menu bar in Windows and Motif style (both look the same)
Figure 4-15. A menu bar in Windows and Motif style (both look the same)

You have already seen the basic use of pull-down menus and pop-up menus in Section 3.1 in Chapter 3, so we will focus on some special uses here.

Providing accelerators in menus is easy. Accelerators are key combinations used to directly access a function bound to a menu item without popping up the menu first. They should not be confused with underlined letters in menu entries, which can be used to jump to the entries when the menu is already open.

Set an accelerator for a menu item by calling QMenuData::setAccel(), which accepts a key code and a numeric identifier. The identifier is the value returned by QMenuData::insertItem() when you insert the menu entry. The keycode is an expression like CTRL + Key_O or SHIFT + ALT + Key_P. Refer to the header file qkeycode.h for a complete list of keycodes. Qt automatically generates a string like Ctrl-O, which is appended to the menu-item label.

Until now, you have mostly seen first-level menus, which pop up in response to a user event like a mouseclick or from a menu bar. But you don’t have to learn anything new to add second-level menus, which pop up from another pop-up menu. Simply add the second-level menu with insertItem() to the first-level menu.

It is possible to implement dynamic menus—that is, menus that change contents as the application changes—in Qt. To do this, connect a slot to the signal aboutToShow() of the menu in question. In this slot, you can change the menu dynamically by calling QMenuData::removeItem(), QMenuData::updateItem(), QMenuData::setItemEnabled(), or whatever you like. The only problem is that you cannot get the QPopupMenu object corresponding to the ID, so you have to keep a table that maps IDs to QPopupMenu objects somewhere.

Apart from bringing up pop-up menus synchronously with exec(), as described in the “Adding Menus” section in Chapter 3, you can also pop them up asynchronously. This makes it possible to continue background processing while the menu is popped up, but since the menu has to grab the mouse, the next mouseclick leads to closing the menu, no matter where it occurs. If you use asynchronous menus, you won’t be able to check the selected menu item via the return code, but only via the signal activated( int ).

If you have used Motif before, you will certainly know the concept of tear-off menus. A tear-off menu has a special menu item (usually a dashed line). When this item is selected, a copy of the menu is created and put in its own top-level window, which can be moved independently of the application window and stays open until it is closed explicitly. This can be achieved by calling insertTearOffHandle() on the QPopupMenu object in question. You specify the position where the tear-off item should be added. If you do not specify anything and just accept the defaults, this will be the very first item, which is also what users accustomed to Motif will expect. Note, however, that people used to Windows platforms will probably not know what this dashed line means and will be irritated when they select it and a new window pops up. Therefore, it might be a good idea to use tear-off menus only in Motif style and other Unix styles (or not at all).

If you need more than text display, pixmaps, and an accelerator, you can also take over the drawing of menu items completely by yourself. This is done by subclassing from QCustomMenuItem and reimplementing at least the two methods—paint(), which should do the actual painting, and sizeHint(), which should return the preferred size of the menu item. You can also override fullSpan() and return true if the menu item should span the entire menu, as well as override isSeparator() and return true to mark the item as a separator. You can then insert an object of your subclass of QCustomMenuItem with QMenuData::insertItem(), just like you do with ordinary items.

Arrangers

Qt contains ten classes that are mainly meant for arranging other widgets: QFrame, QGroupBox, QButtonGroup, QHGroupBox, QVGroupBox, QHButtonGroup, and QVButtonGroup draw a frame around their contents, while QSplitter lets the user change the relative position of two widgets to each other. QWidgetStack arranges widgets on top of each other so that only one widget is visible at any time. Finally, QSizeGrip helps resize other top-level widgets. With one exception (see the next section), QFrame is only useful as a base class for your own widgets. It serves as a base class for several built-in Qt widgets as well. On the other hand, you will rarely derive classes from QGroupBox or QButtonGroup; these are consumed as is, just like QSplitter and QWidgetStack.

Frames

QFrame draws a frame and then calls drawContents(). You should reimplement this method in the classes that you derive from it; the default implementation does nothing. The most useful method of QFrame is setFrameStyle(), which accepts a large number of flags. These flags come in two groups: frame shapes and shadow styles. You can pick one flag from each group and join them with bitwise-or. The frame shapes group contains flags like Box for a simple box and WinPanel for Windows-like panels. The shadow styles group contains the three flags QFrame::Plain, QFrame::Raised, and QFrame::Sunken.

It is hard to imagine how a frame shape and shadow style will look when combined. The Qt reference documentation contains an image that shows all possible combinations. You simply have to pick one!

If you want to customize your frames further, you can also use the methods QFrame::setLineWidth() and QFrame::setMidLineWidth(), but using these methods leads to nonstandard interfaces.

You can also make the contents of the frame keep a certain distance from the frame itself by calling setMargin() and passing the distance as an integer value. This margin is then subtracted from the contents rectangle reported by contentsRect(), and that subtraction should be respected in your implementation of drawContents().

Using a QFrame object alone is useful in one case: QFrame objects make fine separator lines. The object must be enabled to have a line shape, but since this enablement is the default in the constructor, you usually don’t have to worry about this requirement.

Group Boxes

While QFrame is a building block for your widgets, QGroupBox (see Figure 4-16) and QButtonGroup can be used as is. They simply draw a frame that can visually group widgets that are related logically, such as a group of mutually exclusive radio buttons. Both widgets show up the same way, but QButtonGroup has additional facilities for working with buttons, like enforcing radio button behavior.

A group box in Windows and Motif style (both look the same)
Figure 4-16. A group box in Windows and Motif style (both look the same)

It is almost always a good idea to add a description to widgets that are grouped together by a QGroupBox or QButtonGroup object. You can add a description by calling QGroupBox::setTitle(), which adds a label to the upper frame line. You can also pass this title as the first argument in the constructor. While most group boxes have their title on the left side of the upper frame, you can also change this by calling QGroupBox::setAlignment() with the parameter Qt::AlignCenter or Qt::AlignRight.

QButtonGroup is used mainly to enforce radio button behavior so that only one button in a group can be checked at a time. You can either make your radio buttons children of a QButtonGroup object or insert them manually with QButtonGroup::insert(). Inserting a radio button into a button group automatically makes it exclusive. If you want to have the same behavior for nonradio buttons, you have to call QButtonGroup::setExclusive( true ) explicitly.

Another useful application of QButtonGroup is connecting one slot to the signals of several buttons. See the “Connecting Several Buttons to One Slot” section to learn how to do this.

There are special versions of QGroupBox and QButtonGroup that are called QHGroupBox, QVGroupBox, QHButtonGroup, and QVButtonGroup. The only difference to the basic versions of these widgets is that they perform layout management on their children automatically. This difference is advantageous because the widgets can automatically take care of not overwriting the frame and the caption. You can read more about this concept in Chapter 6.

Splitters

Splitters manifest themselves as small knobs (see Figure 4-17) that can be used to distribute the available space between two widgets. In Qt, this distribution is implemented by the class QSplitter, which also contains the machinery for resizing the widgets. Example 4-1 contains a very simple program for using QSplitter.

Splitters
Figure 4-17. Splitters
Example 4-1. splitter.cpp: Using QSplitter to divide the space between two widgets
#include <qapplication.h>
#include <qlabel.h>
#include <qsplitter.h>

int main( int argc, char* argv[] )
{
  QApplication a( argc, argv );

  QSplitter* splitter = new QSplitter();
  a.setMainWidget( splitter );

  QLabel* label1 = new QLabel( "Label 1", splitter );
  QLabel* label2 = new QLabel( "Label 2", splitter );

  splitter->show();

  return a.exec();
}

Two label widgets are created as children of the splitter and are thus arranged by the splitter automatically. Of course, a horizontal arrangement is also possible. This arrangement can be achieved by passing QSplitter::Vertical either as an additional first parameter to the QSplitter constructor or by passing it to setOrientation(). This might sound confusing, but if the splitter’s orientation is vertical, the two widgets will be in a row and vice versa.

When you compile and run the program in Example 4-1, you will find it possible to make one widget almost completely disappear. You may want a certain amount of space to be available for each widget. This is easily done by calling setMinimumSize() at the widget as usual:

label1->setMinimumSize( label1->sizeHint() );

Calling setMinimumSize() ensures that label1 will always be large enough to display its contents.

QSplitter respects this allotted space if you call setMaximumSize() as well. Also, you can determine whether a widget should also be resized when the whole splitter is resized by calling setResizeMode() and passing the pointer to the widget in question as the first parameter, and then by passing either QSplitter::Stretch, QSplitter::KeepSize, or QSplitter::FollowSizeHint as the second parameter.

Normally, QSplitter resizes the widgets it arranges at the end of a resizing operation when the user stops dragging the knob. If you call setOpaqueSize( true ) on the QSplitter object, the widgets get all the resize events. This is useful for providing the user with immediate feedback, but it can slow down the resizing operation if the resized widgets are large, complex, or slow to redraw.

Widget Stacks

The last of the widgets that arrange other widgets is QWidgetStack. Except for a frame drawn around it, a widget stack simply shows one of its children at a time, as specified by the user. This is useful when you have several widgets but want to make only one visible at a time.

After creating an object of this class, create the widgets that you want arranged by the widget stack, and then add these widgets to the widget stack with addWidget(). This method expects a pointer to the widget in question and an integer ID. The widget should be a child of the QWidgetStack object. If it is not, it will be made into one. You can also remove widgets from the widget stack again by calling removeWidget().

To show a certain widget on the stack, call raiseWidget(). You can either pass the method a pointer to the widget that you want to make visible or pass the integer ID that you passed to addWidget().

Size Grips

Size grips (see Figure 4-18) are small handles that let the user resize a top-level window; they are usually located in the lower-right corner of the window. If you write a standard-style application, you probably do not need to include a size grip yourself. Since you will already have a status bar, and status bars include size grips, you are already set.

A size grip in Windows and Motif style (both look the same)
Figure 4-18. A size grip in Windows and Motif style (both look the same)

Should you need to create a size grip explicitly, create an object of class QSizeGrip anywhere in the widget hierarchy and position it as desired (anything but the lower-right corner is likely to surprise your users). The size grip will find out what the top-level window is and let the user resize.

Tab-Related Widgets

When you have a lot of user-interface elements in a main window or a dialog box, you might consider using tabs. Tabs resemble the filing cabinets where each folder has a small heading cardboard that explains what is in the folder.

Qt provides three classes for achieving this: QTabBar, QTabWidget, and QTabDialog. There is also the class QTab, which, though public, is mostly undocumented.

Since most uses of tabs are in the form of a freestanding dialog box, I will cover the use of tabs in the next chapter (where QTabDialog is described). Everything it says there is also valid for QTabWidget, with the exception that a QTabWidget object is not a freestanding dialog box, but is meant to be included elsewhere in a window. In other words, it has no buttons. To put it another way, QTabDialog is a combination of QTabWidget and QDialog. You can see a tab widget in Figure 4-19.

Tab widgets
Figure 4-19. Tab widgets

QTabBar is the class that provides the actual tab row (QTabWidget is a combination of QTabBar and QWidgetStack). Usually, you will only work with QTabDialog or QTabWidget and not use QTabBar directly, but if you have special requirements like special styles, you can make a subclass from QTabBar and draw the tabs yourself. The most important methods in QTabBar are setShape(), used to select one of the four predefined tab styles (tabs at the top with rounded corners, triangular tabs at the top, tabs at the bottom with rounded corners, and triangular tabs at the bottom); setTabEnabled(), which lets you control the accessibility of a tab page; and setCurrentTab(), used to select the current tab page programmatically. QTabBar also emits the signal selected( int ) when one of the tab pages is selected.

Text-Entry Fields

Text entry in Qt is done via the two widgets QLineEdit (see Figure 4-20) and QTextEdit (see Figure 4-21) for single-line and multiline text entry fields, respectively. Their use is very simple. In most cases, it is sufficient to create the widget. You can preset text with setText() and retrieve the entered text with text(). You can restrict the maximum number of characters entered in QLineEdit objects and decide whether the characters entered should be shown, not shown, or replaced by asterisks. The latter two options are useful mainly for entering passwords. QTextEdit does not have these options.

A single-line text-entry field in Windows and Motif style (both look the same)
Figure 4-20. A single-line text-entry field in Windows and Motif style (both look the same)
Multiline text-entry fields
Figure 4-21. Multiline text-entry fields

QTextEdit also lets you insert text programmatically with QTextEdit::insertAt() and QTextEdit::insertLine(), and remove it again with QTextEdit::removeLine(). In addition, you can use this widget as a simple text viewer by calling the method QTextEdit::setReadOnly( true ).

QTextEdit and QLineEdit can work with the system clipboard. Simply use the methods cut(), copy(), and paste(). You do not need to create a QClipboard object for this.

Both widgets report state changes via the signals returnPressed() and textChanged(); QTextEdit also has a couple of other, less-often used signals. The textChanged signal is emitted whenever the text in the text-entry field changes. It has a const QString& parameter with QLineEdit that contains the new contents. Retrieving the text from a QTextEdit is an expensive operation that should not be done often. Note that if you want to connect to textChanged() to validate the input, you should use a QValidator object instead (see Section 10.2 in Chapter 10).

One of the interesting things about QTextEdit is that it not only supports displaying and entering normal unformatted text, but also so-called rich text. If you want to use rich text instead of plain text, call setTextFormat( Qt::RichText ). How rich text is handled in Qt is described in Section 10.5 in Chapter 10.

Labels

Labels are widgets that display information. The user cannot interact with them. In Qt, two classes are provided for this: QLabel, which can display text, a pixmap, or animation; and QLCDNumber, which shows a numerical value with a predefined number of digits.

Simple Labels

QLabel (Figure 4-22) is probably the simplest widget in all of Qt. It can display a piece of text (including rich text), a pixmap, a vector graphic, or an animation that you set via QLabel::setText(), QLabel::setPixmap(), QLabel::setPicture(), or QLabel::setMovie(). If you want to set text, you can also pass it as the first argument in the constructor.

A label in Windows and Motif style (both are the same)
Figure 4-22. A label in Windows and Motif style (both are the same)

You can set the alignment—that is, indicate where in the space for the label the contents should be located. This is done by calling setAlignment(), which accepts several flags, including AlignLeft and AlignVCenter. See the reference documentation for QLabel for the full details.

The setAlignment() method also knows about two flags that are not directly related to alignment: ExpandTabs tells the label to expand tab characters to spaces, and WordBreak tells it to automatically break the lines at the border of the label. These two flags make sense only with labels that show text. They have no effect when the label shows a pixmap or an animation.

Since QLabel inherits from QFrame, it can have various frame styles (see the section about QFrame).

Another interesting QLabel feature is the ability to assign “buddies.” You already know that most intrinsically labeled widgets, such as buttons, can have an ampersand in their label. This is rendered as an underscore below the next letter after the ampersand. This letter serves as a shortcut key for this widget. Labels can have shortcut keys, too, but they would not make much sense without the buddy mechanism because labels don’t provide any means of user interaction. Instead, when you set a buddy widget with QLabel::setBuddy(), keyboard focus is transferred to the buddy widget whenever the shortcut key of the label is pressed. The usual applications of this feature are labeled text-entry fields or combo boxes:

QLabel* label = new QLabel( "&labeltext", parent ); 
label->setGeometry( ... ); 
label->setAlignment( AlignRight \(md AlignVCenter ); 
QLineEdit* edit = new QLineEdit( parent ); 
edit->setGeometry( ... ); // position right to the label 
label->setBuddy( edit );

The fact that labels are the easiest way to show some graphics is often overlooked. For example, Example 4-2 shows a complete graphics viewer that already supports several formats. Note that in this example, qDebug() is used for a diagnostic message. This is very useful for messages that you only want to see during development. In Unix, these messages go to standard error; in Windows, they go to the debugger. Where they are shown depends on your IDE. In Visual C++, they are shown in the Debug window that is by default located in one of the tabs at the bottom of the screen.

Example 4-2. pixview.cpp: A simple graphics viewer with QLabel
#include <qapplication.h>
#include <qlabel.h>
#include <qpixmap.h>

int main( int argc, char* argv[] )
{
  QApplication myapp( argc, argv );

  if( argc != 2 )
    {
      qDebug( "Usage: pixview filename\n" );
      exit( 1 );
    }

  QPixmap mypixmap;
  mypixmap.load( argv[1] );

  QLabel* mylabel = new QLabel( 0 );
  mylabel->resize( mypixmap.size() );
  mylabel->setPixmap( mypixmap );

  myapp.setMainWidget( mylabel );
  mylabel->show();
  return myapp.exec();
}

A QLabel object is not always the best choice for displaying static text: if you want to let the user copy text from the label to the clipboard, use a QLineEdit set to read-only mode instead. For multiline labels, a read-only QTextEdit can give you more flexibility at the cost of higher memory requirements.

QLCDNumber

QLCDNumber (see Figure 4-23) is a nice little widget for displaying numeric information. It can display numbers with a fixed, but arbitrary, number of digits in decimal, octal, hexadecimal, and binary representation, and can add a decimal point and display a restricted number of strings. It uses a so-called “seven segment display,” which should be well-known to everyone who has ever seen a digital clock. QLCDNumber is commonly used in connection with a QSlider object. (See Section 4.4 earlier in this chapter for details.)

An LCD number widget in Windows and Motif style (both are the same)
Figure 4-23. An LCD number widget in Windows and Motif style (both are the same)

You can create, size, and show a QLCDNumber like any other widget. You can either pass the number of digits as the first argument to the constructor or set it afterwards with QLCDNumber::setNumDigits(). The number or text to be shown is set with display(), to which you can pass either an int, a double, or a const QString& parameter. The reference documentation for QLCDNumber contains the letters that can be displayed. All others are replaced by spaces.

When you display a numeric value, you can determine the base in which it will be displayed. This base can either be chosen by calling setMode() with QLCDNumber::Bin, QLCDNumber::Dec, QLCDNumber::Hex, or QLCDNumber::Oct as parameter, or you can use the slotssetBinMode(), setDecMode(), setHexMode(), setOctMode(), or setDecMode(), respectively. Decimal representation is the default.

You can influence the appearance of the displayed data in two ways. You can choose a style for the segments by calling setSegmentStyle() with one of the parameters Outline, Filled, or Flat. In addition, you can determine whether a possible decimal point will be shown in a position of its own, or between two positions, by calling setSmallDecimalPoint() and passing either true or false. Often you’ll want to know whether a given number will fit into the available digits. This information can be determined by calling QLCDNumber::checkOverflow(), to which you can pass either a double or an int parameter. It will return a bool that is true if the number cannot be displayed within the available digits.

If this is not an option for you because the value is set in a slot called by a signal from somewhere else, you can at least take action afterwards by connecting to the signal overflow() that is emitted whenever a number or a string that does not fit into the available digits is passed to display().

Widgets for the Office

The Microsoft Office suite has set an interface standard that most Windows applications follow today. This is becoming the standard even on Unix systems. On top of the application window is a menu bar, below which you can find toolbars—rows of buttons that provide quick access to frequently used functionality. Buttons in toolbars are usually labeled with images rather than text. Because of their small size, these images are not always easy to recognize. Tooltips were invented to remedy this problem. A tooltip is a short phrase that is shown in a small window over a button when the user places the mouse over the button. Most programs have only one toolbar, but it is not uncommon to have three or even more of them. Below the toolbar is the main application area. In a spreadsheet application, this would be the spreadsheet that you are working on. At the bottom margin, a status bar is often found that contains information about the current state of the application.

Recently, docking windows have become a common addition to this “office style.” Docking windows are internal windows that can be “docked” into certain positions, but that also float freely “on top of” the rest of the application.

This section explains the widgets that Qt provides for creating standard application windows. For a more extensive example, see the directory examples/application in your Qt distribution.

The Main Window

QMainWindow organizes a menu bar, any number of toolbars, a status bar, and a central widget, and manages their geometry.

QMainWindow is very easy to use. If you add children of the classes QMenuBar, QToolBar, or QStatusBar, QMainWindow automatically detects and uses them for their respective purposes. You can also have QMainWindow create a menu bar or status bar on demand by calling menuBar() or statusBar(), respectively. These methods either return a previously created menu bar or status bar, or create a new one. Note that you always have to create toolbars yourself; they are never generated on the fly.

Toolbars are added by hand. First, you have to create the toolbar yourself (see the next section for this), and then add it with addToolbar() (or simply make the main window a parent of the toolbar), which accepts a few more parameters. The second parameter is a label that is used as a tooltip and description for the toolbar; the third indicates where you want the tool bar to dock with the main window. Possible values are Top, Bottom, Left, Right, Unmanaged, and TornOff. The first four options should be obvious. The fifth does not show the toolbar, and the last one makes it “float” over the application. Finally, the fourth parameter of addToolbar() indicates whether the toolbar should occupy a new row in the main window (pass true) or whether it should be set to the right of the last toolbar added if there is enough space for it (this is the default).

You can add as many toolbars as you like with addToolbar(), and you can also take them away with removeToolbar(). Two more methods in QMainWindow influence the look of all the toolbars: setUsesBigPixmaps( true ) makes the toolbars use larger pixmaps than usual, and setRightJustification( true ) makes the toolbar extend all the way to the right edge of the main window. This behavior is common, so you might want to always use this setting.

A menu bar, some toolbars, and a status bar do not make a useful application: the “content” is missing. You can use any widget for this content—either predefined Qt widgets, or widgets that you have written yourself. This widget should be a child of the main window and should be registered with setCentralWidget(). QMainWindow does nothing with this widget except manage its geometry. You could, for example, put a QTextEdit widget in as a central widget and have a text editor. However, doing so will not be useful yet because it lacks loading and saving capabilities. These features are simple to add, though.

All in all, QMainWindow makes it easy to achieve a standard look for your applications. Unless your application has special UI needs, you should use QMainWindow.

Toolbars

Toolbars are one of the preeminent features of today’s GUI applications. Qt provides the class QToolBar, which is used in conjunction with the class QToolBarButton.

When working with toolbars, you normally don’t need to do anything but create objects. The parent-child relationship helps Qt figure out where to put which widget. You can create a QToolBar object as a child of a QMainWindow object, and as many QToolButton objects as needed as children of the QToolBar object. That’s all. Here is some sample code:

... 
QMainWindow* mainwindow = new QMainWindow; 
QToolBar* toolbar = new QToolBar( mainwindow ); 
QToolButton* toolbutton1 = new QToolButton( somepixmap, "statusbartext", 
                                            0, this, SLOT( someSlot() ), 
                                            toolbar, "tooltiptext" ); 
QToolButton* toolbutton2 = new QToolButton( someotherpixmap, "statusbartext", 
                                            0, this, SLOT( someOtherSlot() ), 
                                            toolbar, "tooltiptext" ); 
...

You might want to use four other methods in QToolBar. The addSeparator() method inserts space that is not occupied by toolbar buttons and can be used to group toolbar buttons together visually. The setStretchableWidget() method marks one widget as stretchable. This method is used if the QMainWindow that contains the toolbar is configured to have one toolbar over its whole width. The marked widget then takes all remaining space. If you don’t do this, the toolbar will occupy only the space needed for all its children, unless you call setHorizontalStretchable( true ), in which case the toolbar will extend over all the available space as well. There is also setVerticalStretchable(), which is less useful.

QToolButton also has some interesting methods. Normally, only the image is displayed, but when you call setUsesTextLabel( true ); on a toolbar button, it displays text below the image. This text is set with setTextLabel().

In the previous section, you saw that a whole toolbar can be configured to use large pixmaps. This can also be done for each toolbar button by calling setUsesBigPixmap( true ) on the button.

Finally, you can also make a toolbar button toggle with QToolButton::setToggleButton(). This is rarely used, but some buttons toggle font styles such as italics or bold in word processing applications.

Status Bars

A status bar (see Figure 4-24) contains messages for the user. These messages normally consist of text only. Qt, which implements status bars in the form of the class QStatusBar, distinguishes three different types of messages in the status bar:

A status bar in Windows and Motif style (both look the same)
Figure 4-24. A status bar in Windows and Motif style (both look the same)
Temporary

Temporary messages are displayed only briefly. Most are explanatory texts for menu entries or toolbar buttons. Temporary messages are shown by calling QStatusBar::message(). You can use the variant that accepts only the text to be shown and then remove the temporary message afterwards with QStatusBar::clear(). However, it’s easier to specify a time in milliseconds as the second argument. In this case, the temporary message is removed automatically. There is never more than one temporary message; if QStatusBar::message() is called again while the old one is still showing, the old one is deleted.

Normal

Normal messages are shown to the left of the first permanent message (see the next item). They can be obscured temporarily by temporary messages. You can put any widget as a normal message into the status bar, but usually QLabel is used. Another widget that might be useful in a status bar is QProgressBar. You put a widget into a status bar as a normal message by calling QStatusBar::addWidget() and then passing a pointer to the widget as the first parameter. The second parameter is an integer stretch factor that is used to compute the space that is available for each normal and permanent message in the status bar. This value is not an actual width in pixels, but it indicates the relative size in relation to the other widgets. The third parameter is false.

Normal messages are removed again with removeWidget(). Some things that might be useful as normal widgets are information items that are usually displayed but often change, and information items that are not crucial for allowing the user to operate the application. Examples would be line and column indicators in a spreadsheet or a word processor, or an indicator that shows whether the current document has been saved.

Permanent

Messages are made permanent by passing true as the third argument. Permanent messages are arranged to the right of the status bar, and can never be obscured by temporary messages. It is customary to put information there that is relatively static or that is needed for the user to operate the application, such as mode indicators. Though it does not fit the pattern, many applications also put a clock there.

Multiple Document Interface (MDI)

On the Windows platform, the so-called Multiple Document Interface (MDI) is a popular UI paradigm. MDI programs have a subwindow for each document that is confined to the extent of the main window. Each subwindow behaves a lot like the main window itself: they have a titlebar and can be resized, minimized, maximized, and closed. The opposite of this paradigm is the Single Document Interface (SDI) in which you have a new main window for each new document—or can only work with more than one document at a time.

UI research has shown that MDI is probably a bad idea and should be used with care; many users never grasp the concept of two levels of windows (main windows and subwindows) and consequently never open more than one document at a time or do not manage to switch between them. Even Microsoft, previously the strongest proponent of MDI, seems to be backing out slowly.

Nevertheless, Qt supports MDI in case you need it. The class that achieves this support is called QWorkspace. Just create one object of QWorkspace. Then, for each MDI window you want to have, create an ordinary Qt widget (QWidget or subclass thereof) and let it have the QWorkspace object as its parent. Usually, you will want the QWorkspace object to be the central widget in a QMainWindow.

You can manipulate the subwindows as usual; you can even call show(), hide(), showMaximized(), and showNormal(). All these operations will operate within the workspace. An additional feature of QWorkspace is that it can tile the subwindows (arrange them so that each one is visible and there are no overlaps; if there are many windows or the workspace is small, then the subwindows will be very small) or cascade them (arrange them so that only the titlebars of the subwindows are visible; the next subwindow starts right under the titlebar of the previous one, slightly offset to the right; the last subwindow might be fully visible if there is enough space for it). Tiling subwindows is done with the slots QWorkspace::cascade() and QWorkspace::tile().

If you need to know when a subwindow has been activated (moved to the front and given focus) because the window should be changed somehow to reflect this, you can connect to the signal QWorkspace::windowActivated(). Also, QWorkspace::activeWindow() returns the currently active window. The convenience method QWorkspace::windowList() returns a list with pointers to all subwindows.

Docking Windows

Docking windows are windows that can either be “docked” into certain positions (so-called “docks”) in a container window or be “floating”—i.e., be top-level windows on top of the application window. This style was first made popular by IDEs like Visual Studio that need a large number of child windows that are mostly independent of each other, but recently, the style has been used in other types of programs as well. Even Qt uses it for its helper programs like Qt Assistant. Actually, even toolbars are a kind of docking window; in the current Qt version, they are treated like any other docking window.

Docking in Qt is provided by the two classes QDockArea and QDockWindow. QDockArea provides a container or “dock” into which instances of QDockWindow can dock. Since QMainWindow already provides four QDockArea instances, one at each side, and you typically use QMainWindow in applications that use docking anyway, you rarely need to instantiate QDockWindow yourself.

QDockWindow is, on the other hand, something that you need each time you want to implement docking windows. Your window needs to be derived from QDockWindow to be able to dock into a dock area. As you would expect, QToolBar inherits QDockWindow.

When you create a docking window, you need to specify whether it should be docked or not initially. If it should be docked, then the parent must either be a QDockArea or a QMainWindow. If the docking window is not docked at the beginning, this docking is not required; however it won’t be able to dock unless it is reparented, so that would not be a good idea.

The typical way to create a docking window is to create it docked, make the QMainWindow instance the parent, and specify with QMainWindow::moveDockWindow(), into which dock the docking window should go. This is done as follows:

QDockWindow* myDockWindow = new QDockWindow( InDock, mainwindow );
mainwindow->moveDockWindow( myDockWindow, Bottom );

Here, the window would be docked at the bottom of the main window. If the window should not be docked initially, you would use:

QDockWindow* myDockWindow = new QDockWindow( OutsideDock, mainwindow );

In this case, the dock window would initially be floating.

Of course, you also want some docked contents. The contents are specified with QDockWindow::setWidget(). It is not possible to dock the window into a specific dock after it has been created. The method QDockWindow::dock() does not take any parameters, but rather docks the window into the dock it was docked in before. However, the actual docking should be left to the user, who can interact with the docking window with the mouse. Docking or undocking programmatically will probably confuse the user and contradicts the whole idea.

A couple of properties can be set in a dock window; e.g., you can set preferred sizes with setFixedExtentWidth() and setFixedExtentHeight() and specify whether the docking window can stretch with setHorizontallyStretchable() and setVerticallyStretchable(). You can also specify to which extent the user can interact and manipulate the dock window with setCloseMode(), setResizeEnabled(), and setMovingEnabled().

Tooltips and “What’s This” Windows

Tooltips, also known as bubble help, quick help, or Hoover help, are small, rectangular windows with short descriptive text that pop up when you rest the mouse over a widget for a short time. The descriptive text should explain in as few words as possible the meaning of the widget the mouse is over. When the mouse is moved away or a mouse button or a key is pressed, the tooltip disappears.

“What’s This” windows (sometimes also called bubble help) have a similar purpose, but they are larger and allow more text to be shown. They bridge the gap between the low impact and low information content of tooltips and the high impact and high information content of online help systems.

You usually don’t have to hassle with creating tooltip widgets or with sizing and showing them. All you have to do is call the static method QToolTip::add():

QLineEdit* edit = new QLineEdit( parent ); 
QToolTip::add( edit, "Description of edit field" );

This method adds a tooltip to the text-entry field. If you are tired of the tooltip, you can remove it by calling QToolTip::remove(). Note that these two methods of QToolTip are static. You don’t have to create QToolTip objects yourself; this is done internally for you.

Tooltips that are added with the method shown are always available when the mouse is over a widget. You can also restrict the area that is sensitive to tooltips to a certain rectangle within the widget:

QLineEdit* edit = new QLineEdit( parent ); 
QToolTip::add( edit, QRect( 0, 0, edit->width()/2, edit->height() ), 
               "Description of edit field" );

Restricting the area ensures that the tooltip pops up only when the mouse is over the left half of the widget.

There are cases when the text of the tooltip, or even whether a tooltip should be shown at all, can only be determined dynamically. An example would be a scrollbar in a word processor that shows the current page in a tooltip. To show the tooltip, you have to derive a class from QToolTip that reimplements the virtual method maybeTip(). A QPoint reference that contains the mouse position relative to the window is passed to this method. On the basis of this position, and possibly the current application state, you can decide whether a tooltip should be shown or not. You can also access the widget associated with the tooltip by calling QToolTip::parentWidget(). If you should decide to pop up the tooltip, you have to call QToolTip::tip() with a QRect and a QString that indicate size, position, and the text to be shown, respectively. When the mouse is moved out of the widget and back to the same point, maybeTip() is called again. Depending on your application, you might want to do some caching to avoid expensive recalculations. The maybeTip() method can be called often and should therefore be as fast as possible.

In applications that have a status bar or other means of displaying temporary information, it could be useful to display the text shown in a tooltip in this other widget as well. You can use QToolTipGroup to leverage the mechanisms of deciding whether information about a widget that is in QToolTip should be provided. Since status bars usually have more space available than tooltips, you can even show other, possibly longer, text here. To achieve this, you have to do two things: you must pass a pointer to the QToolTipGroup object and the second string to an overloaded form of the method QToolTip::add(); then you must connect the signal QToolTipGroup::showTip( const QString& ) to a slot that will show the text and the signal QToolTipGroup::clear() to a slot that will remove it. These slots are usually the setText() and clear() methods of a label. Here is a code snippet that shows the whole procedure:

QToolTipGroup * ttgroup = new QToolTipGroup( parent ); 
QLabel* mylabel = new QLabel( parent ); // should be in e.g., a status bar 
QObject::connect( ttgroup, SIGNAL( showTip( const QString& ) ), 
                  mylabel, SLOT( setText( const QString & ) ) ); 
QObject::connect( ttgroup, SIGNAL( removeTip() ), 
                  mylabel, SLOT( clear() ) ); 
QLineEdit* myedit = new QLineEdit( this ); 
QToolTip::add( myedit, "short description", ttgroup, "longer description" );

Tooltips are very efficient means of communicating to the user the meaning of the elements in a GUI. They are very easy to program and normally don’t have much overhead. You should use them liberally.

At the beginning of this section, we mentioned that you can also use “What’s This” windows. These windows are represented in Qt by the class QWhatsThis and are used the same way that you use QToolTip. To associate a “What’s This” window containing the text text with a widget mywidget, simply call:

QWhatsThis::add( mywidget, text );

To remove such an association from a widget, call QWhatsThis::remove(), to which you simply pass a pointer to the widget in question.

Tooltip windows are shown automatically. “What’s This” windows are only shown on user request. The user can press Shift and F1 together on the keyboard, in which case the “What’s This” window for the widget that has the keyboard focus is shown; or the user can click on a special toolbar button and then click on the widget whose “What’s This” window she wants to see. Such a special toolbar button should not be created explicitly, but by calling QWhatsThis::whatsThisButton(), to which you pass the parent for the button (normally a QToolBar widget). This method creates and returns a button configured for invoking the “What’s This” feature. You can invoke this method as many times as you like and get a new toolbar button each time. Here is a small example:

QToolBar* toolbar = new QToolBar( this ); 
QToolButton* contexthelpbutton = QWhatsThis::whatsThisButton( toolbar );

Progress Bars

Progress bars are represented in Qt by the class QProgressBar (see Figure 4-25). They are used to show that the program still works during lengthy operations. Normally, you will use the more convenient encapsulation QProgressDialog, which you will learn about in the “Predefined Dialogs” section. Sometimes it can be useful to integrate a progress bar into a dialog box, however, or into the top-level window of your application. In these cases, QProgressBar is used directly. You can create, size, and show it the usual way, and then set the total number of steps that the application will have to perform with setTotalSteps(). During this operation, call setProgress() with the number of currently completed steps. QProgressBar will automatically compute the percentage, and update the progress bar accordingly. Remember that you have to give the progress bar a chance to update its display either by returning to the event loop from time to time or by calling QApplication::processEvents() so that the display can be refreshed and the progress bar can be redrawn.

Progress bars
Figure 4-25. Progress bars

Scrolled Views

A scrolled view can be any widget with a view that might be smaller than its physical size. Users are then provided with scroll bars to select the part of the widget that they want to see. This can be cumbersome to program, but fortunately Qt provides the class QScrollView, which does almost everything automatically. You have already seen how to use it in the “Adding a Scrolled View” section in Chapter 3, so I will only mention characteristics here that have not yet been discussed. Please note that other widgets, including QListView (see Section 4.15), inherit QScrollView.

QScrollView can contain multiple child widgets. You add one child widget with addChild(), and then you pass a pointer to the child as well as the x and y positions where the widget should be located on the overall canvas that the scrolled view uses. Children can be moved with moveChild(), removed with removeChild(), and made visible and invisible with showChild().

If you don’t want to have widgets in your scrolled view, but want to draw on the canvas provided by QScrollView instead, you must derive a class from QScrollView and reimplement drawContentsOffset(). The reference documentation for this method contains an example that shows how to deal with the coordinates. If you draw on the canvas this way, you might want to resize the canvas, too. Resizing can be done with resizeContents(). This method can also be used when you simply add widgets to the canvas.

It might not always be obvious when to use child widgets and when to draw directly into the canvas provided by QScrollView. Here is a possible rule of thumb: whenever you would put in a QWidget as a child (as opposed to a more specialized widget), you might as well use the canvas directly because the most commonly used overloaded methods for receiving low-level events are available in the methods viewportPaintEvent(), viewportMousePressEvent(), viewportMouseReleaseEvent(), viewportMouseMoveEvent(), and viewportMouseDoubleClickEvent(). These methods are called by QScrollView and you must reimplement them as needed in a class derived from QScrollView. If you need any special low-level events, such as enterEvent() or leaveEvent(), you must provide a widget of your own.

Scrolled views can have one of three possible scrollbar policies. These scrollbar policies are set with setHScrollBarMode() and setVScrollBarMode() for the horizontal and vertical scrollbar, respectively. The possible values are AlwaysOn (scrollbar is always shown, even if it would not be necessary), Auto (scrollbar is shown only when the view is smaller than the physical size of the contents), and AlwaysOff (scrollbar is never shown, even if it is necessary). Note that AlwaysOff should be used only when you provide some other means of moving the view; otherwise the user will have no way to get to the parts of the view that are not visible from the beginning.

The view on the canvas can also be done programmatically. Two methods, setContentsPos() and center(), use absolute coordinates and scroll the view so that the coordinates passed as parameters are visible in the upper-left corner or in the center of the view, respectively. There is also an overloaded version of center() that allows you to specify some tolerance as to the center position. Finally, scrollBy() allows you to use relative coordinates and thus mimics what the user does when clicking on the arrows of the scrollbars. QScrollView has only one signal, contentsMoving(). It is emitted just before the contents are moved to their new position.

List Views

QListView (see Figure 4-26) is a very complex widget that can display data in rows and columns, as well as in hierarchical form. Entries that contain subentries can be opened and closed by the user or done programmatically.

List views
Figure 4-26. List views

QListView and its helper class QListViewItem have complex interfaces, and it takes time and experience to master them. But if you confine yourself to the default settings at first and use some things that are done “automagically,” simple operations with QListView are just that.

To demonstrate some features of QListView, we will build a list of countries, their capitals, and their populations. There will be one country per row and three columns: one each for the name of the country, the capital, and the population. To add hierarchical structure to this example, we will group the countries by continent.[17] Before we dive into the workings, let’s look at the code shown in Example 4-3.

Example 4-3. listview.cpp: Information about countries in a list view
#include <qapplication.h>
#include <qlistview.h>

int main( int argc, char* argv[] )
{
  QApplication a( argc, argv );

  QListView* listview = new QListView();

  listview->addColumn( "Country" );
  listview->addColumn( "Capital" );
  listview->addColumn( "Population in mio." );
  listview->setRootIsDecorated( true );

  QListViewItem* europe = new QListViewItem( listview, "Europe" );
  QListViewItem* america = new QListViewItem( listview, "Americas" );
  QListViewItem* africa = new QListViewItem( listview, "Africa" );
  QListViewItem* asia = new QListViewItem( listview, "Asia" );

  QListViewItem* item = new QListViewItem( america, "Brazil", "Brasilia", "161.8" );
  item = new QListViewItem( europe, "Scotland", "Edinburgh", "057.6" );
  item = new QListViewItem( africa, "Morocco", "Rabat", "030.4" );
  item = new QListViewItem( europe, "Norway", "Oslo", "004.4" );
  item = new QListViewItem( europe, "Italy", "Rome", "052.2" );
  item = new QListViewItem( america, "Chile", "Santiago", "014.2" );
  item = new QListViewItem( africa, "Cameroon", "Yaounde", "013.2" );
  item = new QListViewItem( europe, "Austria", "Vienna", "008.1" );
  item = new QListViewItem( asia, "Saudi Arabia", "Riyadh", "020.0" );
  item = new QListViewItem( europe, "Denmark", "Copenhagen", "005.2" );
  item = new QListViewItem( europe, "France", "Paris", "058.0" );
  item = new QListViewItem( africa, "South Africa", "Pretoria", "042.3" );
  item = new QListViewItem( america, "Paraguay", "Asuncion", "005.6" );
  item = new QListViewItem( europe, "Bulgaria", "Sofia", "008.8" );
  item = new QListViewItem( europe, "Spain", "Madrid", "039.1" );
  item = new QListViewItem( africa, "Nigeria", "Abuja", "107.1" );
  item = new QListViewItem( asia, "South Korea", "Seoul", "045.9" );
  item = new QListViewItem( america, "Mexico", "Mexico City", "097.6" );
  item = new QListViewItem( europe, "Netherlands", "Amsterdam", "015.6" );
  item = new QListViewItem( europe, "Belgium", "Brussels", "010.1" );
  item = new QListViewItem( europe, "Yugoslavia", "Belgrade", "010.8" );
  item = new QListViewItem( asia, "Iran", "Tehran", "067.3" );
  item = new QListViewItem( europe, "Germany", "Berlin", "081.2" );
  item = new QListViewItem( america, "USA", "Washington", "268.0" );
  item = new QListViewItem( europe, "England", "London", "048.5" );
  item = new QListViewItem( africa, "Tunisia", "Tunis", "009.2" );
  item = new QListViewItem( europe, "Romania", "Bucharest", "022.5" );
  item = new QListViewItem( america, "Colombia", "Bogota", "035.1" );
  item = new QListViewItem( america, "Argentina", "Buenos Aires", "034.6" );
  item = new QListViewItem( asia, "Japan", "Tokyo", "125.1" );
  item = new QListViewItem( america, "Jamaica", "Kingston", "002.4" );
  item = new QListViewItem( europe, "Croatia", "Zagreb", "004.5" );

  listview->resize( 400, 300 );
  listview->show();

  a.setMainWidget( listview );

  return a.exec();
}

Apart from the mass of data needed to make experimenting with the list view more interesting, this example features several things. Probably the most prominent feature is the lack of an explicit call to a method like insertItem(). The whole hierarchy is built on the normal parent-child widget hierarchy of Qt (even though technically, a QListViewItem is not a widget).

We have four objects of the class QListViewItem, which are children of the list view itself and are thus top-level items. We have 32 more QListViewItem objects that are each children of one of the four top-level items.

Note

Every piece of data displayed in a list view belongs to an object of the class QListViewItem. To fully exploit QListView, you need to master not only the interface of this class, but that of QListViewItem as well.

For now, let’s get back to our example. In the first few lines of the main function, we set up the list view by adding column headers and telling the widget that we want the top-level items to be expandable. If we did not, there would be no way for the user to get at the country entries, but you could still expand one entry programmatically by calling setOn( true ) at the corresponding QListViewItem.

The headers are more than descriptions of what can be found in the columns. Clicking on them sorts the data by that column. You could also sort the data programmatically by calling setSorting(), where the first parameter is the number of the column and the second parameter is a Boolean value that indicates whether sorting should be in ascending (true) or descending (false) order.

Before we go into the details about how to create items for the list view, let’s look at more general formatting features. One of these features is column width. Column widths can be configured in two modes: Maximum and Manual. These modes are set with setColumnWidthMode(). In Maximum mode, the column gets the size needed for the widest entry in that column automatically. In Manual mode, the width is set via setColumnWidth().

Three more methods affect the graphical representation: setTreeStepSize(), setAllColumnsShowFocus(), and setRootIsDecorated(). The first determines how far children are indented below their parent entry (the default is 20 pixels); the second determines whether all or only the first column of an entry should be highlighted when that entry has the keyboard focus; and the third specifies whether even the root items (the ones highest in the hierarchy) should have the boxes with plus and minus signs (in Windows-like styles) or arrows (in Unix-like styles).

With the latter method, we come to the subject of keyboard focus and selection. QListView allows four selection policies: single selection, multiselection, extended selection, and no selection at all. The default is single selection: selecting one entry automatically deselects the previously selected entry. You can switch to multiselection with setMultiSelection(). With multiselection and extended selection, keyboard focus and selection are two different things that can be set with two different methods. The setSelected() method accepts a pointer to a QListViewItem and a bool, which indicates whether or not the item in question should be selected. Keyboard focus, on the other hand, is set with setCurrentItem(). The difference between multiselection and extended selection is that in multiselection, the items are completely independent of one another. In other words, selecting one does not have any influence whatsoever on the selection states of the others. In extended selection mode, however, selecting one item deselects all others unless the Ctrl or the Shift key is held down while selecting. Ctrl allows selecting separate items, while Shift selects a range. A good rule of thumb is to use extended selection mode for things of which you usually want one or more, and multiselection mode for things of which you usually want many.

When you want to iterate over all items in a list view, the easiest way is to retrieve the first item with firstChild(), and then use QListViewItem::itemBelow() to iterate over the items. Unfortunately, this only works if all items with children are expanded. If you cannot guarantee this, you will have to use a combination of the various navigation functions of QListViewItem().

QListView emits several signals. The selectionChanged() signal notifies the program that more, fewer, or other items are selected. If the selection policy is single-selection, selectionChanged( QListViewItem* ), which makes the newly selected item readily available, is also present. currentChanged( QListViewItem* ), in contrast, indicates a change in keyboard focus. Since even in a multiselection list view only one item at a time can have the keyboard focus, this signal can always pass the newly focused item. Besides these higher-level signals, several somewhat lower-level signals all pass a QListViewItem* as well. These signals are emitted when the user clicks or presses the mouse on an item. See the Qt reference documentation for all the details.

Finally three signals could be considered lower-level and report manipulations on the items: doubleClicked( QListViewItem* ), returnPressed( QListViewItem* ), and rightButtonClicked( QListViewItem*, const QPoint&, int ). The first two should usually be connected to the same slot because users normally expect double-clicking and pressing Enter to invoke the same functionality. The rightButtonClicked() signal is mainly meant for popping up context menus. The second and third parameters specify the mouse coordinates (which are a good candidate for the menu coordinates) and the column in which the mouse was clicked. Note that there is a signal rightButtonPressed(), but no rightButtonReleased().

QListViewItem has many methods, but most of them are only useful for special cases. If you want only text entries, the constructors might be all you ever need. You can pass the parent as the first argument: either a QListView* for top-level items, or a QListViewItem* for non-top-level items. In addition, you can pass up to eight strings, which serve as values for the respective columns. If you need more than eight columns, or if you want to have pixmaps in your columns, you will need additional methods. The setText( int, const QString& ) method adds a text entry to a column specified by the first argument. The setPixmap( int, const QPixmap& ) method does the same for pixmaps.

Three more methods allow entry configuration. The setSelected( bool ) method selects or deselects an entry; setExpandable( bool ) determines whether it should be possible for the user to expand or fold back the entry by means of a little icon next to it; and setSelectable( bool ) determines whether the item can be selected by the user.

It is even possible to let the user rename an item in place. By default, this functionality is turned off, but if you call QListViewItem::setRenameEnabled(), the user can rename the text value (of course, this does not work for pixmaps). For the first argument, pass the column where the item you would like to be changed resides, and for the second argument, pass true. Note that you need to call setRenameEnabled() once for each column in each item that you want to let the user rename; this can result in a lot of calls. The QListView object itself renames an itemRenamed() signal whenever an item is renamed.

Furthermore, QListView also supports drag-and-drop—or rather, provides you with the means to implement this functionality. To use drag-and-drop in a list view, you need to do two things:

  • On each item that may be dragged, call setDragEnabled( true ). On each item onto which data may be dropped, call setDropEnabled( true ).

  • Subclass from QListView and override the method dragObject to return a QDragObject object contained the data to transport. For dropping, connect to the signal QListView::dropped(), which gets passed a QDropEvent, which in turn provides you with all necessary information.

For more information about drag-and-drop and about what to do with the QDragObject and QDropEvent, please see Section 12.2.

Note that when you build very large hierarchies, creating all items at startup or widget creation time may not be a good idea because it may take too long. For example, file systems can be very large. It would be better to create the root entries first, and then create all other entries only when they are needed (for instance, when their parent is expanded). To do so, create a class derived from QListViewItem that reimplements setOpen( bool ) and setup(). The setup() virtual method is called before the item is displayed at first and also after attributes such as the font or the widget style have changed. You can take care of any initializations here that might have an effect on the item’s visual appearance. With a file system, this process might include checking whether the directory in question is empty and calling setExpandable( true ) if the user cannot open it. When this item is opened, setOpen( bool ), in which you can read the directory and create items for the files or subdirectories, is called. This way, there is never more file system activity than needed. Since there is already a good dirview example in the Qt distribution, read through it if you want to use QListView.

Icon Views

Icon views are widgets that show a number of icons in a grid that allow you to manipulate them in various ways, e.g., selecting them or dragging them. The icons can have an optional text label that augments the data representation that the icon provides.

Typical uses for icon views are file browsers in which each file or directory is represented by an icon and the optional label that shows the file or directory name, or thumbnail viewers, in which each icon represents an image (often in the form of a file as well).

Qt implements icon views by means of the class QIconView. If you have read through the previous sections about list views, there won’t be many surprises for you here. The simplest way to use the icon view is to create a QIconView object, and then a number of QIconViewItem objects that have the QIconView object as parents. For each QIconViewItem, you should specify a QPixmap. You can also specify a QString as the optional label.

By default, the icons are inserted left to right, top to bottom, and one after the other into the icon view. However, there are several ways to change this default behavior. First, when creating an icon, you can specify another icon that should be right before the one you are creating. This way, you can insert an icon somewhere in the middle instead of only at the end.

You can also change the insertion order to top to bottom, left to right. This is done by calling setArrangement( TopToBottom ) before inserting the icons.

Finally, you can sort the icons or have them inserted from the start. Sorting afterwards is done by calling QIconView::sort(); turning on sorting for insertion is done with QIconView::setSorting(). With both methods, you can specify whether sorting should be in ascending or descending order. The sorting order is determined by the return value of the method QIconViewItem::key(), which is a QString. By subclassing from QIconViewItem, you can return your own string here and thus define your own sorting order for the icons—e.g., by size.

QIconView has, like QListBox, four different selection modes: Single lets you select only one icon; selecting another one deselects the previously selected icon. Multi lets you select more than one icon. Selecting one icon has no influence whatsoever on the selection state of any of the other icons. Extended also lets you select more than one icon; this is done by means of holding the Shift or the Ctrl key, as you do in listboxes. Finally, NoSelection disables the selection of icons completely.

QIconView emits a large number of signals, many of which are similar or identical to the signals emitted by QListView. The reference documentation lists them all.

A special feature of QIconView is the in-place renaming of icons. This renaming applies to the associated label, of course. This feature can be enabled with QIconViewItem::setRenameEnabled( true ) for an item. You can rename an item by first selecting it and then clicking on the name (or pressing F2), upon which the name label turns into a line editor with which you can change it. When you are done editing, the signal itemRenamed() is emitted by the QIconView.

Widgets for Tabular Material

The widget QListView presented in the previous section has a limited capability for displaying tabular data. If you need more flexibility and do not care about the possibility of having hierarchical displays, QListView is probably not the best choice. Qt also provides two widgets especially geared toward displaying tabular material: QGridView (see Figure 4-27) and QTable (see Figure 4-28). QGridView does not have any visual representation itself, but rather helps you develop your own widgets that can display tables. QTable, on the other hand, displays a spreadsheet-like table with cells.

Simple grids
Figure 4-27. Simple grids
A table in Windows and Motif style (both look the same)
Figure 4-28. A table in Windows and Motif style (both look the same)

QGridView is an abstract base class, so it cannot be used by itself. Instead, you have to derive another class from QGridView. There is a comprehensive example of how to use QGridView in Section 14.2 in Chapter 14.

QHeader is another very useful widget for displaying tabular material. QListView and QTable use it for displaying their column or row headers, but you can also use QHeader for your own widgets if they display tabular data.

The most important thing to understand with QHeader is that you do not have one QHeader object per column or row, but one for all columns or rows. You add column or row headers with addLabel() and set the orientation (Horizontal for column headers and Vertical for rows) with setOrientation(). Should you need to change header labels afterwards, you can do so with setLabel().

Most methods in QHeader deal with sizes and are seldom used, but the three signals are very important. sectionClicked() is emitted when one of the header parts (labels) has been clicked. It has become customary for programs to sort the data by the column or row in question when the user clicks on the header. QListView does exactly this. On the other hand, spreadsheets like MS Excel simply select the column or row in question when the header is clicked.

The other two signals are emitted when headers are manipulated. When the user clicks with the mouse exactly between two header labels and then moves it, sizeChanged ( int, int, int ) is emitted. The first parameter of size changed is the logical column or row number, the second is the old size in pixels, and the third is the new size. If you click and drag in one label instead, you can change the physical positions of the labels. When this happens, the signal moved( int, int ) is emitted, in which the first parameter specifies the column being moved and the second specifies its new position.

These three actions—clicking on a column or row header, resizing a header, and moving a header—can be enabled or disabled by calling setClickEnabled( bool ), setResizeEnabled( bool ), and setMovingEnabled( bool ), respectively. Finally, if you want the signal sizeChanged() to be emitted while the sizes are changed, you will have to call setTracking( true ). This could be useful for online resizing of columns or rows, but this feature should be used carefully because it needs a lot of computational resources.

Let’s now move away from the do-it-yourself widgets QGridView and QHeader and go to something more readily useable. If you do not want to implement your own widget, but simply want to display and manipulate data in a spreadsheet-like fashion, QTable is the widget to use. It conserves a lot of memory since it only allocates memory for a cell if there is actually data in the cell.

You can specify the size of the table in the constructor; if you do not specify anything, you get a table with 10 × 10 cells. You can put four things into a cell: a string, a pixmap (also both a string and a pixmap), a user-defined item, and a widget. Putting a string or a pixmap into a cell is easy: just call QTable::setText() or QTable::setPixmap() and specify the coordinates of the cell as well as the string or pixmap to insert. If you insert both a string and a pixmap into a cell, the pixmap is displayed to the left of the text.

To put something that is user-defined into a cell, you need to define a class that inherits from QTableItem. Then you can put any object of this class into the table by calling QTable::setItem() and passing the coordinates of the cell and a pointer to the QTableItem object.

Finally, you can put a Qt widget into a cell by calling QTable::setCellWidget(). Of course, not everything is suitable to be put into a cell, but push buttons and checkboxes make good widgets to put into a table cell.

Actually, when you insert a string or a pixmap into a cell, a QTableItem is created as well. The QTableItem object is also responsible for editing. By double-clicking a cell, you can put it into “edit mode” (if the cell’s contents can be edited). In edit mode, the normal representation of the cell is replaced by an editor widget. This widget can be any Qt widget, but not all widgets are useful here. QTableItem itself uses a QLineEdit object that lets you edit the text in the cell; but a pixmap in a cell cannot be edited unless you develop a subclass of QTableItem that lets you do that. However, developing this subclass would be pretty hard. By subclassing from QTableItem you can define other editors. For example, it can be useful to put a slider into a cell. The most important methods to override in QTableItem are createEditor(), which should return the editor widget and will be called when a cell goes into edit mode, and setContentsFromEditor(), which should copy the data in the editor widget back into the cell and will be called when a cell leaves edit mode. If all you want is to put a combo box or a checkbox into a cell, you can use the ready-made QTableItem subclasses QComboTableItem and QCheckTableItem.

Note the difference between a cell widget and an editor widget. A cell widget is always present; if you use a cell widget, then this cell will never go into edit mode. (Of course, you could, for example, put a push button into a cell and pop up a dialog to edit something when the button is clicked.) An editor widget is only present when the cell is in edit mode, however; when it is not in edit mode, the normal representation as defined by QTableItem or its subclass is used.

QTable supports a lot of methods and signals for selecting, highlighting, and sorting, but most of them are self-explanatory and follow the same pattern as QListView and QIconView, so instead of repeating them here, I would like to refer you to the reference documentation.

Widgets for Displaying Rich Text

Qt provides two widgets for displaying so-called “rich text.” Rich text is text that can have different styles like different fonts, colors, sizes, etc. At the extreme, each letter can have a different style. Rich text is a larger subject that we will cover in Section 10.5 in Chapter 10.

While some of the traditional widgets such as QLabel, QMessageBox, and QWhatsThis can display rich text as well, there are two classes in Qt that are especially geared towards displaying rich text: QTextView and QTextBrowser.

QTextView is much like QLabel because it simply displays the text. Since QLabel can also display rich text, you might not even need to use QTextView for simple cases. QTextView provides additional facilities that might be useful for more advanced uses. For example, you can set the stylesheet with setStyleSheet() (please see Section 10.5 for an explanation of stylesheets). To give the whole widget different colors, use setPaperColorGroup(). You can also set the color and the brush of the background individually by using setPaper(). You can configure two more styles: the color of hyperlinks and whether hyperlinks should be underlined so that they are easier to recognize. QTextView does nothing to support hyperlinks; supporting hyperlinks is the domain of QTextBrowser.

QTextBrowser augments QTextView by hyperlink support facilities. QTextBrowser is not meant to be a full-featured web browser, but rather a lean and fast documentation viewer. For example, the documentation browser that comes with Qt Designer (see the second part of this book) is based on QTextBrowser. You can provide QTextBrowser with the text to be displayed by calling setText() and passing the text as a string or by calling setSource() and passing the name of a document to be displayed.

Writing a documentation browser can be done by following these easy steps:

  1. Create a QMainWindow with an optional menu bar, a toolbar with buttons for Back, Forward, Home, and a QLineEdit for entering the document.

  2. Create a QTextBrowser and make it the central widget of the main window.

  3. Connect the clicked() signal of the Back button to the backward() slot of the text browser, the Forward button’s clicked() signal of the Forward button to the forward() signal, and the Home button’s clicked() signal to the home() slot of the text browser.

  4. Connect the text browser’s backwardAvailable( bool ) signal to the setEnabled( bool ) slot of the Back button and the text browser’s forwardAvailable( bool ) signal to the setEnabled( bool ) slot of the Forward button.

  5. Connect the textChanged() signal of the text browser to a slot in your application (e.g., in your subclass of QMainWindow) that first queries the current document name by calling QTextBrowser::source() and then displays it in the line-entry field in the toolbar. This connection ensures that this field always displays the correct document name.

  6. Connect the returnPressed() signal of the line-entry field in the toolbar to a slot in your application that reads the entered text and calls QTextBrowser::setSource() with this text. This connection ensures that you can use the line-entry field for navigation.

You now have a working documentation browser. Of course, several different kinds bells and whistles could be added, such as a history list, bookmarks, etc. See, for example, the helpwindow example that comes with Qt, which explains a lot of these enhancements.



[16] Of course, when clicking a push button invokes a dialog box, closing with Cancel effectively undoes the action. The user interface has changed, however—at least temporarily.

[17] I chose these countries because they are the 32 nations that qualified for the world soccer championships in the summer of 1998—an event that has more than once stopped me from continuing the work on the first edition of this book.

Get Programming with Qt, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.