-*- html -*- wx Documentation @author Dan Gudmundsson @copyright 2008-2009 Ericsson AB @title wx the erlang binding of wxWidgets @doc The wx application is an erlang binding of wxWidgets. This document describes the erlang mapping to wxWidgets and it's implementation. It is not a complete users guide to wxWidgets. If you need that, you will have to read the wxWidgets documentation instead. wx tries to keep a one-to-one mapping with the original API so that the original documentation and examples shall be as easy as possible to use. wxErlang examples and test suite can be found in the erlang src release. They can also provide some help on how to use the API. This is currently a very brief introduction to wx. The application is still under development, which means the interface may change, and the test suite currently have a poor coverage ratio. == Contents ==
wx:new/0
, wx:destroy/0
, and
some other useful functions.
Objects or object references in wx should be seen as erlang
processes rather than 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
inherited classes.
An object is created with new and destroyed with
destroy. Most functions in the classes are named the same
as their C++ counterpart, except that for 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:
wx.hrl
as defines. Most of these defines are constants
but not all. Some are platform dependent and therefore the global
variables must be instantiated during runtime. These will be acquired 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
.
Additionally some global functions, i.e. non-class functions, exist in
the 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.
wx:new(), MyWin = wxFrame:new(wx:null(), 42, "Example", []), Env = wx:get_env(), spawn(fun() -> wx:set_env(Env), %% Here you can do wx calls from your helper process. ... end), ...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, or at least all the objects which have a parent
argument that is non null. By using
wxCLASS:destroy/1
when possible you can avoid an
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 its sub-classes or wxSizerFlags
.
Currently the dialogs show modal function freezes 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. This will hopefully be fixed in future wxWidgets
releases.
== Event Handling ==
Event handling in wx differs most from the original API.
You must specify every event you want to handle in wxWidgets,
that is the same in the erlang binding but you can 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 a 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 called connect/2. Thus, a handler is a callback fun
or a process which will receive an event message.
Events are handled in order from bottom to top, in the widgets
hierarchy, by the last subscribed handler first. Depending on
if wxEvent:skip()
is called the event will be handled by the
other handler(s) afterwards. Most of the events have default event
handler(s) installed.
Message events looks like {@link wxEvtHandler:wx(). #wx{id=integer(),
obj=wx:wxObject(), userData=term(), event=Rec} }. The id is
the identifier of the object that received the event. The obj
field contains the object that you used connect on.
The userData field contains a user supplied term, this is an
option to connect. And the event field contains 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 Event will be a
wxKey event record where Event#wxKey.type =
key_up
.
In wxWidgets the developer has 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
call to true or false, the default it is false. True means that you get
the message but let the subsequent handlers also handle the event. If
you want to change this behavior dynamically you must use callbacks and
call 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 arguments
where the first is the same as with message events described above and
the second is an object reference to the actual event object. With the
event object you can call wxEvent:skip()
and access all
the data. When using callbacks you must call wxEvent:skip()
by yourself 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 callbacks are always invoked by another process and have
exclusive usage of the GUI when invoked. This means that a callback fun
can not use the process dictionary and should not make calls to other
processes. Calls to another process inside a callback fun may cause a
deadlock if the other process is waiting on completion of his call to
the GUI.
== 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 due to
the limited requirements he had been given by us.
Also thanks to the wxWidgets team that develops and supports
it so we have something to use.