# $Id: Scale.py,v 1.40.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.
"""Slider widgets for selecting a value from a range."""
from pygame import K_KP_PLUS, K_PLUS, K_RIGHT, K_DOWN, K_KP_MINUS, K_MINUS
from pygame import K_LEFT, K_UP, K_PAGEUP, K_PAGEDOWN, K_HOME, K_END, Rect
from Range import Range
from ocempgui.draw import Draw
from Constants import *
from StyleInformation import StyleInformation
import base
class Scale (Range):
"""Scale (minimum, maximum, step=1.0) -> Scale
An abstract scaling widget class for numerical value selections.
The Scale is an abstract widget class, which enhances the Range by
different events and an activation method. Concrete implementations
of it are the HScale, a horizontal scale widget, and the VScale,
vertical scale widget.
Inheriting widgets have to implement the _get_value_from_coords()
method, which calculates the value of the Scale using a pair of
absolute screen coordinates relative to a given area. Example
implementations can be found in the HScale and VScale widget
classes.
Default action (invoked by activate()):
Give the Scale the input focus.
Mnemonic action (invoked by activate_mnemonic()):
None
Signals:
SIG_MOUSEDOWN - Invoked, when a mouse button is pressed on the
Scale.
SIG_MOUSEUP - Invoked, when a mouse buttor is released on the
Scale.
SIG_MOUSEMOVE - Invoked, when the mouse moves over the Scale.
"""
def __init__ (self, minimum, maximum, step=1.0):
Range.__init__ (self, minimum, maximum, step)
# Internal click and mouse enter detection.
self.__click = False
self._signals[SIG_MOUSEDOWN] = []
self._signals[SIG_MOUSEMOVE] = []
self._signals[SIG_MOUSEUP] = []
self._signals[SIG_KEYDOWN] = None # Dummy for keyboard activation.
def activate (self):
"""S.activate () -> None
Activates the Scale default action.
Activates the Scale default action. This usually means giving
the Scale the input focus.
"""
if not self.sensitive:
return
self.focus = True
def _get_value_from_coords (self, area, coords):
"""S._get_value_from_coords (...) -> float
Calculates the float value of the Scale.
Calculates the float value of the Scale from the passed
absolute coordinates tuple relative to the area.
This method has to be implemented by inherited widgets.
"""
raise NotImplementedError
def notify (self, event):
"""S.notify (...) -> None
Notifies the Scale 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
if event.data.button == 1:
# Guarantee a correct look, if the signal
# handlers run a long time
self.state = STATE_ACTIVE
self.run_signal_handlers (SIG_MOUSEDOWN, event.data)
if event.data.button == 1: # Only react upon left clicks.
self.__click = True
val = self._get_value_from_coords (eventarea,
event.data.pos)
if val != self.value:
self.value = val
# Mouse wheel.
elif event.data.button == 4:
val = self.value - 2 * self.step
if val > self.minimum:
self.value = val
else:
self.value = self.minimum
elif event.data.button == 5:
val = self.value + 2 * self.step
if val < self.maximum:
self.value = val
else:
self.value = self.maximum
event.handled = True
elif event.signal == SIG_MOUSEUP:
if eventarea.collidepoint (event.data.pos):
if event.data.button == 1:
if self.state == STATE_ACTIVE:
self.state = STATE_ENTERED
else:
self.state = STATE_NORMAL
self.__click = False
self.run_signal_handlers (SIG_MOUSEUP, event.data)
event.handled = True
elif (event.data.button == 1) and self.__click:
self.state = STATE_NORMAL
self.__click = False
elif event.signal == SIG_MOUSEMOVE:
if eventarea.collidepoint (event.data.pos):
self.focus = True
self.run_signal_handlers (SIG_MOUSEMOVE, event.data)
if self.__click and self.focus:
val = self._get_value_from_coords (eventarea,
event.data.pos)
if val != self.value:
self.value = val
self.entered = True
event.handled = True
else:
self.entered = False
elif (event.signal == SIG_KEYDOWN) and self.focus:
if event.data.key in (K_KP_PLUS, K_PLUS, K_RIGHT, K_DOWN):
self.increase ()
event.handled = True
elif event.data.key in (K_KP_MINUS, K_MINUS, K_LEFT, K_UP):
self.decrease ()
event.handled = True
elif event.data.key == K_PAGEUP:
val = self.value - 10 * self.step
if val > self.minimum:
self.value = val
else:
self.value = self.minimum
event.handled = True
elif event.data.key == K_PAGEDOWN:
val = self.value + 10 * self.step
if val < self.maximum:
self.value = val
else:
self.value = self.maximum
event.handled = True
elif event.data.key == K_END:
self.value = self.maximum
event.handled = True
elif event.data.key == K_HOME:
self.value = self.minimum
event.handled = True
Range.notify (self, event)
class HScale (Scale):
"""HScale (minimum, maximum, step=1.0) -> HScale
A horizontal scaling widget for selecting numerical values.
The HScale widget is a scaling widget with a horizontal orientation
and allows the user to select and adjust a value from a range moving
a slider.
Default action (invoked by activate()):
See the Scale class.
Mnemonic action (invoked by activate_mnemonic()):
See the Scale class.
"""
def __init__ (self, minimum, maximum, step=1.0):
Scale.__init__ (self, minimum, maximum, step)
self.minsize = 120, 20 # Default size.
def _get_value_from_coords (self, area, coords):
"""H._get_value_from_coords (...) -> float
Calculates the float value of the HScale.
Calculates the float value of the HScale from the passed
absolute coordinates tuple relative to the area.
"""
# We need this for a proper calculation.
slider = StyleInformation.get ("HSCALE_SLIDER_SIZE")
# The slide range, in which the slider can move.
slide = self.width - slider[0]
# Calculate the absolute current position
n = coords[0] - area.left - slider[0] / 2.0
# Step range in dependance of the width and value range of the
# Scale.
step = (self.maximum - self.minimum) / float (slide)
# Calculate it.
val = self.minimum + step * n
if val > self.maximum:
val = self.maximum
elif val < self.minimum:
val = self.minimum
return val
def _get_coords_from_value (self):
"""H._get_coords_from_value () -> float
Calculates the coordinates from the current value of the HScale.
Calculates the relative coordinates on the HScale from the
current value.
"""
# We need this for a proper calculation.
slider = StyleInformation.get ("HSCALE_SLIDER_SIZE")
width = self.width
# The slide range in which the slider can move.
slide = width - slider[0]
# Step range in dependance of the width and value range of the
# Scale.
step = (self.maximum - self.minimum) / float (slide)
# Calculate the value
val = (self.value - self.minimum) / step + slider[0] / 2.0
return val
def draw_bg (self):
"""H.draw_bg () -> Surface
Draws the background surface of the HScale and returns it.
Creates the visible surface of the HScale and returns it to the
caller.
"""
return base.GlobalStyle.engine.draw_scale (self,
ORIENTATION_HORIZONTAL)
def draw (self):
"""H.draw () -> None
Draws the HScale surface and its slider.
"""
Scale.draw (self)
cls = self.__class__
style = base.GlobalStyle
active = StyleInformation.get ("ACTIVE_BORDER")
border = style.get_border_size (cls, self.style,
StyleInformation.get ("SCALE_BORDER"))
border_active = style.get_border_size (cls, self.style, active)
slider = StyleInformation.get ("HSCALE_SLIDER_SIZE")
# Creates the slider surface.
sf_slider = style.engine.draw_slider (slider[0], slider[1],
self.state, cls, self.style)
rect_slider = sf_slider.get_rect ()
# Dashed border.
if self.focus:
b = border + border_active
r = Rect (b, b, rect_slider.width - 2 * b,
rect_slider.height - 2 * b)
style.engine.draw_border \
(sf_slider, self.state, cls, self.style, active, r,
StyleInformation.get ("ACTIVE_BORDER_SPACE"))
size = self.width - 2 * border, self.height / 3 - 2 * border
# Fill the scale line.
sf_fill = Draw.draw_rect (size[0], size[1],
StyleInformation.get ("SCALE_COLOR"))
self.image.blit (sf_fill, (border, self.height / 3 + border))
# Blit slider at the correct position.
rect_slider.centerx = self._get_coords_from_value ()
rect_slider.centery = self.image.get_rect ().centery
self.image.blit (sf_slider, rect_slider)
# Fill until the slider start.
if rect_slider.x > 0:
sf_fill = Draw.draw_rect (rect_slider.x - border, size[1],
StyleInformation.get ("PROGRESS_COLOR"))
self.image.blit (sf_fill, (border, self.height / 3 + border))
class VScale (Scale):
"""VScale (minimum, maximum, step=1.0) -> VScale
A vertical scaling widget for selecting numerical values.
The VScale widget is a scaling widget with a vertical orientation
and allows the user to select and adjust a value from a range moving
a slider.
Default action (invoked by activate()):
See the Scale class.
Mnemonic action (invoked by activate_mnemonic()):
See the Scale class.
"""
def __init__ (self, minimum, maximum, step=1.0):
Scale.__init__ (self, minimum, maximum, step)
self.minsize = 20, 120 # Default size.
def _get_value_from_coords (self, area, coords):
"""V._get_value_from_coords (...) -> float
Calculates the float value of the VScale.
Calculates the float value of the VScale from the passed
absolute coordinates tuple relative to the area.
"""
# We need this for a proper calculation.
slider = StyleInformation.get ("VSCALE_SLIDER_SIZE")
# The slide range, in which the slider can move.
slide = self.height - slider[1]
# Calculate the absolute current position
n = coords[1] - area.top - slider[1] / 2.0
# Step range in dependance of the width and value range of the
# Scale.
step = (self.maximum - self.minimum) / float (slide)
# Calculate it.
val = self.minimum + step * n
if val > self.maximum:
val = self.maximum
elif val < self.minimum:
val = self.minimum
return val
def _get_coords_from_value (self):
"""V.get_coords_from_value () -> float
Calculates the coordinates from the current value of the VScale.
Calculates the relative coordinates on the VScale from the
current value.
"""
# We need this for a proper calculation.
slider = StyleInformation.get ("VSCALE_SLIDER_SIZE")
height = self.height
# The slide range in which the slider can move.
slide = height - slider[1]
# Step range in dependance of the width and value range of the
# Scale.
step = (self.maximum - self.minimum) / float (slide)
# Calculate the value
val = (self.value - self.minimum) / step + slider[1] / 2.0
return val
def draw_bg (self):
"""V.draw_bg () -> Surface
Draws the VScale background surface and returns it.
Creates the visible surface of the VScale and returns it to the
caller.
"""
return base.GlobalStyle.engine.draw_scale (self, ORIENTATION_VERTICAL)
def draw (self):
"""V.draw () -> None
Draws the VScale surface and its slider.
"""
Scale.draw (self)
cls = self.__class__
style = base.GlobalStyle
border = style.get_border_size (cls, self.style,
StyleInformation.get ("SCALE_BORDER"))
active = StyleInformation.get ("ACTIVE_BORDER")
border_active = style.get_border_size (cls, self.style, active)
slider = StyleInformation.get ("VSCALE_SLIDER_SIZE")
# Creates the slider surface.
sf_slider = style.engine.draw_slider (slider[0], slider[1], self.state,
cls, self.style)
rect_slider = sf_slider.get_rect ()
# Dashed border.
if self.focus:
b = border + border_active
r = Rect (b, b, rect_slider.width - 2 * b,
rect_slider.height - 2 * b)
style.engine.draw_border \
(sf_slider, self.state, cls, self.style, active, r,
StyleInformation.get ("ACTIVE_BORDER_SPACE"))
size = self.width / 3 - 2 * border, self.height - 2 * border
# Fill the scale line.
sf_fill = Draw.draw_rect (size[0], size[1],
StyleInformation.get ("SCALE_COLOR"))
self.image.blit (sf_fill, (self.width / 3 + border, border))
# Blit slider at the correct position.
rect_slider.centerx = self.image.get_rect ().centerx
rect_slider.centery = self._get_coords_from_value ()
self.image.blit (sf_slider, rect_slider)
# Fill until the slider start.
if rect_slider.y > 0:
sf_fill = Draw.draw_rect (size[0], rect_slider.y - border,
StyleInformation.get ("PROGRESS_COLOR"))
self.image.blit (sf_fill, (self.width / 3 + border, border))
syntax highlighted by Code2HTML, v. 0.9.1