wxserpent = serpent + wxWidgets
Roger B. Dannenberg
Introduction
Serpent has no built-in graphics or user interface classes, but
it
has been interfaced to parts of the wxWidgets library. Unlike
wxPython,
wxserpent is not intended to include all of wxWidgets, so there is
only
a very limited subset available. However, this is enough to
construct
simple but effective graphical interfaces. For example, wxserpent
was
used to create a graphical editor for audio synthesis algorithms,
including pop-up help text, icons with hot-spots, icons that drag,
lines that snap into place, menus, and file browsers.
This document is likely to be incomplete, so you are encouraged
to
read the sources for details, constants, and recent additions.
When you are getting started, you should run wxserpent in the serpent/wxs
directory. The init.srp file here will then be loaded
automatically at startup and create a little window where you can
type
text and have it evaluated by Serpent. You can even type
load "filename"
to load a file. You can of course read init.srp and
modify
it or install your own version here or in another directory.
Serpent
will read from the current directory first, and then use the
search
path.
Two-level Implementation
Level 1
wxserpent interfaces to wxWidgets through a fairly small set of C
functions. Because wxWidget objects are not primitives in serpent
(like
files for example), the wxWidget objects are accessed via
"handles"
that are simply integers. For example, a window might be
designated by
the integer 1, and a menu might be named by integer 27. As you can
imagine, the C interface code has a simple table that maps
integers to
wxWindows objects and then calls methods on these objects.
This is the low-level, direct interface to wxWindows, and you can
use it if you like, but one of the problems is that when wxWindows
events occur (e.g. the user selects a menu item), a single serpent
method, wxs_handler, is called to handle the event. It is up to
this
function to figure out which graphical object, based on the
integer
identifier, generated the event, and what to do about it.
Level 2
The standard way to impose some structure on a user interface
system
like this is to let objects represent things like menus, windows,
sliders, number boxes, etc. In this scheme, the integer handles
are
hidden, and serpent maintains a table to map integers, not to
wxWidget
objects, but to Serpent objects that serve as representations of
the
graphical objects.
When a menu item is selected, the wxs_handler method finds the
corresponding Serpent object and invokes the handle() method.
Thus, if
you use this level, implemented in wxslib/wxserpent.srp, creating
a
graphical object is just a matter of instantiating a class.
Example
Let's go through a simple example. Suppose you want a slider
control. Your serpent program should say something like:
// parameters are: parent, min, max, initial, x, y, w, h
myslider = Slider(default_window.id, 0, 100, 50, 10, 30, 200, 20)
The first parameter tells where to put the slider: this must be
the
integer "handle" for a window. When wxserpent initializes, there
is a
default window created automatically, and serpent creates a
corresponding sepent object called default_window. The id
field contains the integer handle. The next parameters specify the
minimum, maximum, and initial values for the slider. The last four
parameters are coordinates: left, top, width, and height.
Coordinates
are in integer pixels measured from the upper left corner of the
window.
Now we have a slider, and you should see a slider in the window.
How
do we get values from the slider? One way is to start over and
make a
subclass of Slider where we override the handle
method, but this is so common, there's an easier way. Let's look
at
some code to get a thorough understanding. Here's wxs_handler:
def wxs_handler(id, event, x, y):
var obj = control_map[id]
if obj:
if event == WXS_PAINT:
obj.paint(x)
else:
obj.handle(obj, event, x, y)
When an event occurs (e.g. when the slider button is moved),
wxs_handler is called with the number of the slider, a code
indicating
the type of event, and the value of the slider as x. (The y
parameter
is not used in slider update events.) The wxs_handler function
maps id
to a Serpent object. If the event is a paint request, which is
valid
for some objects, the paint message is sent to the object.
Otherwise,
the parameters are forwarded to the handle method of the object.
Most objects, including Sliders, inherit the handle method from
their superclass, Control. Here is the method:
def handle(obj, event, x, y):
if method:
if target:
send(target, method, obj, event, x, y)
else:
funcall(method, obj, event, x, y)
elif parent:
control_map[parent].handle(obj, event, x, y)
Every Control has two fields used for message handling: method
and target. If method is set, then this method will be
called
to handle the event. If target is set to a Serpent object, then
method
will be sent to this target. If target is not set, then method is
regarded as a global function, and the function is called. If
method is
not set, there is no handler defined for this object, so the event
is
sent to the parent of the object. In the case of our slider,
recall
that the parent is the window containing the slider. This
forward-to-parent mechanism could be used if a parent has many
controls
as children and wants to handle all of them in one place (although
in
practice, it is simple to direct each child object's events to a
designated target.)
OK, so now we're ready to handle some slider events. Try this:
myslider.method = 'print_slider_value'
def print_slider_value(obj, event, x, y):
display "print_slider_value", obj, event, x, y
Notice that this is a global function, not a method. It will be
called because myslider.target is nil (the default). Also notice
that
the handler gets called with 4 parameters even though the y
parameter
will never be used. When you move the slider, you should see text
that
tells you the value.
You now know just about everything you need to know except for
the
details about what objects you can create and how to create
a
timer. These are described in the next section. You should
also
be warned about error-handling. It's not pretty. See below.
Graphical Objects
Graphical objects are subclasses of the Control class. There are
some general methods in Control that most objects can use:
- delete()
- Delete the wxWidgets control this object represents. You
should
not use a Control after calling delete().
- get_left()
- Get the left (x) position in pixels.
- get_top()
- Get the top (y) position in piexels.
- get_width()
- Get the width in pixels.
- get_height():
- Get the height in pixels.
- set_size(width, height)
- Set the size in pixels.
- set_width(w)
- Set the width in pixels.
- set_height(w)
- Set the height in pixels.
- set_position(x, y)
- Set the position in pixels, relative to the upper left of the
parent window.
- set_font(size, family, style, weight, underline, name)
- Set font parameters (for text objects only). Font size is in
points. Font family is from the following constants:
- WXS_FONT_DEFAULT - default font.
- WXS_FONT_DECORATIVE - see wxWidgets documentation.
- WXS_FONT_ROMAN - a roman (serif) font.
- WXS_FONT_SCRIPT - see wxWidgets documentation.
- WXS_FONT_SWISS - a sans serif font.
- WXS_FONT_MODERN - see wxWidgets documentation.
Font style is from the following:
- WXS_FONT_NORMAL - plain.
- WXS_FONT_SLANT - see wxWidgets documentation.
- WXS_FONT_ITALIC - italic.
Font weight is from the following:
- WXS_FONT_NORMAL - normal weight.
- WXS_FONT_LIGHT - see wxWidgets documentation.
- WXS_FONT_BOLD - bold.
The underline parameter is boolean. The name parameter is a
string
containing a font face name (see wxWidgets documentation for the
wxFont
class; the empty string is an acceptable value for the name
parameter).
In addition to these constants, the WXS_FONT_NORMAL is 0. For
example,
to change the font size to 24 (large), you can use object.set_font(24,
0,
0,
0,
nil,
"").
- set_color(color)
- Set the color of an object. Colors are designated by strings,
e.g. "RED". See colors.txt for a list
of
available colors and their interpretation. Also, see colorchart.png for a color chart
(image).
- set_value(x)
- Set the value. The folowing take an integer value: Gauge,
SpinCtrl, Slider, RadioBox, and CheckBox. CheckBox takes 0
(unchecked)
or 1 (checked). The following take a string value: Textctrl,
Choice,
ComboBox, ListBox. In the case of Choice and ListBox, the string
must
match an existing choice or nothing happens.
- set_string(string)
- Set the value as a string (works only with Textctrl,
Multitext,
Statictext, ComboBox, ListBox, and Choice)
- set_and_act(x)
- Works like set_value(), but calls the handler as if the user
had
entered the value through the user interface. Note that
set_value() and
set_string() do not invoke handlers; they merely update the
visible
representation on the screen.
- value()
- Get the value of this control (not actually defined in
Control,
but defined by most subclasses).
-
Here is a list of graphical objects you can create (these are all
subclasses of Control):
- Button(parent, label, x, y, w, h)
- Creates a labeled button. label is a string. The
handler
will be called with event = WXS_BUTTON_CLICKED, x = 0, and y =
0. To
invoke the handler from software, call set_and_act() with no
arguments.
- Choice(parent, x, y, w, h)
- Creates a choice box. Use the append(label) method to add
choices
(strings) and clear() to remove all labels from the list. See
also
set_value(). The handler will be called with event =
WXS_CHOICE_SELECTED, x = the selected label (a string), and y =
0.
- Checkbox(parent, label, x, y, w, h)
- Similar to Button, but this creates a two-state check box. The
handler will be called with event = WXS_CHECKBOX_CLICKED, x =
't' (if
the box is checked) or nil (if the box is not checked), and y =
0.
- Combobox(parent, x, y, w, h)
- Similar to a Choice, but you can type in choices as well as
select them. Use append(label) to add choices and clear() to
remove all
choices. The handler will be called with event =
WXS_COMBOBOX_SELECTED,
x = the selected label (a string), and y = 0 when an item is
selected.
When the user types into the combobox, the handler is called
after each
character is typed or deleted with event = WXS_TEXT_UPDATED, x =
the
current label (a string), and y = 0.
- Listbox(parent, x, y, w, h)
- Creates a list box. Use the append(label) method to add to the
list and clear() to remove all items from the list. The handler
will be
called with event = WXS_LISTBOX_SELECTED, x = the selected label
(a
string), and y = 0.
- Multitext(parent, x, y, w, h)
- Creates a multi-line editable text box. The value() method
returns the text. The handler is called after each character is
typed
or deleted with event = WXS_TEXT_UPDATED, x = 0, and y = 0.
Unlike most
controls, the handler is called in response to set_value() or
set_string() (which are equivalent) if the text changes. A call
to
set_and_act() will always call the handler and may call it
twice. Use
value() to retrieve the text.
- Notebook(parent, x, y, w, h)
- Creates a tabbed set of controls with associated tabs. To add
a
page to the notebook, first create a control to be the page, using
the
Notebook as the parent. If you want multiple controls on a
page, use a Panel as the page and add controls to the Panel.
Next, use add_page(control, label) to
add the control to the notebook. The control
will be resized by the Notebook object. If the actual size is
needed,
call get_width() and get_height() methods on
the
page control after calling add_page(). At present
there are
no events when the user selects a new page, but the Notebook
methods value()
and set_value(i) can be used to get and set the
page
number, which is 0-based.
- Textctrl(parent, text, x, y, w, h)
- Creates a one-line text box. The value() method returns the
text.
The handler works just like Multitext (see above).
- Spinctrl(parent, min, max, initial, x, y, w, h)
- Creates a spin control The handler is called with event =
WXS_SPINCTRL_UPDATED, x = the spin control value, and y = 0.
- Statictext(parent, text, x, y, w, h)
- Creates non-editable text.
- Radiobox(parent, label, items, x, y, w, h)
- Creates a radio box, where label is a text label and items is
an
array of strings to label the buttons. The method value()
returns an integer index (not a string), and set_value()
takes an integer index. The handler is called with event =
WXS_RADIOBOX_SELECTED, x = the integer index of the selected
button,
and y = 0.
- Panel(parent, x, y, w, h)
- Creates a panel, essentially a sub-window with its own local
coordinate system.
- Slider(parent, min, max, initial, x, y, w, h, optional
horizontal
= t)
- Creates a slider. The handler is called with event =
WXS_SLIDER_UPDATED, x = the slider value (an integer), and y =
0. The
default is a horizontal slider, but if you specify false or nil
for the
optional parameter, a vertical slider is created. Sliders can be
resized with set_size(), but at least on OS X, it does not seem
possible to resize smaller than the initial size, so create the
slider
at the smallest size you might need and resize it to the size
you want.
- Gauge(parent, range, x, y, w, h)
- Creates a gauge, used to display values or progress as a
horizontal bar. Use set_value(x) to change the display. Values
of range
and x are integer.
- Menu(parent, label)
- Add a menu with the given (string) label. Use the item(label,
help, checkable) method to add menu items with the given
(string)
label, the associated help string, and the checkable attribute
(nil or
true). Menu items including separators are numbered from zero.
Use the
separator() method to insert a separator. Use the is_checked(n)
method
to see if the nth menu item is checked. The handler is called
with
event = WXS_MENU_SELECTED, x = the index of the selected menu
item, and
y = the checked status: -1 means this is not a checkable item, 0
means
unchecked, and 1 means checked. The set_and_act(n, optional
flag)
method takes two parameters: n is the index of the menu item;
flag is
the check state: true means set the check mark, false means
remove the
check mark, and no parameter means this is not a checkable item.
The
caller should provide the second parameter if and only if the
item is
checkable.
- Image(optional w, h)
- Create an image object to be drawn on a canvas. (See the
draw_image method of the Canvas class below.) Unlike other
classes in
this list, an image does not have a parent window. However, it
does
implement some of the generic methods of Control, including
get_width,
get_height, and delete. You can create an image in two ways: you
can
draw on an image similar to a canvas, or you can load an image
from a
file.
- If you initialize with optional width and height parameterrs a
blank bitmap is created that you can draw on. Drawing works
like
a Canvas: you subclass Image and provide a custom paint(x)
method,
which can call any of the Canvas drawing methods. The paint
method
cannot be invoked directly. Instead, call refresh(t) on an
instance of
your Image subclass. This will immediately call paint (after
setting up
some internal context so that the draw commands will work). Once
painted, there is no need to paint an image again, so images can
be
used to store the result of an expensive drawing, which can then
be
copied to a canvas in one fast draw_image() operation.
- If you do not
initiaize
with optional width and height (or pass in nil), you must call
the
create method to load an image from a file:
- load(filename)
- Dispose of the previous image, if any, and load a new
image
from a file. Returns either WXS_IMAGE_NO_FILE,
WXS_IMAGE_CANNOT_READ or
WXS_IMAGE_SUCCESS. (This method could also return
WXS_IMAGE_NO_ID if
somehow the Image gets an invalid ID number, or
WXS_IMAGE_BAD_ID if
somehow the Image has the ID of a non-bitmap object.) After
loading an
image, you can draw the image on a canvas and query for the
width or
height.
- rescale(width, height, quality)
- Change the size of the image to width and height. The
quality
may be WXS_QUALITY_NORMAL or WXS_QUALITY_HIGH.
- subimage(left, top, width, height)
- Create a new Image object that contains a subarea
specified
by left, top, width, and height. The subarea must be
contained within
image, otherwise WXS_IMAGE_BAD_SIZE is returned. The image
must not
have been drawn to an OpenGL window or canvas (a restriction
in the
current implementation), otherwise WXS_IMAGE_NO_DATA will be
returned.
If the operation is
successful, the new Image object is returned. Currently,
image and
control identifiers are not recycled, so generating many
subimages
(e.g. calling subimage for every redraw) should be avoided.
If you
must, you can delete a control, window, or image using the
delete()
method. This will free up the allocated resources that
implement the
graphics, but not the corresponding Serpent object, so you
should then
be sure to destroy (overwrite) any existing refereces to the
Serpent
object, which will then be automatically garbage collected.
The Serpent
garbage collector does not perform any "finalization"
actions, so
merely destroying all references to the Serpent object
without calling
delete() first will leave all the graphics resources
allocated.
- Canvas(parent, x, y, w, h)
- Glcanvas(parent, x, y, w, h)
- Glwindow(title, x, y, w, h)
- Create a canvas, a drawable region. There are three
variations:
Canvas uses a wxWindows wxCanvas object, which has better
support for
fonts and incremental updates, but otherwise is slower. Glcanvas
is
intended to be a direct replacement for Canvas using a
wxGlCanvas
object, which can be an order of magnitude faster than Canvas.
Glwindow
is like Window (see below), but with an embedded Glcanvas.
- There are many methods, but first a word about how this all
works. To create a Canvas (or Glcanvas or Glwindow), you must
make a
subclass and define the paint(x) method, which may call any of
the
following methods. Except for refresh, the following methods can
only
be called after a paint() method begins and before it ends. The
affected canvas will be the one who's paint method is active. You
cannot
simply call draw_line(0, 0, 100, 100) and expect to see a line
on your canvas. Instead, you must first call
refresh(erase). At
some point in the future, paint(x) will be called, and then you
will
have the opportunity to draw on your canvas.
Each Canvas has a full-size bitmap that contains the full
canvas image. If the canvas is obscured or for some reason has
to be
redrawn, wxserpent will just copy bits from the bitmap.
Drawing methods
actually draw on the bitmap; then the bitmap is copied to the
canvas.
Although copying the entire bitmap to update the canvas can be
a lot of
work, the bitmap copy is performed by optimized machine code,
so the
overhead may not be as great as drawing in the first place. By
keeping
the bitmap around, wxserpent programs can do incremental
updates to the
bitmap, minimizing the amount of Serpent execution. This is
only
possible with a Canvas. Glcanvas and Glwinodow may use a
buffer to
avoid redrawing when the window is uncovered, but any updates
to the
image or resizing require a full redraw.
To perform incremental updates, update the state of your
Canvas
subclass and call refresh(false). The parameter says not to
erase the
canvas. In the paint(x) method (which you define), if x is
false, then
refresh was called with false and paint(x) should perform an
incremental update. On the other hand, if x is true, then
either
refresh(true) was called, the Canvas was resized, or some
other event
occurred that requires a full repaint of the entire canvas. In
this
case, the bitmap will be filled with the background color.
Glcanvas and
Glwindow also use the paint(x) method, but the parameter
should be
ignored and the full image should be drawn by the paint(x)
method.
-
Unlike most graphics functions described earlier, most of the
"draw" functions below can take doubles as parameters. These
are
rounded to integers in the case of Canvas, but Glcanvas and
Glwindow
draw with antialiased lines and polygons, thus it is possible
to use
non-integer coordinates.
- draw_line(x, y, x2, y2)
- draw a line from x,y to x2,y2. All parameters are integer
or
double.
- draw_point(x, y)
- draw a 1-pixel point at x,y. All parameters are integer or
double.
- draw_rectangle(x, y, w, h)
- draw a rectangle. All parameters are integer or double.
- draw_text(x, y, text)
- draw a string. x and y are integer or double coordinates
for
the left of the string baseline.
- draw_ellipse(x, y, w, h)
- draw an ellipse. All parameters are integer or double.
- draw_polygon(points)
- draw a closed polygon. Points are an array: [x0, y0, x1,
y1,
x2, y2, ...]. All values are integer or double.
- draw_image(image, optional x = 0, optional y = 0, optional
xscale = 1, optional yscale = 1, optional angle = 0)
- draw an instance of Image at location (x, y) (integer or
double) with scale factors (xscale, yscale) (integer or
double) and
rotated by angle (integer or double in radians, not degrees)
on the
canvas. Note that you pass in the Serpent Image object, not
the integer
id for the object. Canvas objects ignore scale factors and
angle, but
Glcanvas and Glwindow do not. In the current implementation,
drawing an
image to a Glcanvas or Glwindow converts the image to an
OpenGL
texture, after which other image operations will fail,
including
subimage(), rescale(), and drawing on an ordinary Canvas.
- set_pen_color(color, optional alpha = 255)
- color is a string color name (see set_color() above).
Future
lines, points, etc. are in this color. The alpha value is an
integer or
double from 0 (transparent) to 255 (opaque, the default).
For a Canvas,
only transparent (zero) and opaque (non-zero) are supported.
- set_pen_rgb(r, g, b, optional alpha = 255)
- set the color using integer RGB values from 0 to 255. The
alpha works as in set_pen_color(). All parameters
are integer
or double.
- set_pen_width(w)
- set the pen width for lines, rectangles, etc. w is integer
or
double.
- set_brush_color(color, optional alpha)
- set the color (a string) with which to fill rectangles,
ellipses, etc. See set_color() above. The alpha value is an
integer or
double from 0 (transparent) to 255 (opaque, the default).
For a Canvas,
only transparent (zero) and opaque (non-zero) are supported.
- set_brush_rgb(r, g, b, optional alpha = 255)
- specify the brush color with integer or double RGB values
from 0 to 255 and alpha as described for set_brush_color().
- set_text_color(color, optional alpha = 255)
- set the color (a string) with which to draw text. The
alpha
value is an integer or
double from 0 (transparent) to 255 (opaque, the default).
For a Canvas,
only transparent (zero) and opaque (non-zero) are supported.
- set_text_rgb(r, g, b, optional alpha = 255)
- specify the text color with integer or double RGB values
from 0 to 255 and alpha as described for set_text_color().
- clear(color)
- fill the entire canvas with a solid color
- refresh(erase)
- notify wxWidgets that a change has occurred requiring a
graphical update. If erase is true, the canvas will be
erased first.
- Scrolled(parent, x, y, w, h)
- Create a scrollable panel. The panel is initialized to the
size
of the container. The virtual (contained) panel size can be
changed
using the following methods. Scrollbars are automatically
created when
a dimension of the contained panel exceeds the dimensions of the
container (set when the Scrolled is created and changeable with
set_size).
- set_virtualsize(w, h)
- set the size of the virtual scrollable panel.
- get_virtualwidth()
- get the width of the virtual scrollable panel.
- get_virtualheight()
- get the height of the virtual scrollable panel.
- scroll(x, y)
- Scroll to location x, y
- Scrolled_canvas(parent, x, y, w, h)
- Create a scrollable canvas. The canvas is initialized to the
size
of a containing window. The virtual canvas size can be changed
using
the following methods. Scrollbars are automatically created when
a
dimension of the contained canvas exceeds the dimensions of the
container (set when the Scrolled_canvas is created and
changeable with
set_size). A scrolled canvas is a subclass of Canvas. See Canvas
above
for details about defining a subclass and overriding paint in
order to
draw on the scrollable canvas.
- set_virtualsize(w, h)
- set the size of the virtual scrollable panel.
- get_virtualwidth()
- get the width of the virtual scrollable panel.
- get_virtualheight()
- get the height of the virtual scrollable panel.
- scroll(x, y)
- Scroll to location x, y
- Window(title, x, y, w, h)
- Creates a top-level window with title (a string) as title.
When
the window size changes, the on_size(x, y) method is called.
Subclasses
of Window can override on_size(), e.g. to grow or shrink objects
in the
window when the size changes.
Windows have a couple of special methods:
- show_status(flag)
- Windows can display a status region. If flag is nil (false),
do
not show the status region. If flag is not nil, show the
status region. (The current implementation creates and clears
the status bar regardless of the flag value.)
- set_status(status)
- Display status (a string) in the window status region. You
must
have previously created a status bar using show_status for
this method to have any effect.
- set_fullscreen(flag)
- Set the window to be full screen (or not) depending on flag
Dialog Boxes for Requesting Input
You can request the user to enter a file, a number, or text using
dialog boxes.
- wxs_file_selector(message, path, default, extension, wildcard,
flags, window_id)
- Opens a dialog to browse for a file. The message is a string
that
is displayed in the dialog box to prompt the user for the file.
The
path is the initial path where the dialog box should look for
files,
where the empty string means the current path. The default is
the
default file name, where the empty string means no default file
name.
The extension specifies the file extension (also called the type
extension) for default. The wildcard specifies what files are
displayed
in the file selector (use "*.*" to list all files). See also
::wxFileSelector in wxWidgets documentation for more wildcard
syntax,
which can specify multiple types of file. The flags is a
combination of
WXS_FILE_OPEN, WXS_FILE_SAVE, WXS_FILE_OVERWRITE_PROMPT,
WXS_FILE_HIDE_READONLY, or WXS_FILE_MUST_EXIST. The window_id is
the id for the window in which the dialog is opened.
- wxs_get_number(message, prompt, caption, number, min, max,
window_id)
- Opens a dialog to get an integer. The message is a possible
multi-line string to be displayed above a single line prompt
string.
The dialog window title is specified by the string caption. The
number
is the default value, and the number entered by the user must be
in the
range min to max, both of which should be positive. The dialog
is
centered on the window given by window_id. -1 is returned if the
user
enters an invalid value or cancels.
- wxs_get_text(message, caption, default, window_id)
- Opens a dialog to get text from the user. The dialog box
contains
message, a string, and the dialog window title is specified by
caption,
a string. The default return value is given by default, a
string, and
the dialog box is opened at the center of the window given by
window_id. If the user presses Cancel, the empty string is
returned.
- wxs_message_box(message, caption, style, window_id)
- Opens a dialog box to display message, a string, in a window
with
the title specified by caption, a string. The style is a
combination
(logical or) of WXS_STYLE_YES_NOW, WXS_STYLE_CANCEL,
WXS_STYLE_OK,
WXS_STYLE_EXCLAMATION, WXS_STYLE_ERROR, WXS_STYLE_QUESTION, or
WXS_STYLE_INFORMATION. The return value is one of the following:
WXS_MSG_YES, WXS_MSG_NO, WXS_MSG_CANCEL, WXS_MSG_OK.
In wxserpent, the graphic interface does not really
become operational until you finish loading the initial program.
Recall
that the default startup action is to load init.srp
which may
load other files. After loading, control is passed to the
wxWindows
library. Depending on how graphical objects were created and
initialized, various functions and methods will be called in
response
to user actions. If you want other processing to take place, your
only
option is to have wxWindows call a function periodically. This can
be
started by calling wxs_timer_start, as described below.
- wxs_timer_start(interval, function)
- Starts calling the function named by the function parameter, a
symbol, every interval ticks. Under Windows (at least), a tick
is 1/100
seconds. The function should be defined and should take no
parameters.
Error Handling and Debugging
Error handling in Serpent is generally handled by a simple
debugger
that displays some information and prompts the user for a command.
Enable debugging by putting the statement
require "debug"
early in your program.
In wxserpent, we do not have a command line and output is
directed
to a window, so when the debugger waits for input, what happens?
Blocking input in wxserpent is implemented by calling a pop-up
dialog
box to retrieve type-in. This usually puts a dialog box right in
front
of your application, which gets in your way, and if you are in the
debugger, typing something to the dialog box will only encourage
it to
ask you another question.
To make matters worse, printing is disabled during a paint event
(see
description of Canvas) so you will not see any printed information
while drawing on a
canvas object (except under Linux, where stdout is echoed to the
shell
window). To help with this situation, call
log_enable("application.log")
early in your program execution to open a log file. All output to
stdout is
then directed to a file with the given name. (Suggestion: name the
file
after
your application with a ".log" extension.) While your program is
running, you
can print the log file to the shell window (under OS X) using the
following:
tail -f application.log
This will give the effect of program output being printed directly
to
the
shell window even though it is indirected through the file. You may
notice
that the log file contains all text directed to stdout, whereas the
text
window opened by the application contains only text printed while
not
painting a canvas. When text is not printed, a warning will
eventually
appear to indicate text output was skipped.
Aside from a few debugging commands that might be useful, you
basically have two choices when using the debugger with wxserpent:
- Type ">" which tells the debugger to exit -- since Serpent
is
running in order to handle something for wxWidgets, control will
return
to wxWindows. It's possible that you can resume normal
execution, but
chances are good that touching another control will tickle the
same bug
and raise the same error.
- Exit the program, fix the bug, and try again. The "!" debugger
command should exit the program.
OpenGL Classes
If wxserpent is compiled with OpenGL functions (see installation
notes), the following classes are available:
- Glwindow(title, x, y, w, h)
- Creates a window that can be drawn upon with OpenGL. A
Glwindow
is a
subclass of Canvas. All of the Canvas drawing methods are
implemented
by
Glwindow, and alpha parameters are used for transparency. Use
refresh
and paint exactly as for a Canvas. At present, 3D methods and
other
OpenGL
commands are not supported.
- Glcanvas(parent, x, y, w, h)
- Works just like Canvas except with OpenGL capabilities.