%PDF- %PDF-
| Direktori : /usr/lib/blt2.5/ |
| Current File : //usr/lib/blt2.5/ZoomStack.itcl |
#import add itcl
package require Itcl
namespace import itcl::*
class ZoomStackGraph {
# The name of graph (nee the namespace path)
variable graph ""
# Indicates which corner of the rectangular zoom region
# is currently being choosen.
variable corner "first"
# Coordinates of the current zoom region. They represent the
# two corners of a rectangular area. The two points are order
# independent.
variable x1
variable y1
variable x2
variable y2
# A list of axis configuration commmands. Acts as a stack to
# unzoom the graph back to previous axis limits.
variable stack {}
constructor { args } {
# This will need to change when we start using inheritance.
set graph [info namespace tail $this]
# What about collisions between the blt::graph instance
# command and the ZoomStackGraph instance command?
blt::graph $graph
if { [llength $args] > 0 } {
$graph configure $args
}
# Set up the bindings to select/deselect the zoom region
bind $graph <1> [code $this SelectPoint %x %y]
bind $graph <3> [code $this ClearZoom]
# The particular mouse buttons should be configurable.
}
destructor {
if { [winfo exists $graph] } {
destroy $graph
}
}
# These methods are used internally, within this class, to manage the
# zoom stack.
private method SaveCoords { x y }
private method Zoom {}
private method Unzoom {}
private method Empty {}
private method Push { cmd }
private method Pop {}
private method MarkPoint { x y }
private method SetTitle { title }
private method DrawBox { }
# These methods are called by "bind" and "after" from the Tk
# event loop. Is there any way of hiding them, so that it
# doesn't look to the user as part of the public interface?
method ClearZoom {}
method ClearTitle {}
method UpdateOutline { x y }
method SelectPoint { x y }
}
# ----------------------------------------------------------------------
#
# SaveCoords --
#
# Given a point on the screen, transforms the point into graph
# coordinates and saves it as one of the points representing a
# corner of the zoom region.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::SaveCoords { x y } {
set coords [$graph invtransform $x $y]
set x [lindex $coords 0]
set y [lindex $coords 1]
scan [$graph xaxis limits] "%s %s" min max
if { $x > $max } {
set x $max
} elseif { $x < $min } {
set x $min
}
scan [$graph yaxis limits] "%s %s" min max
if { $y > $max } {
set y $max
} elseif { $y < $min } {
set y $min
}
if { $corner == "first" } {
set x1 $x ; set y1 $y
} else {
set x2 $x ; set y2 $y
}
}
# ----------------------------------------------------------------------
#
# MarkPoint --
#
# Adds text around one of the corners of the zoom region.
# The text consists of the x,y graph coordinates of the
# corner.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::MarkPoint { x y } {
set marker "bltZoom_text_$corner"
set text [format "x=%.4g\ny=%.4g" $x $y]
if [$graph marker exists $marker] {
$graph marker configure $marker -coords { $x $y } -text $text
} else {
$graph marker create text -coords { $x $y } -name $marker \
-font *lucida*-r-*-10-* \
-text $text -anchor center -bg {} -justify left
}
}
# ----------------------------------------------------------------------
#
# Empty --
#
# Indicates if the stack of axis configuration commands is
# empty.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::Empty { } {
return [llength $stack]
}
# ----------------------------------------------------------------------
#
# Push --
#
# Appends a command on the list "stack" which can be used
# to return to previous graph x and y axis ranges.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::Push { cmd } {
lappend stack $cmd
}
# ----------------------------------------------------------------------
#
# Pop --
#
# Remove the last item pushed onto the stack and returns it.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::Pop { } {
set cmd [lindex $stack end]
set stack [lreplace $stack end end]
return $cmd
}
# ----------------------------------------------------------------------
#
# ClearTitle --
#
# Clears the zoom title (displayed in the upper left corner
# of the graph). This routine is called from the event queue
# using "after".
#
# ----------------------------------------------------------------------
body ZoomStackGraph::ClearTitle {} {
$graph marker delete "bltZoom_title"
}
# ----------------------------------------------------------------------
#
# Unzoom --
#
# Reverts to a previous zoom. Resets the x and y axis limits
# back to a previous setting. First checks if there's anything
# to pop back to. In addition, displays a title in the upper
# left corner showing the current zoom level.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::Unzoom { } {
if ![Empty] {
# Reset the x and y axis limits, by invoking the saved graph
# command.
eval [Pop]
# Cheat: Using "Empty" to get the number of entries on the stack.
set level [Empty]
if { $level > 0 } {
SetTitle "Zoom #$level"
}
blt::busy hold $graph
update
if { $corner == "first" } {
# Remember to remove the zoom title in a couple of seconds
after 2000 [code $this ClearTitle]
}
blt::busy release $graph
} else {
$graph marker delete "bltZoom_title"
}
}
# ----------------------------------------------------------------------
#
# Zoom --
#
# Push the old axis limits on the stack and set them to the
# zoom region.
#
# ----------------------------------------------------------------------
body ZoomStackGraph::Zoom { } {
$graph marker delete "bltZoom_*"
if { ($x1 == $x2) && ($y1 == $y2) } {
# The first and last points of the zoom region are the same.
# Revert back to the start.
return
}
# Put a command on the stack that lets us revert back to the current
# axis limits.
set cmd [format {
%s xaxis configure -min "%s" -max "%s"
%s yaxis configure -min "%s" -max "%s"
} $graph [$graph xaxis cget -min] [$graph xaxis cget -max] \
$graph [$graph yaxis cget -min] [$graph yaxis cget -max] ]
Push $cmd
# The first and last corners of the zoom region don't have to be
# selected in ascending order. So consider their relative positions
# when setting min and max axis limits.
if { $x1 > $x2 } {
$graph xaxis configure -min $x2 -max $x1
} elseif { $x1 < $x2 } {
$graph xaxis configure -min $x1 -max $x2
}
if { $y1 > $y2 } {
$graph yaxis configure -min $y2 -max $y1
} elseif { $y1 < $y2 } {
$graph yaxis configure -min $y1 -max $y2
}
# Call "update" explicitly here after the graph is made busy.
# This prevents the user from inadvertantly selecting another zoom
# region when the graph is recalculating and redrawing itself.
blt::busy hold $graph
update
blt::busy release $graph
}
# ----------------------------------------------------------------------
#
# ClearZoom --
#
# ----------------------------------------------------------------------
body ZoomStackGraph::ClearZoom { } {
$graph marker delete "bltZoom_*"
if { $corner == "first" } {
# We're haven't started to select a zoom region, so assume
# that we want to revert back to a previous zoom level.
Unzoom
} else {
# Let the user re-pick the first corner again. So reset the
# indicator "corner" and turn off the <Motion> binding.
set corner "first"
bind $graph <Motion> {}
}
}
# ----------------------------------------------------------------------
#
# SetTitle --
#
# ----------------------------------------------------------------------
body ZoomStackGraph::SetTitle { title } {
$graph marker create text -name "bltZoom_title" -text $title \
-coords {-Inf Inf} -anchor nw -bg {}
}
# ----------------------------------------------------------------------
#
# UpdateOutline --
#
# ----------------------------------------------------------------------
body ZoomStackGraph::UpdateOutline { x y } {
SaveCoords $x $y
MarkPoint $x2 $y2
DrawBox
}
# ----------------------------------------------------------------------
#
# SelectPoint --
#
# Invoked from the binding to ButtonPress-1 events. Saves
# a corner of zoom region.
#
#
# ----------------------------------------------------------------------
body ZoomStackGraph::SelectPoint { x y } {
SaveCoords $x $y
if { $corner == "first" } {
MarkPoint $x1 $y1
# Display a new title indicating zoom pick is active
set level [expr [llength $stack] + 1]
SetTitle "Zoom #$level"
# Start watching now for motion events, drawing an outline
bind $graph <Any-Motion> [code $this UpdateOutline %x %y]
# Indicate the next corner is the last
set corner last
} else {
# Stop watching motion events
bind $graph <Any-Motion> {}
# Zoom into the new region defined by the outline
Zoom
# Reset to select the first corner, again
set corner first
}
}
# ----------------------------------------------------------------------
#
# DrawBox --
#
# ----------------------------------------------------------------------
body ZoomStackGraph::DrawBox { } {
set coords {
$x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1
}
if [$graph marker exists "bltZoom_outline"] {
$graph marker configure "bltZoom_outline" -coords $coords
} else {
$graph marker create line -coords $coords -name "bltZoom_outline" \
-dashes { 4 2 }
}
$graph marker before "bltZoom_outline"
}