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