Chapter 4. Creating Custom KDE Widgets

by David Sweet

In This Chapter

By now you should have a good idea of what simple KDE code looks like and what Qt has to offer. Now we will look in more detail at the building blocks of GUIs: the widgets. Although KDE and Qt offer many useful and powerful widgets, you still need to create your own to customize your UI. It is easy to do this—and to do it with good form—if you know how.

4.1. Widget Basics

Widgets are graphical user-interface elements. Simple widgets can be controls or indicators such as a pushbutton or a text label. More complex widgets can perform more significant computation or may require significant user input, such as the spell checker widget or the HTML-rendering widget.

In KDE, widgets are implemented using C++ classes. Usually there is a one-to-one widget-to-class correspondence. For example, a pushbutton is implemented by QPushButton. All widgets are ultimately derived from the QWidget base class.

4.1.1. Understanding the QWidget Base Class

QWidget handles window system events, manages generic widget attributes, knows about its parent and children, and handles functions unique to a top-level widget (if it should be one). Window system events include geometry changes and user input. The widget is clipped by its parent's borders and by the children that lay on top of it. Top-level widgets have no parent. They lie in a window on the desktop and have window borders and decorations drawn by the window manager.

4.1.1.1. System Events

Window system events tell the widget when it needs to repaint, reposition, or resize itself, when mouse clicks or keystrokes have been directed toward that widget, when the widget receives or loses the focus, and so on. QWidget handles the events by calling a virtual method for each event. Each method gets passed, as an argument, a class containing information about the event. To handle the event, the corresponding method must be reimplemented in the subclass of QWidget.

A very important system event is the paint event. In response to this event, a widget draws (or "paints") itself. It is sent to the widget every time the widget needs to be displayed or redisplayed. For example, the event is sent when the widget is first created, when it is made visible after being hidden, or when it is being uncovered after having been fully or partially obscured. The paint event is discussed in detail in the next section, and techniques for repainting efficiently are discussed in Chapter 9, "Constructing a Responsive User Interface."

The following is a list of the (protected, virtual) QWidget event handlers and corresponding events.


   1 
   2 void event (QEvent *)

This is the main event handler. This method dispatches the events to their specialized event handlers. Normally, you do not need to reimplement this method. The argument tells the type of event.


   1 
   2 void mousePressEvent (QMouseEvent * )

Gets called when one of the mouse buttons is pressed down with the mouse cursor inside this widget. The argument tells which button was pressed, whether a modifier key (Ctrl, Alt, or Shift) was pressed in combination with it, and where the cursor was when the button was pressed.


   1 
   2 void mouseReleaseEvent (QMouseEvent *)

Gets called when one of the mouse buttons is released with the mouse cursor inside this widget. (See mousePressEvent() for a description of the argument.)


   1 
   2 void mouseDoubleClickEvent (QMouseEvent * )

Gets called when the user double-clicks on the widget. (See mousePressEvent() for a description of the argument.)


   1 
   2 void mouseMoveEvent (QMouseEvent *)

Gets called when the user moves the mouse with the cursor over the widget. This event is generated only when a button is held down, unless you turn on mouse tracking with QWidget::setMouseTracking (true). (See mousePressEvent() for a description of the argument.)


   1 
   2 void wheelEvent (QWheelEvent *)

Gets called when the user moves the mouse wheel (if there is one) and this widget has the focus. The argument tells how far and in which direction the wheel has been rotated. This event can be ignored by calling QWheelEvent::ignore() if it is not processed. In this case, the event gets passed to the parent widget for processing.


   1 
   2 void keyPressEvent (QKeyEvent *)
   3 

Gets called when a key is pressed and this widget has the focus. The argument contains a code telling which key was pressed and whether a modifier key (Ctrl, Alt, or Shift) was being held down. If you have turned on key compression with QWidget::setKeyCompression (true), the argument may contain a text string representing all the keys that were pressed since the last time you received this event.


   1 
   2 void keyReleaseEvent (QKeyEvent *)

Gets called when a key has been released and this widget has the focus.


   1 
   2 void focusInEvent (QFocusEvent *)

Gets called when this widget receives the focus. If this widget is willing to accept the focus, the default implementation calls QWidget::repaint() to redraw the widget with a focused look.


   1 
   2 void focusOutEvent (QFocusEvent *)

Gets called when this widget loses the focus. If this widget is willing to accept the focus, the default implementation calls QWidget::repaint() to redraw the widget with an unfocused look.


   1 
   2 void enterEvent (QEvent *)
   3 

Gets called when the mouse cursor enters the widget.


   1 
   2 void leaveEvent (QEvent *)
   3 

Gets called when the mouse cursor leaves the widget.


   1 
   2 void paintEvent (QPaintEvent *)
   3 

Gets called when the widget needs to be repainted, such as when it is first created or uncovered after being totally or partially obscured by another window. The argument tells which part of the widget needs to be repainted.


   1 
   2 void moveEvent (QMoveEvent *)
   3 

Gets called when the widget has been moved relative to its parent. The argument tells the new and old positions.


   1 
   2 void resizeEvent (QResizeEvent *)

Gets called when the widget has been resized. The argument tells the new and old sizes.


   1 
   2 void closeEvent (QCloseEvent *)

For a top-level widget, this gets called when the user tries to close the window (using the close button on the window frame, for example). For other widgets, this gets called when the application calls the QWidget::close() method. In a Qt (non-KDE) application, this would be a good place to ask, "Are you sure?" KDE applications should use KTMainWindow::queryClose() for this purpose. You can accept or ignore the close event by setting a flag in the QCloseEvent argument.


   1 
   2 void dragEnterEvent (QDragEnterEvent *)

Gets called when a user is dragging data and the mouse cursor first enters this widget. The argument tells where the mouse cursor is, what kind of data is being dragged, and what the data is.


   1 
   2 void dragMoveEvent (QDragMoveEvent *)

Gets called when a user drags data over this widget. (See dragEnterEvent() for a description of the argument.)


   1 
   2 void dragLeaveEvent (QDragLeaveEvent *)

Gets called when a user is dragging data and the mouse cursor leaves this widget. (See dragEnterEvent() for a description of the argument.)


   1 
   2 void dropEvent (QDropEvent *)

Gets called when a user drops (in a drag-and-drop operation) data onto your widget. (See dragEnterEvent() for a description of the argument.)


   1 
   2 void showEvent (QShowEvent *)

Gets called when the widget is first created or when the window in which it lies is deiconified. The argument tells whether the show event originated inside the application or outside the application (for example, the user clicked the deiconify button on the taskbar).


   1 
   2 void hideEvent (QHideEvent *)

Gets called right after the widget has been hidden. The argument tells whether the show event originated inside the application or outside the application (for example, the user clicked the iconify button on the window).

4.1.2. Widget Attributes

QWidget keeps track of various properties that may be of use to it or to subclasses. It holds a Qfont, which describes a font for the widget. The font is not used by QWidget directly, but by subclasses. A QCursor is kept in QWidget and is drawn as the mouse cursor whenever the cursor passes over the widget. You can also access the position, size, colors, and other widget attributes via the QWidget public interface.

QWidget also holds a pointer to a QStyle object. This object describes many common look-and-feel characteristics of Qt widgets, as discussed in Chapter 3, "The Qt Toolkit."

4.1.3. Signals and Slots

QWidget is a subclass of QObject, therefore it may make use of signals and slots. Widgets use signals to communicate user interaction and/or changes in their state. A pushbutton, for example, might emit a clicked() signal to indicate that the user has clicked the button. A check box might emit a checked() signal to indicate that it has just been put into the checked state either by direct user interaction or by a call from another part of the application.

Slots are used to change the widget's state. This way, a widget can be easily configured to react to events that do not directly affect it. Signals from other widgets (or more generally, other subclasses of QObject) can be connected to a widget's slots to affect its state. Imagine an FM radio application: You connect the clicked() signals of a set of radio buttons to a station name display so that the display reflects the station chosen by the user, even though the user didn't interact directly with the display.

4.1.4. Sample Widget Class Declaration

A sample widget header file containing its public interface is shown in Listing 4.1.


Example 4.1. kpushbutton.h: Class Declaration for a KDE Widget

   1 
   2 1: #include <qwidget.h>
   3 2:
   4 3: /**
   5 4:  * KPushButton
   6 5:  * A sample widget header file. This widget would
   7 6:  *  implement a standard pushbutton.
   8 7:  **/
   9 8:
  10 9: class KPushButton : public QWidget
  11 10: {
  12 11:  Q_OBJECT
  13 12:
  14 13:  public:
  15 14:   /**
  16 15:    * Create the pushbutton.
  17 16:    **/
  18 17:   KPushButton (QWidget *parent, const char *name=0);
  19 18:
  20 19:   /**
  21 20:    * Set the text to be drawn on the button.
  22 21:    **/
  23 22:   void text (QString _text);
  24 23:
  25 24:   /**
  26 25:    * Get the text being drawn on the button.
  27 26:    **/
  28 27:   QString text () const;
  29 28:
  30 29:  signals:
  31 30:   /**
  32 31:    * Button was clicked by the user.
  33 32:    **/
  34 33:   void clicked ();
  35 34:
  36 35:  slots:
  37 36:   /**
  38 37:    * Animate a button press.
  39 38:    **/
  40 39:   void animate ();
  41 40:
  42 41:  private:
  43 42:   QString theText;
  44 43:
  45 44: }

Listing 4.1 shows kpushbutton.h, a class declaration for a fictitious class called KPushButton. Included in the declaration are important method types and class documentation.

KPushButton is a declared as a subclass of QWidget (line 9). The constructor (line 17) takes a pointer to a QWidget as an argument that specifies the parent widget. This, along with the class name, is passed to QWidget. The name is used only internally, and thus, the often-used value of 0 is made the default.

Methods are provided to get and set the configurable UI parameters (in this case, just the text; see lines 19-27). In the standard style of KDE 2.0 and Qt 2.0, the same method name is used for getting and setting a parameter. The function overloading feature of C++ allows this to be done in most cases. (It would fail if both methods required the same arguments in the same order!)

The widget emits a signal (declared on line 33) when the button is clicked, as discussed previously, and accepts, via a slot (declared on line 39), a command to animate the clicking of the button.

4.1.5. Documentation

The class in Listing 4.1 is documented in the kdoc style introduced in Chapter 2, "A Simple KDE Application." It is discussed in detail in Chapter 15, "Creating Documentation." You should be familiarizing yourself with the basic form of the documentation: The documentation appears in comments that look like /** … **/. You should also get used to the idea of documenting your classes, if you are not already, because it is such an important part of writing code for a multiprogrammer project.