#!/usr/bin/env python
#
# $Id: HandledBox.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 subclassed from ShadowBox, which adds handles.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: HandledBox.py,v $',
'rcs_id' : '$Id: HandledBox.py,v 1.4 2001/11/03 11:05:22 doughellmann Exp $',
'creator' : 'Doug Hellmann <doug@hellfly.net>',
'project' : 'PmwContribD',
'created' : 'Sun, 01-Apr-2001 17:33:38 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
#
import colormath
import ShadowBox
#
# Module
#
class Handle(ShadowBox.ShadowBox):
"A visible handle attached to a ShadowBox."
def __init__(self,
canvas,
parent,
name=None,
ulx=0, uly=0,
background='grey',
outline='black',
allowMotion=0,
width=25,
height=20,
bd=2,
ridgeWidth=0,
relief=Tkinter.RIDGE,
connectEdge=Tkinter.LEFT,
helpMessage=None,
balloon=None,
):
"""Create a Handle.
Arguments
'canvas' -- The canvas widget on which we draw.
'parent' -- The parent.
'name' -- Name of the handle.
'ulx' -- Upper left X coordinate of the handle.
'uly' -- Upper left Y coordinate of the handle.
'background' -- Background color of the handle area. Defaults to 'grey'.
'outline' -- Outline color. Defaults to 'black'.
'allowMotion' -- Boolean indicating whether drag and drop
needs to be supported. Setting to true will cause handle to
install drag and drop callbacks on canvas.
'width' -- How wide should the handle be?
'height' -- How tall should the handle be?
'bd' -- Border width for shadow and outline. Defaults to 2.
'ridgeWidth' -- Width of ridge between outside edge and inside edge.
'relief' -- Tkinter relief specification. Defaults to Tkinter.RIDGE.
'connectEdge' -- Tkinter edge specification. Defaults to
Tkinter.LEFT. Controls which side of the handle is butted
against the box and controls the drawing of shadows and
outlines.
'helpMessage' -- Balloon help to be displayed on mouseover.
'balloon' -- Balloon help manager.
"""
ShadowBox.ShadowBox.__init__(self,
canvas = canvas,
name = name,
ulx = ulx,
uly = uly,
background = background,
outline = outline,
allowMotion = allowMotion,
width = width,
height = height,
bd = bd,
ridgeWidth = ridgeWidth,
relief = relief,
helpMessage=helpMessage,
balloon=balloon,
)
self.edge = connectEdge
#self.connect_edge(connectEdge)
self.parent = parent
if self.relief == Tkinter.RAISED:
self.mitre_RAISED()
else:
pass
return
def shadow_edge(self):
"Returns the Tkinter edge specification for shadow edge of handle."
edge = self.edge
if edge == Tkinter.LEFT:
shadow_edge = Tkinter.RIGHT
elif edge == Tkinter.RIGHT:
shadow_edge = Tkinter.LEFT
elif edge == Tkinter.TOP:
shadow_edge = Tkinter.BOTTOM
elif edge == Tkinter.BOTTOM:
shadow_edge = Tkinter.TOP
else:
raise 'Unknown edge %s' % edge
return shadow_edge
def mitre_RAISED(self):
"Configure the mitre settings for RAISED mode."
if self.edge == Tkinter.LEFT:
self.canvas_objects_dict['right'].config(fill=self.background,
outline=self.background)
self.canvas_objects_dict['top_right'].coords( (self.uir,
self.lur,
self.uri) )
self.canvas_objects_dict['right_top'].coords( (self.uir,
self.ur,
self.lur) )
self.canvas_objects_dict['right_top'].config(fill=self.light_shadow,
outline=self.light_shadow)
self.canvas_objects_dict['right_bottom'].coords( (self.lir,
self.ulr,
self.lr) )
self.canvas_objects_dict['right_bottom'].config(fill=self.light_shadow,
outline=self.light_shadow)
self.canvas_objects_dict['bottom_right'].coords( (self.lir,
self.lri,
self.ulr) )
self.canvas_objects_dict['bottom_right'].tkraise()
elif self.edge == Tkinter.RIGHT:
self.canvas_objects_dict['left'].config(fill=self.background,
outline=self.background)
self.canvas_objects_dict['top_left'].coords( (self.lul, self.uil, self.uli) )
self.canvas_objects_dict['left_top'].coords( (self.lul, self.ul, self.uil) )
self.canvas_objects_dict['left_top'].config(fill=self.dark_shadow,
outline=self.dark_shadow)
self.canvas_objects_dict['left_bottom'].coords( (self.ll,
self.ull,
self.lil) )
self.canvas_objects_dict['left_bottom'].config(fill=self.dark_shadow,
outline=self.dark_shadow)
self.canvas_objects_dict['bottom_left'].coords( (self.ull,
self.lli,
self.lil) )
self.canvas_objects_dict['bottom_left'].tkraise()
elif self.edge == Tkinter.TOP:
self.canvas_objects_dict['bottom'].config(fill=self.background,
outline=self.background)
self.canvas_objects_dict['bottom'].tkraise()
self.canvas_objects_dict['bottom_left'].coords( (self.ull,
self.lli,
self.lil) )
self.canvas_objects_dict['bottom_left'].config( fill=self.light_shadow,
outline=self.light_shadow )
self.canvas_objects_dict['bottom_right'].coords( (self.lir,
self.lri,
self.ulr) )
self.canvas_objects_dict['bottom_right'].config( fill=self.dark_shadow,
outline=self.dark_shadow)
self.canvas_objects_dict['bottom_right'].tkraise()
self.canvas_objects_dict['right_bottom'].coords( (self.lir,
self.ulr,
self.lr) )
self.canvas_objects_dict['right_bottom'].config(fill=self.light_shadow,
outline=self.light_shadow)
self.canvas_objects_dict['left_bottom'].coords( (self.ll,
self.ull,
self.lil) )
self.canvas_objects_dict['left_bottom'].config(fill=self.light_shadow,
outline=self.light_shadow)
elif self.edge == Tkinter.BOTTOM:
self.canvas_objects_dict['top'].config(fill=self.background,
outline=self.background)
self.canvas_objects_dict['top'].tkraise()
self.canvas_objects_dict['top_left'].coords( (self.lul, self.uil, self.uli) )
self.canvas_objects_dict['top_left'].config(fill=self.light_shadow,
outline=self.light_shadow)
self.canvas_objects_dict['left_top'].coords( (self.lul, self.ul, self.uil) )
self.canvas_objects_dict['left_top'].config(fill=self.dark_shadow,
outline=self.dark_shadow)
self.canvas_objects_dict['top_right'].coords( (self.uir,
self.lur,
self.uri) )
self.canvas_objects_dict['top_right'].config( fill=self.dark_shadow,
outline=self.dark_shadow)
self.canvas_objects_dict['top_right'].tkraise()
self.canvas_objects_dict['right_top'].coords( (self.uir, self.ur, self.lur) )
self.canvas_objects_dict['right_top'].config(fill=self.dark_shadow,
outline=self.dark_shadow)
else:
pass
return
class HandledBox(ShadowBox.ShadowBox):
"""A ShadowBox with handles on it.
"""
def __init__(self,
canvas,
name=None,
ulx=0, uly=0,
background='grey',
outline='black',
allowMotion=0,
width=25,
height=20,
bd=2,
ridgeWidth=0,
relief=Tkinter.RAISED,
ipad=6,
helpMessage=None,
balloon=None,
handleWidth=10,
handleHeight=10,
):
"""Create a HandledBox.
Arguments
'canvas' -- Canvas on which all of this is being drawn.
'name' -- Name of the HandledBox.
'ulx' -- Upper left X coordinate.
'uly' -- Upper left Y coordinate.
'background' -- Background color of the box. Defaults to 'grey'.
'outline' -- Outline color for the box. Defaults to 'black'.
'allowMotion' -- Boolean controlling whether the icon should
allow the user to move it.
'width' -- How wide is the box?
'height' -- How high is the box?
'bd' -- Border width for shadow and outline area. Defaults to 2.
'ridgeWidth' -- Width of ridge around edge of box.
'relief' -- Tkinter relief mode specification. Defaults to
Tkinter.RAISED.
'ipad' -- Internal padding value.
'helpMessage' -- Balloon help message to be shown on mouseover.
'balloon' -- Balloon help manager.
'handleWidth' -- How wide are handles attached to this box?
'handleHeight' -- How tall are handles attached to this box?
"""
self.__handles = {}
self.ipad = ipad
self.balloon = balloon
self.handle_width = handleWidth
self.handle_height = handleHeight
ShadowBox.ShadowBox.__init__(self,
canvas = canvas,
name = name,
ulx = ulx,
uly = uly,
background = background,
outline = outline,
allowMotion = allowMotion,
width = width,
height = height,
bd = bd,
ridgeWidth = ridgeWidth,
relief = relief,
helpMessage=helpMessage,
balloon=balloon)
self.__handle_offsets = {
#Tkinter.LEFT:self.bd,
#Tkinter.RIGHT:self.bd,
#Tkinter.TOP:self.bd,
#Tkinter.BOTTOM:self.bd,
Tkinter.LEFT:self.ipad,
Tkinter.RIGHT: self.height - self.ipad,
Tkinter.TOP:self.ipad,
Tkinter.BOTTOM: self.width - self.ipad,
}
return
def handle(self, handle_name):
"Retrieve a named handle."
return self.__handles[handle_name]
def add_handle(self, handle_name=None, edge=Tkinter.LEFT,
helpMessage=None, relief=Tkinter.RAISED):
"""Add a handle to the box.
Arguments
handle_name -- Name of the handle.
edge -- Edge of the box on which the handle should appear.
Defaults to Tkinter.LEFT.
helpMessage -- Balloon help to be displayed on mouseover.
relief -- Relief mode specification for the handle.
Add a named handle to the specified edge. A separate
CanvasObject which can be treated as the handle will be
returned.
"""
if self.__handles.has_key(handle_name):
raise ValueError('There is already a handle named %s' % handle_name)
else:
shadow_thickness = math.floor((self.bd - self.ridge_width) / 2)
handle_width = self.handle_width
handle_height = self.handle_height
if (relief in [ Tkinter.RAISED, Tkinter.SUNKEN ]):
shadow_offset = shadow_thickness
else:
shadow_offset = math.ceil(shadow_thickness/2)
if edge == Tkinter.LEFT:
ulx = self.ulx - handle_width + shadow_offset
uly = self.uly + self.__handle_offsets[edge]
#ulx = self.ulx - handle_width
#uly = self.uly + self.__handle_offsets[edge]
self.__handle_offsets[edge] = \
self.__handle_offsets[edge] + self.ipad + handle_height
elif edge == Tkinter.TOP:
#ulx = self.ulx + self.__handle_offsets[edge] + shadow_thickness
ulx = self.ulx + self.__handle_offsets[edge]
uly = self.uly - handle_height + shadow_offset
self.__handle_offsets[edge] = \
self.__handle_offsets[edge] + self.ipad + handle_width
elif edge == Tkinter.RIGHT:
ulx = self.ulx + self.width - shadow_offset
uly = self.uly + self.__handle_offsets[edge] - handle_height
self.__handle_offsets[edge] = \
self.__handle_offsets[edge] - self.ipad - handle_height
elif edge == Tkinter.BOTTOM:
ulx = self.ulx + self.__handle_offsets[edge] - handle_width
uly = self.uly + self.height - shadow_offset
self.__handle_offsets[edge] = \
self.__handle_offsets[edge] - self.ipad - handle_width
new_handle = Handle(self.canvas,
parent = self,
name = '%s_%s' % (self.name, handle_name),
ulx = ulx,
uly = uly,
allowMotion = 0,
background = self.background,
#background = 'lightblue',
bd = shadow_thickness,
ridgeWidth = shadow_thickness,
relief = relief,
width = handle_width,
height = handle_height,
connectEdge = edge,
helpMessage = helpMessage,
balloon = self.balloon
)
new_handle.parent = self
#
# Here we need to insert the tag for
# the HandledBox (self) before the tag for the
# new handle (new_handle) so that when the
# mouse moves onto the handle, the help message
# for the handle is displayed; and when the
# mouse moves onto the HandledBox main area,
# the help message for the HandledBox is displayed.
#
# If we assume that the last tag added to the
# handle was the one for HandledBox, we can
# just insert our tag before the last one in
# the tuple.
#
# We do all of this fanciness to the main_outline
# component of the handle. This isn't safe, especially
# if we subclass the handle and start creating things
# with other canvas objects on top of the main_outline
#
tags = new_handle.main_outline.gettags()
newtags = tags[:-1] + (self.unique_name, tags[-1])
new_handle.main_outline.config(tags=newtags)
#
# Now we throw the HandledBox tag back on the
# end of the tag list, so that when we pick
# up the box and move it with the mouse all
# of the parts of the handles move with it.
#
new_handle.addtag(self.unique_name)
#
# Store the handle so we can retrieve it later.
#
self.__handles[new_handle.name] = new_handle
#print 'storing handle "%s" (%s) for %s' % (handle_name, new_handle.name, self.name)
#
# Return the handle so the caller can bind
# to it right away.
#
return new_handle
if __name__ == '__main__':
import TestCases.CanvasTestApp
import AnimatedFileIcon
import AnimatedFolder
class DoubleClickFolder(AnimatedFolder.AnimatedFolder):
def __init__(self, canvas,
name=None,
ulx=0, uly=0,
foreground='AntiqueWhite',
lightshadow='tan3',
darkshadow='tan1',
outline='black',
allowMotion=0,
width=25,
height=20,
):
AnimatedFolder.AnimatedFolder.__init__(self, canvas,
name=name,
ulx=ulx, uly=uly,
foreground=foreground,
lightshadow=lightshadow,
darkshadow=darkshadow,
outline=outline,
allowMotion=allowMotion,
width=width,
height=height,
defaultSequence='<Double-1>',
command=self.callback)
def callback(self, event):
print '%s got a callback' % self
print 'and is %s' % self.state
class HandledBoxTest(TestCases.CanvasTestApp.CanvasTestApp):
appname = 'Test handled box'
handle_message_type = 'userevent'
def showHandleInfo(self, event, handle=None):
self.showMessage(self.handle_message_type, 'Over %s' % handle.name)
def hideHandleInfo(self, event):
#print 'in hideHandleInfo (%s)' % event
self.showMessage(self.handle_message_type)
def createCanvasObjects(self):
box = HandledBox(self.canvas, name='bigtest',
ulx=50, uly=30,
width=150,
height=150,
relief=Tkinter.RIDGE,
bd=15,
ridgeWidth=5,
allowMotion=1,
#background='lightblue',
balloon = self.balloon(),
helpMessage = 'help for handled box',
handleWidth=15,
handleHeight=15,
)
for side in [ Tkinter.LEFT, Tkinter.RIGHT, Tkinter.TOP, Tkinter.BOTTOM ]:
for style in [ Tkinter.SUNKEN, Tkinter.RIDGE, Tkinter.GROOVE, Tkinter.RAISED ] :
name = '%s_%s' % (side, style)
h = box.add_handle(name,
helpMessage = 'help for %s' % name,
edge = side,
relief = style)
#
# Put a circle in the middle of the box
#
((ulx, uly), (lrx, lry)) = box.ibbox()
circle = Canvas.Oval(self.canvas, ulx + 1, uly + 1, lrx - 2, lry - 2,
fill='green',
outline='black',
width=2)
box.add_object(circle)
ignore = """
box2 = HandledBox(self.canvas, name='bigtest',
ulx=200, uly=50,
width=85,
height=110,
relief=Tkinter.RIDGE,
bd=10,
ridgeWidth=6,
allowMotion=1,
#background='lightblue',
balloon = self.balloon(),
helpMessage = 'help for 2nd handled box',
handleWidth=15,
handleHeight=15,
)
for n in range(0, 2):
for side in [ Tkinter.LEFT, Tkinter.RIGHT, Tkinter.TOP, Tkinter.BOTTOM ]:
name = '%s_%d' % (side, n)
h = box2.add_handle(name, helpMessage = 'help for %s' % name, edge = side)
#
# Put a circle in the middle of the box
#
((ulx, uly), (lrx, lry)) = box2.ibbox()
icon = AnimatedFileIcon.AnimatedFileIcon(self.canvas,
ulx=ulx + 1,
uly=uly + 1,
width = lrx - ulx - 2,
height = lry - uly - 2,
)
box2.add_object(icon)
box3 = HandledBox(self.canvas, name='bigtest',
ulx=200, uly=200,
width=110,
height=85,
relief=Tkinter.RIDGE,
bd=20,
ridgeWidth=5,
allowMotion=1,
#background='lightblue',
balloon = self.balloon(),
helpMessage = 'help for 2nd handled box',
handleWidth=15,
handleHeight=15,
)
for n in range(0, 2):
for side in [ Tkinter.LEFT, Tkinter.RIGHT, Tkinter.TOP, Tkinter.BOTTOM ]:
name = '%s_%d' % (side, n)
h = box3.add_handle(name, helpMessage = 'help for %s' % name, edge = side)
#
# Put a circle in the middle of the box
#
((ulx, uly), (lrx, lry)) = box3.ibbox()
icon = DoubleClickFolder(self.canvas,
ulx=ulx + 1,
uly=uly + 1,
width = lrx - ulx - 2,
height = lry - uly - 2,
name = 'the folder',
)
box3.add_object(icon)
"""
def folderClickCB(self, event, icon):
print 'clicked on folder %s (%s)' % (icon, icon.name)
print 'icon is now in %s state' % (icon.get_state())
#print 'closing'
icon.set_state('closed')
print ''
HandledBoxTest().run()
|