Container widgets are able to hold other widgets and take care of
drawing them on their own surface. They are mostly used for layout
purposes or complex widgets, which consist of several other
widgets or which need to add additional functionality to different
widget types (like the ScrolledWindow
widget). They allow to bind one or more widgets as child(ren) to
themselves and take over the role as parent widget.
The abstract Bin
class is a container,
that is able to hold exactly one child. It allows to bind und
unbind a child widget and supports setting an additional padding
between its surface borders and the child surface.
You usually will not create a Bin
object directly, but inherit from it in your own widget classes.
The child of a Bin
can be set with the
child attribute or
set_child()
method. It is not necessary
to register the child at an event manager after binding it to
the Bin
as this will set the event
manager of the child to its own one.
label = Label ("Label for a bin") bin.set_child (label) # No need to set an event manager explicitly for the label. bin.manager = eventmanager
For layout purposes the Bin
can make use
of additional pixels to place between its outer surface edges
and the child surface. This pixel amount can be modified and
used using the padding attribute of the
Bin
. Various inheritors within the
widgets module of OcempGUI make heavy use of this attribute to
adjust the look of themselves. The following example
demonstrates this.
from ocempgui.widgets import Button, Renderer renderer = Renderer () renderer.create_screen (200, 120) button_5px = Button ("Button with 5px padding") button_5px.topleft = 5, 5 button_5px.padding = 5 button_10px = Button ("Button with 10px padding") button_10px.topleft = 5, 60 button_10px.padding = 10 renderer.add_widget (button_5px, button_10px) renderer.start ()
Example 34. Bin.padding example
The following example provides a complete
Bin
implementation which can rotate the
visible of its child (and only the surface).
You can find the following example as a python script under
examples/bin.py
.
# Bin examples. import pygame from ocempgui.widgets import * from ocempgui.widgets.Constants import * class PivotBin (Bin): """PivotBin (widget) -> OwnBin A Bin implementation example class. This class does not support real rotations of widgets. Instead it simply rotates their image surface and displays it. Any other behaviour and information of the widget stay the same. Thus event capable widgets will not work correctly. """ def __init__ (self): Bin.__init__ (self) self._orientation = ORIENTATION_HORIZONTAL def set_orientation (self, orientation=ORIENTATION_HORIZONTAL): """P.set_orientation (...) -> None Sets the orientation of the attached child. """ if orientation not in ORIENTATION_TYPES: raise ValueError("orientation must be a value of ORIENATION_TYPES") self._orientation = orientation self.dirty = True def draw_bg (self): width, height = self.padding, self.padding cls = self.__class__ if self.child: width += self.child.width height += self.child.height if self.orientation == ORIENTATION_VERTICAL: # Swap width and height on demand width, height = height, width # Guarantee the set minimum and maximum sizes. width, height = self.check_sizes (width, height) surface = base.GlobalStyle.engine.draw_rect (width, height, self.state, cls, self.style) return surface def draw (self): """Draws the PivotBin and its child according to the set orientation.""" Bin.draw (self) if self.child: rect = self.image.get_rect () self.child.center = rect.center if self.orientation == ORIENTATION_VERTICAL: # Rotate the child image on demand. image = pygame.transform.rotate (self.child.image, 90) rotate_rect = image.get_rect () rotate_rect.center = rect.center self.image.blit (image, rotate_rect) else: self.image.blit (self.child.image, self.child.rect) orientation = property (lambda self: self._orientation, lambda self, var: self.set_orientation (var), doc = "The orientation of the child.") def rotate_bin (bin, button): # Set the Bin orientation and replace the button. if bin.orientation == ORIENTATION_HORIZONTAL: bin.orientation = ORIENTATION_VERTICAL else: bin.orientation = ORIENTATION_HORIZONTAL button.topleft = bin.left, bin.bottom + 10 if __name__ == "__main__": bin = PivotBin () bin.topleft = 10, 10 bin.child = Label ("Simple label in PivotBin") button = Button ("Switch orientation") button.topleft = bin.left, bin.bottom button.connect_signal (SIG_CLICKED, rotate_bin, bin, button) # Initialize the drawing window. re = Renderer () re.create_screen (300, 300) re.title = "Bin implementation example" re.color = (234, 228, 223) re.add_widget (bin, button) # Start the main rendering loop. re.start ()
Example 35. Bin example
The abstract Container
widget class is
similar to the Bin
class, except that it
can hold more than one widget.
As well as with the Bin
, you usually will
not create a Container
object directly,
but inherit from it in your own class.
The children can be added using
add_child()
, which allows to add
multiple children at once, or
the insert_child()
method.
# Add a single child. container.add_child (label1) # Add multiple children at once. container.add_child (label1, button1, label2, entry1) # Insert a child at a specific position container.insert_child (1, entry2)
Container
implementation, insert_child()
can
cause the inserted child to appear at a specific position.
The removal of children can be done with one widget at a time only
using the remove_child()
method of the
Container
.
# Only one child can be removed at a time. container.remove_child (button1)
You also can set the children directly using the
children attribute. The
Container
will remove all its children
first and then add the new list of widgets. Simply setting the
children to None will remove all the
widgets of the Container
.
# Set a list of widgets as children of a container. container.children = [label1, button1, entry1] # Remove all children of the container. container.children = None
Besides the padding attribute, which was
already explained in the section called “Bin” the
Container
has a
spacing attribute, which indicates the
pixel amount to place between its children.
container.spacing = 10
Frame
examples in the later section
will show you possible concrete results.
TODO: provide implementation examples
The Box
widgets are
Container
s which allow an absolute
placement of their attached children. This enables you to let
widgets overlap or place them next o each other according to
your needs while keeping them relative to other widgets without
extensive position calculations. You could e.g. place a
Box
into a Frame
so that the contents of the Box
are
aligned relative to other widgets of the
Frame
while being positioned exactly as
you need.
To create a Box
you have to call its
constructor with the size it should occupy.
box = Box (100, 100)
Container
methods. The topleft
coordinates of the attached widgets are used to position them
within the Box. Thus a Button
with the
topleft value of 10, 10 will be placed 10
pixels from the topleft corner of the Box
.
box = Box (100, 100) button = Button ("A Button") button.topleft = 10, 10 box.add_child (button)
The Box
widget class does not make use
of the padding and
spacing attributes of its
Container
parent class. It also does
not resize itself, when a widget leaves its visible area or
occupies more space than the Box
.
Below you will find an example to illustrate most of the
abilities of the Box
widget
classes. You do not need to care about other widgets like the
VFrame
class for now as those are
explained later on.
You can find the following example as a python script under
examples/box.py
.
# Box examples. from ocempgui.widgets import * from ocempgui.widgets.Constants import * def create_box_view (): frame = VFrame (Label ("Box example")) frame.topleft = 10, 10 # The Box with 200x200 pixels in size. box = Box (200, 200) # Widgets to place into it. label = ImageLabel ("image.png") label.topleft = 10, 10 button = Button ("A Button") button.topleft = 30, 30 frame1 = VFrame (Label ("A VFrame")) frame1.add_child (Label ("Label in the VFrame")) frame1.topleft = 60, 80 chk = CheckButton ("A CheckButton") chk.topleft = 130, 110 box.children = label, button, frame1, chk frame.add_child (box) return frame if __name__ == "__main__": # Initialize the drawing window. re = Renderer () re.create_screen (300, 300) re.title = "Box examples" re.color = (234, 228, 223) re.show_layer_info = True re.add_widget (create_box_view ()) # Start the main rendering loop. re.start ()
Example 36. Box example
Frame
widgets are
Container
s, that support drawing a
decorative border and title widget around their children. The
basic Frame
class is an abstract class,
which defines the common attributes and methods for its
inheritors, while the HFrame
and
VFrame
widgets are concrete
implementations, that place their children horizontally or
vertically. Both subclasses only differ in the placing behaviour
of their widgets and the basic aligning possibilities, so that,
if not stated otherwise, the following examples always apply to
both widget types, although the HFrame
is
used.
The creation of a HFrame
is done using
frame = HFrame () frame = HFrame (widget)
Frame
. The title widget can be any valid
BaseWidget
subclass and typically be
placed at the topleft corner of the
Frame
. If no title widget is supplied, a
complete border will be drawn around the
Frame
, while a set title widget will
discontinue that border at the topleft corner. You can change
or set the title widget at any later time. A few possibilities
should be shown here.
frame = HFrame (Label ("A title label")) frame.widget = Button ("Button as frame title") frame.set_widget (VFrame (Label ("Frame in a frame")))
To adjust the look of the Frame
, it is
possible to change its border style using the
border attribute
frame.border = BORDER_NONE frame.set_border (BORDER_SUNKEN)
frame.align = ALIGN_TOP frame.align = ALIGN_LEFT
HFrame
and
VFrame
has to be made, because each one
only supports a subset of the alignment possibilities. The
HFrame
widget natively supports aligning
its children at the top or bottom only
hframe.align = ALIGN_TOP hframe.set_align (ALIGN_BOTTOM)
VFrame
supports only the left
and right alignment.
vframe.align = ALIGN_LEFT vframe.set_align (ALIGN_RIGHT)
Below you will find an example to illustrate most of the
abilities of the Frame
widget
classes. You do not need to care about other widgets like the
Table
class for now as those are
explained later on.
You can find the following example as a python script under
examples/frame.py
.
# Frame examples. import os from ocempgui.widgets import * from ocempgui.widgets.Constants import * def create_frame_view (): table = Table (2, 3) table.topleft = 5, 5 table.spacing = 5 # Create and display two 'standard' frames. hframe = HFrame (Label ("Horizontal Frame")) table.add_child (0, 0, hframe) lbl = Label ("Vertical Frame" + os.linesep + "with 5 px spacing") lbl.multiline = True vframe = VFrame (lbl) vframe.spacing = 5 table.add_child (0, 1, vframe) for i in xrange(3): btn = Button ("Button %d" % i) hframe.add_child (btn) btn2 = Button ("Button %d" % i) vframe.add_child (btn2) # Create framed frames. framed1 = VFrame (Label ("VFrame")) framed2 = HFrame () framed3 = VFrame (Label ("VFrame as HFrame.widget")) framed3.add_child (Label ("Child of a VFrame")) framed2.widget = framed3 framed2.add_child (Button ("Button 1"), Button ("Button 2")) button = Button ("Simple Button") framed1.add_child (framed2, button) table.add_child (1, 0, framed1) # Create a Frame with alignment. frame_align = VFrame (Label ("VFrame with right alignment")) frame_align.align = ALIGN_RIGHT label1 = Label ("Label") label2 = Label ("Even longer label") button = CheckButton ("A CheckButton") frame_align.add_child (label1, label2, button) table.add_child (1, 1, frame_align) # Add insensitive frames. hframe = HFrame (Label ("Insensitive HFrame")) hframe.sensitive = False table.add_child (0, 2, hframe) vframe = VFrame (Label ("Insensitive VFrame")) vframe.sensitive = False table.add_child (1, 2, vframe) for i in xrange(3): btn = Button ("Button %d" % i) hframe.add_child (btn) btn2 = Button ("Button %d" % i) vframe.add_child (btn2) return table if __name__ == "__main__": # Initialize the drawing window. re = Renderer () re.create_screen (600, 300) re.title = "Frame examples" re.color = (234, 228, 223) re.add_widget (create_frame_view ()) # Start the main rendering loop. re.start ()
Example 37. Frame example
Table
widgets, which inherit from the
Container
class, allow a table-like
placement of their children in rows and columns. The children
can be placed in the table cells and each cell supports an
individual alignment.
The Table
constructor expects the row and
column dimensions to set up for the table, so that
table = Table (2, 2) table2 = Table (3, 5)
Adding children is different from the usual
Container
methods. The
Table
needs the additional information,
in which cell to place the child. Thus instead of using
table.add_child (child_widget1, child_widget2) # This does not work!
table.add_child (row, column, child_widget1) table.add_child (row1, column1, child_widget2)
Table
in
contrast works in the same way you already learned about in
the section called “Container”.
Assigning the children attribute of the
Table
with a list of widgets adds the
children row for row to it.
As said above, each cell of the Table
supports using an own alignment, which can be set with the
set_align()
method.
table.set_align (0, 1, ALIGN_TOP) table.set_align (1, 1, ALIGN_LEFT)
Table
documentation.
# Aligning the child at the topleft. table.set_align (0, 1, ALIGN_TOP | ALIGN_LEFT) # Conflicting alignments, thus using the higher priority of ALIGN_TOP. table.set_align (0, 1, ALIGN_TOP | ALIGN_BOTTOM) # Again a conflict. ALIGN_NONE has the lowest priority, thus it will be # left aligned. table.set_align (0, 1, ALIGN_LEFT | ALIGN_NONE)
table.set_row_align (row, alignment) table.set_column_align (column, alignment)
Below you will find an example to illustrate most of the
abilities of the Table
widget class.
You can find the following example as a python script under
examples/table.py
.
# Table examples. from ocempgui.widgets import Renderer, Table, Label, Button from ocempgui.widgets.Constants import * def create_table_view (): # Crate and display a Table. table = Table (9, 2) table.spacing = 5 table.topleft = 5, 5 label = Label ("Nonaligned wide Label") table.add_child (0, 0, label) table.add_child (0, 1, Button ("Simple Button")) label = Label ("Top align") table.add_child (1, 0, label) table.set_align (1, 0, ALIGN_TOP) table.add_child (1, 1, Button ("Simple Button")) label = Label ("Bottom align") table.add_child (2, 0, label) table.set_align (2, 0, ALIGN_BOTTOM) table.add_child (2, 1, Button ("Simple Button")) label = Label ("Left align") table.add_child (3, 0, label) table.set_align (3, 0, ALIGN_LEFT) table.add_child (3, 1, Button ("Simple Button")) label = Label ("Right align") table.add_child (4, 0, label) table.set_align (4, 0, ALIGN_RIGHT) table.add_child (4, 1, Button ("Simple Button")) label = Label ("Topleft align") table.add_child (5, 0, label) table.set_align (5, 0, ALIGN_TOP | ALIGN_LEFT) table.add_child (5, 1, Button ("Simple Button")) label = Label ("Topright align") table.add_child (6, 0, label) table.set_align (6, 0, ALIGN_TOP | ALIGN_RIGHT) table.add_child (6, 1, Button ("Simple Button")) label = Label ("Bottomleft align") table.add_child (7, 0, label) table.set_align (7, 0, ALIGN_BOTTOM |ALIGN_LEFT) table.add_child (7, 1, Button ("Simple Button")) label = Label ("Bottomright align") table.add_child (8, 0, label) table.set_align (8, 0, ALIGN_BOTTOM |ALIGN_RIGHT) table.add_child (8, 1, Button ("Simple Button")) return table if __name__ == "__main__": # Initialize the drawing window. re = Renderer () re.create_screen (250, 350) re.title = "Table examples" re.color = (234, 228, 223) re.add_widget (create_table_view ()) # Start the main rendering loop. re.start ()
Example 38. Table example
ScrolledWindow
widgets are
Bin
widgets that add scrolling abilities
to their attached child. This is extremely useful for
situations, in which the widget to scroll should not exceed a
specific size, but has to display all of its data. Putting such
a widget in a ScrolledWindow
allows you
to respect this specific size, while the widget can grow as it
wants.
You create a ScrolledWindow
using
window = ScrolledWindow (width, height)
width
and
height
denote values for the
size attribute of the
ScrolledWindow
. The newly created
ScrolledWindow
will not exceed this size
by default.
The widget, which needs to be scrolled is packed into the
ScrolledWindow
the usual
Bin
way.
window.child = widget_to_scroll
To allow a flexibly scrolling behaviour, you can adjust the
scrolling attribute to make the
ScrolledWindow
automatic scrolling,
always scrolling or never scrolling.
window.scrolling = SCROLL_ALWAYS window.scrolling = SCROLL_NEVER window.scrolling = SCROLL_AUTO
ScrollBar
controls attached to the window
and is described in details in the
set_scrolling()
method documentation.
You also can influence the scrollbars of the
ScrolledWindow
programmatically.
window.vscrollbar.value = 0 window.hscrollbar.value = window.vscrollbar.maximum
ScrolledWindow
to scroll to the topright
of its attached child.
The ScrolledWindow
widget natively
listens to two signals to support better navigation
possibilities. Those are
SIG_MOUSEDOWN - Invoked, when a mouse button is pressed down on the ScrolledWindow.
SIG_KEYDOWN - Invoked, when a key gets pressed.
The ScrolledList
widget class can be used
to display and manage collections in a list-style look and
feel. Technically it is a ScrolledWindow
,
which holdes a ListViewPort
that takes
care of displaying the list contents.
As the ScrolledWindow
the
ScrolledList
supports different scrolling
types and anything else, while the keyboard and mouse behaviour
differ slightly to match the needs of list browsing.
To create a ScrolledList
you have to
supply the sizing dimensions and an optional collection, which
makes up the contents.
scrolledlist = ScrolledList (width, height, collection=None)
ListItemCollection
(explained later)
object is automatically bound to it. The
ScrolledList
however only accepts
collections, which inherit from the
ListItemCollection
.
Items can be added and removed dynamically to the list using the items property.
for no in xrange (10): scrolledlist.items.append (TextListItem ("Item no. %d" % no)) # Last one was too much. scrolledlist.items.remove (scrolledlist.items[-1])
ListItemCollection
wraps a list
and fully supports all important operations of, including
slicing, indexed item access and sorting. More details about the
ListItemCollection
can be found in the section called “ListItemCollection”.
The FileList
widget is useful to display
filesystem contents in a list-style manner. It supports the
distinction between different filetypes through the
FileListItem
item type and allows you to
browse filesystem contents.
As the ScrolledList
, from which the
FileList
inherits, you need to pass the
size dimensions it should occupy and optionally the initial
starting directory to list the contents of.
filelist = FileList (200, 400, "/usr/home")
FileList
will list the contents of the
current directory as set in os.curdir
.
It allows to list directories programmatically through the
directory attribute and
set_directory()
method.
filelist.directory = "C:\" filelist.set_directory ("/tmp")
FileList
will not
list it, but instead provide an acoustic signal using
print "\a"
.
The FileList
additionally supports the
SIG_DOUBLECLICKED signal to allow changing directories
interactively. Of course own methods can be bound to it.