Copyright © 2005-2007 Marcus von Appen
Redistribution and use in source (XML DocBook) and 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code (XML DocBook) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified.
Redistributions in compiled form (transformed to other DTDs, converted to PDF, PostScript, RTF and other formats) must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Important: THIS DOCUMENTATION IS PROVIDED BY THE OCEMPGUI PROJECT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OCEMPGUI PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Abstract
A GUI library for Pygame
Table of Contents
This manual gives an overview about the OcempGUI library and explains how to install, configure and use it. It is meant to be an introduction into the OcempGUI library. This manual is not an API reference. Instead the most classes, methods and functions of the OcempGUI library will be briefly described and examples about how to use them will be provided.
The descriptions, mentioned classes, methods and functions as well as the examples may get out of date and thus using any portion or information of this manual could crash your application or behave in another unexpected way. Although the author tries to keep the manual in synchronization with changes in the library, this can happen from time to time.
If you experience such a misbehaviour or are unsure about a description, refer to the extensive inline documentations of the library. You can accomplish this easily using the pydoc application, for example.
The first versions of OcempGUI were created in 2004, when the author of the package started the Ocean Empire project. Ocean Empire is a reimplementation of an old MS-DOS™ game named Ocean Trader.
The author decided to use the pygame library, which is a python wrapper of the SDL library and noted later, that no matching GUI like extensions exist for it. The first task was to implement such a GUI extension to have a suitable graphical environment for the game. The result of this attempt is OcempGUI.
OcempGUIs focus lies on a GUI toolkit implementation based on the Sprite concept of pygame and is completely implemented in Python. It can be easily integrated in projects and reduces the development time by providing an own event mangement system and a reliable collection of user interface elements, of which all components are easy to use, extensible and support styling for the look and feel.
Besides GUI functionality OcempGUI offers modules for other needs such as accessibility and a fast event management system. Both parts do not need rely on pygame and thus can be used in any Python project.
This section describes the process to configure and install OcempGUI.
The following applications and libraries are needed before OcempGUI can be installed:
Python, version 2.3 or higher
pygame, version 1.7.1 or higher
ATK, version 1.12.1 or higher (optional)
Please refer to the documentation of the respective package about how to install it.
OcempGUI is not provided as binary package by the author. However it might be that someone else set up such a package for your wanted operating system or distribution. Those packages are usually not supported by the author, what means that installation problems or similar issues, which do not target the library directly, should be escalated to the respective supplier of that package.
The unpacked package contains two possibilities of building and installing it. Both ways are mostly identical but the one or other user might tend to prefer a specific way. The first is the python way of installing software, the second follows the tradition of the unix environment and uses a Makefile (which actually simply starts the python way). While being in the top source directory, it is possible to type either
make install
for the traditional unix way or
python setup.py install
for the python way.
The package might have some special options, which are described in the README file shipped with it.
Using the latest development sources is possible via CVS. More information about how to use the SourceForge repository can be found on SourceForge.
It should be noted that using the development sources can cause higher risks to the environment than the usual releases can do. Thus it is highly recommended to read the ocemp-devel mailing list on which actual development ist discussed.
The various components of OcempGUI can be used alone without the
need to use another module of the package. If only a small event
management system is needed, only the
ocempgui.events module can be imported. If
an event management exists and widgets are needed, only the
ocempgui.widgets module needs to be
imported and so on.
The following OcempGUI modules are currently available:
ocempgui.access
Provides various accessibility tools and interfaces for
python, so that people with disabilities can easily use
and access python applications. Generic interfaces for
objects are available, which enable them to provide
information for access-related technologies like braille
keyboards or speech synthesizers. The
ocempgui.widgets widget classes
will integrate the interfaces of this module in future
versions.
ocempgui.draw
Provides various drawing primitives, on which the
ocempgui.widgets module
relies. Usually the methods of this module provide
simplified wrappers for the pygame drawing functions as
well as some more complex drawing object types.
ocempgui.events
A small and fast event management system. It comes with an
EventManager class, which takes
care of distributing events to connected objects through
signal queues. The event management system can deal with
any type of data, which you want to use as event.
ocempgui.object
Abstract object definitions, that allow you to rapidly
create own classes, which are event capable through signal
slots. Function or method callbacks
can be connected to or disconnected from the signals, the
object listens to. The BaseObject
class is ready to be used with the
ocempgui.events module.
ocempgui.widgetsVarious GUI elements for the creation and integration of interactive user interfaces. This module contains most commonly used user interface elements as well as abstract core definitions and interfaces to rapidly create own user interface elements. It also provides an own rendering class, which allows you to instantly create your pygame application without the need of taking care about an event and update loop.
The ocempgui.access module provides a
wrapper around the ATK accessibility library and several classes
to ease the development of accessible applications. To serve
different ability ranges, objects can be aware, the ATK
developers use an C interface system, that has to be implemented
for each toolkit. OcempGUI implements those interfaces using the
AtkObject only and flags, that will
enable or disable certain interfaces within that instance.
To use accessibility features in your code, you have to import the module using:
import
ocempgui.accessimport ocempgui.access.papi
To make python objects accessible and usable by
accessibility-aware applications and hardware they should
implement IAccessible interface
class. It provides a single method interface,
get_accessible(), which has to return
an AtkObject object for the specific
python instances.
class A11yObject (IAccessible):
def __init__ (self):
IAccessible.__init__ (self)
...
def get_accessible (self):
obj = AtkObject (...)
...
return obj
Dependant on the capabilities of the python object and the
information it provides, it has to set up several attributes
and/or implement various interfaces of the
AtkObject, which will be returned.
TODO: provide more details
You can find the following example as a python script
under examples/a11y_test.py.
# papi test example.
import ocempgui.access.papi as papi
import atexit
# Main object - somewhat similar to the GailTopLevel object.
application = papi.AtkObject ()
application.name = "Application object"
application.description = "Application description"
application.role = papi.ATK_ROLE_APPLICATION
application.parent = None
def get_application ():
global application
return application
# Register the interfaces and initialize the atk-bridge.
papi.set_atk_root (get_application)
atexit.register (papi.shutdown)
papi.init ()
class SimpleA11y (papi.AtkObject):
def __init__ (self):
ifaces = papi.ATK_IFACE_COMPONENT | papi.ATK_IFACE_ACTION
papi.AtkObject.__init__ (self, ifaces)
# Implement some interfaces of ATK_IFACE_ACTION and
# ATK_IFACE_COMPONENT.
self.action_get_n_actions = self.__get_n_actions
self.action_get_description = self.__get_description
self.action_get_name = self.__get_name
self.component_get_extents = self.__get_pos
def __get_pos (self, coords):
return 10, 10, 99, 99
def __get_n_actions (self):
return 1
def __get_description (self, i):
return "Example action."
def __get_name (self, i):
return "Example action name"
# Window dummy as child of the toplevel application object.
window = SimpleA11y ()
# Retrieve the state set, so we can set it active.
set = window.ref_state_set ()
set.add_state (papi.ATK_STATE_ACTIVE)
# Set some necessary information for accessibility applications.
window.role = papi.ATK_ROLE_WINDOW
window.name = "Window A11y Object"
window.description = "Window Description"
# Link it with the application object
window.parent = application
# Signal testing - window:create will cause accessibility applications
# to note, that a new window was created for the application.
window.emit ("window:create")
print "Keeping myself alive. Press CTRL-C to exit the application."
while True:
# Iterate the main processing loop of the ATK wrapper internals,
# so that external applications can interact with the objects.
papi.iterate ()
Example 1. ocempgui.access.papi test example
The Magnifier class of the
ocempgui.access module is a screen
magnification tool for pygame screens. It allows users to zoom
portions of the current pygame screen, which are determined by
the mouse cursor position. It allows different magnification
factors and a variable sizing of the area to zoom.
The Magnifier can be integrated easily
into any pygame mainloop and adjusted with minimal effort. To
create a new instance of it, you simply can invoke its
constructor with no arguments.
magnifier = Magnifier ()
# Use a magnification area of 100, 100 and a factor of 3.
magnifier = Magnifier (100, 100, 3)
The zoom factor can be adjusted using the
factor attribute and
set_factor() method.
magnifier.factor = 5.2
magnifier.set_factor (0.5)
Values smaller than 1 will zoom out the affected area.
The size of the area around the mouse cursor, which should be
magnified, can be adjusted using the size
attribute and set_size() method.
magnifier.size = 50, 50
magnifier.set_size (40, 70)
Integrating the Magnifier in a pygame
application is done by just adding one or two lines of code.
The most important line is to notify it about the available
pygame events.
while True:
events = pygame.event.get ()
magnifier.notify (*events)
...
pygame.MOUSEMOTION events. Any other event
will cause it to update its area only.
The update is done only once per
notify() invocation. If multiple
pygame.MOUSEMOTION events are in the
passed list, only the last on will be used for repositioning.
If the pygame display is manipulated directly and thus needs to
contain its original surface information (without the magnified
area), the restore() method should be
invoked before the manipulation occurs.
while True:
events = pygame.event.get ()
magnifier.restore ()
# Display manipulation
....
magnifier.notify (*events)
...
Magnifier will be suspended) before
any manipulation takes place. Afterwards the
Magnifier will be enabled again by
passing the current events to it.
The Magnifier allows you to setup your
own zoom function for best results when zooming parts of your
application screen. The zoom_func attribute
and set_zoom_func() method allow you to
provide an own zoom function, which has to return the zoomed
surface. It receives additional arguments, which are explained
in detail in the inline documentation of the
Magnifier class.
Implementing the Magnifier's default zoom
function could be achieved using the following code.
def own_zoom_func (screen, mousepos, resultsize, size, factor):
offset = mousepos[0] - size[0] / 2, mousepos[1] - size[1] / 2
# Create zoomable surface.
surface = pygame.Surface ((size[0], size[1]))
surface.blit (screen, (0, 0), (offset[0], offset[1], size[0], size[1]))
# Zoom and blit.
return pygame.transform.scale (surface, (resultsize[0], resultsize[1]))
# Assign the new zoom function.
magnifier.zoom_func = own_zoom_func
The ocempgui.draw module contains several
wrapper functions around various pygame drawing functions, which
are used by the ocempgui.widgets module. It
is divided in several submodules, of which each one contains
various related functions such as creating rectangle surfaces,
drawing strings or loading images. Although not any function
defined within the ocempgui.draw module
simplifies the usage of the pygame drawing functions, they can
reduce the amount of code to write and several of them enable you
to simplify specific operations.
To use the drawing routines in your own code, you can simply import the module using:
import ocempgui.draw
The ocempgui.draw.Draw submodule contains
several functions for geometric objects. Although most of them
are only wrappers around the respective pygame functions, some
of them can be used to create more complex geometric
objects. The following list gives an overview about the
functions defined within this submodule.
draw_line (surface, color, a, b, width=1)
Draws a line with the given width
from a to b
on the passed surface. This
function simply wraps the
pygame.draw.line() function.
You can find the following example as a python script
under examples/draw_line.py.
# Draw.draw_line () usage example.
import pygame, pygame.locals
from ocempgui.draw import Draw
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((200, 200))
screen.fill ((250, 250, 250))
pygame.display.set_caption ('Draw.draw_line ()')
# Draw horizontal lines in different colors and sizes.
for i in range (10):
val = i * 10
Draw.draw_line (screen, (0 + val, 50 + val, 40 + 2 * val),
(5, val), (195, val), i)
# Draw vertical lines in different colors and sizes.
for i in range (10):
val = i * 8
Draw.draw_line (screen, (0 + 2 * val, 30 + val, 35 + 2 * val),
(5 + i * 10, 100), (5 + i * 10, 195), i)
# Draw a cross.
Draw.draw_line (screen, (0, 0, 0), (120, 100), (195, 195), 3)
Draw.draw_line (screen, (0, 0, 0), (195, 100), (120, 195), 3)
# Show anything.
pygame.display.flip ()
# Wait for input.
while not pygame.event.get ([pygame.locals.QUIT]):
pass
Example 2. Draw.draw_line ()
draw_rect (width, height,color=None)
Creates a rectangle surface with a size of
width and
height, which can be manipulated
and blitted on other surfaces. This function simply wraps
the pygame.Surface function and
calls Surface.fill() on demand.
You can find the following example as a python script
under examples/draw_rect.py.
# Draw.draw_rect () usage example.
import random
import pygame, pygame.locals
from ocempgui.draw import Draw
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((200, 200))
screen.fill ((250, 250, 250))
pygame.display.set_caption ('Draw.draw_rect ()')
# Draw rectangles with various colors.
rect = Draw.draw_rect (55, 40, (255, 0, 0))
screen.blit (rect, (5, 5))
rect = Draw.draw_rect (55, 40, (0, 255, 0))
screen.blit (rect, (65, 5))
rect = Draw.draw_rect (55, 40, (0, 0, 255))
screen.blit (rect, (125, 5))
# Draw encapsulated rectangles.
for i in range (30):
val = i + 3
rnd = (random.randint (0, 5), random.randint (0, 5), random.randint (0, 5))
color = (rnd[0] * i + 100, rnd[1] * i + 100, rnd[2] * i + 100)
rect = Draw.draw_rect (100 - 2 * val, 100 - 2 * val, color)
screen.blit (rect, (5 + val, 50 + val))
# Show anything.
pygame.display.flip ()
# Wait for input.
while not pygame.event.get ([pygame.locals.QUIT]):
pass
Example 3. Draw.draw_rect ()
draw_triangle (surface, color, a, b, c, width=0)
Draws a triangle using the vertices
a, b and
c on the passed
surface. This function simply wraps
the pygame.draw.polygon() function.
You can find the following example as a python script
under examples/draw_triangle.py.
# Draw.draw_triangle () usage example.
import pygame, pygame.locals
from ocempgui.draw import Draw
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((200, 200))
screen.fill ((250, 250, 250))
pygame.display.set_caption ('Draw.draw_triangle ()')
# Draw three triangles.
Draw.draw_triangle (screen, (255, 0, 0), (20, 5), (5, 30), (35, 30), 0)
Draw.draw_triangle (screen, (0, 255, 0), (25, 5), (40, 30), (55, 5), 0)
Draw.draw_triangle (screen, (0, 0, 255), (60, 5), (45, 30), (75, 30), 0)
# Draw a 'tunnel effect' of triangles.
for i in range (30):
val = i + 3
color = (val * 4, val * 7, val * 5)
Draw.draw_triangle (screen, color, (5 + 2 * val, 50 + val),
(195 - 2 * val, 50 + val), (100, 195 - 2 * val), 1)
# Show anything.
pygame.display.flip ()
# Wait for input.
while not pygame.event.get ([pygame.locals.QUIT]):
pass
Example 4. Draw.draw_triangle ()
The ocempgui.draw.Image submodule
contains image related functions, such as loading or saving
image data.
load_image (filename, alpha=False, colorkey=None)
Loads an image from the specified
filename and automatically converts
it to the current display pixel format. If
alpha is set to True, the method
will try enable alpha transparency by using the
pygame.Surface.convert_alpha()
method. If the colorkey argument is
set to a color value, the method tries to add color based
transparency using the
pygame.Surface.set_colorkey()
method. This function is just a wrapper around
pygame.image.load() and
additionally calls
Surface.convert().
You can find the following example as a python script
under examples/load_image.py.
# Image.load_image () usage example.
import pygame, pygame.locals
from ocempgui.draw import Image
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((120, 100))
screen.fill ((250, 250, 250))
pygame.display.set_caption ('Image.load_image ()')
# Load an image and blit it on the screen.
image = Image.load_image ("./image.png")
screen.blit (image, (10, 10))
# Show anything.
pygame.display.flip ()
# Wait for input.
while not pygame.event.get ([pygame.locals.QUIT]):
pass
Example 5. Image.load_image ()
The ocempgui.draw.String submodule
contains functions, which allow the creation and manipulation of
fonts and string surfaces. It includes a simple font caching
system, which provides a fast availability of fonts, which were
created earlier. Besides this feature, the string surface
related functions are mostly wrappers around the respective
pygame functions.
create_font (fontfile, size, style)
Creates and returns a pygame.Font
object from the given fontfile
using the passed size and
style. The font will be cached
internally, so that a second invocation using the same
fontfile and
size will return the cached font.
If you manipulate the returned
pygame.Font directly, the
manipulation will be applied to the cached font, too. To
circumvent this behaviour, create a copy of the return
value using the
pygame.Font.copy() method and
manipulate the copy.
You can find the following example as a python script
under examples/create_font.py.
# String.create_font () usage example.
import pygame
from ocempgui.draw import String
def check (font, name):
bold = "not bold"
if font.get_bold ():
bold = "bold"
print "%s at %s is %s" % (name, font, bold)
# Initialize the pygame engine.
pygame.init ()
# Create a font from the ttf located in the current directory.
font = String.create_font ("tuffy.ttf", 14)
check (font, "font")
# Now create a second font and manipulate it.
# NOTE: Due to the caching we are using the same font object as above!
font_mod = String.create_font ("tuffy.ttf", 14)
font_mod.set_bold (True)
# Output the bold state of both fonts.
check (font, "font")
check (font_mod, "font_mod")
Example 6. String.create_font ()
create_system_font (fontname, size, style)
Creates and returns a pygame.Font
object from the given system font with the specified
fontname and the given
size and
style. Like the
create_font() function, the font
will be cached internally, so that a second invocation
with the same parameters will return the cached font.
If you manipulate the returned
pygame.Font directly, the
manipulation will be applied to the cached font, too. To
circumvent this behaviour, create a copy of the return
value using the
pygame.Font.copy() method and
manipulate the copy.
The pygame.SysFont
documentation also notes this:
This will always return a valid Font object, and will fallback on the builtin pygame font if the given font is not found. | ||
| --Pygame documentation | ||
You can find the following example as a python script
under examples/create_system_font.py.
# String.create_system_font () usage example.
import pygame
from ocempgui.draw import String
# Initialize the pygame engine.
pygame.init ()
# Create some fonts.
fonts = {}
names = ( "Arial", "Helvetica", "Sans", "Serif", "Times" )
for name in names:
fonts[name] = String.create_system_font (name, 14)
# Output the fonts as well as their object address.
for name in fonts:
print "Loaded: %s at %s" % (name, fonts[name])
Example 7. String.create_system_font ()
draw_string (text, font, size, antialias, color, style)
Creates a transparent surface displaying the
text in the given
color with the specified
style applied. If
antialias evaluates to True, the
text will be rendered using antialiasing (if possible).
The function first tries to resolve
font as font file. If that fails,
it looks for a system font name, which matches the
font name and returns a Font
object based on those information (or the fallback font
of pygame, see also
create_system_font (fontname, size, style)
.
You can find the following example as a python script
under examples/draw_string.py.
# String.draw_string () usage example.
import pygame, pygame.locals
from ocempgui.draw import String
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((400, 100))
screen.fill ((250, 250, 250))
pygame.display.set_caption ('String.draw_string ()')
# Create a text using the ttf located in the current directory.
text = String.draw_string ("This is tuffy.ttf", "tuffy.ttf", 16, 1, (0, 0, 0))
screen.blit (text, (5, 5))
# Create a text using the 'Times' system font
text = String.draw_string ("This is Times", "Times", 16, 1, (255, 0, 0))
screen.blit (text, (5, 35))
# Create a text using the fallback python font by specifying a wrong
# font name (hopefully ;-).
text = String.draw_string ("This is the fallback", "invalid_font_name_here",
16, 1, (0, 0, 255))
screen.blit (text, (5, 60))
# Now the same again without antialiasing.
text = String.draw_string ("This is tuffy.ttf (no aa)", "tuffy.ttf",
16, 0, (0, 0, 0))
screen.blit (text, (200, 5))
text = String.draw_string ("This is Times (no aa)", "Times",
16, 0, (255, 0, 0))
screen.blit (text, (200, 35))
text = String.draw_string ("This is the fallback (no aa)",
"invalid_font_name_here", 16, 1, (0, 0, 255))
screen.blit (text, (200, 60))
# Show anything.
pygame.display.flip ()
# Wait for input.
while not pygame.event.get ([pygame.locals.QUIT]):
pass
Example 8. String.draw_string ()
draw_string_with_bg (text, font, size, antialias, color,
bgcolor, style)
This function is identical to the
draw_string (text, font, size, antialias, color, style)
function, except that it provides
a background color via the bgcolor
parameter.
You can find the following example as a python script
under examples/draw_string_with_bg.py.
# String.draw_string_with_bg () usage example.
import pygame, pygame.locals
from ocempgui.draw import String
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((100, 100))
screen.fill ((250, 250, 250))
pygame.display.set_caption ('String.draw_string_with_bg ()')
# Create texts using the 'Times' system font and different background
# colors.
text = String.draw_string_with_bg ("This is Times", "Times", 16, 1, (0, 0, 0),
(200, 200, 200))
screen.blit (text, (5, 5))
text = String.draw_string_with_bg ("This is Times", "Times", 16, 1, (0, 0, 0),
(0, 200, 0))
screen.blit (text, (5, 60))
# Show anything.
pygame.display.flip ()
# Wait for input.
while not pygame.event.get ([pygame.locals.QUIT]):
pass
Example 9. String.draw_string_with_bg ()
The ocempgui.draw.Complex module contains
more advanced drawing objects, that implement interesting
features for 2D graphics.
The FaderSurface class enhances the
pygame.Surface class by some additional
attributes and methods to support fade-in/fade-out operations.
You can adjust them to fit its behaviour to your needs, such as
setting the current transparency value using the
alpha attribute
surface.alpha = 0 # Make it completely transparent
surface.step = 5
You can find the following example as a python script
under examples/fader_surface.py.
# Complex.FaderSurface usage example.
import pygame, pygame.locals
from ocempgui.draw import Complex, Image
# Initialize the drawing window.
pygame.init ()
screen = pygame.display.set_mode ((120, 100))
screen.fill ((180, 180, 180))
pygame.display.set_caption ('Complex.FaderSurface')
# Create a surface we can use to display.
image = Image.load_image ("./image.png")
r = image.get_rect ()
# Create a new FaderSurface with the same dimensions and an initial
# transparency of 1.
surface = Complex.FaderSurface (r.width, r.height, 1)
# Blit the original on the FaderSurface.
surface.blit (image, (0, 0))
# The default step value is -1, but we want to fade the image in.
surface.step = 1
# Loop until the FaderSurface reached the maximum or minimum alpha
# transparency value.
while surface.update ():
# Clean up and blit the surface..
screen.fill ((180, 180, 180))
screen.blit (surface, (10, 10))
pygame.display.flip ()
# Check the bounds. We have to check the maximum values - 1, because
# 255 and 0 cause the surface to return False and we would exit the
# loop.
if surface.alpha == 254 or surface.alpha == 1:
surface.step = -surface.step
pygame.time.delay (50 / 4)
# Wait for input.
if pygame.event.get ([pygame.locals.QUIT]):
break
Example 10. FaderSurface example
OcempGUI provides a minimalistic observer pattern implementation
in the ocempgui.events module, which
enables objects to track changes ('observe') from others. Two
classes, the Subject and
IObserver, are used for this.
An object, which should expose changes is usually called a subject
to which other objects, the observers, subscribe for notification
about those changes. To create a subject, the class just needs to
inherit from the Subject class, which
provides a minimum set of methods and attributes to become
observable.
class MyObject (Subject):
def __init__ (self):
Subject.__init__ (self, "UniqueSubjectName")
...
Subject receives a
string argument, the name, which identifies the object instance
for possible observers.
The object now features all attributes and methods to register
observers and to notify them about state changes. State changes
however have to be emitted manually, so that the object should
invoke its notify() method, whenever this
is necessary.
class MyObject (Subject):
def __init__ (self):
Subject.__init__ (self, "UniqueSubjectName")
self._value = None
def set_value (self, value):
# Preserve old value.
oldval = self._value
# Set new value.
self._value = value
# Notify observers
self.notify ('value', old, value)
...
set_value() is invoked now, any
registered observer will be notified about
The object, that changed its state (UniqueSubjectName).
The name of the object part that changed (here the attribute value).
The old and new value of the object part that changed.
An object that shall act as an observer should inherit from the
IObserver class and implement its
update() method, which will receive the
state change notification from the Subject.
The signature of the method looks like the following:
class ObserverObject (IObserver):
...
def update (self, subject, prop, oldval, newval):
...
Your classes do not need to explicitly inherit from
IObserver, but have to implement the
update() method with its correct
signature.
The subject argument is the unique name of
the Subject that just
changed. prop identifies the detail that
changed and oldval and
newval contain the old and new value of the
detail.
The following example is a complete example based on the
excerpts from above. You can find it as python script under
examples/observer.py
# Subject/Observer usage example.
from ocempgui.events import Subject, IObserver
# The subject that should notify observers about state changes.
class MyObject (Subject):
def __init__ (self):
Subject.__init__ (self, "MyObject")
self._x = "Simple Attribute"
self._y = 1234567890
self._z = None
def get_x (self):
return self._x
def set_x (self, value):
# Preserve old value.
old = self._x
self._x = value
# Notify about change.
self.notify ("x", old, value)
def get_y (self):
return self._y
def set_y (self, value):
# Preserve old value.
old = self._y
self._y = value
# Notify about change.
self.notify ("y", old, value)
def get_z (self):
return self._z
def set_z (self, value):
# Preserve old value.
old = self._z
self._z = value
# Notify about change.
self.notify ("z", old, value)
x = property (get_x, set_x)
y = property (get_y, set_y)
z = property (get_z, set_z)
class OwnObserver (IObserver):
def __init__ (self):
pass
def update (self, subject, prop, oldval, newval):
if subject == "MyObject": # A MyObject instance, check details.
if prop == "x":
# Its x value changed.
print "The x value of a MyObject instance changed from " \
"%s to %s" % (str (oldval), str (newval))
elif prop == "y":
# Its y value changed.
print "The y value of a MyObject instance changed from " \
"%s to %s" % (str (oldval), str (newval))
else:
# Another value changed.
print "The %s value of a MyObject instance changed from" \
"%s to %s" % (str (prop), str (oldval), str (newval))
class AnotherObserver (IObserver):
def __init__ (self):
pass
def update (self, subject, prop, oldval, newval):
print "Detail %s of %s changed from %s to %s" % (str (prop), subject,
str (oldval),
str (newval))
subject = MyObject ()
# Add tow observers doing
observer1 = OwnObserver ()
observer2 = AnotherObserver ()
subject.add (observer1, observer2)
subject.x = "FooBarBaz"
subject.y = subject.x * 3
subject.z = 100
Example 11. Observer example
The ocempgui.events module provides a small
and fast event management system. It is currently separated into
three different classes, of which the most important is the
EventManager class. Besides the
EventManager, an
Event class for sending event data and an
EventCallback class for connecting
functions or methods to signals are available.
To create an own event driven application system or to enhance an existing application, only a few guidelines have to be respected and only a minimal set of changes be made on existing code. You will need to
enable objects to receive events,
set up the event management system.
To understand, what you are doing and to know the pitfalls of the event management system, you first have to know, how it works. The next subsection will give you a short explanation of it.
The event management system of OcempGUI uses a simple approach using signal slots. This means, that objects will register themselves only for specific event types, of which they want to be notified. Any other event will not be sent to them.This reduces the overhead of events the objects have to deal with (either by dropping or processing them) and improves the performance and scalability of the event management system (especially with many objects).
An object, which shall be event aware, should inherit from the
INotifyable class and implement its
notify() method, which will receive
events distributed by the EventManager.
The signature of the method looks like the following:
class OwnObject (INotifyable):
...
def notify (self, event):
...
Your classes do not need to explicitly inherit from
INotifyable, but have to implement the
notify() method with its correct
signature.
The event argument of the method will be
an Event object, which can be used to
perform certain actions within the method body then:
class OwnObject (INotifyable):
...
def move (self, coords):
# Moves the object to the desired coordinates (x, y).
self.x = coords[0]
self.y = coords[1]
print "Moved to %d,%d" % (self.x, self.y)
def notify (self, event):
# Check the event signal and run a certain action with its data.
if event.signal == "clicked":
print "Something was clicked!"
elif event.signal == "move":
# Assuming that the event.data contains a coordinate tuple.
self.move (event.data)
Example 12. Enabling an object to receive events
Setting up the main event management is nearly as easy as
enhancing the objects. To add objects to the
EventManager, the
add_object() method has to be
invoked. It receives the object to add and a list of signal ids
as arguments. The signal ids will cause the object to be
registered in specific queues, to which events with matching
signal ids then will be sent.
Objects can be removed from the
EventManager using the
remove_object() method. The method
allows you to either remove the object from specific
slots or from all
slots, it is registered for, at once.
We use the OwnObject class of the
previous example and will (un)register it for the signals
"move" and "clicked".
# Create an EventManager and OwnObject instance.
manager = EventManager ()
myobj = OwnObject ()
# Add the object to the EventManager.
manager.add_object (myobj, "move", "clicked")
# Remove the object from the 'clicked' slot.
manager.remove_object (myobj, "clicked")
# Remove the object from _all_ slots it still listens on.
manager.remove_object (myobj)
Example 13.
Adding and removing an object to the
EventManager
Now let us proceed to the most important: sending events. To
send events to the objects of the
EventManager, you can use the
emit() method. It receives two
arguments, which will become the signal and data of a
Event object. The
Event will be created by the
EventManager and then sent to the
matching objects. So all you have to do is to pass the
emit() method the correct information
for the event.
Both arguments the emit() receives,
have no limitations of type, length or whatsoever. It is up to
you to to send correct information through the event management
system and to check for correct information on the object side.
# Send events to the registered objects via the emit() method.
manager.emit ("clicked", None)
manager.emit ("move", (10, 10))
Example 14.
Sending events through the EventManager
The following example is a complete example based on the
excerpts from above. You can find it as python script under
examples/eventmanager.py
# EventManager usage example.
from ocempgui.events import EventManager, INotifyable
# Create a new event capable object. This can be acquired by adding a
# 'notify ()' method to the object, which receives a single argument.
class OwnObject (INotifyable):
def __init__ (self):
self.x = 0
self.y = 0
def move (self, coords):
# Moves the object to the desired coordinates (x, y).
self.x = coords[0]
self.y = coords[1]
print "Moved to %d,%d" % (self.x, self.y)
def notify (self, event):
# Check the event signal and run a certain action with its data.
if event.signal == "clicked":
print "Something was clicked!"
elif event.signal == "move":
# Assuming that the event.data contains a coordinate tuple.
self.move (event.data)
# Create an EventManager and OwnObject instance.
manager = EventManager ()
myobj = OwnObject ()
# Add the object to the EventManager.
manager.add_object (myobj, "move", "clicked")
# Send events to the registered objects via the emit() method.
manager.emit ("clicked", None)
manager.emit ("move", (10, 10))
# Remove the object from the 'clicked' slot.
manager.remove_object (myobj, "clicked")
# Send the 'clicked' event once more.
manager.emit ("clicked", None)
# Remove the object from _all_ slots it still listens on.
manager.remove_object (myobj)
# Send the 'move' event again.
manager.emit ("move", (40, 40))
Example 15. Complete event management example
The previous section gave you a rough overview about how to use
the OcempGUI event system with your own objects. As you might
have seen, using only the notify()
method and lengthy if-else conditions might not always be the
best idea. Also, overriding the
notify() method whenever the
functionality of an object should change is not the best,
especially, if it should be runtime dependant.
You might consider using the BaseObject
from the ocempgui.object module
instead. It offers ready to use signal slots and methods to bind
callbacks at runtime, improving its and your flexibility without
big effort.
The BaseObject is event driven, which
means, that it acts and reacts upon events it receives and
that it can raise events. Hereby you have to distinguish
between Signals and
Events. Signals are
certain identifiers, a BaseObject can
listen to, while Events are a combination
of a Signal and additional data.
What does that mean in practice? As you already know from
the section called “Building event driven systems”, objects will register
themselves at an event manager with a specific signal, they
listen to. Events in turn carry a specific signal id and
additional data. A BaseObject will
register itself at an event manager with its signals. If
events are passed to the event manager, it will distribute
them to the object, if needed. The object in turn will react
according to its programming instructions.
To allow your object to react according to the application
needs on certain events easily, the
BaseObject supports the connection of
callbacks to signals you can define for
it. A callback is a method or function,
which should be invoked, when the object receives a certain
event.
The first thing you should do is to let your existing object or
the newly created one inherit from the
BaseObject class. Afterwards you can
unleash its full power by adding just a minimal set of code.
from ocempgui.object import BaseObject
class OwnObject (BaseObject):
def __init__ (self):
BaseObject.__init__ (self)
...
Example 16.
Inheriting from the BaseObject class
That is not all of course. You also have to set up the signals the object has to listen to and create callbacks. Let us create a small ping-pong example, where two objects react upon a 'ping' and 'pong' signal.
The BaseObject has a
_signals attribute, which basically is a
dictionary with the signal ids it listens to as keys and list
for the callbacks as values. To allow your object to listen to
the 'ping' or 'pong' signal, you have to add those to this
dictionary.
from ocempgui.object import BaseObject
class OwnObject (BaseObject):
def __init__ (self):
BaseObject.__init__ (self)
self._signals["ping"] = []
self._signals["pong"] = []
...
Example 17. Adding a signal to the object
The list as value is mandatory to allow callbacks to be connected to those signals. If you are going to supply other types as value, keep in mind, that it is unlikely that connecting or disconnecting callbacks will work as supposed.
Now we just need to make the notify()
aware of those signal types and let it invoke the appropriate
callbacks, which will be connected to those signals.
class OwnObject (BaseObject):
...
def notify (self, event):
if event.signal == "ping":
self.run_signal_handlers ("ping")
elif event.signal == "pong":
self.run_signal_handlers ("pong")
Example 18. Setting up the notify() method
As you see, the run_signal_handlers()
takes care of invoking the connected callbacks. Now you have
anything set up to allow the object to listen to specific
events, to connect callbacks to it and let it invoke them,
when it receives the specific signal.
To connect methods or functions as callbacks to a specific
signal, the connect_signal() method of
the BaseObject can be used. It allows
additional data to be passed to the callback by specifiying the
data right after the signal and callback. If the callbacks is
not needed anymore, it can be disconnected using the
disconnect_signal() method.
class OwnObject (BaseObject):
...
my_obj = OwnObject ()
ev_callback1 = my_obj.connect_signal ("ping", ping_callback, data)
ev_callback2 = my_obj.connect_signal ("pong", pong_callback, data1, data2)
...
my_obj.disconnect_signal (ev_callback1)
my_obj.disconnect_signal (ev_callback2)
Example 19. Connecting and disconnecting callbacks.
Now it just needs to be connected to an event manager. In
contrast to the earlier section, you do not need to register
any signal of your BaseObject inheritor
manually. When you connect it to an event manager, it will
automatically do that for you.
class OwnObject (BaseObject):
...
manager = EventManager
my_obj = OwnObject ()
# Any signal of the object will be registered automatically.
my_obj.manager = manager
Example 20. Connecting the object to an event manager.
The last important thing to know about the
BaseObject is its ability to emit
events. If the object is connected to an event manager, you
can let it send events through the manager with the object its
emit() method. The syntax is the same
as if you would emit events on the
EventManager directly.
Now let us look at the example we just went through (the
following one is slightly modified only). You can find the
example as python script under
examples/baseobject.py
# BaseObject usage example.
from ocempgui.object import BaseObject
from ocempgui.events import EventManager
# Callbacks, which should be invoked for the object.
def ping_callback (obj, additional_data):
print "The object is: %s" % obj.name
print "Passed data is: %s" % additional_data
def pong_callback ():
print "Another callback with no arguments."
# Object implementation, which can listen to specific events.
class OwnObject (BaseObject):
def __init__ (self, name):
BaseObject.__init__ (self)
self.name = name
# The object should be able to listen to 'ping' and 'pong'
# events.
self._signals["ping"] = []
self._signals["pong"] = []
def notify (self, event):
# This simple notify method will not be used in this
# example. Instead, the signals are invoked directly.
if event.signal == "ping":
self.run_signal_handlers ("ping")
elif event.signal == "pong":
self.run_signal_handlers ("pong")
manager = EventManager ()
# Create an object and connect callbacks to its both events.
my_obj = OwnObject ("First object")
ev1 = my_obj.connect_signal ("ping", ping_callback, my_obj, "data")
ev2 = my_obj.connect_signal ("pong", pong_callback)
# Connect it to the event manager
my_obj.manager = manager
# Invoke the connected signals handlers for a specific event.
manager.emit ("ping", None)
manager.emit ("pong", None)
# After disconnecting a callback, it will not be invoked anymore.
my_obj.disconnect_signal (ev1)
my_obj.disconnect_signal (ev2)
manager.emit ("ping", None)
manager.emit ("pong", None)
Example 21. Ping-Pong with a BaseObject
The ocempgui.object module includes
another event capable object class, the
ActionListener, which inherits from the
BaseObject class, but allows you to
create and delete signals and listening queues as you need
them without the necessity to subclass.
The ActionListener creates the signal
id and a callback queue, when you connect a callback to it and
registers itself for this signal automatically at its event
manager. This can be extremeley useful, if a more flexible
event capable object type is needed, which does not need to do
any sanity checks on the event data. Instead it will send the
event data to the callback as well, which then can work with it.
Once more let us create a ping-ping example using the
ActionListener class instead of a
BaseObject now.
You can find the example as python script under
examples/actionlistener.py
# ActionListener usage example.
import sys
from ocempgui.events import EventManager
from ocempgui.object import ActionListener
count = 0
def emit_pong (event, manager):
print "emit_pong received: [%s] - emitting pong..." % event
manager.emit ("pong", "pong_event")
def emit_ping (event, manager):
global count
if count > 10:
sys.exit ()
count += 1
print "emit_ping received: [%s] - emitting ping..." % event
manager.emit ("ping", "ping_event")
# Create an event manager and two ping-pong listeners.
manager = EventManager ()
listener1 = ActionListener ()
listener1.connect_signal ("ping", emit_pong, manager)
listener1.manager = manager
listener2 = ActionListener ()
listener2.connect_signal ("pong", emit_ping, manager)
listener2.manager = manager
# start ping-pong actions
print "Starting Ping-Pong"
manager.emit ("ping", "ping_event")
Example 22. Ping-Pong with the ActionListener
Looking at the first interesting line, line eight,
def emit_pong (event, manager):
print "emit_pong received: [%s] - emitting pong..." % event
manager.emit ("pong", "pong_event")
EventManager
object, on which it emits a pong event with additional data then.
Line twelve and following does the same, but breaks, if it was invoked more than ten times.
The next lines of interest are line twenty-three to twenty-nine,
listener1 = ActionListener ()
listener1.connect_signal ("ping", emit_pong, manager)
listener1.manager = manager
ActionLister objects will
be created and signal slots and callbacks for the 'ping' and
'pong' events will be set up. In line twenty-five and
twenty-nine the objects will be registered at the event
manager created earlier in the code.
Although this class is very mighty, you should not use it as base for own event capable classes. When the feature set and code amount of your own classes grow, it easily can happen, that you oversee events or that you do not understand which signals it should deal with anymore.
It is however a good and valuable class type to work as proxy or to delegate events to different functions or methods in a context sensitive manner.
The first thing an application using OcempGUI should do is to
initialize the renderering system. The
Renderer class from the
ocempgui.widgets package contains all
necessary parts to take care of this. It includes
the event mangement
a sprite based render engine
methods to create the pygame window
The Renderer can be set up with only
three lines of code.
from ocempgui.widgets import *
re = Renderer ()
re.create_screen (200, 200) # Creates the pygame window
Example 23. Setting up the Renderer
Now that the Renderer is set up, you
can start to place widgets on the pygame window by adding them
using the Renderer.add_widget()
method. Let us do this by building a simple (and well-known)
application with a Button widget on it,
that displays 'Hello World'.
You can find the example as python script under
examples/hello_world.py
# Hello World example.
from ocempgui.widgets import *
# Initialize the drawing window.
re = Renderer ()
re.create_screen (100, 50)
re.title = "Hello World"
re.color = (250, 250, 250)
button = Button ("Hello World")
button.topleft = (10, 10)
re.add_widget (button)
# Start the main rendering loop.
re.start ()
Example 24. Hello World with OcempGUI
The second line
from ocempgui.widgets import*
ocempgui.widgets module you will need
to build an application. It is also possible to use a fine
grained selection of classes and submodules to import, but
mostly the above code will serve well.
The fifth and sixth lines
re = Renderer ()
re.create_screen (100, 50)
Renderer object, which
takes care of updating the screen and the event management and
create a pygame window with a 100x50 size.
The seventh and eight line
re.title = "Hello World"
re.color = (250, 250, 250)
In line ten
button = Button ("Hello World")Button widget is created. The
constructor can receive an additional argument with the text,
the Button should display.
The next line will place the Button at
a specific position.
button.topleft = (10, 10)
Line twelve
re.add_widget (button)
The last line will start the main processing loop of the
Renderer.
re.start ()
Renderer, which will wait
for events, draw and update the widgets and so on.
Every widget of OcempGUI inherits from the
ocempgui.object.BaseObject class and
its event handling makes heavy usage of the
BaseObject features.
To cause a Button to print a message
upon a mouse click, you would connect a message printing
function to the click signal of the
Button. In turn, if this callback is
not needed anymore in the later program flow, you would
disconnect the function from the
Button's signal.
This theoretical model is used in many different toolkits and
OcempGUI stays with it. We will enhance our 'Hello world'
example application from the previous chapter with a callback
now, wich prints a message each time the
Button is clicked.
You can find the example as python script under
examples/hello_world_signals.py
# Hello World example.
from ocempgui.widgets import *
from ocempgui.widgets.Constants import *
def print_message ():
print "The button was clicked!"
# Initialize the drawing window.
re = Renderer ()
re.create_screen (100, 50)
re.title = "Hello World"
re.color = (250, 250, 250)
button = Button ("Hello World")
button.topleft = (10, 10)
button.connect_signal (SIG_CLICKED, print_message)
re.add_widget (button)
# Start the main rendering loop.
re.start ()
Example 25. Hello World with callbacks
The first change you note is the new import directive in line three.
from ocempgui.widgets.Constants import *
ocempgui.widgets module. The availabe
signal identifiers used by the various widgets of OcempGUI
area prefixed with SIG_.
Line five and six contain the function, which will be used as the callback for the button. It is indifferent, if the callback is a class or object method or a function. Both cases will work in the same way.
The next notably change was done in line 16
button.connect_signal (SIG_CLICKED, print_message)
Button to invoke the
print_message function each time it
is clicked. You also could send additional data to the
callback as you already know from the section called “Making objects event capable - the better way”. Anything written about
the signal handling of the BaseObject
class applies to the widgets, too.
The following sections cover the possibilities and capabilities of
the different widgets, the ocempgui.widgets
module offers. The sections will not cover any method and
attribute of the widgets in detail, but just the most important
ones. It is strongly recommended, that you read through the doc
strings of the widgets, too, to get a complete overview about
them.
The ocempgui.widgets module contains some
globally accessed settings, that influence its complete
behaviour. Those can be found in the
ocempgui.widgets.base part and contain
the Style, which is currently in use, the
timer rate for double-clicks and a debugging flag. You can
adjust those settings easily by simply binding them to new
values.
Before you go ahead and change base.GlobalStyle, you should read the section called “Changing the widget appearance”.
The base.debug setting is for additional debugging output and should not be set to True usually (unless you are using a development version).
The last one, base.DoubleClickRate should
be set using the
base.set_doubleclick_rate() method only
and denotes the maximum time to elaps between two click
operations to identify them as a double-click.
The BaseWidget is the most basic class of
all widgets and contains common attributes and methods, each
widget of the ocempui.widgets module has to
include. Every widget inherits from it, so that any description
and explanation in this section also to the widgets, which are
explained later on.
A widget can be set to a specific position on the main screen using the topleft attribute.
widget.topleft = 10, 15
This will not work as supposed using widgets, that are bound
to a Container or
Bin widget, which will be explained
in one of the following sections.
If you read the current topleft value of a widget, keep in mind, that it will return a tuple containing the both, x and y, coordinates.
Every widget supports a minimum size, that will be respected by the default drawing methods.
widget.minsize = 80, 20
The currently used width and height, which can differ from its minsize can be retrieved using the width and height attributes or as tuple using the size.
if (widget.width == widget.minsize[0]) and (widget.height == widget.minsize[1]):
print "Widget does not exceed its minimum size."
The counterpart to the minsize attribute is maxsize. A widget will never grow beyond the values of it, if set.
The BaseWidget class exports all
pygame.Rect attributes, so you are not
limited to the topleft only, but can also
use the pygame.Rect attributes you are
used to. See the pygame.Rect documentation for more
details.
To allow an easy and logical keyboard navigation, widgets have an index attribute, which influences the navigation order using the keyboard.
widget.index = 3
The input focus mentioned above denotes a state of the widget,
in which the user can interact with it using the keyboard only.
A Button widget will react upon pressing
the space bar with a click while an Entry
widget will let the user type text. You can set the input focus
of a widget manually with the focus attribute.
widget.focus = True
Table). Dependant
on the widget the set_focus() method of it
will return either True or False, which indicates, that the
input focus could be sucessfully set for that widget or not.
frame = VFrame ()
focusok = frame.set_focus (True)
if not focusok:
print "Focus could not be set."
Widgets can be disabled from user interaction and receiving events, if you set their sensitive attribute to False.
widget.sensitive = False
The renderering system of the
ocempgui.widgets module can use different
layers, on which widgets are drawn. This is especially useful
and often necessary to place widgets above others (e.g. to place
a Window widget above another one).
widget.depth = 3
Widgets support transparency using the alpha color channel. To set it for a widget use the opacity attribute of it. The opacity attribute accepts values betwee 0 (fully transparent) and 255 (not transparent, default).
widget.opacity = 100
To change the appearance of a single widget instance, the
create_style() method and
style attribute of a widget can be used.
While create_style() will generate an
instance specific style dictionary for the widget,
style can be used to have quick access to
it, once it is created.
# Create an instance specific style dictionary for the widget and return a
# reference to it.
style = widget.create_style()
...
# Access the created style dictionary of the widget.
widget.style[...] = ...
create_style().
Changing instance specific styles is explained in detail in the section called “Changing the appearance of single instances”.
Labels are non-interactive decorative user interface elements, which provide certain information to the user.
The Label class can display a short amount
of text and allows you to control interaction with other widgets
using keyboard mnemonics.
To create a Label, you typically will use
my_label = Label (text)
To set the text after creation, use the text
attribute or set_text() method.
label.text = "New Text"
label.set_text ("New Text")
It is possible to have multiline text by setting the multiline attribute to True:
label.multiline = True
label.set_multiline (True)
Labels support keyboard accelerators, so called mnemonics, which
can activate other widgets. The '#' will cause the directly
following to work as mnemonic character. If you want to cause
the Label to display a normal '#', use
'##' in the text.
label.text = '#Mnemonic'
label.set_text ('A simple hash: ##')
If a mnemonic is set up, you usually have to set the widget, which should be activated by the mnemonic as well.
label.widget = another_widget
Below you will find an example to illustrate most of the
abilities of the Label widget class. You
do not need to care about other widgets like the
Frame class for now as those are
explained later on.
You can find the following example as a python script under
examples/label.py.
# Label examples.
import os
from ocempgui.widgets import *
from ocempgui.widgets.Constants import *
def _create_vframe (text):
frame = VFrame (Label (text))
frame.spacing = 5
frame.align = ALIGN_LEFT
return frame
def create_label_view ():
states = ("STATE_NORMAL", "STATE_ENTERED", "STATE_ACTIVE",
"STATE_INSENSITIVE")
table = Table (2, 3)
table.spacing = 5
table.set_row_align (0, ALIGN_TOP)
table.set_row_align (1, ALIGN_TOP)
# Frame with the states.
frm_states = _create_vframe ("States")
for i, s in enumerate (states):
lbl = Label (s)
if STATE_TYPES[i] == STATE_INSENSITIVE:
lbl.sensitive = False
else:
lbl.state = STATE_TYPES[i]
frm_states.add_child (lbl)
table.add_child (0, 0, frm_states)
# Frame with different padding.
frm_padding = _create_vframe ("Padding")
for i in xrange (5):
lbl = Label ("Padding: %dpx" % (i * 2))
lbl.padding = i * 2
frm_padding.add_child (lbl)
table.add_child (0, 1, frm_padding)
# Frame with mnemonic support.
frm_mnemonics = _create_vframe ("Mnemonics")
strings = ("#Simple Mnemonic", "A ## is displayed using '####'",
"M#ultiple M#nemonics #have no #effect")
for s in strings:
lbl = Label (s)
frm_mnemonics.add_child (lbl)
table.add_child (0, 2, frm_mnemonics)
# Frame with multiline labels.
frm_multiline = _create_vframe ("Multiline labels")
frm_multiline.align = ALIGN_NONE
strings = ("Single line", "First lines\nSecond line",
"First line\nSecond line\nThird Line",
"Two lines with a\n#mnemonic")
for s in strings:
lbl = Label (s)
lbl.multiline = True
frm_multiline.add_child (lbl)
table.add_child (1, 0, frm_multiline)
return table
if __name__ == "__main__":
# Initialize the drawing window.
re = Renderer ()
re.create_screen (500, 350)
re.title = "Label examples"
re.color = (234, 228, 223)
re.add_widget (create_label_view ())
# Start the main rendering loop.
re.start ()
Example 26. Label example
The ImageLabel is a decorative widget
holding an image and not much functionality besides that. It
does not support mnemonics nor text and is mainly used to
display images in a GUI, whenever they should fit smoothly into
the layout.
To create an ImageLabel, you have pass
either the name of a file to load (including the full path to
it) or a pygame.Surface object to
display.
imagelabel = ImageLabel ("path/to/an/image.png")
imagelabel = ImageLabel (pygame_surface)
set_picture() method.
In contrast to the Label class, the
ImageLabel supports different border
styles to adjust its look and feel without the need to override
its drawing methods.
imagelabel.border = BORDER_NONE
imagelabel.set_border (BORDER_NONE)
Below you will find an example to illustrate most of the
abilities of the ImageLabel widget
class. You do not need to care about other widgets like the
Frame class for now as those are
explained later on.
You can find the following example as a python script under
examples/imagelabel.py.
# ImageLabel examples.
import os
from ocempgui.draw import Image
from ocempgui.widgets import *
from ocempgui.widgets.Constants import *
def _create_vframe (text):
frame = VFrame (Label (text))
frame.spacing = 5
frame.align = ALIGN_LEFT
return frame
def create_imagelabel_view ():
image = Image.load_image ("./image.png")
table = Table (1, 3)
table.spacing = 5
table.set_row_align (0, ALIGN_TOP)
# Frame with the states.
frm_states = _create_vframe ("States")
for i, s in enumerate (STATE_TYPES):
lbl = ImageLabel (image)
if s == STATE_INSENSITIVE:
lbl.sensitive = False
else:
lbl.state = s
frm_states.add_child (lbl)
table.add_child (0, 0, frm_states)
# Frame with different padding.
frm_padding = _create_vframe ("Padding")
for i in xrange (5):
lbl = ImageLabel (image)
lbl.border = BORDER_FLAT
lbl.padding = i * 2
frm_padding.add_child (lbl)
table.add_child (0, 1, frm_padding)
# Borders.
frm_borders = _create_vframe ("Borders")
for border in BORDER_TYPES:
lbl = ImageLabel (image)
lbl.border = border
frm_borders.add_child (lbl)
table.add_child (0, 2, frm_borders)
return table
if __name__ == "__main__":
# Initialize the drawing window.
re = Renderer ()
re.create_screen (500, 350)
re.title = "ImageLabel examples"
re.color = (234, 228, 223)
re.add_widget (create_imagelabel_view ())
# Start the main rendering loop.
re.start ()
Example 27. ImageLabel example
Buttons are interactive user interface elements, which usually react upon mouse events such as clicks or similar events.
You already learned about the Button widget
in an earlier section, so let us look at some interesting details
of it now.
The Button widget is a interactive
widget, which reacts upon mouse input such as clicks. Basically
it is a container (which will be explained detailled later on),
which holds a Label widget to display its
text.
To create a Button, you usually will type
button = Button (text)
The usage of mnemonics for the Button is easy to achieve by
simply supplying a mnemonic text as described in the section called “Label”. You can set the text directly through
the text attribute or
set_text() method.
button.text = "#Mnemonic"
button.set_text ("#Mnemonic")
Button its
Label as this already has been done on
creation of the Button.
The Button supports different border
styles to adjust its look and feel without the need to override
its drawing methods.
button.border = BORDER_NONE
button.set_border (BORDER_NONE)
The Button widget has some default
signals, it listens to. Those are
SIG_MOUSEDOWN - Invoked, when a mouse button is pressed down on the Button.
SIG_MOUSEUP - Invoked, when a mouse button is released on the Button.
SIG_MOUSEMOVE - Invoked, when the mouse moves over the Button area.
SIG_CLICKED - Invoked, when the left mouse button is pressed and released over the Button.
Below you will find an example to illustrate most of the
abilities of the Button widget class. You
do not need to care about other widgets like the
Frame class for now as those are
explained later on.
You can find the following example as a python script under
examples/button.py.
# Button examples.
import os
from ocempgui.widgets import *
from ocempgui.widgets.Constants import *
def _create_vframe (text):
frame = VFrame (Label (text))
frame.spacing = 5
frame.align = ALIGN_LEFT
return frame
def create_button_view ():