
                        Anygui: Generic GUI for Python
                                       
                             Magnus Lie Hetland
                                      
                               June 6th, 2002
                                      
1. Introduction

   Note: This version of the manual deals with the upcoming 0.2 release,
   and is not usable with the current release. Please consult the
   documentation 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: 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.2) 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,
   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.
   
  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.2/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 textguiif 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 Buttons and TextFields.
   The Windows 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 *

   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 Windows.
   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 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.
   
  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(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.)
   
  3.10 About Models, Views, and Controllers
  
   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, Components 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 Models 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.
   
   Note: 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

   Caution: Because of the controller behaviour of Components, 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(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.
   
  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
  
   RadioButtons are toggle buttons, just like CheckBoxes. The main
   differences are that they look slightly different, and that they
   should belong to a RadioGroup.
   
   A RadioGroup is a set of RadioButtons where only oneRadioButton 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.
   
   RadioButtons 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)

   Caution: 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.
   
  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
   Frames, 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.
   
  3.16 More Stuff
  
   More stuff to come... See the API reference.
   
4. API Reference

   The following reference will describe the full official API of the
   current version (0.2) of Anygui but is currently under development.
   
  4.1 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 widthxheight,
   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):
      ...     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.
   
   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 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.
   
   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 Buttons. 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 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.
   
   Note: 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 Models). 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 Labellbl, 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. Components 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
   
   Note: 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 Models.
   
   Model and Assignee
   
   Note: 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). Models
   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 Windows, 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 Frames (see below). A Window will not be visible until it has
   been added to the current Application object, and that Application is
   running. When constructing new Windows 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 Windows 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 defaultevent.
   
   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 LayoutManagers 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 defaultevent.
   
   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 Frames and Windows:
   

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

   Windows 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.
   
  4.4 Planned Components
  
   The following components are planned as part of release 0.2.
   
   Note: 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 Frames 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

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.
