KDE Könyvtár Referencia: Eseménykezelés

Következő Előző Tartalom

## 4. Eseménykezelés

This chapter will illuminate some of the internals of KDE/Qt programming, and will dive into the fascinating subject of event handling as well. It is meant for people who feel they need deeper insight to the more advanced topics of Qt based programming. In particular the Signals and Slots mechanism is explained in detail. This because users have indicated that resources about the meaning and usage of signals and slots are somehow hard to find in other documentation. For non-english persons the translated version are available, offering easier access than the originals.

Event handling covers the communication of an application. Withn that, we mean the interaction between different objects and between objects and the user. Beginners in particular have a hard time to learn the mechanisms underlying event handling. Many of the misconstructions found in beginners' code can be attributed to a lack of understanding of the Qt signals and slots framework.

Signals and slots are formally not a part of event handling but are used during the event processing often and are a major reason why simple widget elements such as buttons can be usd in such a simple way.

## 4.1 Signals and Slots

This section covers the Qt concept of advanced object communication. In this context objects are the instances of classes that are created during runtime by the application. The instances normally don't know about each other, but they have to communicate to allow method calls of other object's methods. The usual way for XWindow has been using function pointers, but this lead to very complex code. It also gives no opportunity for the compiler to check types (a function pointer is actually just a (void*) pointer).

The Qt library offers a far better solution to this by a mechanism that is called signals and slots.

Before going into the technical details, I'll explain the mechanism by a comparison to everyday-life. Imagine you have a bunch of people somewhere and someone looks into the sky. He sees a balloon and points with his finger to the balloon and says: "Hey, there's a balloon !". Now, what do other people do that are standing around him ? One who is interested in balloons will look up as well and have a look at it, maybe take a picture with a camera. Others won't because they were never interested in balloons and don't want to know about it; they just ignore what was said.

With Signals and Slots, things are just the same, except that instead of people objects interact. Objects are instances of classes that can send out a signal in a certain situation. Objects which are interested in that kind of signal react to it, others do not. While humans have the choice to react interactively, class objects can't because they have no ears. But they can provide a kind of ears that listen to signals that were sent out. Then, those special kind of ears have to be connected to the signal an object emits to provide the medium to transmit the message to the receiver. Any kind of ears that could react won't if they are not connected with the signal and therefore ignore the message transmission.

I hope this made somehow clear how the signal/slot mechanism works generally. The chart shows this a bit more appropriate:

    Person_1                            Person_2                       Person_3

    signal balloon_seen();              slot i_see_it();               slot not_interested();

    watch_out(){                        i_see_it(){
      if(balloon){                         look_where();
        emit balloon_seen();            }
      }
    }

    connect (Person_1, SIGNAL(balloon_seen()), Person_2, SLOT(i_see_it()));

This explains more about the functionality. The class that builds Person_1 provides a signal balloon_seen(). Also it has a method watch_out() that symbolizes that he watches for something. If this method detects that a balloon is there, it emits the signal and transmits the message to the outside of its responsibility. After the emit, Person_1 is not responsible for any actions that follow as a reaction to this signal; it just does the message invocation.

Now, the table contains the other persons Person_2 and Person_3. Both provide methods that are slots; Person_2 has one slot that is called i_see_it() and Person_3 a slot not_interested(). Those slots are just like any method with the difference that they can also be connected to a signal and build a receiver for the connection. They will execute the slot implementation when the object receives the signal message. In this case, we have a typical connection method at the last line. The connect() takes Person_1 as a signaler object. It connects the signal balloon_seen() that the object may send out when he sees a balloon with Person_2. Person_2 is then the receiver object. Now, the receiver has to do something with the signal; we have to tell him, which method to execute whenever Person_1 sees a balloon. The implementation of the slot i_see_it() just calls another method to make this example short. look_where can symbolize a method to localize the coordinates, the color, the size of the balloon or how many people the balloon carries.

Person_3 then is anther object. The class that builds it provides a slot as well, the method not_interested(). The implementation doesn't matter for our example here, because we don't want Person_3 to react to Person_1's detection of a balloon. We could, if we add another connect though, just with Person_3 instead of Person_2 and the according slot Person_3's class provides.

### Requirements

This way of object communication is not provided by C++; it is a part of the Qt library.

If you want to use Signals and slots they have to be declared as such in the class-declaration for two good reasons:

1. you know which signals and slots a class provides and the parameters they need
2. the moc (Meta Object Compiler) of Qt can create the implementation for signals and slots automatically and include it to the compile process (since signals and slots are not part of the C++ standard, your C++ compiler wont do that)

Now, we're going into the details of the Qt library. To make use of the signal/slot mechanism, you have to:

- inherit from `QObject` or any subclass of `QObject`
- add the macro Q_OBJECT at the beginning of the class-declaration (without a semicolon !)
- run moc over the header file to produce an implementation file to be compiled

Normally, KDE and Qt applications constructed with automake and autoconf (amongst them all programs made with KDevelop) already run moc when necessary. This is done by the program `automoc`, which also does everything needed to create the meta object implementation for signals and slots as well as incorporating the correct headers for the implementation and the inclusion into the build-process. So you don't have to take care of updating any moc output files after changing header file implementations nor about the integration of the moc source file output into the project. Those will be automatically generated by detection of the Q_OBJECT macro in the class declaration.

If you have further questions please read the page "Using The Meta Object Compiler" of you Qt Online Reference. It covers all restrictions on using signals and slots.

### Emitting Signals

This part describes the several ways of signal emission. It is important to know where signals are emitted and for what purpose you would do so.

We separate two ways of signal usage, one which is the usual way through sub-classing `QObject`, and the other to use the `QSignal` class from within classes that don't want to inherit from `QObject` but want to use the signal emission features.

So, when deriving from `QObject`, we already said that we have to add the `Q_OBJECT` macro into the class declaration. Then any signal that a class object will emit has to be inserted in the class declaration with the modifier `signals:`

Example:

    class Foo : public QObject{
      Q_OBJECT

    public:
      Foo();

    signals:
      void mySignal();
      void myParameterSignal(int, int);
    };

This shows the insertion into the class declaration and also shows that you can use signals to emit values as well. This is one of the best features and is widely used throughout KDE and Qt.

Now, this shows one half of the work. The other is: where does the signal get emitted ? For this, you have to use the keyword `emit` in connection with the signal name and the transmitted actual parameters. The place where to emit is usually within a method that is processed and wants to inform about the state of the object by the signal to outside objects.

#### Comment

The keyword `emit` is technically only an empty #define, therefore the C++ -compiler only sees a normal method call. The `moc` takes care to add the according meta-object creation and initialization, which finally implements the signal as a member function in the moc output.

As an example, we have a look at a snippet of code where a method of `KMyClass` cuts out a part of a visible area the user works with:

    void KMyClass::cut(){

      int xpos=view->xPos();
      int ypos=view->yPos();
      view->cut(xpos, ypos);

      emit cutting(xpos, ypos);
    }

This method could be called e.g. from a toolbar icon "Cut" or the according menu entry of the application's menu bar. We assume that we have a view area that we retrieve as a pointer `view`. The class providing the view area offers cutting a selection by an x and y integer value. The actual values can be found out with `xPos()` and `yPos()` and stored into xpos and ypos to avoid temporaries and to reuse the values for emitting the signal. Then, we call the cutting method via the view object by the actual parameters. Finally, we want to inform about what's being done by emitting a signal `cutting()`. In case anyone needs to know about what happened, we have also included the exact information about where we have done the action by transmitting the values with the signal.

Another way to produce a signal is, as mentioned, possible without sub-classing `QObject`. Qt provides this by the class `QSignal`. The usage is rather simple, though inheritance of `QObject` should always be preferred.

To use `QSignal`, write a normal C++ class. Then add the following:

1. #include <qsignal.h>
2. add a `QSignal` member attribute to the class declaration
3. add a method `void connect(QObject* receiver, const char* member);` to the class
4. create the signal in the constructor with new
5. destroy the signal in the destructor with delete
6. implement the `connect()` method by calling `connect(receiver, member)` on the signal to emit
7. emit the signal at any place in your class code with `yoursignal->activate()`

### Slot Implementation

After explaining the ways of how to produce signals by objects, those can only be of important use if an application's classes provide slots that get connected with signals. The slots themselves are normal C++ class member functions and can therefore be called any place any time you need to, only depending on the class access rights. They just have an additional feature that they can be called automatically during runtime by their connected signals. The main difference is the declaration of the methods within the class:

    class Foo : public QObject{
      Q_OBJECT

    public:
      Foo();

    public slots:
      void mySlot();
      void myParameterSlot(int, int);
    };

Above, you see that the class `Foo` has two slots declared in the class-declaration. As the modifier is also preset, here to public, it follows that you can also restrict slot usage by access rights to public, protected and private. The only thing to watch out for is that all methods after `public slots:` are slots, so you have to start with `public:` again, if you want to add public methods behind the slots declarations. Whe connecting signals to slots, the sender can only connect to slots the receiver allows to call depending on the access rights e.g. a private slot cannot be called by an instance of another class than the own (which means only instances of the same class can connect signals to this slot).

Another restriction is the return type. As slots are most often called by signals, where should they deliver any return values ? Therefore, your slots will always have `void` as return type.

> For all restrictions of implementing slots in classes, see the Qt online reference documentation, section The Meta Object Compiler.

### Connections

The last section of this chapter deals with connecting signals and slots. As stated in the Signals section, there are two ways to produce signals, and in the Slots section we saw that slots are methods which have modifiers as well.

When it comes to connecting signals and slots, you generally will use the static method of `QObject` to send a signal to a method:

> `

>     bool connect(const QObject* sender, const char* signal,
>                  const QObject* receiver, const char* member)
>     bool disconnect(const QObject* sender, const char* signal,
>                  const QObject * receiver, const char* member)

> `

Both are static public members of `QObject` and can be called everywhere in the code if you want to connect/disconnect a sender and receiver by certain signals and slots. The signal in these methods have to be used with the `SIGNAL()` macro; the slot of the receiver has to be used with the macro `SLOT()`.

#### Comment

Note: within classes that inherit `QObject` you don't have to use the static variant, so instead of using `QObject::connect()`, you can also use the overloaded methods that either the sender provides (such as `QMenuData` to connect `activated()` directly to the receiver's slot while inserting a menu entry) or just call `connect()` directly.

Further, the signal and slot should have the same parameter list as parameters are translated from the signal to the slot method. Slot implementations that don't require using any transmitted parameter only have to declare the type but do not need a formal parameter. This avoids the unused parameter warnings you usually get when declaring formal parameters which aren't processed in the method. The slot methods itself can also have less parameters than the signal emits.

Also, signals can be forwarded. This means, you can use the `connect()` method to connect two signals, meaning that the sender's signal will cause the receiver to emit the connected signal. If several slots are connected to one signal, the slots will be ececuted one after the other, in an arbitrary order, when the signal is emitted. (Qt documentation: metaobjects.html)

## 4.2 The Event Queue

The previous chapter dealt with the object communication by Qt's signal/slot mechanism and we know how an application can arrange a certain functionality. But we have not yet discussed the events produced by the user. Generally, he communicates with an application by the keyboard and the mouse. When running an application under XWindow, the X11 protocol ensures that the right application is called to process the events. Only the application object receives the event and can provide means to handle them. This is called event handling. The application object therefore has to keep an event queue when initialized where events run into and get processed to the right application window. The application itself is running in a so-called `main event loop`, which indicates that it waits for user interaction until the user quits the application either via the `quit()` slot or by calling `exit()`. The `exit()` function also returns the value to the main() function's call of `exec()` to terminate. If the number `exit()` is called with is higher than 0, an error has occurred. The `exec()` function call in `main()` starts the event handling.

X11 event handling starts by defining the events the window is willing to process. All other events are already filtered out. The events which are selected, go into the event queue and wait to be processed. Typical events include the XExposeEvent, XDestroyWindowEvent and XResizeRequestEvent. All these events have to be handled by the application. Fortunately (for you) Qt takes care of most of the event processing. This is done using the `QEvent` class; the Qt event abstraction.

This QEvent is then processed by `QApplication`'s `notify()` function. This sends all receivers which are derived from `QObject` and are part of the application the according event with `receiver->event(QEvent* event)`. The application objects therefore get notified about any event that happened and can process the event via the re-implemented `event()` method of `QObject` if needed. `QObject` also allows a self-created event filter functionality by installing an event filter on the class. The event filter is processed first if one is installed and then the event method returns control over the event if the event filter returned false. If the `event()` method doesn't find any event processing, it returns false and the application gets to know that the object didn't sign responsible for the event. If the event was successfully processed and the `event()` returns true, the event is deleted from the event queue.

### Processing Events

Qt and KDE applications use a graphical interface to make themselves visible to the user. Application windows are deived from `QWidget`, the baseclass for any graphical object drawn in windows. Independent of how the widget is created, the application object notices all widgets that are created and keeps a list of these. Furthermore, the windows can have several states dependent on how they are created.

The `QWidget` class is most important to understand because it re-implements the `event()` method already to transform the incoming event to some commonly occurring events, e.g. a mouse event, and creates appropriate filter event functions which are easier to re-implement for the special purpose a widget may need. This is e.g. used for any widget that inherits the `QWidget` class, because those events can be used to send out signals that are avoiding any sub-classing of common widgets such as pushbuttons. The `pressed()` signal e.g. is emitted on the re-implementation of `QWidget`'s `mousePressEvent()`, showing that you don't have to subclass a simple pushbutton to find out the event and to get notified that the user pressed it.

Re-implementing these methods is one of the common tasks of a programmer writing his own widgets, therefore you will have to know about the virtual event functions of `QWidget` and the event queue processing very well.

Above, we mentioned that a widget can have several states. This predefines the behavior of the widget towards the user as well as towards the application object.

A widget can be:

1. a main widget when set as the main widget with `QApplication`'s `setMainWidget()` or `KApplication`'s `setTopWidget`.
2. a top widget when the parent of the widget is 0.
3. a modal widget usually a `QDialog` which has its own event loop
4. a semimodal widget like a `QDialog`, but without its own event loop
5. a popup widget when the widget flag is set to WType_Popup, is also a top widget

The specialized behavior of the widgets depending on their creation is then:

1. main widget: a main widget is the most important widget of the application, but the application doesn't need to have a main widget of course. If it has, and the main widget gets closed, the application terminates automatically by calling `quit()`. The `QApplication` method `mainWidget()` returns the pointer to the main widget.
2. top widget: a top widget is a widget which has no parent. All widgets that have non-zero parents are sub-widgets of the parent. The list of top level widgets can be found with `QApplication::topLevelWidgets()`. If an application doesn't have a main widget but only top widgets, connect `quit()` to `QApplication::lastWindowClosed()` to terminate the application, otherwise the application object will still exist even if all windows are closed. The application finds the currently active (focus enabled) widget with `QApplication::focusWidget()`.
3. modal widget: a modal widget is a widget derived from `QDialog`. `QDialog` widgets have their own local event loop which is entered when calling `exec()` on the dialog object. The dialog is modal, if the third widget flag is set to true, meaning that the dialog has to be terminated before the event processing can return to other application windows. All events are sent to the dialog by the application object. The current modal widget is found by the application by `QApplication::activeModalWidget()`.
4. semimodal widget: is a widget that disables events to other widgets like a modal dialog but does not have its own event loop. The modal flag has to be set to true like for a `QDialog`, although the semimodal dialog is derived from `QWidget`.
5. popup widget: a popup widget is a popup that, when it appears, makes the application object send all events to it. The popup has to be finished before the event returns to any other widget, except for another popup. The current popup widget is found by the application object by `QApplication::activePopupWidget()` to post the events to.

The application object itself keeps track of all widgets that it is responsible for. The list of widgets can be retrieved by `QApplication::allWidgets()`.

Summary: The application object is responsible for retrieving the events that were invoked by the user from the underlying window system. Then it converts these events via `QEvent` and can sent the event to any widget that is currently active. The widget itself is responsible to process the event either by accepting the event after finding out that it has an event-handler (or to be precise: the event handler has to return true to the notifying of the application's event posting). The event is deleted from the queue if an event handler was found, if all possible event handlers return false, the application is not responsible for the event and the event is ignored (deleted from the queue as well).

What is left to explain about event processing is the installation of own event filters for widgets or any other object derived from `QObject` and the way `QWidget` contains a pre-defined event handling that has to be overwritten for processing events on custom widgets. Mind that as a guideline to define own event handling, you should reimplement `QObject::event()` for all classes that do not inherit `QWidget` and the more specialized event handlers described below for all `QWidget` inherited classes. Also, preserve the declarations as virtual protected to ensure reusability and consistency for your code.

### Event Types

The events sent to the application are, as described, converted by `QEvent` to Qt events. The event type can be found out by using the `type()` method of `QEvent`, which can then be compared with the event that you want to know about. Now, the event type that `type()` delivers is an integer number; those are declared with #define in the file `qevent.h`.

> Qt 2.0 uses an `enum` for all available event-types whose entries are similar to the current defines but generally leave out the `Event_` prefix. The event type can be retrieved as described above, so you only have to change the comparison of the event type.

After filtering events for the specialized event class, more information can be found out by explicit conversion to the event class to retrieve exact data about the event.

Example:

    bool MyClass::event( QEvent* event ){

      if( event->type() == Event_MouseButtonPress){
        if( (QMouseEvent*)event->button() == RightButton ){
          // do something with the event, eg. pop up a contextmenu
          return true;
        }
        else{
          return false;
        }
      }
      else return false;
    }

The event has been explicitely converted to `QMouseEvent*` here to find out the button type. You could also find out the position of the mouse pointer at the time of the event, see the following section about mouse events

As there are so many event types that can occur, I have sorted the events defined in `qevent.h` logically according to the general event type and the subclasses that provide an event handling and offer the exact information about specific events. The sorting contains:

- Window Events
- Focus Events
- Mouse Events
- Keyboard Events
- Drag'n Drop Events

This will allow you to logically have a look at what might be interesting to reimplement or use before having to browse the Qt online documentation in depth.

### Window Events

By window events, all events that are produced by the window system in regards to handling any visible part of the application windows. This does also include the event processing in the other direction, because by methods like `QWidget::close()` or `QWidget::repaint()` events are sent to the window system to execute a synthetic events to manipulate the window behavior (either inside the window or affecting the whole window).

This is sometimes a bit hard to understand, so I will give another short example here. Assuming you have an application that has a window on the desktop. This window can be manipulated by the user through actions like:

- resizing
- moving
- obscuring with another window
- closing
- showing by execution

These are incoming events that are sent to the application. The event type is determined by `QWidget`'s `event()` re-implementation and converted to the according event class that provides methods to handle the event specifically. Now, when you have a look at the `QWidget` class, a lot of methods are provided for window manipulation, e.g. `resize()`. You're using these methods, but you probably never thought about their way of execution. In effect, these methods work the other way round: they produce an event that is sent to the display by `qt_` functions to execute actions like simulating a user action. This way, events can also be produced to gain synthetic events ( see below ).

Within a window, the widgets are arranged somehow. As each widget is treated like a separate window internally (it always is a `QWidget` or inherits it), the same events can be processed randomly inside the window for incoming events as well as manipulating internal parts of a window.

The following chart shows the according event classes with the event types they process:

- QShowEvent: Processed by `QWidget::show()`
  - Event_Show
- QHideEvent: Processed by `QWidget::hide()`
  - Event_Hide
- QCloseEvent: Processed by `QWidget::close()`
  - Event_Close
  - event handler: `QWidget::closeEvent(QCloseEvent*)`
- QResizeEvent: Processed by `QWidget::resize()`
  - Event_Resize
  - event handler: `QWidget::resizeEvent(QResizeEvent*)`
- QPaintEvent: Processed by `QWidget::repaint()` calling the event handler directly, and `QWidget::update()` which generates a window system paint event.
  - Event_Paint
  - event handler: `QWidget::paintEvent(QPainEvent*)`
- QChildEvent: not included in the release version of Qt; to handle these events reimplement `QObject::event()` or install an event filter. Child events are inserting a child widget or removing it
  - Event_ChildInserted
  - Event_ChildRemoved
  - Event_LayoutHint

> Qt 2.0 includes another event class `QWheelEvent` to handle events that occur by wheel-mice. The `QWidget` class also provides an already existing event handler for this, `wheelEvent(QWheelEvent*)`. Also all drag'n drop events have their event-handlers already in `QWidget`, see the notes for drag'n drop

### Focus Events

Focus events are somehow special to windows, but I have added a separate section for those due to the filtering of focus events in `QWidget`. A focus event is generally the fact that a window consists of several widgets who have a focus policy, which means that there can only be one widget at a time that can have the current input focus. The focus itself can be activated by a mouse click to activate the clicked widget or pressing the TAB key to forward the focus to the next widget in the tabring focus. Backwards focus setting can be done with SHIFT+TAB. This is a common usability and users expect windows to have this behavior so they can navigate the focus to the next widget. A good example for this is a dialog. If the dialog is a modal widget, it has to be finished first, otherwise is active when it gets the focus if it is the active window. Now, on dialogs widgets can be disabled as well to prohibit any user input. These disabled widgets don't get the focus either and are painted disabled.

The `QWidget` class defines the focus handling already when receiving an event. If the event type is `Event_FocusIn`, the widget gets the keyboard focus by `event()`'s conversion into a `QFocusEvent`.

This already catches a key event of the keys TAB and the combination SHIFT+TAB without processing these keys to `QKeyEvent` if there is a widget the focus can be forwarded to. Anyway, you can influence this filtering by setting focus policy. The focus policy can be set to:

- QWidget::TabFocus TAB-focusing
- QWidget::ClickFocus focus on mouse clicks
- QWidget::StrongFocus focus on TAB and mouse clicks
- QWidget::NoFocus no focus at all

The `QFocusEvent` class delivers information about the focus event by comparing the event type with `type()`. The method `gotFocus()` returns true on `Event_FocusIn` and `lostFocus()` returns true on `Event_FocusOut`. The `QWidget` predefined event handlers are:

- focusInEvent(QFocusEvent*) for Event_FocusIn
- focusOutEvent(QFocusEvent*) for Event_FocusOut

You have a lot of choices to influence the default focus handling by the methods provided by `QWidget`, e.g. you can forward the focus to another widget with setting another focus order. Mind that the focus is arranged in a ring and your implementation of this manipulation should take care that it doesn't break the focus handling. The default focus ring depends on the declaration of your widgets while constructing; if your tests result in a fuzzy focus order you have to recheck the declaration. The default design should always be left to right and top to bottom for forwarding the tab-focus. When using the geometry layout management you should declare your widget order first and then implement the layout.

`Hint:` if your widgets use multilineedits, the user expects the tab key to produce a tab in the text, not the forwarding of the focus. Therefore a simple method is to use `setFocusPolicy(NoFocus)` or `setFocusPolicy(ClickFocus)`on all additional widgets that are in the current window. Menubars and Toolbars do not have the tabfocus by default, so you don't have to set the focus policy there. An exception is the `QWhatsThis` button, which although mostly used in a toolbar, receives the input focus on TAB.

### Mouse Events

Mouse events are, as the word says, generated by the user's handling of the mouse. As these will only be of interest if the mouse is over a widget, the best use to process mouse events is to reimplement the virtual methods `QWidget` provides for this. Now, the window system sends the following event types to the application by mouse actions:

- Event_MouseButtonPress
- Event_MouseButtonRelease
- Event_MouseButtonDblClick
- Event_MouseMove

This means, that the user can handle the mouse with moving the cursor in X and Y direction, press any button and release it. A button can also be doubleclicked, which is a special event and requires special handling. As the event message is filtered by the `event()` method of `QWidget`, these event types are converted from a `QEvent` to a `QMouseEvent`. Then, the mouse event is processed, whereby `QWidget` provides a set of event handlers already. What is interesting about a mouse event is not only the type, but the other parameters, as mentioned, to implement certain actions on specific events. One of the most recently used event types are probably a right button press over a widget to open a context menu to allow quick access to commands that are available. This requires the exact position of the event's occurrence and a comparison of the button type. Double clicks are processed by the user as producing a mouse press event followed by a mouse release event and another mouse press event. As the time between the release and the next press cannot be easily determined, the `QApplication` class has methods to define the click time which is by default 400 milliseconds: `QApplication::setDoubleClickInterval(int ms)` is what you need.

The `QMouseEvent` class allows finding out the exact event by providing information about: Button type: using `button()`

- NoButton
- LeftButton
- RightButton
- MidButton

Mouse Position:

- pos() : relative mouse position within the widget (x,y)
- globalPos() : absolute mouse position on the desktop (x,y)
- globalX() : global x position of the mouse pointer from left to right
- globalY() : global y position of the mouse pointer from top to bottom
- x(): relative mouse position within the widget from left to right
- y(): relative mouse position within the widget from top to bottom

Additional Keyboard presses at the same time: using `state()` and OR'ed with Left,Right and MidButton

- ShiftButton
- ControlButton
- AltButton

The provided event handlers are:

- Event_MouseButtonPress
  - virtual void mousePressEvent ( QMouseEvent * )
- Event_MouseButtonRelease
  - virtual void mouseReleaseEvent ( QMouseEvent * )
- Event_MouseButtonDblClick
  - virtual void mouseDoubleClickEvent ( QMouseEvent * )
- Event_MouseMove
  - virtual void mouseMoveEvent ( QMouseEvent * )

Thereby, the `mouseDoubleClickEvent()` by default only produces a mousePressEvent. You have to reimplement the `mouseDoubleClickEvent()` to receive the event and process it as it is produced as an hypothetic event, not produced by the window system under X11. Set the double click time with `QApplication::setDoubleClickInterval()`.

For MouseMove events, you have to watch that the mouse event is only handled if a button is pressed. This can be configured by `QWidget::setMouseTracking(true)` to receive all mouse movements as `QMouseEvent`s in the event handler. The implementation therefore is on QWidget: the event is raised, `event()` asks if mousetracking is set to true. If not (default), the event is ignored, if yes, the event is converted to a `QMouseEvent` and delivered to the `mouseMoveEvent()` event handler.

Additionally, the widget can detect if the mouse enters the widget's space. This is done by filtering out the mouse movement before generating the `QMouseEvent` in `QWidget::event()`:

- Event_Enter
  - virtual void enterEvent ( QEvent * )
- Event_Leave
  - virtual void leaveEvent ( QEvent * )

An example for reimplementing an enter and leave event is `QToolButton`. The buttons in the toolbar have a automatic raising behavior in windows style, therefore the widget uses an enter event to raise the button in 3D and lowers it when the mouse leaves the widget area.

> Wheel mice are offering an additional functionality for scrolling by the wheel. Qt 2.0 offers solutions for handling wheel events in a separate event class `QWheelEvent`, therefore these are not handled as mouse-events.

### Keyboard Events

A keyboard event is generally sent to the application if the user pressed or released a keyboard button, therefore can determine the event by:

- Event_KeyPress
- Event_KeyRelease

### Handling

Now, the `QWidget` class converts a keyboard event from `QEvent` to a `QKeyEvent` if the widget has the keyboard input focus; if the widget has tabfocus policy, the TAB and SHIFT+TAB key-presses are filtered out to produce a `QFocusEvent` instead a `QKeyEvent`. The `QKeyEvent` class provides more convenient methods to process the key event. Those have some specialties which I want to discuss.

Event Handlers:

`QWidget` provides two event handlers for the two event types the keyboard produces:

- virtual void keyPressEvent(QKeyEvent*) for Event_KeyPress
- virtual void keyReleaseEvent(QKeyEvent*) for Event_KeyRelease

Acceptance:

The widget that receives a `QKeyEvent` and re-implements the event handlers from `QWidget` has to determine if it wants to accept or ignore the keyevent, so the widget can sent it back to the parent widget. Therefore you have to know that the accept flag is set to true in the constructor of a `QKeyEvent`. You can clear this flag with calling `ignore()` if you don't want to process the key and sent it back.

Modifiers

The user can press so-called key-modifiers. Those are the ShiftButton, ControlButton and AltButton. The currently pressed modifier keys can be found out with `state()`, which returns the modifiers OR'ed together.

Key Values

The key values for all keyboard keys are defined in the include file `qkeycode.h`.

> Qt 2.0 has all keycodes coded into namespaces of the class `Qt` located in `qnamespace.h`, `enum keys`.

The key that produced the event can be retrieved with `key()` and then compared to the defined keycode. The ASCII value can be found with `ascii()`. Mind that the symbolic constants for key values are platform independent and allow the best usage as they are simple to remind.

### Keyboard Accelerator Questions

A question that often occurs is the implementation of keyboard accelerators. As this handbook primarily targets KDE programming, I will go into that as well.

Qt has a class `QAccel` which offers connections of key presses with actions. This is done by installing an event filter that filters out keyboard events that match any item inserted into the `QAccel` object. The keyboard accelerator itself has to be a combination of the `CTRL`, `SHIFT` or `ALT` keys with a normal keyboard key. Another value can be `ASCII_ACCEL` here to use the ASCII keyboard value for the accelerator.

An accelerator instance is then created by using the widget that it should work for as an event filter with the widget as its parent. Insert the keys with `insertItem(keycode, ID )`. Although setting the ID is not necessary, you should write yourself a logical ID table containing integer value defines that allow using the ID later to find the accelerator item and helps keeping an overview over the used numbers.

Then, the item has to be connected to the object and slot it shall work for on its signal `activated(int ID)` using the `connectItem()` method instead of the usual `QObject::connect()` variant.

Popup menus (only within menu-bars) already provide accelerator usage without explicitely creating a `QAccel` instance. You only have to use `setAccel()` there; see `QMenuData` for more details.

Now, when it comes to KDE, things will be a bit different because KDE offers some additional features. First of all, you have to use the class `KAccel` instead of `QAccel`; the usage is almost the same. The `KAccel` class (part of `kdecore`) also offers an insertion into menus and configuration of accelerator keys, which then can change the menu entry as well.

Further, KDE provides globally configured accelerators for standard keys.Those are defined in `kaccel.h` and only have to be inserted. The class documentation also shows the usage of standard accelerators and accelerators in general by examples.

Whenever an application offers keyboard accelerators, users often feel uncomfortable with the given values and want to change them themselves. Also, the programmer usually sets keyboard accelerators for those slots that he thinks are the most needed functions in his program; in fact he should in any case add accelerators to all of his available menu entries and functions. Further, KDE has two ways to offer configurating the `KAccel` object as well as saving the configuration to the application config file by providing a ready-to use dialog for configuration as well as a widget that can be used within a custom configuration dialog (most often a tab dialog) to configure the keys.

For accelerator configuration dialogs, see section Keyboard Accelerators.

### Drag'n Drop Events

One of the most advanced techniques to allow application communication is drag'n drop. This offers users a cool and fast feature to handle the objects they work with in an application by an intuitive interface, catching it by a symbolic icon or by marking parts of a document and move the dragged object away from the current area. The area the dragged object comes from is therefore called a `dragsource`. Then the user moves the object away to another area of the application, to the desktop or into the area of another application. After releasing the mouse button over there, he expects the data dragged to be dropped into the drop area. Therefore the drop area is also called a `drop site` or a `drop sink`. The window system provides a protocol for this, the XDND protocol, which causes the emission of the according events. The application windows can support these events by providing methods to drag object out of the window and methods to accept a drop event. Qt implements this by a class `QDropSite`.

> Qt 2.0 makes this a lot easier. `QWidget` already contains all event handlers that are mentionend here for the class `QDropSite`, therefore you only have to remove the inheritance from `QDropSite` of your drag'n drop enabled widget and add a call to `setAcceptDrops(TRUE)` in the widget's constructor.

The widget that wants to use drag'n drop has to inherit this class additionally to the base widget class. Then, the `QDropSite` offers additional event handlers that convert the `QEvent` types for drag'n drop to one of the according specialized event classes. The programmer also has to take care in his re-implementation of the mouse event handlers by which mouse button holding a drag can occur. Also, Qt currently provides two types of data to decode, text and images, which should be the most common usage. The following chart contains the window system events, the event classes handling these events and the event handlers of `QDropSite`:

- Event_DragEnter
  - QDragEnterEvent
  - event handler:virtual void dragEnterEvent(QDragEnterEvent*)
- Event_DragMove
  - QDragMoveEvent
  - event handler:virtual void dragMoveEvent(QDragMoveEvent*)
- Event_DragLeave
  - QDragLeaveEvent
  - event handler:virtual void dragLeaveEvent(QDragLeaveEvent*)
- Event_Drop
  - QDropEvent
  - event handler:virtual void dropEvent(QDropEvent*)

The event handlers are all implemented as public and reimplementations should preserve to

Note: the system event `Event_DragResponse` is automatically handled by the application object internally through the Qt implementation. It causes a `QDragResponseEvent` that accepts/rejects the drag action.

#### Comment

KDE also contains another implementation of Drag'n Drop functionality. The description of using KDE 1.x Drag'n Drop has been left out because this will be removed in KDE 2 and only the Qt implementation is going to be used with an extended implementation of the XDND protocol.

#### Comment

There exists a zoo of DND protocols. At the moment of writing, the Qt DND protocol does not support the Motif DND protocol (as used by for instance Netscape and GNOME). Please be aware of this if you reimplement the Qt DND functions.

## 4.3 QWidget Virtual Methods

As the event handling generally is implemented by virtual protected methods, especially the `event()` method provided by `QObject`, the `QWidget` class reimplements this function in order to sort out the incoming event and convert it to other event types that can be handled by more specialized classes. Furthermore, it calls the provided additional virtual methods by default implementations. The programmer has a good advantage by this pre-selection of events as the widgets he creates are all derived from `QWidget` and therefore will need one or more special event handler implementation. The most common events that are processed are mouse events and for text input mostly keyboard events. The other events mostly deal with focus handling, which moves on the keyboard input focus from one widget to the next. Programmers need to know about focus handling well, because the user will expect a certain behavior over his widget when using the TAB key and the SHIFT+TAB combination to move the input focus forward.

Like explained in the Event Queue chapter, the `QApplication` takes care of converting window system events to objects of the `QEvent` class that are handled by the `QObject::event()` method. Therefore all classes that are derived from `QObject` can process event handling. The class `QWidget` already contains an overwritten `event()` method. It first checks for installed event filters (which are additionally created event filters by the programmer to redefine the default behavior by processing the event themselves or only the wanted events). Then it decides by the `type()` of the event which kind of event was called and converts it to one of the following event classes derived from `QEvent` who are delivered to the according virtual methods:

- QCloseEvent
  - virtual void closeEvent ( QCloseEvent * )
- QFocusEvent: keyboard input focus event; widget gets the focus and looses it due to preselection of TAB and SHIFT+TAB by `event()`
  - virtual void focusInEvent ( QFocusEvent * )
  - virtual void focusOutEvent ( QFocusEvent * )
- QMouseEvent: mouse events
  - virtual void mousePressEvent ( QMouseEvent * )
  - virtual void mouseReleaseEvent ( QMouseEvent * )
  - virtual void mouseDoubleClickEvent ( QMouseEvent * )
  - virtual void mouseMoveEvent ( QMouseEvent * ) :with pressed mouse button by default. Use `setMouseTracking(true)` to receive all movements
- QMoveEvent: window move event, position change
  - virtual void moveEvent ( QMoveEvent * )
- QKeyEvent: keyboard events
  - virtual void keyPressEvent ( QKeyEvent * )
  - virtual void keyReleaseEvent ( QKeyEvent * )
- QResizeEvent: widget is resized
  - virtual void resizeEvent ( QResizeEvent * )
- QPaintEvent: widget needs repainting
  - virtual void paintEvent ( QPaintEvent * )

Additionally, two events are called that don't match any other event type but may be important sometimes:

virtual void enterEvent ( QEvent * ): the mouse enters the widget space

virtual void leaveEvent ( QEvent * ): the mouse leaves the widget space

Reimplementing is always needed if your custom widget wants to process the event and react to it. The reason why the event gets split up to other `QEvent` types is that the other event classes provide methods that are suitable to directly retrieving the needed event-specific data. This means, that e.g. a `QMouseEvent` can be asked for the button that caused the event or was active at that particular event as well as the global and relative mouse position where the event occurred. Mouse events are always used to pop up context menus over widgets which need to know the button (right mousebutton) and the position, because the user expects the context menu to pop up at the same position the mouse cursor currently is.

The paint event is often needed if a widget has to draw something. Instead of creating a synthetic event (a logical event caused by the program internally), call repaint() here.

## 4.4 Event Filters

In addition to the normal processing of the event queue that is provided by the application object, the programmer can influence the default behavior by installing event filters. As explained above, all `QObject` inherited classes use event processing through the `event()` method. Instead of writing a completely new event handler in situations where you only need some events processed by your own methods, you should write an event filter. The event filter gets installed where you like to and filters out the event directly when `QObject::event()` is called internally.

To write an event filter, your class has to overwrite the `QObject::eventFilter()` method and call `installEventFilter()` as well as `removeEventFilter()`. The declaration of these methods in `QObject` are:

> `

>     bool QObject::eventFilter ( QObject *, QEvent * ) [virtual]
>     void QObject::installEventFilter ( const QObject * obj )
>     void QObject::removeEventFilter ( const QObject * obj )

> `

The implementation of an event filter can be done in several ways. One that is possible is to create a new class for special event filters and create an instance of this class in the program. Then you can install the event filter on every instance you like to to achieve the same event filter on all instances independent of their class as well as redefining event processing of existing classes without inheriting them.

An example would be:

> `

>     // Classdeclaration

>     class KMyAppFilter: public QObject
>     {
>     protected:
>       virtual bool eventFilter(QObject* object, QEvent* event);
>     };

>     // Filterimplementation

>     bool KMyAppFilter::eventFilter(QObject* object, QEvent* event){

>       if(event->type() == [the eventtype you like to filter])
>       {
>         [your filter implementation]
>         return true;  // the event has been caught and processed
>       }
>       else
>       {
>         return false; // return false to continue processing the event with QObject::event()
>       }
>     }

>     // installing the filter

>     QObject* myfilter= new KMyAppFilter();
>     QPushButton* mybutton= new QPushButton();

>     mybutton->installEventFilter(myfilter);

> `

Another solution would be to reimplement the `eventFilter()` method in your inherited class as long as the base class is `QObject`, e.g. if your view area of your application wants to process a certain event that is not covered by the virtual methods `QWidget` provides. Then you have to install the event filter at the place you like to; normally this would be in the constructor of your class. With removeEventFilter() you can stop the event filter from processing the events any time.

#### Comment

Note: `KApplication` already has a global application event filter installed to filter out CTRL+ALT+F12 for KDebug

## 4.5 Synthetic Events

Before describing what synthetic events are and how they can be used by the programmer, I want to review the last sections briefly.

We saw that the application object receives the window system events, processes them and creates event objects from the classes the library provides. The converted event can then be handled by event handlers that are specialized on the event class to retrieve further information about the event. Finally, we can influence the event handling itself by installing event filters and overwriting provided event handlers.

This does the "normal" job of an application to execute actions according to user invoked events. On the other side, this system offers another possibility: the fact that the events are converted to class instances can be reversed - a so-called synthetic event can be created which fakes an original window system event. The next advantage is that these events are independent of the underlying window system.

A good possibility where this feature could be used would be e.g. for learning programs. Those are almost non-existent for Unix but could offer a market to teach beginners how to handle programs similar to commercial products already available on other platforms. Also this could be a part of a help-system which an application can provide.

An example description how to implement this:

Provide a help window with a button that invokes a step e.g. "Show me". On pressing the button, the cursor will move to the desired location, e.g. to a pushbutton on the screen. The implementation then has to find out the exact position of the button and calculate the center coordinates the mouse pointer has to move to. Then the mouse pointer could move there by construction of a `QCursor` and using `setPos()`. The start position can be found out in the mouse event that called the function. Then, the cursor has to move visually by using `setPos()` in a loop where a `QTimer` could be used to run between positions to slow down the move so that the user can follow the mouse pointer.

### Creating Events

Now, to come to the actual implementation of a synthetic event, you have to know the event you want to create. Therefore, you need the constructor parameters for the event classes. The following list contains the constructors including the event-classes hierarchy:

QEvent(int type)

  `type` is one of the events declared in `qevent.h`

  > Qt 2.0 uses all events from an `enum` instead of the #defines. See `QEvent`. The types are almost the same except they leave out the `Event_` prefix.

QCloseEvent()

  takes no parameter. Mind that the accept flag is set to false

QFocusEvent(int type)

  `type` is either `Event_FocusIn` or `Event_FocusOut`.

QKeyEvent(int type, int key, int ascii, int state)

  takes `Event_KeyPress` and `Event_KeyRelease` as `type`. `key` is one of the keys defined in `qkeycode.h`. `state` is `ShiftButton, ControlButton, AltButton` OR'ed.

QMouseEvent (int type, const QPoint & pos, int button, int state)

  The `type` parameter must be `Event_MouseButtonPress, Event_MouseButtonRelease, Event_MouseButtonDblClick` or `Event_MouseMove`. The `button` is `LeftButton, RightButton, MidButton, NoButton`. `state` is `ShiftButton, ControlButton and AltButton` OR'ed for event `Event_MouseButtonRelease`, for events `Event_MouseButtonPress, Event_MouseButtonDblClick` `state` includes `LeftButton, RightButton, MidButton`.

QMoveEvent(const QPoint & pos, const QPoint & oldPos)

  `pos` is the new position the widget shall move to, `oldPos` the old position. Retrieve the old position before creating the event with `QWidget::pos()`.

QPaintEvent(const QRect & paintRect)

  raise a paint event to repaint the area `paintRect`

QResizeEvent(const QSize & size, const QSize & oldSize)

  resizes the widget from `oldSize` to `size`. Retrieve the old size before creating the event with `QWidget::size()`.

An example on how to create an event would be:

> `

>     QMouseEvent press_quit(Event_MouseButtonPress,
>                            quit_button->pos(), LeftButton, LeftButton);

> `

This creates a mousePressEvent() for the widget `quit_button` with the left button.

### Sending Events

After creation, the event has to be sent to the application instance to call its execution. Thereby, two ways can be used: one that directly processes the event and one that will place the event in the event queue at the last position:

Direct execution:

> `

>     QApplication::sendEvent(quit_button, &press_quit);

> `

The `sendEvent()` waits for the result and returns true or false depending if the event has been accepted or not.

Placement into event queue:

> `

>     QApplication::postEvent(quit_button, &press_quit);

> `

#### Comment

The event for postEvent() must be allocated on the heap as it gets deleted immediately after the posing.

To turn a posted event into a send event, use `sendPostedEvents(QObject * receiver, int event_type)`. This requires the options given at the constructor. As you may see, some constructors don't need an event type, therefore the according event type can be found in Event Types but is also simple to guess as they are only responsible for one event type. Example: `QCloseEvent` only takes `Event_Close`, `QPaintEvent` only takes `Event_Paint`.

## 4.6 Event Precedence

In relation to influence the event behavior of the application, the programmer often faces situations where long operations block the `Event_Paint` and lead to a scrambled look of the application windows. These situations can be solved either by using a progressdialog that indicates the operation progress or by event precedence. This means that the current event gets stopped and the event queue is processed. The class `QApplication` offers a solution for this by two methods which are identical except the parameters. One is `processEvents()`, which processes pending events for 3 seconds or until there are no more events in the event queue. The other, more likely used method is `processEvents(int maxtime)`, where `maxtime` is the time in milliseconds during which pending events can be processed.

On one hand this means stopping the current long operation which then would take even longer to get finished if pending events are in the queue, but the user cares more about the visible state of an application than if an operation which takes some time will take a second longer (or even parts of a second).

KDE offers an additional library for I/O operations in the upcoming KDE 2, which is under development. This will allow running the long I/O operations outside the application's process as multi-threading is not supported by Qt directly.

## 4.7 Summary

After this long chapter about signals, slots and events, I want to append a short summary so you can recapitulate the collected knowledge about application behavior.

- An application can communicate internally by signals and slots
- Signals are sent out without caring about who will catch it
- Slots are normal methods that can connect to signals and react as well as they can be called where allowed by their access attribute
- Slots cannot have default arguments
- Slots return void
- The user communicates with the application through the window system
- The window system reports the events to the application
- The application converts window system events to `QEvent`s
- The events are processed through an optional application global event filter
- The event that passes the filter gets forwarded to the according window e.g. the current modal window
- The widget receives the event and can have an event filter that comes first when the reimplemented `QObject::event()` is called.
- If the event passes the widget event filter, `event()` proceeds to convert it to the according `Q***Event` class
- The event filters for these event types are called to react on the event

Further, we saw that the programmer can influence the behavior by:

- reimplementing any stage of virtual methods filtering events
- creating synthetic events
- sending synthetic events directly or into the event queue
- the event queue can be given precedence that stop long processes to allow execution of waiting events to be processed

Finally, I hope this has given at least experienced C++ programmers a good insight and explanation on how Qt and KDE work. I have collected the information by working myself into the class structure and I hope that this collection makes it a lot easier for other programmers to get started especially in the advanced chapters of application design and programming. The information value is therefore not granted to be exact; if you may find any misconcepted or incorrect information, please contact me via email.

Következő Előző Tartalom