Skip to content

HowTo : A basic application

Martin Corino edited this page Mar 21, 2024 · 4 revisions
     About      FAQ      User Guide      Reference documentation

A basic application

This document shows how to create a simple wxRuby application that can be used as a template.

While this app does nothing useful, it demonstrates a couple basic concepts and explains how to write a valid, working, wxRuby application. Trying to run this application is also a good way of checking that wxRuby is correctly installed on your system.

Accessing the wxRuby modules

To create an application with wxRuby you first need to require the wxRuby modules:

require 'wx'

This code uses the all-in-one approach for loading the wxRuby modules. It is also possible to require separate modules but we will not cover that here (see the wxRuby Modules document for more information).

Application entry point

Next would be the application code and a main entry point. With wxRuby (as with wxWidgets) the entry point is mostly just a simple call to start the applications event loop (as we're talking about event based GUI applications here). In wxRuby the Wx::App class provides some typically Ruby-style magic to make this as easy as possible.

Using this the simplest Hello World application could be:

require 'wx'
Wx::App.run { puts 'Hello world!' }

As you can see there is no obligation to create an instance of the Wx::App class in wxRuby for (admittedly extremely) simple applications. Calling the Wx::App.run class method with a block will suffice.
The class method will create an instance of the generic Wx::App class under the hood and use the provided block as the #on_init callback. As the code inside the block returns a false-type value (#puts returns nil) the application will terminate immediately after writing "Hello world!" to standard output (actually not even starting the event loop at all).

The application class

For more complex applications the approach demonstrated above will quickly become insufficient. So for practically all real-world applications creating a specialized derived App class is the better option. This provides the possibility (as with all Ruby classes) to override the constructor (Wx::App#initialize) for custom initialization, attribute definitions and create customized #on_init and/or #on_exit methods like this:

require 'wx'

class MyApp < Wx::App
  def initialize
    super
    # initialize something ...
  end
  
  def on_init
    # startup the application ...
  end
  
  def on_exit
    # cleanup the application ...
    puts 'Exiting.'
  end
end

When creating #on_init/#on_exit methods it is important to understand that those would not be overrides (as is the case with wxWidgets itself). The base Wx::App class actually does not define these methods so it's also not needed (even not possible) to call super in the implementation. The wxRuby application class implementation will call the wxWidget OnInit base implementation itself and after successful completion check for the existence of an #on_init method (which could also be 'automagicallly' created from a block passed to #run) and call that if available or terminate the application if not. The exit sequence of executions are similar but reversed (first a possible #on_exit method and than the wxWidgets base OnExit).

What remains though is that for a derived application class it is still not necessary to explicitly create a class instance. Simply calling the Wx::App.run class method will suffice.

MyApp.run

The current application instance (as long as the application is active) can always be retrieved by calling Wx.get_app (note that during the #initialize call this will still return nil but at that point you can reference self).

Application main window

Many applications start with a main window derived from the Wx::Frame class and provide it with a menu and a status bar in its constructor. Connecting event handlers to respond to events like mouse clicks, menu and/or widget messages usually also happens at construction time.

In order to be able to respond to a menu message menu items must be given a unique identifier which is usually defined as a constant.
Identifiers for common menu items like 'About' or 'Exit' do not (normally) need to be defined as wxRuby defines a set of stock identifiers for items like that (like Wx::ID_ABOUT and Wx::ID_EXIT). Using these is usually preferable as these can be processed in platform specific ways.

Our example class would look like this than:

class MyFrame < Wx::Frame
  
  module ID
    Hello = 1
  end
  
  def initialize
    super(nil, Wx::ID_ANY, 'Hello World')
    
    file_menu = Wx::Menu.new
    file_menu.append(ID::Hello, "&Hello...\tCtrl-H",
                     'Help string shown in status bar for this menu item')
    file_menu.append_separator
    file_menu.append(Wx::ID_EXIT)
    
    help_menu = Wx::Menu.new
    help_menu.append(Wx::ID_ABOUT)
    
    menu_bar = Wx::MenuBar.new
    menu_bar.append(file_menu, '&File')
    menu_bar.append(help_menu, '&Help')
    
    self.menu_bar = menu_bar
    
    create_status_bar
    set_status_text('Welcome to wxRuby!')
    
    # connect event handlers
    evt_menu ID::Hello, :on_hello
    evt_menu Wx::ID_ABOUT, :on_about
    evt_menu Wx::ID_EXIT, :on_exit
  end
  
  # ... continued below ...

Notice that we don't need to specify the labels for the standard menu items Wx::ID_ABOUT and Wx::ID_EXIT — they will be given standard labels and standard accelerators correct for the current platform, making our program behaviour more native. For this reason, you should prefer reusing the stock identifiers where possible.

We also connect our event handlers to the events we want them to handle. We do this by calling the event connectors specific to the type of event we want to handle. Depending on the event type we pass a single identifier or range of identifiers to select event type instances to handle and specify the actual handler by passing a method identifier, a method object or a block.

In our example we connect instance methods by specifying the method identifiers for the following standard event handler implementations:

  # .. continued from above ...

  def on_exit(_evt)
    close(true)
  end

  def on_about(_evt)
    Wx.message_box('This is a wxWidgets Hello World example',
                   'About Hello World', Wx::OK | Wx::ICON_INFORMATION)
  end
  
  def on_hello
    Wx.log_message('Hello world from wxWidgets!')
  end

end # class MyFrame

MyFrame#on_exit closes the main window by calling Wx::Window#close. The parameter true indicates that other windows have no veto power such as after asking "Do you really want to close?". If there is no other main window left, the application will quit.

MyFrame#on_about will display a small window with some text in it. In this case a typical "About" window with information about the program.

The implementation of a custom menu command handler may perform whatever task your program needs to do, in this case it simply shows an appropriate message.

NOTE In case of (very) simple event handlers as with :on_exit and :on_about it can be convenient to use Ruby blocks instead of defining actual instance methods like this:

  evt_menu Wx::ID_EXIT { close(true) }

Now that we have a custom main window class we still need to instantiate and show that window as the application starts. This is usually done from the applications #on_init method like this:

class MyApp < Wx::App

  # ...
  
  def on_init
    frame = MyFrame.new
    frame.show(true)
  end

  # ...
  
end

Be aware that the MyApp#on_init should return a 'thruthy' value (not false and not nil) to indicate all was well and starting the application should continue. As the return value for showing a window that is not shown yet should be true if succeeded the code above should function properly.

All together

Here is the complete program to be copied and pasted:

require 'wx'

class MyFrame < Wx::Frame

  module ID
    Hello = 1
  end

  def initialize
    super(nil, Wx::ID_ANY, 'Hello World')

    file_menu = Wx::Menu.new
    file_menu.append(ID::Hello, "&Hello...\tCtrl-H",
                     'Help string shown in status bar for this menu item')
    file_menu.append_separator
    file_menu.append(Wx::ID_EXIT)

    help_menu = Wx::Menu.new
    help_menu.append(Wx::ID_ABOUT)

    menu_bar = Wx::MenuBar.new
    menu_bar.append(file_menu, '&File')
    menu_bar.append(help_menu, '&Help')

    self.menu_bar = menu_bar

    create_status_bar
    set_status_text('Welcome to wxRuby!')

    # connect event handlers
    evt_menu ID::Hello, :on_hello
    evt_menu Wx::ID_ABOUT, :on_about
    evt_menu Wx::ID_EXIT, :on_exit
  end
  
  def on_exit(_evt)
    close(true)
  end

  def on_about(_evt)
    Wx.message_box('This is a wxWidgets Hello World example',
                   'About Hello World', Wx::OK | Wx::ICON_INFORMATION)
  end

  def on_hello
    Wx.log_message('Hello world from wxWidgets!')
  end

end # class MyFrame

class MyApp < Wx::App
  def initialize
    super
    # initialize something ...
  end
  
  def on_init
    frame = MyFrame.new
    frame.show(true)
  end
  
  def on_exit
    # cleanup the application ...
    puts 'Exiting.'
  end
end

MyApp.run
Clone this wiki locally