wxRuby Documentation Home

Event handling overview

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

Setting up 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:

  1. Writing the code that should be run
  2. Connecting that code to the event

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:

evt_size { puts "I was resized" }

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:

my_button = Wx::Button.new(self, :label => 'Press me')
evt_button(my_button) { puts "Button was pressed" }

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.

Working with event objects

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.

evt_size { | event | puts "Width is now #{event.size.width}" }

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.

Using methods to handle events

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:

def initialize
  ..
  evt_size { | event | on_size(event) }
  evt_button(my_button) { | event | on_button_pressed(event) }
end
def on_size(event)
  ...
def on_button_pressed(event)
  ...

For convenience, wxRuby permits the block here to be replaced simply with the name of the method that should handle the event.

evt_size :on_size
evt_button my_button, :on_button_pressed

Obviously, this can give considerably clearer and shorter code. The method may still choose whether or not to receive the Event object.

CommandEvents and others

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.

Command 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:

MY_BUTTON_ID = 1001
my_button = Wx::Button.new(panel, MY_BUTTON_ID, 'Press me')
evt_button(MY_BUTTON_ID) { 'my_button was pressed' }

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:

my_button = Wx::Button.new(panel, :label => 'Press me')
evt_button(my_button) { 'my_button was pressed' }

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:

evt_button(Wx::ID_ANY) { 'some button was pressed' }

Other events

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.

evt_size { "I was resized" }

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

class MyFrame < Wx::Frame
  def initialize
    ... 
    my_panel = Wx::Panel.new(self)
    my_panel.evt_size { on_panel_sized }
  end
def on_panel_sized
end

Just note that in this case it is not possible to use the shortcut method-name notation shown above; the following will not work

my_panel.evt_size :on_panel_sized

Non-gui events

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.

Vetoing and skipping events

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:

if not ready
  event.veto
end

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.

event.skip

This is important, for example, in evt_close handlers. If skip is not called, the Frame or Dialog will not be closed at all.

Disabling event handlers

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.

Custom event types

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 Thu Aug 28 20:29:08 +0100 2008]