Subscribe:

Pages

Dialogs in wxRuby

A dialog is an independent window that your application can use to provide or require some information. Typical examples are the file dialog, to locate a file, and the printer dialog, to configure a print job. wxRuby provides a number of standard dialogs to provide platform standard tools to get information from the user or provide information to the user. This post introduces the simplest dialogs, message dialogs, moves on to ways to retrieve information from the user, and finally considers custom-built dialogs. To explore how these dialogs are used, it is convenient to run them within the simplest possible wxRuby program:
require 'wx'

Wx::App.run do
#
# Put dialog code here
#
exit
end


Message Dialog

The Wx::MessageDialog class is used to display information to the user. We can set the title of the dialog box, the text that is displayed, and the icon. Finally, we can change the buttons on the dialog, to get some feedback from the user. Let us begin with the simplest application, displaying some information.



md = Wx::MessageDialog.new(
nil,
"Text within dialog\ncan be on multiple lines",
"Title of Dialog",
Wx::ICON_INFORMATION)
md.show_modal

The variable 'md' is set up to hold an instance of the MessageDialog class. The instance takes four parameters:
  1. the parent of the dialog: usually the parent frame, so the dialog is centred on it;
  2. the text to display in the body of the dialog;
  3. the title of the dialog; and
  4. the style.
The use of styles is important within wxRuby, as almost every widget within the library can be customised using one or more flags provided within its style parameter. In the example above, the style is simply Wx::ICON_INFORMATION, which tells the dialog to use an information icon on display. The choices for the icon include Wx::ICON_INFORMATION, Wx::ICON_QUESTION, and Wx::ICON_ERROR. The dialog is run by calling its 'show_modal' method. 'Modal' means that the dialog will take over interactions with the application whilst it is on the screen. This is to prevent the user doing something else in the meantime, such as trying to open another file before finishing with the dialog that is currently displayed.

Our second example shows how to get information back from a user. The MessageDialog provides control over which buttons will be displayed in the dialog, and what their labels might be. I have combined the style for an error icon (Wx::ICON_ERROR) with the style for yes/no buttons (Wx::YES_NO), to get the dialog below.

How to see which button the user clicked? The answer is to check the return value from the call to md.show_modal to see if it equals Wx::ID_YES, which is what you get back if you click on the 'yes' button. The other button styles include Wx::ID_CANCEL and Wx::ID_OK to use 'ok' and 'cancel' buttons. Note that styles are joined using bitwise or, '|'.




md = Wx::MessageDialog.new(
nil,
"Error in program. Shall we continue?",
"Error Dialog",
Wx::YES_NO | Wx::ICON_ERROR)
if md.show_modal == Wx::ID_YES
puts "Clicked YES"
else
puts "Clicked NO"
end


File Dialogs


After the MessageDialog, there is a welcome sense of deja vu with the remaining dialogs. Let us consider the file dialog, which has a place in most applications as a means to identify the file to work on.


The file dialog can be created just by providing it a parent window and a dialog. By default, you will get an 'open file' dialog. If you would like a 'save file' dialog, add the following as the third argument: ':style => Wx::FD_SAVE | Wx::FD_OVERWRITE_PROMPT'. Having created the dialog, we see if the 'Open' or 'Save' button was clicked by checking if the return value is Wx::ID_OK. The name of the filename is retrieved by accessing the 'filename' attribute of the dialog itself.

fd = Wx::FileDialog.new(
nil,
"Title of Dialog"
)
if fd.show_modal == Wx::ID_OK
puts "User selected file: #{fd.filename}"
end


Tip:
If you would like your file dialog to open up at the directory you left it in, then store your file dialog in an instance variable of your topmost frame and create it in your frame's initialize method.

Selection Dialogs
Apart from the file dialog, more general ways to retrieve information from the user include the TextEntryDialog, PasswordDialog, and the selection dialogs. The selection dialogs let the user choose one or more items from a list. Below is shown the MultiSelectionDialog. All of these dialogs work in very similar ways, with slightly different methods to retrieve the final information.



mcd = Wx::MultiChoiceDialog.new(
nil,
"Choose one or more languages:",
"Example dialog",
["C", "C++", "Java", "Lisp", "Ruby", "Scheme"]
)
if mcd.show_modal == Wx::ID_OK
puts "You selected: #{mcd.selections}"
end
exit
end


Custom Dialogs


Missing from the standard wxRuby toolbox is a way to retrieve numeric information from the user. Also, what happens if you want more than one piece of information at a time? Indeed, can we create our own dialogs within wxRuby? The answer is yes, we can construct our own dialogs, by subclassing the Dialog class. We need to fill out the dialog with the widgets of our choosing, catch events when they change to save off the current values, and read the values when the dialog exits. The code below contains various techniques I have yet to cover in this weblog, but hopefully will still be understandable.

There is a magic touch: wxRuby provides a very clever method to create and layout buttons for us too. The method: create_separated_button_sizer will generate a Sizer with the horizontal separator line and buttons based on the flags you provide it. Possible buttons include: OK, CANCEL, YES, NO and HELP. What is particularly clever about this method is that it will put the buttons in the right order and with the right spacing depending on the operating system you are using!


require 'wx'

class NumberDialog < Wx::Dialog
attr_reader :value
def initialize(parent, title, label, min_value=0, max_value=100)
super(parent, Wx::ID_ANY, title)


spin_control = Wx::SpinCtrl.new(self, Wx::ID_ANY,
:min => min_value, :max => max_value)
evt_spinctrl(spin_control) {|e| @value = spin_control.value}

items_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
items_sizer.add(Wx::StaticText.new(self, Wx::ID_ANY, label))
items_sizer.add(spin_control, 1, Wx::GROW)

main_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
main_sizer.add(items_sizer, 0, Wx::ALL | Wx::GROW, 10)
main_sizer.add(create_separated_button_sizer(Wx::OK | Wx::CANCEL),
0, Wx::ALL | Wx::GROW, 5)

set_sizer main_sizer

end
end

Wx::App.run do
nd = NumberDialog.new(nil, "Sample numbers", "Choose:", 10, 50)
if nd.show_modal == Wx::ID_OK
puts "Value is: #{nd.value}"
end
exit
end

Finally ...

This post has not touched on all the standard dialogs or methods for using a custom dialog. There are dialogs to select colours, fonts, and to manage a sequence of tasks through a Wizard. Nor have I covered all the options that most of these dialogs provide. But I have tried to cover the more important ones for information input and display. Once you understand these, you should find the principles for using the remaining dialogs are similar.