GUI programming is sometimes described as being
“event-driven”. Typically, a wxRuby application will set up an initial
state within the App’s on_init method, showing a
starting Frame or Dialog. After this, the
application’s behaviour will be determined by its responses to the
user’s interaction with the GUI elements.
These interactions with an application are modelled as
Events. Events include pressing a key, clicking a
button, typing text in a text control,
moving the mouse, selecting a drop-down menu item, resizing
a window and so on. In wxRuby, all these user actions are dealt with
using event handlers
Event handlers are pieces of code that are run when an event
occurs. There are two parts to setting up an event handler:
The basic way to set up an event handler in WxRuby combines these two
steps in a single line of code. This is to call a method named
evt_xxx
, where xxx is the type of event that should be handled, and
pass it a block that should be run when the event occurs. For example:
Some types of event handlers, those that deal with events arising from a
user operating UI controls like buttons, checkboxes and choices, require
that the relevant control be identified in the handler:
This is discussed in more detail below
The evt_xxx methods are instance methods of the wxRuby class
EvtHandler. All GUI classes that are displayed on the
screen (that is, subclasses of Window) inherit from this
class, as does App. Therefore, event handling can be done by
Frames, Dialogs or within individual
controls.
As there are very many different types of evt_xxx method, they are
described alongside the type of control or event that they belong
to. Therefore, the events generated by a TextCtrl are
given in the documentation for that class. Similarly, available event
handlers relating to the activation and disactivation of a Window are
listed in the documentation for ActivateEvent.
Whenever an event occurs, an Event object is created
containing additional information about the event. For example, for a
mouse event, this might be where on the screen it took
place; for a key event, what key was pressed; for a
activation event event, whether the Frame became
active or inactive.
To access this additional information within an event handler, simply
set up the block to accept a single parameter, the Event object.
The event object will be of the appropriate class for the type of event
being handled – for example, a SizeEvent for a evt_size
handler, or a TreeEvent from a TreeCtrl. All the event
classes are listed on the home page.
Note that Events are short-lived objects and are destroyed by wxWidgets
once all the relevant handlers have been called. Therefore you shouldn’t
attempt to store an event object passed into a handler in a variable
referenced outside the handler block.
As an application develops, more event handlers are used, and each
contains more code. It then becomes easier to organise this code into
methods, for example:
For convenience, wxRuby permits the block here to be replaced simply
with the name of the method that should handle the event.
Obviously, this can give considerably clearer and shorter code. The
method may still choose whether or not to receive the Event object.
As mentioned above, some event handlers, for example, evt_button
require that the control which generates the event be identified. This
is one aspect of a distinction between two broad groups of events in
wxRuby. The first is CommandEvents, which are
generated by user interaction with controls. The second includes all
other events.
CommandEvents are generated by user interface
controls such as buttons, text boxes, radio buttons, lists, menus and so
on. These controls enable particular types of interaction, such as
typing in a text box, selecting a radio item, or choosing an item from a
drop-down list or menu. Such actions can be thought of as “commands”,
hence the name. It’s often desirable to manage the event handlers for a
related group of such controls within a container window, such as a
Frame or Panel.
To facilitate this, CommandEvents ‘bubble’ up to
parent windows, so parent windows can set up handlers to listen to
events generated by child controls. This also means that you have to
explicitly tell WxRuby the source of events you want to handle events
from. In C++ this is done by referring to the integer id of the control
whose events are being listened to, and this is permitted in wxRuby too:
However, this use of explicit constants is rather cumbersome and
unnecessary in a dynamic language like Ruby. Therefore, it is typically
easier to allow wxRuby to assign ids to new windows, and then simply
pass the control itself as the parameter to the event handler:
If there are many similar controls within a Frame or Dialog, it can be
easier to set up a global event handler, then work out what specific
action should be taken within the event handling code itself, perhaps
using the event_object method in Event
to identify the control. To set up a catch-all event handler for
CommandEvent types, use the special Wx::ID_ANY
identifier:
Other events in WxRuby include sizing, activating, closing and moving
windows, changing layouts with splitters and sashes, and moving through
multi-pane organisers like Wizards and
Notebooks. Importantly, this also includes generic
mouse movement and keyboard presses
that are not associated with any particular command action. All these
kind of events are only visible to the window itself. They are not
visible to parent windows. The practical effect of this is that the
evt_xxx method must be set up the window that generates the event, and
no id
parameter is needed.
The fact that non-CommandEvents are only sent to the window that
generated them is not, in practice, a serious restriction, given
wxRuby’s dynamic nature. It is entirely permitted to call the evt_xxx
method on the widget whose events should be listened for, but keep the
handling code in another instance
Just note that in this case it is not possible to use the shortcut
method-name notation shown above; the following will not work
As well events that are triggered by UI actions, wxRuby is also able to
use event handling to carry out timed or regular actions. The
Timer class can be used to trigger one-off or repeating
TimerEvents events which can be handled as with other
events. Note that use of this approach is preferred to ruby’s own
‘timeout’ library as a way to handle timed events in wxRuby. This is
because Ruby’s interpreter threads do not co-operate well with C and C++
based toolkits like WxWidgets.
There is also an evt_idle
handler which specifies actions to be done
when the application is otherwise doing nothing. See
IdleEvent for more information.
Sometimes a given event should not be permitted. For example, a user has
tried to close a dialog, but there is an invalid value in a text box, so
the application should prevent the dialog being closed until the value
is corrected. The action can be blocked by calling the
veto method:
Sometimes you want the opposite – to ensure that the event’s original
aim is completed, or passed on upwards to a parent window for
handling. In this case, the skip method should
be called.
This is important, for example, in evt_close
handlers. If skip
is
not called, the Frame or Dialog will not be closed at all.
Although it is not a frequent need, it is possible to disable any event
handler dynamically within program code. This is achieved by calling the
disconnect method of EvtHandler. An
example of this can be found in the “events” sample included with
wxRuby.
wxRuby permits completely custom event types and handlers to be
defined. This is useful when writing custom control types. The central
method for doing this is the EvtHandler class method
register_class. The
documentation there describes how to integrate user-defined Event
classes with the wxRuby event system; example code can be found in the
“events” sample.
[This page automatically generated from the Textile source at 2023-06-13 21:31:41 +0000]