# The Anygui Manual

Magnus Lie Hetland
August 2002

## Introduction

This is the development version of the documentation and should not be relied on until release 0.2a0. The documentation for each release is found in the distribution itself.

The Python standard library currently does not contain any platform-independent GUI packages. It is the goal of the Anygui project to change this situation. There are many such packages available, but none has been defined as standard, so when writing GUI programs for Python, you cannot assume that your user has the right package installed.

The problem is that declaring a GUI package as standard would be quite controversial. There are some packages that are quite commonly available, such as Tkinter; but it would not be practical to require all installations to include it, nor would it be desirable to require all Python GUI programs to use it, since there are many programmers who prefer other packages.

Anygui tries to solve this problem in a manner similar to the standard anydbm package. There is no need to choose one package at the expense of all others. Instead, Anygui gives generic access to several popular packages through a simple API, which makes it possible to write GUI applications that work with all these packages. Thus, one gets a platform-independent GUI module which is written entirely in Python.

To get the latest Anygui distribution, or to get in touch with the developers, please visit the Anygui website, at `anygui.org`.

Although the Anygui API is stabilising, it is not yet completely frozen.

### Design Goals

- Anygui should be an easy to use GUI package that may be used to create simple graphical programs, or that may serve as the basis for more complex application frameworks.
- Anygui should be a pure Python package that serves as a front-end for as many as possible of the GUI packages available for Python, in a transparent manner.
- Anygui should include functionality needed to perform most GUI tasks, but should remain as simple and basic as possible.

### Structure of this Manual

This manual first gives you some short instructions on installing Anygui. It then gives a thorough description of how to use Anygui to build GUI applications in Python. Following the tutorial is a description of how Anygui functions internally, and after that comes an API reference, which describes all classes and functions available in the `anygui` package.

## Installation

The Anygui package comes in the form of a `gzip` compressed `tar` archive, a binary Windows installer and as an RPM package. The Windows installer and RPM package should be fairly easy to install. To install the `tar` archive, it you will first have to uncompress the archive. On Windows this can be done with WinZip. in Mac OS, you can use StuffIt Expander. In Unix, first move to a directory where you'd like to put Anygui, and then do something like the following:

    foo:~/python$ tar xzvf anygui-0.2.tar.gz

If your version of tar doesn't support the `z` switch, you can do something like this:

    foo:~/python$ zcat anygui-0.2.tar.gz | tar xvf

Another possibility is:

    foo:~/python$ gunzip anygui-0.2.tar.gz
    foo:~/python$ tar -xvf anygui-0.2.tar

No matter which version you choose, you should end up with a directory named `anygui-0.2`.

### Running setup.py

The simple way of installing Anygui is to use the installation script that's included in the distribution. This requires Distutils (`http://www.python.org/sigs/distutils-sig`), which is included in Python distributions from version 2.0. To install the Anygui package in the default location, simply run the setup script with the `install` command:

    foo:~$ python setup.py install

This will install Anygui in your standard Python directory structure. If you don't have access to this directory (e.g. because Python was installed by a sysadmin, and you don't have root access) you can install it somewhere else with the `--prefix` option:

    foo:~$ python setup.py install --prefix=${HOME}/python

### Doing it Manually

Since Anygui consists of only Python code, nothing needs to be compiled. And the only thing needed to install Python code is to ensure that the packages and modules are found by your Python interpreter. This is as simple as including the `lib` directory of the Anygui distribution in your `PYTHONPATH` environment variable. In `bash` (`http://www.gnu.org/manual/bash/`), you could do something like this:

    foo:~$ export PYTHONPATH=$PYTHONPATH:/path/to/anygui/lib

To make this permanent, you should put it in your `.bash_profile` file, or something equivalent. If you don't want to mess around with this, and already have a standard directory where you place your Python modules, you can simply copy (or move) the `anygui` package (found in `anygui-0.2/lib`) there, or possibly place a symlink in that directory to the `anygui` package.

### Making Sure You Have a Usable GUI Toolkit

Once you have Anygui installed, you'll want to make sure you have a usable GUI toolkit. This is easy to check: Simply start an interactive Python interpreter and try to execute the following:

    >>> from anygui import *
    >>> backend()

The `backend` function will return the name of the backend in use. If it is neither `'curses'` nor `'text'` you should be all set for making GUI programs with Anygui. (The `'curses'` and `'text'` backends use plain text to emulate graphical interfaces on platforms that don't have them.) Anygui currently supports the following packages:

    PythonWin  (mswgui)    http://starship.python.net/crew/mhammond/win32
    Tkinter    (tkgui)     http://www.python.org/topics/tkinter
    wxPython   (wxgui)     http://www.wxpython.org
    Java Swing (javagui)   http://www.jython.org
    PyGTK      (gtkgui)    http://www.daa.com.au/~james/pygtk
    Bethon     (beosgui)   http://www.bebits.com/app/1564
    PyQt       (qtgui)     http://www.thekompany.com/projects/pykde
    Curses     (cursesgui) -- used when no GUI package is available
    Plain text (textgui)   -- used if curses is not available

Add `gui` to name returned by the `backend` function to get the full name of the backend module (in the `anygui.backends` package). For instance, the `msw` backend is found in `anygui.backends.mswgui` module.

In general, if you end up with a text-based solution, `cursesgui` will be preferred over `textgui`if your Python-installation has a wrorking `curses` module. The exception is if you are using Anygui in the interactive interpreter, in which `textgui` will be preferred, to avoid interfering with the terminal and locking up the interpreter prompt. (If you'd like to, for some reason, you can override this behaviour with the environment variable `ANYGUI_FORCE_CURSES`; see the API Reference later in this document.)

The BeOS backend (`beosgui`) is currently not fully functional, but is included nonetheless.

Of these, Tkinter is compiled in by default in the MS Windows distribution of Python (available from `http://www.python.org`), PythonWin (as well as Tkinter) is included in the ActiveState distribution, ActivePython (available from `http://www.activestate.com`), and Java Swing is automatically available in Jython, the Java implementation of Python.

In Mac OS 9, Anygui (using Tkinter) works with with Python Classic and recent versions of Python Carbon, but older versions have problems with Tkinter.

## Using Anygui

Using Anygui is simple; it's simply a matter of importing the classes and functions you need from the `anygui` module, e.g.:

    from anygui import *

After doing this you must create an `Application` object, at least one `Window`, and probably a few components such as `Button`s and `TextField`s. The `Window`s are added to the `Application` (through its `add` method), and the various components are added to the `Window`. When you have done this, you call the `run` method of your `Application` instance.

    # Make components here
    win = Window()
    # Add components to the Window
    app = Application()
    app.add(win)
    app.run()

For some examples of working Anygui code, see the `test` and `demo` directories of the distribution. Remember that the test scripts are written to test certain features of Anygui, not to represent recommended coding practices.

### Avoiding Namespace Pollution

Importing everything from Anygui (as in `from anygui import *`) is fine for small programs, where you're certain that there will be no name clashes. You may also simply import the names you need:

    from anygui import Application, Window

The preferred way to use modules like this is usually to avoid cluttering your namespace, by using simply `import anygui`. However, if you are going to create a lot of widgets, the `anygui` prefix may be cumbersome. Therefore, I suggest renaming it to `gui`, either with a simple assignment...

    import anygui; gui = anygui

... or, in recent versions of Python:

    import anygui as gui

Then you can instantiate widgets like this:

    win = gui.Window()

The examples in this documentation use the starred import, for simplicity.

### Importing the Backends Directly

If you wish to import a backend directly (and "hardwire it" into your program), you may do so. For instance, if you wanted to use the wxPython backend, `wxgui`, you'd replace

    from anygui import *

with

    from anygui.backends.wxgui import *

Note that the namespace handling still works just fine:

    import anygui.backends.tkgui as gui

### Creating a Window

One of the most important classes in Anygui is `Window`. Without a `Window` you have no GUI; all the other widgets are added to `Window`s. Knowing this, we may suspect that the following is a minimal Anygui program (and we would be right):

    from anygui import *
    app = Application()
    win = Window()
    app.add(win)
    app.run()

This example gives us a rather uninteresting default window. You may customise it by setting some of its properties, like `title` and `size`:

    w = Window()
    w.title = 'Hello, world!'
    w.size = (200, 100)

If we want to, we can supply the widget properties as keyword arguments to the constructor:

    w = Window(title='Hello, world!', size=(200,100))

### The set Method and the Options Class

If you want to change some attributes of a widget, you can either just set them directly, or (if you'd like to set several at once), use the `set` method, just like the constructor:

    w.set(title='Hello, again', size=(300,200))

Supplying the same attributes with the same values to a lot of widgets (if you are making several buttons with the same size, for instance) can be a bit impractical (you'll learn more about buttons in a little while):

    bt1 = Button(left=10, width=50, height=30, text='Button 1')
    bt2 = Button(left=10, width=50, height=30, text='Button 2')
    bt3 = Button(left=10, width=50, height=30, text='Button 3')

To deal with this, the widget constructors (and the `set` method) can take `Options` objects as positional parameters:

    opt = Options(left=10, width=50, height=30)
    bt1 = Button(opt, text='Button 1')
    bt2 = Button(opt, text='Button 2')
    bt3 = Button(opt, text='Button 3')

As you can see, this saves quite a lot of typing. You can use as many `Options` arguments as you like.

### The modify Method

Just like `set` can be used to set the attributes of a `Component`, the `modify` method can be used to modify them, without rebinding them to another value. To show the difference, consider the following example (where `foo` is an attribute that does nothing special):

    >>> from anygui import *
    >>> btn = Button()
    >>> some_list = [1, 2, 3]
    >>> btn.foo = some_list
    >>> btn.modify(foo=[4, 5, 6])
    >>> btn.foo
    [4, 5, 6]
    >>> some_list
    [4, 5, 6]
    >>> btn.set(foo=[7, 8, 9])
    >>> btn.foo
    [7, 8, 9]
    >>> some_list
    [4, 5, 6]

As you can see, using `modify` modifies the list, while `set` replaces it. The `modify` method is used for (among other things) implementing Model-View-Controller systems. (More about that later.)

The `modify` method works as follows: If there is a specific internal method for modifying an attribute, that is called. Otherwise, the supplied value will be assigned to `self.name[:]` (where `name` is the attribute in question). If that doesn't work (a `TypeError` exception is raised), the value will be assigned to `self.name.value`. If that doesn't work either, the attribute will be rebound to the new value, with the same result as using `set`. So, to avoid any in-place modification, all you need to do is use immutable values:

    >>> from anygui import *
    >>> btn = Button()
    >>> some_list = [1, 2, 3]
    >>> btn.foo = tuple(some_list)
    >>> btn.modify(foo=[4, 5, 6])
    >>> btn.foo
    [4, 5, 6]
    >>> some_list
    [1, 2, 3]

### The sync Method

The `modify` method is used to modify attributes in-place, e.g. to keep them in sync with a widget. This is done automatically when you change a widget through the graphical interface. In a way, the `sync` method works the other way: If you modify an attribute, you can call the `sync` method to keep the widget's appearance in sync with its state. When you assign to an attribute, `sync` is called automatically; you only have to call it yourself if you have an attribute which is a mutable object, and you modify that object.

For more info about the use of `sync`, see the section "About Models, Views, and Controllers", below.

### Adding a Label

Simple labels are created with the `Label` class:

    lab = Label(text='Hello, again!', position=(10,10))

Here we have specified a position just for fun; we don't really have to. If we add the label to our window, we'll see that it's placed with its left topmost corner at the point `(10,10)`:

    w.add(lab)

### Layout: Placing Widgets in a Frame

This section goves a simple example of positioning `Components`; for more information about the Anygui layout mechanism, please refer to the API Reference (below).

    win.add(lab, position=(10,10))
    win.add(lab, left=10, top=10)
    win.add(lab, top=10, right=10)
    win.add(lab, position=(10,10), right=10, hstretch=1)

In the last example `hstretch` is a Boolean value indicating whether the widget should be stretched horizontally (to maintain the other specifications) when the containing `Frame` is resized. (The vertical version is `vstretch`.)

Just like in component constructors, you can use `Options` objects in the `add` method, after the component to be added:

    win.add(lab, opt, left=10)

#### Placing More Than One Widget

The `add` method can also position a sequence of widgets. The first widget will be placed as before, while the subsequent ones will be placed either to the `right`, to the `left`, above (`up`), or below (`down`), according to the `direction` argument, at a given distance (`space`):

    win.add((lab1, lab2), position=(10,10),
            direction='right', space=10)

Remember to enclose your components in a sequence (such as a tuple or a list), since `add` allows you to use more positional arguments, but will treat them differently. If you want to use `Options` objects, place them outside (after) the sequence. For more information see the section about the `Frame` class in the API Reference below.

### Buttons and Event Handling

`Buttons` (as most components) work more or less the same way as `Labels`. You can set their `size`, their `position`, their `text`, etc. and then `add` them to a `Frame` (such as a `Window`). The thing that makes them interesting is that they emit events. Each time the user clicks a button, it sends out a `click` event. You can catch these events by linking your button to one or more event handlers. It's really simple:

    btn = Button(text='Greet Environment')
    def greeting(event):
        print 'Hello, World!'
    link(btn, greeting)

The event handling is taken care of by the call to `link`. An event handler receives a single `Event` object as its parameter. (For information about these, see the API Reference, below.)

### About Models, Views, and Controllers

This section is currently inconsistent with the way things work in version 0.2. Will be rewritten shortly.

The Anygui MVC mechanism (based on the `sync` method and the `Assignee` protocol) is described in the API Reference below. Here is a short overview on how to use it.

A model is an object that can be modified, and that can notify other objects, called `views`, when it has been modified. A controller is an object that can modify the model, in particular as a direct response to a user action (such as clicking the mouse or typing some text). In Anygui, `Component`s double as both views (showing a model's state to the user) and controllers (letting the user modify the model). Even though Anygui supports using models this way, you can also create complete applications without using them (or, at least, without thinking about them).

Models are in general instances of some subclass of the `Model` class, although they don't have to be; see the API Reference below for a description of how they work. (The `Model` class is currently internal to the `Anygui` package, but it can be found in the `anygui.Models` module.) The `Model`s that are included in Anygui are:

    BooleanModel     -- represents a Boolean value
    ListModel        -- behaves like a list
    NumberModel      -- represents a numerical value
    TextModel        -- acts like a mutable string

These all have a `value` attribute which may be used to change their internal value. They also support other operations, such as indexing and slicing etc. for `ListModel`. These are very easy to use: Just assign them to an attribute of a `Component`:

    # You'll learn about CheckBoxes in a minute
    cbx = CheckBox(text='Simple model test')
    state = BooleanModel(value=1)
    cbx.on = state

Now, if you change `state` (e.g. with the statement `state.value=0`) this will automatically be reflected in the `CheckBox` (which will be acting like a view). If the user clicks the `CheckBox`, the model will be changed.

To keep a view up-to-date manually you can call its `sync` method. This can be useful if you use a simple (non-`Model`) mutable value such as a list in an attribute:

    btn = Button()
    rect = [0, 0, 10, 10]
    btn.geometry = rect
    rect[3] = 20
    btn.sync('geometry')

After modifying `rect`, the button will not have changed, since it can't detect the change by itself. (That's only possible when you use a real model.) Therefore, you call `btn.sync` to tell it to update itself.

When modifying either `x`, `y`, `width`, `height`, `position`, `size`, or `geometry` in place like this, you should supply the name of the attribute as an argument to `sync`, to avoid confusing Anygui. That is because these attributes mirror each other in various ways, and if you don't supply a name, Anygui won't know which one you modified.

If you assign a value to an attribute, the `sync` method will be called automatically, so another way of doing the same thing is:

    btn = Button()
    rect = [0, 0, 10, 10]
    btn.geometry = rect
    rect[3] = 20
    btn.geometry = rect

Because of the controller behaviour of `Component`s, if the `Button` is resized, `rect` will be modified. If you don't want this behaviour, use a tuple instead of a list, since tuples can't be modified.

If you want another object to monitor a `Model`, you can simply use the `link` method, since all `Model`s generate an event (of the type `default`) when they are modified.

Example:

    >>> from anygui import *
    >>> mdl = BooleanModel()
    >>> mdl.value = 1
    >>> def model_changed(event):
    >>>     print 'The model has changed!'

    >>> link(mdl, model_changed)
    >>> mdl.value = 0
    The model has changed
    >>> mdl.value = 0
    The model has changed

Note the last two lines: We haven't really changed the model, but the event handler is called nonetheless. If you want to know whether the model really changed, you must retain a copy of its state, and compare it with the new value.

### Using CheckBoxes

A `CheckBox` is a toggle button, a button which can be in one of two states, "on" or "off". Except for that, it works more or less like any other button in that you can place it, set its text, and link an event handler to it.

Whether a `CheckBox` is currently on or off is indicated by its `on` attribute.

### RadioButtons and RadioGroups

`RadioButton`s are toggle buttons, just like `CheckBox`es. The main differences are that they look slightly different, and that they should belong to a `RadioGroup`.

A `RadioGroup` is a set of `RadioButton`s where only one `RadioButton` is permitted to be "on" at one time. Thus, when one of the buttons in the group is turned on, the others are automatically turned off. This can be useful for selecting among different alternatives.

`RadioButton`s are added to a `RadioGroup` by setting their `group` property:

    radiobutton.group = radiogroup

This may also be done when constructing the button:

    grp = RadioGroup()
    rbn = RadioButton(group=grp)

The behaviour of a `RadioButton` when it does not belong to a `RadioGroup` is not defined by the Anygui API, and may vary between backends. Basically, a `RadioButton` without a `RadioGroup` is meaningless; use a `CheckBox` instead.

`RadioGroups` also support an `add` method, as all other Anygui container-like objects:

`add`(button)

Adds the button to the group, including setting button`.group` to the group. As with the other `add` methods, the argument may be either a single object, or a sequence of objects.

### ListBox

A `ListBox` is a vertical list of items that can be selected, either by clicking on them, or by moving the selection up and down with the arrow keys. (For the arrow keys to work, you must make sure that the `ListBox` has keyboard focus. In some backends this requires using the `tab` key.)

Note: When using Anygui with Tkinter, using the arrow keys won't change the selection, only which item is underlined. You'll have to use the arrow keys until the item you want to select is underlined; then select it by pressing the space bar.

A `ListBox`'s items are stored in its attribute `items`, a sequence of arbitrary objects. The text displayed in the widget will be the result of applying the built-in Python function `str` to each object.

    lbx = ListBox()
    lbx.items = 'This is a test'.split()

The currently selected item can be queried or set through the `selection` property (an integer index, counting from zero). Also, when an item is selected, a `select` event is generated, which is the default event type for a `ListBox`. This means that you can either do

    link(lbx, 'select', handler)

or

    link(lbx, handler)

with the same result. (This is similar to the `click` event, which is default for `Buttons`; for more information, see the API Reference below.)

### TextField and TextArea

Anygui's two text widgets, `TextField` and `TextArea` are quite similar. The difference between them is that `TextField` permits neither newlines or tab characters to be typed, while `TextArea` does. Typing a tab in a `TextField` will simply move the focus to another widget, while pressing the enter key will send an `enterkey` event (which is the `TextField`'s default event type).

The text in a text component is stored in its `text` property (a string or equivalent), and the current selection is stored in its `selection` property (a tuple of two integer indices).

### Making Your Own Components and LayoutManagers

Currently, you can create your own components by combining others in `Frame`s, and wrapping the whole thing up as a class. One of the main reasons for doing this would be to emulate a feature (such as a tabbed pane) available in some backends, but not in others. One could then actually use the native version in the backends where it is available (such as `wx`, in this case), and use the "emulation" in the others. There is some limited support for this in the `backend` function (which will allow you to check whether you are currently using the correct backend), but in the future, a more complete API will be developed for this, allowing you access to the coolest features of your favorite GUI package, while staying "package independent".

You can already create your own layout managers, by properly supporting the methods `add`, `remove`, and `resized`. The simplest way of doing this is to subclass `LayoutManager`, which gives you the `add` and `remove` methods for free. You can then concentrate on the method `resized` which takes two positional arguments, `dw`, and `dh` (change in width and change in height) and is responsible for changing the geometries of all the components in the `Frame` the `LayoutManager` is managing. (This frame is available through the attribute `self.container`.)

To get more control over things, you should probably also override the two internal methods `internalAdd` and `internalRemove`:

`internalAdd`(self, *items, **kws)

Should add all the components in items, and associate them with the options in `kws`, for later resizing.

`internalRemove`(self, item)

Should remove the given item.

## Anygui Internals

In this section I will give a broad overview of how Anygui works behind the scenes. This information will be useful if you're trying to understand the source code, or using the library in advanced ways. If you are an ordinary user, simply using Anygui to create relatively simple GUI applications, this section may not be for you.

### The Big Picture

The Anygui architecture is based on a division between the front-end, various back-ends, and, for each back-end, a native toolkit. The front-end is the part of Anygui you use in your GUI programs. It is designed to appear as a simple GUI toolkit, such as Tkinter or wxPython. The truth is, however, that the front-end does not implement any GUI functionality itself. It delegates all requests for such functionality to the back-end that is currently in use.

There are several back-ends to choose from. When Anygui is imported, an attempt will be made to import each of these back-ends in turn, in a given order (as described in the API reference later in this document). Once a back-end is imported without raising any exceptions (indicating that it can be used in the current Python installation), that back-end will be used.

Anygui is mainly a library of components, but for each component there are actually three objects at work: The proxy is the front-end object that is instantiated and manipulated by the application programmer, the wrapper is a back-end object that gives the proxy access to the currently used GUI toolkit through a simple interface, and the widget is the object that the wrapper is wrapper, an object from the GUI toolkit itself.

As an example, consider what happens when a `Button` object is created with the `javagui` back-end in use. First, the application instantiates the `Button` class, which is actually only a proxy object. This proxy will then (automatically) make sure that it has a `ButtonWrapper` from `javagui` available when needed (that is, when the `Button` has been added to a `Window` and the application is runing). This `ButtonWrapper` will, in turn, manage a `JButton` object from the `java.swing` package. This native widget is completely hidden from the proxy, which need not know which back-end is in use. The application, of course, only interacts with the proxy object, and has no idea the back-end even exists.

### Pushing and Pulling State Variables

Each proxy object has a state dictionary, which is stored in the attribute `state`. Communication between the proxies and the wrappers is done by using the `push`, which sends information from this dictionary from the proxy to the wrapper, and the `pull` method, which fetches information from the wrapper and updates the state dictionary.

For instance, to update the width of a button, you might do the following:

    btn.state['width'] = 100
    btn.push('width')

In this example, the first line updates the button's state dictionary. That in itself will have no effect on the back-end wrapper, because it has not been notified of the change. The next line calls the `push` method. This is what is needed to notify the wrapper (which can, in turn, tell the native widget what to do). In this example, the name of the state variable (`'width'`) is supplied. That is only an optimisation, which tells the back-end wrapper which state variables it must update. If no such names are supplied, all state variables are pushed through to the native widget. The `push` method of the proxies simply construct a state dictionary with the given state variables from the proxy's own state variables, and calls the `push` method of the wrapper, with the state dictionary as the only argument. (This method is described in the section on wrappers, later in this document.)

Luckily, the application programmer need not use this pushing mechanism directly. It is automated through the use of proxy attributes. When you assign a value to a proxy attribute (that does not begin with an underscode, and that is not called `state`) it will be stored in the state dictionary, and automatically pushed through to the back-end. So, for example, to change the width of a button, you need perform one assignment, as shown in the following example:

    btn.width = 100

This assignment is equivalent to the two statements used earlier in this section.

The pushing mechanism is used to make the state of the GUI reflect changes made to the proxy objects. However, as the user manipulates the GUI, it may change directly, and thus foul up this synchronisation between front-end and back-end. To correct this, the `pull` method is used.

Again, both the proxies and the wrappers have `pull` methods. And, again, the proxy version takes a number of attribute names (possibly none) that are to be pulled out of the back-end, while the wrapper version takes a state dictionary. Unlike `Wrapper.push`, however, the `Wrapper.pull` method modifies the supplied state dictionary. It modifies only the state variables that are already there — it does not add new keys. When you supply attribute names to `Proxy.pull` you restrict which keys are used in the state dictionary supplied to `Wrapper.pull`, and thus you restrict which parts of the proxy that are updated. This is (as with `push`) only an optimisation; calling `pull` with no arguments is always legal.

So, if a user resizes a window, and you want to retrieve the correct width of a button, you can use the following two statements:

    btn.pull('width')
    width = btn.state['width']

As with the push mechanism, this has been automated when you access state variables through proxy attributes, so the previous two lines of code are equivalent to the following:

    width = btn.width

Alias attributes implicitly call `push` and `pull` because they access proxy attributes behind the scenes.

### Widgets and Wrappers

### Proxies

### The Model-View-Controller Architecture in Anygui

#### In-Place Modification

#### The Trouble with Dumb Models

#### Models and the Assignee Protocol

### Two Examples

#### Implementing a ButtonWrapper for Java Swing

#### Implementing a Simple Model

### Writing Your Own Back-End

## API Reference

The following reference will describe the full official API of the current version (0.2) of Anygui but is currently under development.

### Environment Variables

Note:The environment variables may be renamed, and their use may be deprecated. Use the `setup` function instead.

Some environment variables affect the behaviour of the Anygui package. These must be set in the environment of the program using Anygui. They may either be set permanently through normal operating system channels (check your OS documentation for this), or possibly just set temporarily when running your program. In Unix shells like bash, you can set the variables on the command line before your comand, like this:

    foo:~$ ANYGUI_SOMEVAR='some value' python someprogram.py

where `ANYGUI_SOMEVAR` is some environment variable used by Anygui.

Since Jython doesn't support OS environment variables, you'll have to supply them with the command-line switch `-D`:

    foo:~$ jython -DANYGUI_SOMEVAR='some value' someprogram.py

You can also set these environment variables in your own program, by using code like the following before you import Anygui:

    import os
    os.environ['ANYGUI_SOMEVAR'] = 'some value'

This will probably not work well in Jython, though.

The environment variables used by Anygui are:

`ANYGUI_WISHLIST`: A whitespace separated list of backend names in the order you wish for Anygui to try to use them. The backends are identified with a short prefix such as `wx` for `wxgui`, or `tk` for `tkgui`. For a full list of available backends, see the section "Making Sure You Have a GUI Backend" above. Only the backends in this list will be tried; if you don't set `ANYGUI_WISHLIST`, then the following is the default:

    'msw gtk java wx tk beos qt curses text'

If you insert an asterisk in the wishlist, it will be interpreted as "the rest of the backends, in default order". So, for instance,

    ANYGUI_WISHLIST='tk wx * text curses'

is equivalent to

    ANYGUI_WISHLIST='tk wx msq gtk java beos qt text curses'

Example:

    foo:~$ ANYGUI_WISHLIST='tk wx qt' python someprogram.py

`ANYGUI_DEBUG`: When Anygui tries to import a backend, it hides all exceptions, assuming they are caused by the fact that a given backend doesn't work in your installation (because you don't have it installed or something similar). However, at times this may not be the reason; it may simple be that a given backend contains a bug. To track down the bug, set the `ANYGUI_DEBUG` to some true (in a Python sense) value. (If the value supplied can be converted to an integer, it will. Otherwise, it will be treated as a string.) This will make Anygui print out the stack traces from each backend it tries to import.

There is one exception to this rule: If the true value supplied is the name of one of the backends (such as `tk` or `curses`) only the traceback caused by importing that backend will be shown. This can be useful to make the output somewhat less verbose.

Example:

    foo:~$ ANYGUI_DEBUG=1 python someprogram.py

`ANYGUI_ALTERNATE_BORDER`: This Boolean variable affects `cursesgui`, making it use the same border-drawing characters as `textgui` (`'+'`, `'-'`, and `'|'`). This may be useful if your terminal can't show the special `curses` box-drawing characters properly.

`ANYGUI_SCREENSIZE`: Affects `textgui`. Gives the terminal ("screen") dimensions, in characters. This should be in the format width `x` height, e.g. `80x24`. If this environment variable is not supplied, the standard Unix variables `COLUMNS` and `LINES` will be used. If neither is provided, the default size `80x23` will be used.

`ANYGUI_FORCE_CURSES`: Normally, `cursesgui` will not be selected if you are in the interactive interpreter. If you want to force the normal selection order (trying to use `cursesgui` before resorting to `textgui`) you can set this variable to a true value. Note that this is not the same as setting `ANYGUI_WISHLIST` to `'curses'`, since that will ignore all other backends.

`ANYGUI_CURSES_NOHELP`: If you don't want the help-screen that appears when an Anygui application is started using `cursesgui` (or `textgui`), you can set this variable to a true value.

### Global Functions

`application`()

Returns the current `Application` object.

`backend`()

Returns the name (as used in `ANYGUI_WISHLIST`) of the backend currently in use.

Example:

    if backend() == 'wx':
        some_wx_code()
    else:
        some_generic_code()

`link`(source, [event,] handler, weak=0, loop=0)

Creates a link in the Anygui event system, between the `source` (any object) and the `handler` (any callable, or a `(obj,func)` pair, where `func` is an unbound method or function, and `obj` is an object which will be supplied as the first parameter to `func`). Supplying an `event` (a string) will make the link carry only information about events of that type. If no event is supplied, `'default'` will be assumed. Setting `weak` to a true value will use weak references when setting up the link, so that no objects will be "kept alive" by the link.

A send-loop occurs if an object sends an event "to itself" (i.e. it is the `source` argument of a call to `send` which hasn't returned at the point where one of its methods are about to be activated as a handler). The truth value `loop` decides whether this handler will be activated in such a loop. (If `send` was called with `loop=1`, loops will be allowed anyway.)

Note that `source`, `event`, and `handler` are strictly positional parameters, while the others (`weak`, and `loop`) must be supplied as keyword parameters.

Sometimes one might want an event handler that reacts to a specific event from any source, or any event from a specific source; or even any event from any source. To do that, simply use the special value `any` as either source, event, or both.

Example:

    from anygui import *
    >>> def monitor_events(event):
    ...     print 'An event occurred:', event.event
    ...
    >>> link(any, any, monitor_events)
    >>> btn = Button()
    >>> send(btn, 'foobar')
    An event occurred: foobar

If you use `send(btn, 'click')` in this example, you will get two events, since the `Button` will detect the `click` event (which is its default), and issue a `default` event as well.

You need to explicitly supply the event type if you want to respond to any event type; otherwise you will only respond to the `default` type.

Event handlers that react to the same event will be called in the order they were registered (with `link`), subject to the following: (1) All handlers registered with a specific source will be called before handlers with the value `any` as source; (2) all handlers registered with a specific event (including `default`) are called before handlers with the value `any` as event.

For more information on sending events, see `send`, below.

`send`(source, event=`'default'`, loop=0, **kwds)

When this is called, any handlers (callables) linked to the source, but which will not cause a send-loop (unless `loop` is true) will be called with an `Event` object which stores all the keyword arguments provided (except `loop`), in the order in which they were linked. In addition to the supplied keyword arguments, the event framework will add `source`, `event`, and the time (as measured by the standard Python function `time.time`) when `send` was called, supplied with the `time` argument.

The `Event` object is simly a "bunch" object, with each keyword argument stored as an attribute.

Note that `source`, and `event`, are strictly positional parameters, while the others (`loop`, and any additional arguments the user might add) must be supplied as keyword parameters.

Example:

    # Link an event handler to a button, and then manually send a
    # default event from the button. This event would have been
    # sent automatically if we clicked the button.

    def click(event):
        source = event.source
        time = event.time
        print 'Button %s clicked at %f.' % (source.text, event.time)

    btn = Button(text='Click me')
    link(btn, click)

    send(btn) # Fake a button click -- will call click()

For information about the order in which event handlers are called, see `link`, above.

Important: Due to the current semantics of the `any` value, using it in `send` may not be a good idea, since the result might not be what you expect. For instance, calling `send(any, any)` will only activate event handlers which have been linked to the value `any` as both source and event, not to "event handlers with any source and any event". This may change in future releases. The current behaviour of `send` with `any` is consistent with `unlink`.

`unlink`(source, [event,] handler)

Undoes a call to `link` with the same positional arguments. If handler has been registered with either source or event as `any`, that parameter will be irrelevant when deciding whether or not to remove that link. For instance:

    link(foo, any, bar)
    unlink(foo, baz, bar)

Here the link created by `link(foo, any, bar)` will be removed by the call to `unlink`.

This behaviour (unlinking handlers registered with the `any` value) may change in future releases.

Default Events: When used without the event argument, both `link` and `send` use an event type called `default`. Most event-generating components have a default event type, such as `click` for `Button`s. The fact that this event type is default for `Button` means that when a `Button` generates a `click` event it will also generate a `default` event. So, if you listen to both `click` events and `default` events from a `Button`, your event handler will always be called twice.

`unlinkHandler`(handler)

Removes a handler completely from the event framework.

`unlinkMethods`(obj)

Unlinks all handlers that are methods of `obj`.

`unlinkSource`(source)

Remove the source (and all handlers linked to it) from the event framework.

### Classes

Base Classes and Common Behaviour

All components are subclasses the base class `Component`, which means that they all share some common behaviour.

Perhaps the most important behaviour is attribute handling (inherited from the `Attrib` mixin), which means that setting a components attributes may trigger some internal method calls. For instance,

    win.size = 300, 200

will automatically resize the component `win`. Attributes common to all components are:

    x         -- x-coordinate of upper left corner
    y         -- y-coordinate of upper left corner
    position  -- equivalent to (x, y)
    width     -- component width
    height    -- component height
    size      -- equivalent to (width, height)
    geometry  -- equivalent to (x, y, width, height)
    visible   -- whether the component is visible
    enabled   -- whether the component is enabled
    text      -- text associated with the component
    container -- the containing component

These can all be set as keyword arguments to the component constructors. Also, `Options` objects (with similar constructors) can be used as positional arguments in the constructor, with all the `Options`'s attributes being set in the component as well.

The `container` attribute will rarely be set directly, but rather implicitly by using the containing component's `add` method.

Common to `Application`, `Window`, and `Frame` are the `add` and `remove` methods. These will be described with the individual classes below.

All `Attrib` subclasses (including components, `Application`, and `RadioGroup`) share the following methods:

`set`(*args, **kwds)

Used to set attributes. Works like the `Attrib` constructor, setting attributes, and optionally using `Options` objects.

There is a variant called `rawSet` which does not call `sync`.

`modify`(*args, **kwds)

Works like the `set` method, except that the attributes are modified in place. That means the following (for an attribute named `foo`): (1) try to use slice assignment to change the value (will work for lists and `ListModels` etc.); if that doesn't work, (2) assign to the value's `value` attribute (used to modify `Model`s). If neither of these approaches work, simply rebind the attribute (equivalent to using the `set` method).

As with `set` and ordinary attribute assignment, the `sync` method will automatically be called when you use `modify`.

There is a variant called `rawModify` which does not call `sync`.

`sync`(*names)

When an attribute of a `Component` (or `Application`, `RadioGroup`, or an instance of another `Attrib` subclass) is assigned a value, the `Component` is automatically updated to reflect its new state. For instance, if you have a `Label` `lbl`, assigning a value to `lbl.geometry` would immediately change the `Label`'s geometry, and assigning to `lbl.text` would change its text.

This is good enough for most cases, but sometimes an attribute can contain a mutable value, such as a list, and changing that will not update the `Component`. For instance, if you use a list to hold the `items` of a `ListBox`, you could end up in the following situation:

    lbx = ListBox()
    lbx.items = 'first second third'.split()
    # More code...
    lbx.items.append('fourth')

After performing this code, nothing will have happened to the `ListBox`, because it has no way of knowing that the list has changed. To fix that, you can simply call its `sync` method:

    lbx.sync()

This method checks whether any attributes have changed, and make sure that the `Component` us up to date.

Optionally, you can supply the name(s) of the attribute(s) which have changed. Usually this is just an optimisation (which may be used by some backends to avoid unnecessary updates), but in one particular case it is very important: When modifying either `x`, `y`, `width`, `height`, `position`, `size`, or `geometry`. `Component`s automatically synchronise these geometric attributes, but since these depend on each other, you need to specify which one(s) you have modified.

For instance, the following will confuse Anygui and will raise an exception:

    btn.geometry = [10, 10, 50, 50]
    btn.geometry[1] += 1
    btn.sync()

The first line is fine, since it will automatically lead to an internal call to `btn.sync('geometry')`. The last line, however, will have Anygui trying to synchronise `btn.geometry`, `btn.position`, and `btn.y` without knowing which one was modified. It refuses to guess, and raises an exception. This could be solved by changing the last line to:

    btn.sync('geometry')

Updating Automatically

The following section is not currently correct.

Updating `Components` explicitly can be useful, but sometimes you would want it to be done for you, automatically, each time you modify an object that is referred to by a `Component` attribute. This can be taken care of by `link` and `send`. If your object uses `send` every time it's modified, and you `link` the object to your `Component`'s `sync` method, things will happen by themselves:

    class TriggerList:
        def __init__(self):
            self.list = []
        def append(self, obj):
            self.list.append(obj)
            send(self)
        def __getitem__(self, i):
            return self.list[i]

    lbx = ListBox()
    lbx.items = TriggerList()
    link(lbx.items, lbx.sync)

Now, if we call `lbx.items.append('fourth')`, `lbx.sync` will automatically be called. To make your life easier, Anygui already contains some classes that send signals whend they are modified; these classes are called `Model`s.

Model and Assignee

The following section is not currently correct.

The Anygui models (`BooleanModel`, `ListModel`, `TextModel`, and `NumberModel`) are objects that call `send` (with the `'default'` event) when they are modified.

An `Assignee` (part of the Anygui Model-View-Controller mechanism) is an object that supports the methods `assigned` and `removed`. These are automatically called (if present) when the object is assigned to one of the attributes of an `Attrib` object (such as a `Component`). `Model`s use this behaviour to automatically call `link` and `unlink`, so when the `Model` is modified, the `sync` method of the `Attrib` object is called automatically.

All models have a `value` attribute, which contains a "simple" version of its state (such as a number for `NumberModel`, a list for `ListModel`, etc.) Assigning to this attribute is a simple way of modifying the model in place.

class Application

To instantiate `Window`s, you must have an `Application` to manage them. You typically instantiate an application at the beginning of your program:

    app = Application()
    # Build GUI and run application

In some cases subclassing `Application` might be a useful way of structuring your program, but it is in no way required.

`Application` has the following methods:

`run`()

Starts the main event loop of the graphical user interface. Usually called at the end of the program which set up the interface:

    app = Application()
    # Set up interface
    app.run()

`add`(win)

Adds a `Window` to the `Application`, in the same way `Components` can be added to `Frame`s (see below). A `Window` will not be visible until it has been `add`ed to the current `Application` object, and that `Application` is running. When constructing new `Window`s after `Application.run` has been called, you should ensure that you `add` your `Window` to your running `Application` after all the `Components` have been added to your `Window`; otherwise, you may see them appearing and moving about as Anygui takes care of the layout. (Before `Application.run` is called, this is not an issue, since no `Window`s will be appear before that time.)

The parameter `win` can be either a single `Window`, or a sequence of `Windows`.

`remove`(win)

Removes a `Window` from the application. This will make the `Window` disappear.

class Button

A component which, when pressed, generate a `'click'` event, as well as a `'default'` event. Thus, in the following example, both `handler1` and `handler2` will be called when the button is pressed:

    btn = Button()
    def handler1(event): print 'Handler 1'
    def handler2(event): print 'Handler 2'
    link(btn, 'click', handler1)
    link(btn, handler2)

class CheckBox

`CheckBox` is a kind of button, and thus will also generate `'click'` and `'default'` events when clicked. But in addition, each `CheckBox` has a Boolean attribute `on`, which is toggled each time the box is clicked. The state of the `CheckBox` can be altered by assigning to this attribute.

The `on` property will be automatically modified (as per the MVC mechanism) when the user clicks the `CheckBox`. This will also cause the `CheckBox` to `send` a `click` and a `default`event.

The `on` attribute is a useful place to use a `BooleanModel`.

class Frame

`Frame` is a component which can contain other components. Components are added to the `Frame` with the `add` method:

`add`(comp, [opts,] **kwds)

Adds one or more components. The parameter `comp` may be either a single component, or a sequence of components. In the latter case, all the components will be added.

The `opts` parameter containes an `Options` object (see below) which gives information about how the object should be laid out. These options can be overridden with keyword arguments, and all this information will be passed to the `LayoutManager` (see below) of the `Frame`, if any. This `LayoutManager` is stored in the `layout` property.

`remove`(comp)

Removes a component from the `Frame`.

class Label

A `Label` is a simple component which displays a string of text. (`Label` can only handle one line of text.)

class LayoutManager

A layout manager is responsible for setting the `geometry` properties of a set of components when their parent `Frame` changes shape. The default `LayoutManager` is the `Placer` (see below).

Note: Other `LayoutManager`s will be documented later.

class ListBox

Shows a list of options, of which one may be selected. The `ListBox` has two special attributes: `items`, a sequence of items to display, and `selection`, the currently selected (as an index in the `items` sequence).

The `selection` property will be automatically modified (as per the MVC mechanism) when the user makes a selection. This will also cause the `ListBox` to `send` a `select` and a `default`event.

class Model

See the section on `Model` and `Assignee` above.

class Options

`Options` is a very simple class. It is simply used to store a bunch of named values; basically a dictionary with a different syntax. (For more information about the bunch class, see `http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52308`.)

You can set the attributes of an `Options` object and then supply it as an optional first parameter to the constructors of widgets:

    opt = Options()
    opt.width = 100
    opt.height = 50
    opt.x = 10
    btn = Button(opt, y=10)
    lbl = Label(opt, y=70)

Here `btn` and `lbl` will have the same `width`, `height`, and `x` attributes, but differing `y` attributes.

You can also set the attributes of an `Options` object through its constructur, just like with components:

    opt = Options(width=100, height=50, x=10)

`Options` objects can also be used when supplying arguments to the `add` method of `Frame`:

    # Assuming a Placer LayoutManager:
    opt = Options(left=10, right=10, hstretch=1)
    win.add(lbl, opt, top=10)
    win.add(btn, opt, top=(lbl,10))

class Placer

A simple but powerful layout manager. When adding components to a `Frame` whose `layout` attribute is set to a `Placer`, you can supply the following keyword arguments:

    left         -- the Component's left edge
    right        -- the Component's right edge
    top          -- the Component's top edge
    bottom       -- the Component's bottom edge
    hmove        -- move horizontally on resize
    vmove        -- move vertically on resize
    hstretch     -- stretch horizontally on resize
    vstretch     -- stretch vertically on resize
    direction    -- 'left', 'right', 'up', or 'down'
    space        -- spacing between multiple Components

The geometry specifiers (`left`, `right`, `top`, and `bottom`) can be set to either `None` (the default; will use the `Component`'s existing coordinates), a distance (from the corresponding `Frame` edge), a `Component` (will align the edge with the opposite edge of the given component), or a tuple (component, distance) (as with only a `Component`, except that a gap of size distance is inserted between the two).

The movement arguments (`hmove` and `vmove`) specify (with a Boolean value) whether the `Component` should be moved (horizontally, vertically, or both) to maintain the given distance to the surrounding `Frame`'s edges; the stretching arguments (`hstretch` and `vstretch`) specify whether the `Component` may be stretched to maintain these distances.

class RadioButton

A `RadioButton` is a toggle button, just like `CheckBox`, with slightly different appearance, and with the difference that it belongs to a `RadioGroup`. Only one `RadioButton` can be active (have its `on` attribute be a true Boolean value)in the `RadioGroup` at one time, so when one is clicked or programmatically turned on, the others are automatically switched off by the `RadioGroup`. Each `RadioButton` also has a `value` attribute, which should be unique within its `RadioGroup`. When one `RadioButton` is active, the `value` attribute of its `RadioGroup` is automatically set to that of the active `RadioButton`. The `RadioGroup` of a `RadioButton` is set by assigning the `RadioGroup` to the `group` attribute of the `RadioButton`. Setting the `value` attribute of the `RadioGroup` will automatically activate the correct `RadioButton`.

class RadioGroup

See `RadioButton` above.

class TextArea

A multiline text-editing `Component`. Its text is stored in the `text` attribute, which will be modified (according to the MVC mechanism) when the component loses focus. It also supports the Boolean `editable` property, which may be used to control whether the user can edit the text area or not.

class TextField

A one-line text-editing `Component`. (See also `TextArea`, above.) If the enter/return key is pressed within a `TextField`, the `TextField` will `send` a `enterkey` event.

class Window

A window, plain and simple. `Window` is a type of `Frame`, so you can add components to it and set its `layout` property etc. To make your window appear, you must remember to add it to your `Application`, just like you add other components to `Frame`s and `Window`s:

    win = Window()
    app = Application()
    app.add(win)
    app.run()

`Window`s have a `title` attribute which may be used by the operating system or window manager to identify the window to the user in various ways.

### Planned Components

The following components are planned as part of release 0.2.

The current API description is very tentative.

class Canvas

    TBD

class ComboBox

    box.items             -- C.f. ListBox

class Dialog

    TBD

class FileDialog

    TBD

class Menu

    menu.add(item)        -- Add a MenuItem

class MenuBar

    menubar.add(menu)     -- Add a Menu

A `MenuBar` is added to a `Window` with `win.add(bar)`. This will automatically set `win.menubar` to `bar`.

class MenuItem

    item.text             -- The MenuItem text

class ProgressBar

    bar.value             -- Progress (from 0 to 1)

class StatusBar

    bar.text              -- StatusBar text

class Notebook

    book.add(frame)       -- Add a Frame

The `Frame`s added to a `Notebook` should have their `title` attribute set. This will be used as the text on the tabs of the `Notebook`.

class Table

    TBD

class TreeBox

    TBD

## Contributing

If you want to contribute to the Anygui project, we could certainly use your help. First of all, you should visit the Anygui web site at `http://www.anygui.org`, subscribe to the developer's mailing list (`devel@anygui.org`) and the user's list (`users@anygui.org`), and try to familiarise yourself with how the package works behind the scenes. Then, you may either help develop the currently supported GUI packages, or you may start writing a backend of your own. Several potential backend targets may be found at `http://starbase.neosoft.com/~claird/comp.lang.python/python_GUI.html`.

## Anygui License

Copyright © 2001, 2002 Magnus Lie Hetland, Thomas Heller, Alex Martelli, Greg Ewing, Joseph A. Knapka, Matthew Schinckel, Kalle Svensson, Shanky Tiwari, Laura Creighton, Dallas T. Johnston, Patrick K. O'Brien, Phil Cook.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.