# Anygui: Generic GUI for Python

Magnus Lie Hetland

February 8th, 2002

## 1. Introduction

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: `http://www.anygui.org`.

### 1.1 Design Goals

A. Anygui should be an easy to use GUI package which may be used to create simple graphical programs, or which may serve as the basis for more complex application frameworks.

B. Anygui should be a pure Python package which serves as a front-end for as many as possible of the GUI packages available for Python, in a transparent manner.

C. Anygui should include functionality needed to perform most GUI tasks, but should remain as simple and basic as possible.

### 1.2 Warning

The Anygui API is currently very much in flux as the Anygui team keeps experimenting with it. Because of that, incompatibilities may occur between releases. The current release (0.1.1) should be regarded as a prototype.

### 1.3 Tutorial

There is also a short tutorial available, which is included in the installation (`doc/tutorial.txt`) and is available from the website (`http://www.anygui.org`).

## 2. Installation

The Anygui package comes in the form of a `gzip` compressed `tar` archive. To install 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.1.1.tar.gz

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

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

Another possibility is:

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

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

### 2.1 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

### 2.2 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.1.1/lib`) there, or possibly place a symlink in that directory to the `anygui` package.

### 2.3 Making Sure You Have a Usable GUI Package

Once you have Anygui installed, you'll want to make sure you have a usable GUI package. 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 below.)

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

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

## 3. Using Anygui

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

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()

### 3.1 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.

### 3.2 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 *

This way you may use Anygui in standalone executables built with tools like `py2exe` (`http://starship.python.net/crew/theller/py2exe/`) or the McMillan installer (`http://www.mcmillan-inc.com/install1.html`), or with `jythonc` with the `--deep` option or equivalent.

Note: Compiling `jar` files of Anygui programs with Jython may not work in the current version.

Note that the namespace handling still works just fine:

      import anygui.backends.tkgui as gui

### 3.3 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))

### 3.4 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.

### 3.5 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]

### 3.6 The refresh 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 `refresh` method works the other way: If you modify an attribute, you can call the `refresh` method to keep the widget's appearance in sync with its state. When you assign to an attribute, `refresh` 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 `refresh`, see the section "About Models, Views, and Controllers", below.

### 3.7 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)

### 3.8 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)

#### 3.8.1 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)

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

### 3.9 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(**args):
          print 'Hello, World!'
      link(btn, greeting)

The event handling is taken care of by the call to `link`. An event handler may receive several keyword arguments, and if you're not particularly interested in any of them, simply use something like `**args` above. (For more information about this, see the section about global functions in the API Reference below.)

### 3.10 About Models, Views, and Controllers

The Anygui MVC mechanism (based on the `refresh` 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 application without using 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 int he `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 `refresh` 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.refresh()

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.refresh` to tell it to update itself.

If you assign a value to an attribute, the `refresh` 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

Warning: 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 models generate an event (of the type `default`) when they are modified.

Example:

      from anygui import *
      >>> mdl = BooleanModel()
      >>> mdl.value = 1
      >>> def model_changed(**kw):
      >>>     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 the new value.

### 3.11 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.

### 3.12 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)

Note: The behaviour of a `RadioButton` when it does not belong to a `RadioGroup` is not defined by the Anygui API, and may vary across backend. 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.

### 3.13 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.)

### 3.14 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).

### 3.15 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 private attribute `self._container`.)

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

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

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

`remove_component`(self, item)

Should remove the given item.

## 4. API Reference

The following reference describes the full official API of the current version (0.1.1) of Anygui.

### 4.1 Environment Variables

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_WISHLIST` 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.

### 4.2 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, **kw):
      ...     print 'An event occurred:', 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.

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

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. Note that we
      # only use the arguments we need, and lump the rest in **kw.

      def click(source, time, **kw):
          print 'Button %s clicked at %f.' % (source.text, 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`.

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

### 4.3 Classes

Base Classes and Common Behaviour

All components are subclasses of corresponding abstract components which implement behaviour common to all the backends. So, for instance, `Button` subclasses `AbstractButton`. These abstract components, again, subclass `AbstractComponent`, which implements behaviour common to all components.

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

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.

Common to `Application`, `Window`, and `Frame` is the `contents` attribute, as well as 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.

`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) If there exists an internal method (implemented in Anygui) for modifying the attribute inplace (called `_modify_foo`), use that; otherwise (2) try to use slice assignment to change the value (will work for lists and `ListModels` etc.); if that doesn't work, (3) 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 `refresh` method will automatically be called when you use `modify`.

`refresh`()

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 `refresh` method:

      lbx.refresh()

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

Updating Automatically

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 `refresh` 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.refresh)

Now, if we call `lbx.items.append('fourth')`, `lbx.refresh` 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 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 `refresh` 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.

`contents`

A read-only property containing a tuple of the `Window`s the `Application` currently manages.

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(**kw): print 'Handler 1'
      def handler2(**kw): 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`.

`contents`

This is a read-only property which contains the contents (a tuple of `Components`) of 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` (and the only one supplied with the current release) is the `Placer` (see below).

Note: Although Anygui 0.1 comes only with this layout manager, more will appear in the future.

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.

## 5. Known Problems

For an overview of known bugs in the current release, see the file `KNOWN_BUGS` found in the distribution.

## 6. Plans for Future Releases

For an overview of future plans, see the `TODO` file found in the distribution.

## 7. 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`.

## 8. 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. .

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.