wx the erlang binding of wxWidgets

The wx application is an erlang binding of wxWidgets. This document describes the erlang mapping to wxWidgets and it's implementation, it doesn't try to be a complete users guide to wxWidgets you will have to read the wxWidgets documentation. wx tries to keep a one-to-one mapping with original api so that the original documentation and examples shall be as easy as possible to use.

This is currently a very brief introduction to wx. The application is still under development so the interface can change, it probably contains a lot of bugs and misses a lot of wxWidgets features. I would like to get some help and feedback on the binding, with documentation, coding, testing and debugging. Send me an email at dgud on erix.ericsson.se if you are interested.

Contents

  1. Introduction
  2. Multiple processes and memory handling
  3. Event Handling
  4. Acknowledgments

Introduction

The original wxWidgets is an object-oriented (C++) api and that is reflected in the erlang mapping. In most cases each class in wxWidgets is represented as a module in erlang. This gives the wx application a huge interface spread over several modules, and it all starts with the wx module. The wx module contains functions to create and destroy the Gui, i.e. wx:new/0,wx:destroy/0, and some other useful functions.

Objects or object references in wx should be seen as erlang processes rather then erlang terms, when you operate on them they can change state, e.g. they are not functional objects as erlang terms are. Each object has a type or rather a class, which is manipulated with the corresponding module or by sub-classes of that object. Type checking is done so that a module only operates on it's objects or of it's inherited classes.

An object is created with new and destroyed with destroy. Most of functions in the classes are named the same as their C++ counterpart, except that for the convenience in erlang they start with a lowercase letter, and the first argument is the object reference. Optional arguments are last and expressed as tagged tuples in any order.

For example the wxWindow C++ class is implemented in the wxWindow erlang module and the member wxWindow::CenterOnParent is thus wxWindow:centerOnParent. The following C++ code:
  wxWindow MyWin = new wxWindow();
  MyWin.CenterOnParent(wxVERTICAL);
  ...
  delete MyWin;
would in erlang look like:
  MyWin = wxWindow:new(),
  wxWindow:centerOnParent(MyWin, [{dir,?wxVERTICAL}]),
  ...
  wxWindow:destroy(MyWin),

When you are reading wxWidgets documentation or the examples you will notice that some of the most basic classes are missing in wx, they are directly mapped to corresponding erlang terms:

wxPoint is represented by {Xcoord,Ycoord}
wxSize is represented by {Width,Heigth}
wxRect is represented by {Xcoord,Ycoord,Width,Heigth}
wxColour is represented by {Red,Green,Blue[,Alpha]}
wxPoint is represented by {Xcoord,Ycoord}
wxGBPosition is represented by {Row,Column}
wxGBSpan is represented by {RowSpan,ColumnSPan}
wxGridCellCoords is represented by {Row,Column}
wxString is represented by a list of integers

In the places where the erlang differs from the original api it should be obvious from the erlang documentation which representation have been used. I.e. the C++ arrays and/or lists are sometimes represented as erlang lists and sometimes as tuples.

NOTE: Strings in wx are assumed to be encoded in UTF-32 in native architecture format, for ASCII that is the normal erlang usage, for other character sets you need to translate them.

Colours are represented with {Red,Green,Blue[,Alpha]} the Alpha value is optional when used as arguments to functions, but it will always be returned from wx functions.

Defines, enumerations and global variables exists in wx.hrl as defines. Most of these defines are constants but not all, some are platform dependent and the global variables must be instantiated during runtime. These will be get from the driver with a call, so not all defines can be used in matching statements. Class local enumerations will be prefixed with the class name and a underscore as in ClassName_Enum.

Some global non-class functions exist in the wx_misc module.

Multiple processes and memory handling

The intention is that each erlang application calls wx:new() once to setup it's Gui which creates an environment and a memory mapping. To be able to use wx from several processes in your application, you must share the environment, you can get the active environment with wx:get_env/0 and set it in the new processes with wx:set_env/1. Two processes or applications which have both called wx:new() will not be able use each others objects.

When wx:destroy/0 is invoked or when all processes in the application have died, the memory is deleted and all windows created by that application are closed.

The wx application never cleans or garbage collects memory as long as the user application is alive. Most of the objects are deleted when a window is closed, at least all the objects which have a parent argument and that is used (i.e. not wx:null()). By using wxCLASS:destroy/1 when possible you can avoid a increasing memory usage. This is especially important when wxWidgets assumes or recommends that you (or rather the C++ programmer) have allocated the object on the stack since that will never be done in the erlang binding. For example wxDC class or it's sub-classes or wxSizerFlags but there are others.

Currently the dialogs show modal function, uses wxWidgets implementation which thus frezzes wxwidgets until the dialog is closed, that is intended but in erlang where you can have several gui applications running at the same time it causes trouble.

Event Handling

Event handling in wx differs most from the original api. You must specify every event you want to handle in wxWidgets, in erlang can you choose to receive the events as messages or handle them with callback funs.

Otherwise the event subscription is handled as wxWidgets dynamic event-handler connection. You subscribe to events of certain type from objects with an ID or within a range of Ids. The callback fun is optional if not supplied the event will be sent to the process that did the connect call.

Events are handled in order by the last subscribed handler first, and depending on if wxEvent:skip() is called it will be handled by the other handler(s) afterwords. Most of the events have default event handler(s) installed.

Message events looks like #wx{id=integer(), obj=wx:wxObject(), user_data=term(), event=Rec }. The id is the identifier of the object that received the event, obj is the object that you used connect on and event is a record with event type dependent information. The first element in the event record is always the type you subscribed to. For example if you subscribed to key_up events you will receive the #wx{event=Event} where Eventwill be a wxKey event record where Event#wxKey.type = key_up.

In wxWidgets the developer have to call wxEvent:skip() if he wants the event to be processed by other handlers. You can do the same in wx if you use callbacks. If you want the event as messages you just don't supply a callback and you can set the skip option in connect to true or false (default it is false). True means that you get the message but lets the following handlers also handle the event. If you want change this behavior dynamicly you must use callbacks and call (or not) wxEvent:skip().

Callback event handling is done by using the optional callback fun/2 when attaching the handler. The fun(#wx{},wxObject() must take two argument where the first is the same as with message events described above and the other is an object reference to the actual event object. With the event object you can call wxEvent:skip() and access all data. When using callbacks you must call wxEvent:skip() by your self if you want any of the events to be forwarded to the following handlers. The actual event objects are deleted after the fun returns.

The callback fun will execute in another erlang process which during the execution of fun will be run in exclusive access mode. It's necessary to not synchronously communicate with other gui handling processes from the callback since that may cause a deadlock. The process the callback tries to communicate with may hang in another call to wxWidgets.

Acknowledgments

Mats-Ola Persson wrote the initial wxWidgets binding as part of his master thesis. The current version is a total re-write but many ideas have been reused. The reason for the re-write was mostly because of the bad requirements he had been given by us.

Also thanks to the wxWidgets team that develops and supports it so I have something to use.


Generated by EDoc, May 26 2008, 12:35:47.