# $Id: ToggleButton.py,v 1.29.2.5 2007/01/26 22:51:33 marcusva Exp $
#
# Copyright (c) 2004-2007, Marcus von Appen
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form 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.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""A button widget, which can retain its state."""
from ButtonBase import ButtonBase
from Label import Label
from Constants import *
import base
class ToggleButton (ButtonBase):
"""ToggleButton (text) -> ToggleButton
A button widget class, which can retain its state.
The default ToggleButton widget looks and behaves usually the same
as the Button widget except that it will retain its state upon
clicks.
The state of the ToggleButton can be set with the 'active' attribute
or set_active() method. If the ToggleButton is active, the 'state'
attribute will be set to STATE_ACTIVE by default and will be reset,
if the ToggleButton is not active anymore.
toggle.active = True
toggle.set_active (False)
The ToggleButton supports different border types by setting its
'border' attribute to a valid value of the BORDER_TYPES constants.
toggle.border = BORDER_SUNKEN
toggle.set_border (BORDER_SUNKEN)
Default action (invoked by activate()):
The Button emulates a SIG_TOGGLED event and runs the connected
callbacks.
Mnemonic action (invoked by activate_mnemonic()):
See the Button class.
Signals:
SIG_TOGGLED - Invoked, when the ToggleButton is toggled.
Attributes:
active - The current state of the ToggleButton as boolean.
border - The border style to set for the ToggleButton.
text - The text to display on the ToggleButton.
"""
def __init__ (self, text=None):
ButtonBase.__init__ (self)
self._border = BORDER_RAISED
# Internal click handler
self.__click = False
self._active = False
# The ToggleButton emits a 'toggled' event.
self._signals[SIG_TOGGLED] = []
self.set_text (text)
def set_text (self, text=None):
"""T.set_text (...) -> None
Sets the text to display on the ToggleButton.
Sets the text to display on the ToggleButton by referring to the
'text' attribute of its child Label.
"""
if text != None:
if self.child:
self.child.set_text (text)
else:
self.child = Label (text)
else:
self.child = None
def get_text (self):
"""T.get_text () -> string
Returns the set text of the ToggleButton.
Returns the text set on the Label of the ToggleButton.
"""
if self.child:
return self.child.text
return ""
def set_border (self, border):
"""T.set_border (...) -> None
Sets the border type to be used by the ToggleButton.
Raises a ValueError, if the passed argument is not a value from
BORDER_TYPES
"""
if border not in BORDER_TYPES:
raise ValueError ("border must be a value from BORDER_TYPES")
self._border = border
self.dirty = True
def set_child (self, child=None):
"""B.set_child (...) -> None
Sets the Label to display on the Button.
Creates a parent-child relationship from the Button to a Label
and causes the Label to set its mnemonic widget to the Button.
Raises a TypeError, if the passed argument does not inherit
from the Label class.
"""
self.lock ()
if child and not isinstance (child, Label):
raise TypeError ("child must inherit from Label")
ButtonBase.set_child (self, child)
if child:
child.set_widget (self)
if not child.style:
child.style = self.style or \
base.GlobalStyle.get_style (self.__class__)
self.unlock ()
def set_state (self, state):
"""T.set_state (...) -> None
Sets the state of the ToggleButton.
Sets the state of the ToggleButton and causes its child to set
its state to the same value.
"""
if (self.active and (state != STATE_ACTIVE)) or (self.state == state):
return
self.lock ()
if self.child:
self.child.state = state
ButtonBase.set_state (self, state)
self.unlock ()
def set_active (self, active):
"""T.set_active (...) -> None
Sets the state of the ToggleButton.
"""
if active:
if not self._active:
self._active = True
if self.sensitive:
self.state = STATE_ACTIVE
# Enforce an update here, because the state might have
# been already modified in the notify() method.
self.dirty = True
else:
if self._active:
self._active = False
if self.sensitive:
self.state = STATE_NORMAL
# Enforce an update here, because the state might have
# been already modified in the notify() method.
self.dirty = True
def activate (self):
"""T.activate () -> None
Activates the ToggleButton default action.
Activates the Button default action. This usually means toggling
the button, emulated by inverting the 'active' attribute and
running the attached callbacks for the SIG_TOGGLED signal.
"""
self.lock ()
if not self.sensitive:
return
self.focus = True
self.set_active (not self.active)
self.unlock ()
self.run_signal_handlers (SIG_TOGGLED)
def notify (self, event):
"""T.notify (event) -> None
Notifies the ToggleButton about an event.
"""
if not self.sensitive:
return
if event.signal in SIGNALS_MOUSE:
eventarea = self.rect_to_client ()
if event.signal == SIG_MOUSEDOWN:
if eventarea.collidepoint (event.data.pos):
self.focus = True
# The button only acts upon left clicks.
if event.data.button == 1:
self.__click = True
self.state = STATE_ACTIVE
self.run_signal_handlers (SIG_MOUSEDOWN, event.data)
event.handled = True
elif event.signal == SIG_MOUSEUP:
if eventarea.collidepoint (event.data.pos):
self.run_signal_handlers (SIG_MOUSEUP, event.data)
if event.data.button == 1:
if self.__click:
# The usual order for a ToggleButton: get a
# click and toggle state upon it.
self.run_signal_handlers (SIG_CLICKED)
self.set_active (not self.active)
self.run_signal_handlers (SIG_TOGGLED)
if not self.active:
self.state = STATE_ENTERED
event.handled = True
elif (event.data.button == 1) and self.__click and \
not self.active:
# Only a half click was made, reset the state of the
# ToggleButton.
self.state = STATE_NORMAL
elif event.signal == SIG_MOUSEMOVE:
ButtonBase.notify (self, event)
else:
# Any other event will be escalated to the parent(s).
ButtonBase.notify (self, event)
def draw_bg (self):
"""T.draw () -> Surface
Draws the ToggleButton background surface and returns it.
Creates the visible surface of the ToggleButton and returns it
to the caller.
"""
return base.GlobalStyle.engine.draw_button (self)
def draw (self):
"""R.draw () -> None
Draws the ToggleButton surface and places its Label on it.
"""
ButtonBase.draw (self)
if self.child:
self.child.center = self.image.get_rect ().center
self.image.blit (self.child.image, self.child.rect)
active = property (lambda self: self._active,
lambda self, var: self.set_active (var),
doc = "The state of the ToggleButton.")
text = property (lambda self: self.get_text (),
lambda self, var: self.set_text (var),
doc = "The text of the ToggleButton.")
border = property (lambda self: self._border,
lambda self, var: self.set_border (var),
doc = "The border style to set for the ToggleButton.")
syntax highlighted by Code2HTML, v. 0.9.1