#!/usr/bin/env python
#
# $Id: ShadowBox.py,v 1.4 2001/11/03 11:05:22 doughellmann Exp $
#
# Copyright 2001 Doug Hellmann.
#
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""A canvas object which draws a box around something.
The box can have edges drawn using the relief methods of standard
widgets, such as the Pmw.Group.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: ShadowBox.py,v $',
'rcs_id' : '$Id: ShadowBox.py,v 1.4 2001/11/03 11:05:22 doughellmann Exp $',
'creator' : 'Doug Hellmann <doug@hellfly.net>',
'project' : 'PmwContribD',
'created' : 'Sat, 05-May-2001 12:46:37 EDT',
#
# Current Information
#
'author' : '$Author: doughellmann $',
'version' : '$Revision: 1.4 $',
'date' : '$Date: 2001/11/03 11:05:22 $',
}
#
# Import system modules
#
import Tkinter
import Pmw
import sys, os, string
import Canvas
import math
#
# Import Local modules
#
from CanvasGroup import CanvasGroup
import colormath
#
# Module
#
class ShadowBox:
"""A canvas object which draws a box around something.
The box can have edges drawn using the relief methods of standard
widgets, such as the Pmw.Group.
"""
def __init__(self, canvas,
name=None,
ulx=0, uly=0,
ipadx=2, ipady=2,
background='grey',
outline='black',
allowMotion=0,
width=25,
height=20,
bd=2,
ridgeWidth=0,
relief=Tkinter.RAISED,
helpMessage=None,
balloon=None):
"""Create a ShadowBox.
Arguments
'allowMotion=0' -- Boolean indicating whether or not the
ShadowBox should allow the user to move it with the mouse.
'background=grey' -- Background color of the box.
'balloon=None' -- Balloon help message manager.
'bd=2' -- Border shadow width.
'canvas' -- Tkinter Canvas widget on which to draw.
'height=20' -- Height of the full ShadowBox on the canvas.
'helpMessage=None' -- Balloon help message for this
ShadowBox.
'ipadx' -- Internal space (X) allocated between the box edge
and the interior.
'ipady' -- Internal space (Y) allocated between the box edge
and the interior.
'name' -- Name of the box. This is used to create the tag
of the canvas objects by prefixing it with 'AnimatedFolder'.
'outline=black' -- The color of the outline of the
ShadowBox.
'relief=Tkinter.RAISED' -- Tkinter relief definition.
'ridgeWidth=0' -- Width of ridge area when relief set to
Tkinter.RIDGE.
'ulx' -- Upper left X coordinate.
'uly' -- Upper left Y coordinate.
'width=25' -- Width of the entire ShadowBox.
"""
#
# Store input parameters
#
self.canvas = canvas
self.name = name
self.ulx = ulx
self.uly = uly
self.ipadx = ipadx
self.ipady = ipady
self.background, self.light_shadow, self.dark_shadow = \
colormath.computeColorTriplet(self.canvas, background)
#self.background, self.light_shadow, self.dark_shadow = \
# background, 'red', 'green'
self.outline = outline
self.width = width
self.height = height
self.bd = bd
self.ridge_width = ridgeWidth
self.relief = relief
self.allow_motion = allowMotion
#
# Store the current coordinates as the "previous"
# for the movement controls
#
self.previous_coords = None
#
# Create a list to hold on to all of our components
#
self.canvas_objects = []
self.canvas_objects_dict = {}
self.shadows = { Tkinter.LEFT: [],
Tkinter.RIGHT: [],
Tkinter.BOTTOM: [],
Tkinter.TOP: [],
}
#
# Create Canvas objects
#
self.unique_name = 'AnimatedFolder%s' % id(self)
#self.canvas_group = Canvas.Group(self.canvas, tag=self.unique_name)
self.canvas_group = CanvasGroup(self.canvas, tag=self.unique_name)
self.canvas_group.bind('<ButtonPress-1>', self.__clickCB)
if allowMotion:
self.canvas_group.bind('<Button1-Motion>', self.__motionCB)
self.create_canvas_objects()
#
# Set up the help message, if any
#
self.helpMessage = helpMessage
self.balloon = balloon
if self.balloon and self.helpMessage:
self.balloon.tagbind(self.canvas, self.canvas_group,
self.helpMessage, self.helpMessage)
return
def compute_object_points(self):
"""Compute canvas object coordinates.
Compute the locations of the various corners of the objects
used to render the shadow box.
Corner point definitions::
a__d___ a = ul (upper left) d = uil (upper inside left)
|\\
| \\b____ b = ulm (upper left middle)
| |\\
e | \c___ c = uli (upper left inside) e = lul (lower upper left)
| | |
"""
#
# Sizes and locations
#
if self.ridge_width >= self.bd:
rw = 0
else:
rw = self.ridge_width
shadow_thickness = self.bd - rw
if shadow_thickness > math.floor(self.width / 2):
shadow_thickness = math.floor(self.width / 2)
if shadow_thickness > math.floor( self.height / 2 ):
shadow_thickness = math.floor( self.height / 2 )
if shadow_thickness > math.floor( self.width / 2 ):
shadow_thickness = math.floor( self.width / 2)
self.shadow_thickness = shadow_thickness
#print self.unique_name, ' zw is ', rw
#print self.unique_name, ' self.bd is ', self.bd
#print self.unique_name, ' shadow_thickness is ', shadow_thickness
#print ''
self.ul = (self.ulx, self.uly)
self.uil = (self.ulx + self.bd, self.uly)
self.lul = (self.ulx, self.uly + self.bd)
self.uli = (self.ulx + self.bd, self.uly + self.bd)
self.ulmi = (self.ulx + math.floor(shadow_thickness / 2) + rw, self.uly + math.floor(shadow_thickness / 2) + rw)
self.ulmo = (self.ulx + math.floor(shadow_thickness / 2), self.uly + math.floor(shadow_thickness / 2))
self.ur = (self.ulx + self.width, self.uly)
self.uir = (self.ulx + self.width - self.bd, self.uly)
self.lur = (self.ulx + self.width, self.uly + self.bd)
self.uri = (self.ulx + self.width - self.bd, self.uly + self.bd)
self.urmi = (self.ulx + self.width - (math.floor(shadow_thickness / 2) + rw), self.uly + math.floor(shadow_thickness / 2) + rw)
self.urmo = (self.ulx + self.width - math.floor(shadow_thickness / 2), self.uly + math.floor(shadow_thickness / 2))
self.lr = (self.ulx + self.width, self.uly + self.height)
self.lir = (self.ulx + self.width - self.bd, self.uly + self.height)
self.ulr = (self.ulx + self.width, self.uly + self.height - self.bd)
self.lri = (self.ulx + self.width - self.bd, self.uly + self.height - self.bd)
self.lrmi = (self.ulx + self.width - (math.floor(shadow_thickness / 2) + rw), self.uly + self.height - (math.floor(shadow_thickness / 2) + rw))
self.lrmo = (self.ulx + self.width - math.floor(shadow_thickness / 2), self.uly + self.height - math.floor(shadow_thickness / 2))
self.ll = (self.ulx, self.uly + self.height)
self.lil = (self.ulx + self.bd, self.uly + self.height)
self.ull = (self.ulx, self.uly + self.height - self.bd)
self.lli = (self.ulx + self.bd, self.uly + self.height - self.bd)
self.llmi = (self.ulx + math.floor(shadow_thickness / 2) + rw, self.uly + self.height - (math.floor(shadow_thickness / 2) + rw))
self.llmo = (self.ulx + math.floor(shadow_thickness / 2), self.uly + self.height - math.floor(shadow_thickness / 2))
return
def create_canvas_objects(self):
"""Create the visible canvas objects.
The group has already been created.
"""
#
# Color variables:
#
# outline - the color of the outline of the main rectangle
#
outline = self.background
#
# Size variables:
#
border_width = 0
three_d = 1
#
# Compute the locations for the points of our
# constituent shapes
#
self.compute_object_points()
self.background_rect = Canvas.Rectangle(
self.canvas,
( self.ul, self.lr ),
outline=self.background,
fill=self.background,
width=0,
)
self.background_rect.addtag(self.unique_name)
self.canvas_objects.append(self.background_rect)
if self.relief in [ Tkinter.RAISED, Tkinter.SUNKEN ]:
#
# Top and left sides are light color,
# bottom and right sides are dark color
#
# TOP rectangle
top = Canvas.Polygon(
self.canvas,
( self.uil, self.uir, self.uri, self.uli ),
width=0,
)
top.addtag(self.unique_name)
self.canvas_objects.append(top)
self.shadows[Tkinter.TOP].append(top)
self.canvas_objects_dict['top'] = top
# TOP LEFT triangle
top_left = Canvas.Polygon(
self.canvas,
( self.ul, self.uil, self.uli ),
width = 0,
)
top_left.addtag(self.unique_name)
self.canvas_objects.append(top_left)
self.shadows[Tkinter.TOP].append(top_left)
self.canvas_objects_dict['top_left'] = top_left
# TOP RIGHT triangle
top_right = Canvas.Polygon(
self.canvas,
( self.uir, self.ur, self.uri ),
width = 0,
)
top_right.addtag(self.unique_name)
self.canvas_objects.append(top_right)
self.shadows[Tkinter.TOP].append(top_right)
self.canvas_objects_dict['top_right'] = top_right
# LEFT rectangle
left = Canvas.Polygon(
self.canvas,
( self.lul, self.uli, self.lli, self.ull ),
width = 0,
)
left.addtag(self.unique_name)
self.canvas_objects.append(left)
self.shadows[Tkinter.LEFT].append(left)
self.canvas_objects_dict['left'] = left
# LEFT TOP triangle
left_top = Canvas.Polygon(
self.canvas,
( self.ul, self.uli, self.lul ),
width = 0,
)
left_top.addtag(self.unique_name)
self.canvas_objects.append(left_top)
self.shadows[Tkinter.LEFT].append(left_top)
self.canvas_objects_dict['left_top'] = left_top
# LEFT BOTTOM triangle
left_bottom = Canvas.Polygon(
self.canvas,
( self.ull, self.lli, self.ll ),
width = 0,
)
left_bottom.addtag(self.unique_name)
self.canvas_objects.append(left_bottom)
self.shadows[Tkinter.LEFT].append(left_bottom)
self.canvas_objects_dict['left_bottom'] = left_bottom
# BOTTOM rectangle
bottom = Canvas.Polygon(
self.canvas,
( self.lil, self.lli, self.lri, self.lir ),
width = 0,
)
bottom.addtag(self.unique_name)
self.canvas_objects.append(bottom)
self.shadows[Tkinter.BOTTOM].append(bottom)
self.canvas_objects_dict['bottom'] = bottom
# BOTTOM LEFT triangle
bottom_left = Canvas.Polygon(
self.canvas,
( self.ll, self.lli, self.lil ),
width = 0,
)
bottom_left.addtag(self.unique_name)
self.canvas_objects.append(bottom_left)
self.shadows[Tkinter.BOTTOM].append(bottom_left)
self.canvas_objects_dict['bottom_left'] = bottom_left
# BOTTOM RIGHT triangle
bottom_right = Canvas.Polygon(
self.canvas,
( self.lir, self.lri, self.lr ),
width = 0,
)
bottom_right.addtag(self.unique_name)
self.canvas_objects.append(bottom_right)
self.shadows[Tkinter.BOTTOM].append(bottom_right)
self.canvas_objects_dict['bottom_right'] = bottom_right
# RIGHT rectangle
right = Canvas.Polygon(
self.canvas,
( self.uri, self.lur, self.ulr, self.lri ),
outline = self.dark_shadow,
fill = self.dark_shadow,
width=0,
)
right.addtag(self.unique_name)
self.canvas_objects.append(right)
self.shadows[Tkinter.RIGHT].append(right)
self.canvas_objects_dict['right'] = right
# RIGHT TOP triangle
right_top = Canvas.Polygon(
self.canvas,
( self.uri, self.ur, self.lur ),
width = 0,
)
right_top.addtag(self.unique_name)
self.canvas_objects.append(right_top)
self.shadows[Tkinter.RIGHT].append(right_top)
self.canvas_objects_dict['right_top'] = right_top
# RIGHT BOTTOM triangle
right_bottom = Canvas.Polygon(
self.canvas,
( self.lri, self.ulr, self.lr ),
width = 0,
)
right_bottom.addtag(self.unique_name)
self.canvas_objects.append(right_bottom)
self.shadows[Tkinter.RIGHT].append(right_bottom)
self.canvas_objects_dict['right_bottom'] = right_bottom
if self.relief == Tkinter.SUNKEN:
top_left_color = self.dark_shadow
bottom_right_color = self.light_shadow
elif self.relief == Tkinter.RAISED:
top_left_color = self.light_shadow
bottom_right_color = self.dark_shadow
for obj in [ top,
top_left,
top_right,
left,
left_top,
left_bottom ]:
obj.config( fill=top_left_color,
outline=top_left_color)
for obj in [bottom,
bottom_left,
bottom_right,
right,
right_top,
right_bottom ]:
obj.config( fill=bottom_right_color,
outline=bottom_right_color)
elif self.relief == Tkinter.FLAT:
#
# No border at all
#
border_width=0
three_d=0
elif self.relief == Tkinter.RIDGE:
#
# Border line sticks up
#
top_left_outer = Canvas.Polygon(
self.canvas,
( self.ll, self.ul, self.ur, self.urmo,
self.ulmo, self.llmo, self.ll ),
outline=self.light_shadow,
fill=self.light_shadow,
width=0,
)
top_left_outer.addtag(self.unique_name)
self.canvas_objects.append(top_left_outer)
top_left_inner = Canvas.Polygon(
self.canvas,
( self.llmi, self.ulmi, self.urmi,
self.uri, self.uli, self.lli, self.llmi ),
outline=self.dark_shadow,
fill=self.dark_shadow,
width=0,
)
top_left_inner.addtag(self.unique_name)
self.canvas_objects.append(top_left_inner)
bottom_right_outer = Canvas.Polygon(
self.canvas,
( self.ll, self.llmo, self.lrmo,
self.urmo, self.ur, self.lr, self.ll ),
outline=self.dark_shadow,
fill=self.dark_shadow,
width=0,
)
bottom_right_outer.addtag(self.unique_name)
self.canvas_objects.append(bottom_right_outer)
bottom_right_inner = Canvas.Polygon(
self.canvas,
( self.llmi, self.lli, self.lri,
self.uri, self.urmi, self.lrmi, self.llmi ),
outline=self.light_shadow,
fill=self.light_shadow,
width=0,
)
bottom_right_inner.addtag(self.unique_name)
self.canvas_objects.append(bottom_right_inner)
elif self.relief == Tkinter.GROOVE:
#
# Border line engraved
#
top_left_outer = Canvas.Polygon(
self.canvas,
( self.ll, self.ul, self.ur,
self.urmo, self.ulmo, self.llmo, self.ll ),
outline=self.dark_shadow,
fill=self.dark_shadow,
width=0,
)
top_left_outer.addtag(self.unique_name)
self.canvas_objects.append(top_left_outer)
top_left_inner = Canvas.Polygon(
self.canvas,
( self.llmi, self.ulmi, self.urmi,
self.uri, self.uli, self.lli, self.llmi ),
outline=self.light_shadow,
fill=self.light_shadow,
width=0,
)
top_left_inner.addtag(self.unique_name)
self.canvas_objects.append(top_left_inner)
bottom_right_outer = Canvas.Polygon(
self.canvas,
( self.ll, self.llmo, self.lrmo,
self.urmo, self.ur, self.lr, self.ll ),
outline=self.light_shadow,
fill=self.light_shadow,
width=0,
)
bottom_right_outer.addtag(self.unique_name)
self.canvas_objects.append(bottom_right_outer)
bottom_right_inner = Canvas.Polygon(
self.canvas,
( self.llmi, self.lli, self.lri,
self.uri, self.urmi, self.lrmi, self.llmi ),
outline=self.dark_shadow,
fill=self.dark_shadow,
width=0,
)
bottom_right_inner.addtag(self.unique_name)
self.canvas_objects.append(bottom_right_inner)
elif self.relief == Tkinter.SOLID:
#
# Solid just has the outer rectangle
#
border_width = self.bd
outline = self.outline
three_d = 0
#
# Add the created pieces to our group so that
# the actions we want to receive can come from
# any piece.
#
if three_d:
outline_coords = ( self.uli, self.lri )
else:
outline_coords = ( self.ul, self.lr )
self.main_outline = Canvas.Rectangle(
self.canvas,
outline_coords,
outline=outline,
fill=self.background,
width=border_width,
)
self.main_outline.addtag(self.unique_name)
self.canvas_objects.append(self.main_outline)
return
def addtag(self, tag):
"Add a canvas tag to the ShadowBox."
map(lambda x, t=tag: x.addtag(t), self.canvas_objects)
return
def lower(self):
"Lower the ShadowBox on the canvas stack."
return self.canvas_group.lower()
def __main_outline_coords(self):
"Return the coordinates of the main outline."
return ( (self.ulx, self.uly),
(self.ulx + self.width, self.uly + self.height),
)
def __motionCB(self, event):
"Motion callback."
#print 'previous coords = ', self.previous_coords
#print 'click = (%d, %d)' % (event.x_root, event.y_root)
move_x = event.x_root - self.previous_coords[0]
move_y = event.y_root - self.previous_coords[1]
#print 'move = (%d, %d)' % (move_x, move_y)
self.move(move_x, move_y)
self.previous_coords = (event.x_root, event.y_root)
self.ulx = self.ulx + move_x
self.uly = self.uly + move_y
return
def move(self, delta_x, delta_y):
"""Move the object by the X and Y ammounts specified.
"""
return self.canvas_group.move(delta_x, delta_y)
def __clickCB(self, event):
"Click callback."
#print 'CLICK @ (%d, %d)' % (event.x_root, event.y_root)
#print 'BOUNDING BOX : ', self.bbox()
#print 'INTERIOR : ', self.ibbox()
self.previous_coords = (event.x_root, event.y_root)
return
def bbox(self):
"Returns the bounding box of the ShadowBox."
return self.canvas_group.bbox()
#return self.background_rect.bbox()
def ibbox(self):
"Returns the internal bounding box where something can be drawn inside the ShadowBox."
ibbox = self.main_outline.bbox()
ibbox = ( (ibbox[0][0] + self.ipadx, ibbox[0][1] + self.ipady),
(ibbox[1][0] - self.ipadx, ibbox[1][1] - self.ipady),
)
return ibbox
def bind(self, sequence=None, command=None):
"Bind an event sequence to a command."
return self.canvas_group.bind(sequence, command)
def delete(self):
"Delete the ShadowBox from the screen."
return self.canvas_group.delete()
def add_object(self, canvasObject):
"Add another canvas object to those monitored by the ShadowBox."
return canvasObject.addtag(self.unique_name)
if __name__ == '__main__':
import TestCases.CanvasTestApp
class ShadowBoxTest(TestCases.CanvasTestApp.CanvasTestApp):
appname = 'Test shadow box'
def createCanvasObjects(self):
self.canvas.configure(background='lightblue')
bd = 2
label_test_list = [
Tkinter.SUNKEN,
Tkinter.RAISED,
Tkinter.FLAT,
Tkinter.GROOVE,
Tkinter.RIDGE,
Tkinter.SOLID,
]
#label_test_list = []
for relief in label_test_list:
self.createcomponent(relief, (), None,
Tkinter.Label,
(self.interior(),),
background='grey',
relief=relief,
text=relief,
bd = bd,
).pack(side=Tkinter.LEFT,
expand=Tkinter.NO,
fill=Tkinter.NONE,
padx=5,
pady=5)
self.button_down = None
self.contents = []
bd = 4
width=30
height=25
x = 10
y = 10
for relief, color in [
( Tkinter.SUNKEN, 'red'),
( Tkinter.RAISED, 'lightblue'),
( Tkinter.FLAT, 'tan'),
( Tkinter.GROOVE, 'cyan'),
( Tkinter.RIDGE, 'grey'),
( Tkinter.SOLID, 'green'),
]:
ShadowBox(self.canvas, name=relief, ulx=x, uly=y,
width=width, height=height,
relief=relief,
bd = bd,
#background = color,
allowMotion=1,
ridgeWidth=2 )
y = y + height + 5
ShadowBox(self.canvas, name='bigtest', ulx=50, uly=50,
width=50, height=50,
relief=Tkinter.RIDGE,
bd=10,
ridgeWidth=8,
allowMotion=1,
background='lightblue',
)
ShadowBox(self.canvas, name='bigtest', ulx=150, uly=50,
width=50, height=50,
relief=Tkinter.GROOVE,
bd=10,
ridgeWidth=8,
allowMotion=1,
background='lightblue',
)
ShadowBoxTest().run()
|