source: trunk/gui/scripts/xyresult.tcl @ 1837

Last change on this file since 1837 was 1837, checked in by gah, 14 years ago

add -tkwait flag to molvisviewer addmethod

File size: 56.2 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: xyresult - X/Y plot in a ResultSet
3#
4#  This widget is an X/Y plot, meant to view line graphs produced
5#  as output from the run of a Rappture tool.  Use the "add" and
6#  "delete" methods to control the curves showing on the plot.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005  Purdue Research Foundation
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15package require BLT
16
17option add *XyResult.width 3i widgetDefault
18option add *XyResult.height 3i widgetDefault
19option add *XyResult.gridColor #d9d9d9 widgetDefault
20option add *XyResult.activeColor blue widgetDefault
21option add *XyResult.dimColor gray widgetDefault
22option add *XyResult.controlBackground gray widgetDefault
23option add *XyResult.font \
24    -*-helvetica-medium-r-normal-*-12-* widgetDefault
25
26set autocolors {
27    #0000ff #ff0000 #00cc00
28    #cc00cc #ff9900 #cccc00
29    #000080 #800000 #006600
30    #660066 #996600 #666600
31}
32set autocolors {
33#0000cd
34#cd0000
35#00cd00
36#3a5fcd
37#cdcd00
38#cd1076
39#009acd
40#00c5cd
41#a2b5cd
42#7ac5cd
43#66cdaa
44#a2cd5a
45#cd9b9b
46#cdba96
47#cd3333
48#cd6600
49#cd8c95
50#cd00cd
51#9a32cd
52#6ca6cd
53#9ac0cd
54#9bcd9b
55#00cd66
56#cdc673
57#cdad00
58#cd5555
59#cd853f
60#cd7054
61#cd5b45
62#cd6889
63#cd69c9
64#551a8b
65}
66
67option add *XyResult.autoColors $autocolors widgetDefault
68option add *XyResult*Balloon*Entry.background white widgetDefault
69
70itcl::class Rappture::XyResult {
71    inherit itk::Widget
72
73    itk_option define -gridcolor gridColor GridColor ""
74    itk_option define -activecolor activeColor ActiveColor ""
75    itk_option define -dimcolor dimColor DimColor ""
76    itk_option define -autocolors autoColors AutoColors ""
77
78    constructor {args} { # defined below }
79    destructor { # defined below }
80
81    public method add {curve {settings ""}}
82    public method get {}
83    public method delete {args}
84    public method scale {args}
85    public method parameters {title args} { # do nothing }
86    public method download {option args}
87
88    protected method _rebuild {}
89    protected method _resetLimits {}
90    protected method _zoom {option args}
91    protected method _hilite {state x y}
92    protected method _axis {option args}
93    protected method _getAxes {curve}
94    protected method _getLineMarkerOptions { style }
95    protected method _getTextMarkerOptions { style }
96    protected method _enterMarker { g name x y text }
97    protected method _leaveMarker { g name }
98
99    private variable _dispatcher "" ;# dispatcher for !events
100    private variable _clist ""     ;# list of curve objects
101    private variable _curve2color  ;# maps curve => plotting color
102    private variable _curve2width  ;# maps curve => line width
103    private variable _curve2dashes ;# maps curve => BLT -dashes list
104    private variable _curve2raise  ;# maps curve => raise flag 0/1
105    private variable _curve2desc   ;# maps curve => description of data
106    private variable _elem2curve   ;# maps graph element => curve
107    private variable _label2axis   ;# maps axis label => axis ID
108    private variable _limits       ;# axis limits:  x-min, x-max, etc.
109    private variable _autoColorI 0 ;# index for next "-color auto"
110
111    private variable _hilite       ;# info for element currently highlighted
112    private variable _axis         ;# info for axis manipulations
113    private variable _axisPopup    ;# info for axis being edited in popup
114    common _downloadPopup          ;# download options from popup
115    private variable _markers
116    private variable cur_ ""
117    private variable initialized_ 0
118}
119                                                                               
120itk::usual XyResult {
121    keep -background -foreground -cursor -font
122}
123
124itk::usual Panedwindow {
125    keep -background -cursor
126}
127
128# ----------------------------------------------------------------------
129# CONSTRUCTOR
130# ----------------------------------------------------------------------
131itcl::body Rappture::XyResult::constructor {args} {
132    Rappture::dispatcher _dispatcher
133    $_dispatcher register !rebuild
134    $_dispatcher dispatch $this !rebuild "[itcl::code $this _rebuild]; list"
135
136    array set _downloadPopup {
137        format csv
138    }
139
140    option add hull.width hull.height
141    pack propagate $itk_component(hull) no
142
143    itk_component add main {
144        Rappture::SidebarFrame $itk_interior.main
145    }
146    pack $itk_component(main) -expand yes -fill both
147    set f [$itk_component(main) component controls]
148
149    itk_component add reset {
150        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
151            -highlightthickness 0 \
152            -image [Rappture::icon reset-view] \
153            -command [itcl::code $this _zoom reset]
154    } {
155        usual
156        ignore -borderwidth -highlightthickness
157    }
158    pack $itk_component(reset) -padx 4 -pady 2 -anchor e
159    Rappture::Tooltip::for $itk_component(reset) \
160        "Reset the view to the default zoom level"
161
162    set f [$itk_component(main) component frame]
163    itk_component add plot {
164        blt::graph $f.plot \
165            -highlightthickness 0 -plotpadx 0 -plotpady 4 \
166            -rightmargin 10
167    } {
168        keep -background -foreground -cursor -font
169    }
170    pack $itk_component(plot) -expand yes -fill both
171
172    $itk_component(plot) pen configure activeLine \
173        -symbol square -pixels 3 -linewidth 2 \
174        -outline black -fill red -color black
175
176    #
177    # Add bindings so you can mouse over points to see values:
178    #
179    bind $itk_component(plot) <Motion> \
180        [itcl::code $this _hilite at %x %y]
181    bind $itk_component(plot) <Leave> \
182        [itcl::code $this _hilite off %x %y]
183
184    #
185    # Add support for editing axes:
186    #
187    Rappture::Balloon $itk_component(hull).axes -title "Axis Options"
188    set inner [$itk_component(hull).axes component inner]
189
190    label $inner.labell -text "Label:"
191    entry $inner.label -width 15 -highlightbackground $itk_option(-background)
192    grid $inner.labell -row 1 -column 0 -sticky e
193    grid $inner.label -row 1 -column 1 -sticky ew -pady 4
194
195    label $inner.minl -text "Minimum:"
196    entry $inner.min -width 15 -highlightbackground $itk_option(-background)
197    grid $inner.minl -row 2 -column 0 -sticky e
198    grid $inner.min -row 2 -column 1 -sticky ew -pady 4
199
200    label $inner.maxl -text "Maximum:"
201    entry $inner.max -width 15 -highlightbackground $itk_option(-background)
202    grid $inner.maxl -row 3 -column 0 -sticky e
203    grid $inner.max -row 3 -column 1 -sticky ew -pady 4
204
205    label $inner.formatl -text "Format:"
206    Rappture::Combobox $inner.format -width 15 -editable no
207    $inner.format choices insert end \
208        "%.3g"  "Auto"         \
209        "%.0f"  "X"          \
210        "%.1f"  "X.X"          \
211        "%.2f"  "X.XX"         \
212        "%.3f"  "X.XXX"        \
213        "%.6f"  "X.XXXXXX"     \
214        "%.1e"  "X.Xe+XX"      \
215        "%.2e"  "X.XXe+XX"     \
216        "%.3e"  "X.XXXe+XX"    \
217        "%.6e"  "X.XXXXXXe+XX"
218    grid $inner.formatl -row 4 -column 0 -sticky e
219    grid $inner.format -row 4 -column 1 -sticky ew -pady 4
220
221    label $inner.scalel -text "Scale:"
222    frame $inner.scales
223    radiobutton $inner.scales.linear -text "Linear" \
224        -variable [itcl::scope _axisPopup(scale)] -value "linear"
225    pack $inner.scales.linear -side left
226    radiobutton $inner.scales.log -text "Logarithmic" \
227        -variable [itcl::scope _axisPopup(scale)] -value "log"
228    pack $inner.scales.log -side left
229    grid $inner.scalel -row 5 -column 0 -sticky e
230    grid $inner.scales -row 5 -column 1 -sticky ew -pady 4
231
232    foreach axis {x y} {
233        set _axisPopup(format-$axis) "%.3g"
234    }
235    _axis scale x linear
236    _axis scale y linear
237
238    $itk_component(plot) legend configure -hide yes
239
240    #
241    # Add legend for editing hidden/elements:
242    #
243    set inner [$itk_component(main) insert end \
244        -title "Legend" \
245        -icon [Rappture::icon wrench]]
246    $inner configure -borderwidth 4
247
248    itk_component add legend {
249        Rappture::XyLegend $inner.legend $itk_component(plot)
250    }
251    pack $itk_component(legend) -expand yes -fill both
252
253    after idle [subst {
254        update idletasks
255        $itk_component(legend) reset
256    }]
257
258    # quick-and-dirty zoom functionality, for now...
259    Blt_ZoomStack $itk_component(plot)
260    eval itk_initialize $args
261
262    set _hilite(elem) ""
263}
264
265# ----------------------------------------------------------------------
266# DESTRUCTOR
267# ----------------------------------------------------------------------
268itcl::body Rappture::XyResult::destructor {} {
269}
270
271# ----------------------------------------------------------------------
272# USAGE: add <curve> ?<settings>?
273#
274# Clients use this to add a curve to the plot.  The optional <settings>
275# are used to configure the plot.  Allowed settings are -color,
276# -brightness, -width, -linestyle and -raise.
277# ----------------------------------------------------------------------
278itcl::body Rappture::XyResult::add {curve {settings ""}} {
279    array set params {
280        -color auto
281        -brightness 0
282        -width 1
283        -type "line"
284        -raise 0
285        -linestyle solid
286        -description ""
287        -param ""
288        -tkwait no
289    }
290    foreach {opt val} $settings {
291        if {![info exists params($opt)]} {
292            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
293        }
294        set params($opt) $val
295    }
296
297    # if type is set to "scatter", then override the width
298    if {"scatter" == $params(-type)} {
299        set params(-width) 0
300    }
301
302    # if the color is "auto", then select a color from -autocolors
303    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
304        if {$params(-color) == "autoreset"} {
305            set _autoColorI 0
306        }
307        set color [lindex $itk_option(-autocolors) $_autoColorI]
308        if {"" == $color} { set color black }
309        set params(-color) $color
310
311        # set up for next auto color
312        if {[incr _autoColorI] >= [llength $itk_option(-autocolors)]} {
313            set _autoColorI 0
314        }
315    }
316
317    # convert -linestyle to BLT -dashes
318    switch -- $params(-linestyle) {
319        dashed { set params(-linestyle) {4 4} }
320        dotted { set params(-linestyle) {2 4} }
321        default { set params(-linestyle) {} }
322    }
323
324    # if -brightness is set, then update the color
325    if {$params(-brightness) != 0} {
326        set params(-color) [Rappture::color::brightness \
327            $params(-color) $params(-brightness)]
328
329        set bg [$itk_component(plot) cget -plotbackground]
330        foreach {h s v} [Rappture::color::RGBtoHSV $bg] break
331        if {$v > 0.5} {
332            set params(-color) [Rappture::color::brightness_max \
333                $params(-color) 0.8]
334        } else {
335            set params(-color) [Rappture::color::brightness_min \
336                $params(-color) 0.2]
337        }
338    }
339
340    set pos [lsearch -exact $curve $_clist]
341    if {$pos < 0} {
342        lappend _clist $curve
343        set _curve2color($curve) $params(-color)
344        set _curve2width($curve) $params(-width)
345        set _curve2dashes($curve) $params(-linestyle)
346        set _curve2raise($curve) $params(-raise)
347        set _curve2desc($curve) $params(-description)
348
349        $_dispatcher event -idle !rebuild
350    }
351}
352
353# ----------------------------------------------------------------------
354# USAGE: get
355#
356# Clients use this to query the list of objects being plotted, in
357# order from bottom to top of this result.
358# ----------------------------------------------------------------------
359itcl::body Rappture::XyResult::get {} {
360    # put the dataobj list in order according to -raise options
361    set clist $_clist
362    foreach obj $clist {
363        if {[info exists _curve2raise($obj)] && $_curve2raise($obj)} {
364            set i [lsearch -exact $clist $obj]
365            if {$i >= 0} {
366                set clist [lreplace $clist $i $i]
367                lappend clist $obj
368            }
369        }
370    }
371    return $clist
372}
373
374# ----------------------------------------------------------------------
375# USAGE: delete ?<curve1> <curve2> ...?
376#
377# Clients use this to delete a curve from the plot.  If no curves
378# are specified, then all curves are deleted.
379# ----------------------------------------------------------------------
380itcl::body Rappture::XyResult::delete {args} {
381    if {[llength $args] == 0} {
382        set args $_clist
383    }
384
385    # delete all specified curves
386    set changed 0
387    foreach curve $args {
388        set pos [lsearch -exact $_clist $curve]
389        if {$pos >= 0} {
390            set _clist [lreplace $_clist $pos $pos]
391            catch {unset _curve2color($curve)}
392            catch {unset _curve2width($curve)}
393            catch {unset _curve2dashes($curve)}
394            catch {unset _curve2raise($curve)}
395            foreach elem [array names _elem2curve] {
396                if {$_elem2curve($elem) == $curve} {
397                    unset _elem2curve($elem)
398                }
399            }
400            set changed 1
401        }
402    }
403
404    # If anything changed, then rebuild the plot
405    if {$changed} {
406        $_dispatcher event -idle !rebuild
407    }
408
409    # Nothing left? then start over with auto colors
410    if {[llength $_clist] == 0} {
411        set _autoColorI 0
412    }
413}
414
415# ----------------------------------------------------------------------
416# USAGE: scale ?<curve1> <curve2> ...?
417#
418# Sets the default limits for the overall plot according to the
419# limits of the data for all of the given <curve> objects.  This
420# accounts for all curves--even those not showing on the screen.
421# Because of this, the limits are appropriate for all curves as
422# the user scans through data in the ResultSet viewer.
423# ----------------------------------------------------------------------
424itcl::body Rappture::XyResult::scale {args} {
425    set allx [$itk_component(plot) x2axis use]
426    lappend allx x  ;# fix main x-axis too
427    foreach axis $allx {
428        _axis scale $axis linear
429    }
430
431    set ally [$itk_component(plot) y2axis use]
432    lappend ally y  ;# fix main y-axis too
433    foreach axis $ally {
434        _axis scale $axis linear
435    }
436
437    catch {unset _limits}
438    foreach curve $args {
439        # find the axes for this curve (e.g., {x y2})
440        foreach {map(x) map(y)} [_getAxes $curve] break
441
442        foreach axis {x y} {
443            # get defaults for both linear and log scales
444            foreach type {lin log} {
445                # store results -- ex: _limits(x2log-min)
446                set id $map($axis)$type
447                foreach {min max} [$curve limits $axis$type] break
448                if {"" != $min && "" != $max} {
449                    if {![info exists _limits($id-min)]} {
450                        set _limits($id-min) $min
451                        set _limits($id-max) $max
452                    } else {
453                        if {$min < $_limits($id-min)} {
454                            set _limits($id-min) $min
455                        }
456                        if {$max > $_limits($id-max)} {
457                            set _limits($id-max) $max
458                        }
459                    }
460                }
461            }
462
463            if {[$curve hints ${axis}scale] == "log"} {
464                _axis scale $map($axis) log
465            }
466        }
467    }
468    _resetLimits
469}
470
471# ----------------------------------------------------------------------
472# USAGE: download coming
473# USAGE: download controls <downloadCommand>
474# USAGE: download now
475#
476# Clients use this method to create a downloadable representation
477# of the plot.  Returns a list of the form {ext string}, where
478# "ext" is the file extension (indicating the type of data) and
479# "string" is the data itself.
480# ----------------------------------------------------------------------
481itcl::body Rappture::XyResult::download {option args} {
482    switch $option {
483        coming {
484            # nothing to do
485        }
486        controls {
487            set popup .xyresultdownload
488            if {![winfo exists .xyresultdownload]} {
489                # if we haven't created the popup yet, do it now
490                Rappture::Balloon $popup \
491                    -title "[Rappture::filexfer::label downloadWord] as..."
492                set inner [$popup component inner]
493                label $inner.summary -text "" -anchor w
494                pack $inner.summary -side top
495                radiobutton $inner.csv -text "Data as Comma-Separated Values" \
496                    -variable Rappture::XyResult::_downloadPopup(format) \
497                    -value csv
498                pack $inner.csv -anchor w
499                radiobutton $inner.image -text "Image (PS/PDF/PNG/JPEG)" \
500                    -variable Rappture::XyResult::_downloadPopup(format) \
501                    -value image
502                pack $inner.image -anchor w
503                button $inner.go -text [Rappture::filexfer::label download] \
504                    -command [lindex $args 0]
505                pack $inner.go -pady 4
506            } else {
507                set inner [$popup component inner]
508            }
509            set num [llength [get]]
510            set num [expr {($num == 1) ? "1 result" : "$num results"}]
511            $inner.summary configure -text "[Rappture::filexfer::label downloadWord] $num in the following format:"
512            update idletasks ;# fix initial sizes
513            return $popup
514        }
515        now {
516            set popup .xyresultdownload
517            if {[winfo exists .xyresultdownload]} {
518                $popup deactivate
519            }
520            switch -- $_downloadPopup(format) {
521                csv {
522                    # reverse the objects so the selected data appears on top
523                    set dlist ""
524                    foreach dataobj [get] {
525                        set dlist [linsert $dlist 0 $dataobj]
526                    }
527
528                    # generate the comma-separated value data for these objects
529                    set csvdata ""
530                    foreach dataobj $dlist {
531                        append csvdata "[string repeat - 60]\n"
532                        append csvdata " [$dataobj hints label]\n"
533                        if {[info exists _curve2desc($dataobj)]
534                            && [llength [split $_curve2desc($dataobj) \n]] > 1} {
535                            set indent "for:"
536                            foreach line [split $_curve2desc($dataobj) \n] {
537                                append csvdata " $indent $line\n"
538                                set indent "    "
539                            }
540                        }
541                        append csvdata "[string repeat - 60]\n"
542
543                        append csvdata "[$dataobj hints xlabel], [$dataobj hints ylabel]\n"
544                        set first 1
545                        foreach comp [$dataobj components] {
546                            if {!$first} {
547                                # blank line between components
548                                append csvdata "\n"
549                            }
550                            set xv [$dataobj mesh $comp]
551                            set yv [$dataobj values $comp]
552                            foreach x [$xv range 0 end] y [$yv range 0 end] {
553                                append csvdata [format "%20.15g, %20.15g\n" $x $y]
554                            }
555                            set first 0
556                        }
557                        append csvdata "\n"
558                    }
559                    return [list .txt $csvdata]
560                }
561                image {
562                    set popup .xyprintdownload
563                    if { ![winfo exists $popup] } {
564                        # Create a popup for the print dialog
565                        Rappture::Balloon $popup -title "Save as image..."
566                        set inner [$popup component inner]
567                        # Create the print dialog widget and add it to the
568                        # the balloon popup.
569                        Rappture::XyPrint $inner.print
570                        $popup configure \
571                            -deactivatecommand [list $inner.print reset]
572                        blt::table $inner 0,0 $inner.print -fill both
573                    }
574                    update
575                    # Activate the popup and call for the output.
576                    foreach { widget toolName plotName } $args break
577                    $popup activate $widget left
578                    set inner [$popup component inner]
579                    set output [$inner.print print $itk_component(plot) \
580                                    $toolName $plotName]
581                    $popup deactivate
582                    return $output
583                }
584            }
585        }
586        default {
587            error "bad option \"$option\": should be coming, controls, now"
588        }
589    }
590}
591
592# ----------------------------------------------------------------------
593# USAGE: _rebuild
594#
595# Called automatically whenever something changes that affects the
596# data in the widget.  Clears any existing data and rebuilds the
597# widget to display new data.
598# ----------------------------------------------------------------------
599itcl::body Rappture::XyResult::_rebuild {} {
600    set g $itk_component(plot)
601
602    # first clear out the widget
603    eval $g element delete [$g element names]
604    eval $g marker delete [$g marker names]
605    foreach axis [$g axis names] {
606        $g axis configure $axis -hide yes -checklimits no
607    }
608    # Presumably you want at least an X-axis and Y-axis displayed.
609    $g xaxis configure -hide no
610    $g yaxis configure -hide no
611    catch {unset _label2axis}
612
613    #
614    # Scan through all objects and create a list of all axes.
615    # The first x-axis gets mapped to "x".  The second, to "x2".
616    # Beyond that, we must create new axes "x3", "x4", etc.
617    # We do the same for y.
618    #
619    set anum(x) 0
620    set anum(y) 0
621    foreach curve [get] {
622        foreach ax {x y} {
623            set label [$curve hints ${ax}label]
624            if {"" != $label} {
625                if {![info exists _label2axis($ax-$label)]} {
626                    switch [incr anum($ax)] {
627                        1 { set axis $ax }
628                        2 { set axis ${ax}2 }
629                        default {
630                            set axis $ax$anum($ax)
631                            catch {$g axis create $axis}
632                        }
633                    }
634                    $g axis configure $axis -title $label -hide no \
635                        -checklimits no
636                    set _label2axis($ax-$label) $axis
637
638                    # if this axis has a description, add it as a tooltip
639                    set desc [string trim [$curve hints ${ax}desc]]
640                    Rappture::Tooltip::text $g-$axis $desc
641                }
642            }
643        }
644    }
645
646    #
647    # All of the extra axes get mapped to the x2/y2 (top/right)
648    # position.
649    #
650    set all ""
651    foreach ax {x y} {
652        lappend all $ax
653
654        set extra ""
655        for {set i 2} {$i <= $anum($ax)} {incr i} {
656            lappend extra ${ax}$i
657        }
658        eval lappend all $extra
659        $g ${ax}2axis use $extra
660        if {$ax == "y"} {
661            $g configure -rightmargin [expr {($extra == "") ? 10 : 0}]
662        }
663    }
664
665    foreach axis $all {
666        set _axisPopup(format-$axis) "%.3g"
667
668        $g axis bind $axis <Enter> \
669            [itcl::code $this _axis hilite $axis on]
670        $g axis bind $axis <Leave> \
671            [itcl::code $this _axis hilite $axis off]
672        $g axis bind $axis <ButtonPress> \
673            [itcl::code $this _axis click $axis %x %y]
674        $g axis bind $axis <B1-Motion> \
675            [itcl::code $this _axis drag $axis %x %y]
676        $g axis bind $axis <ButtonRelease> \
677            [itcl::code $this _axis release $axis %x %y]
678        $g axis bind $axis <KeyPress> \
679            [list ::Rappture::Tooltip::tooltip cancel]
680    }
681
682    #
683    # Plot all of the curves.
684    #
685    set count 0
686    foreach curve $_clist {
687        set label [$curve hints label]
688        foreach {mapx mapy} [_getAxes $curve] break
689
690        foreach comp [$curve components] {
691            set xv [$curve mesh $comp]
692            set yv [$curve values $comp]
693
694            if {[info exists _curve2color($curve)]} {
695                set color $_curve2color($curve)
696            } else {
697                set color [$curve hints color]
698                if {"" == $color} {
699                    set color black
700                }
701            }
702
703            if {[info exists _curve2width($curve)]} {
704                set lwidth $_curve2width($curve)
705            } else {
706                set lwidth 2
707            }
708
709            if {[info exists _curve2dashes($curve)]} {
710                set dashes $_curve2dashes($curve)
711            } else {
712                set dashes ""
713            }
714
715            if {([$xv length] <= 1) || ($lwidth == 0)} {
716                set sym square
717                set pixels 2
718            } else {
719                set sym ""
720                set pixels 6
721            }
722
723            set elem "elem[incr count]"
724            set _elem2curve($elem) $curve
725            lappend label2elem($label) $elem
726            $g element create $elem -x $xv -y $yv \
727                -symbol $sym -pixels $pixels -linewidth $lwidth \
728                -label $label \
729                -color $color -dashes $dashes \
730                -mapx $mapx -mapy $mapy
731        }
732    }
733
734    # Fix duplicate labels by appending the simulation number
735    foreach label [array names label2elem] {
736        if { [llength $label2elem($label)] == 1 } {
737            continue
738        }
739        foreach elem $label2elem($label) {
740            set curve $_elem2curve($elem)
741            scan [$curve hints xmlobj] "::libraryObj%d" suffix
742            incr suffix
743            set elabel [format "%s \#%d" $label $suffix]
744            $g element configure $elem -label $elabel
745        }
746    }       
747
748    foreach curve $_clist {
749        set xmin -Inf
750        set ymin -Inf
751        set xmax Inf
752        set ymax Inf
753        #
754        # Create text/line markers for each *axis.marker specified.
755        #
756        foreach m [$curve xmarkers] {
757            foreach {at label style} $m break
758            set id [$g marker create line -coords [list $at $ymin $at $ymax]]
759            $g marker bind $id <Enter> \
760                [itcl::code $this _enterMarker $g x-$label $at $ymin $at]
761            $g marker bind $id <Leave> \
762                [itcl::code $this _leaveMarker $g x-$label]
763            set options [_getLineMarkerOptions $style]
764            if { $options != "" } {
765                eval $g marker configure $id $options
766            }
767            if { $label != "" } {
768                set id [$g marker create text -anchor nw \
769                            -text $label -coords [list $at $ymax]]
770                set options [_getTextMarkerOptions $style]
771                if { $options != "" } {
772                    eval $g marker configure $id $options
773                }
774            }
775        }
776        foreach m [$curve ymarkers] {
777            foreach {at label style} $m break
778            set id [$g marker create line -coords [list $xmin $at $xmax $at]]
779            $g marker bind $id <Enter> \
780                [itcl::code $this _enterMarker $g y-$label $at $xmin $at]
781            $g marker bind $id <Leave> \
782                [itcl::code $this _leaveMarker $g y-$label]
783            set options [_getLineMarkerOptions $style]
784            if { $options != "" } {
785                eval $g marker configure $id $options
786            }
787            if { $label != "" } {
788                set id [$g marker create text -anchor se \
789                        -text $label -coords [list $xmax $at]]
790                set options [_getTextMarkerOptions $style]
791                if { $options != "" } {
792                    eval $g marker configure $id $options
793                }
794            }
795        }
796    }
797    $itk_component(legend) reset
798}
799
800# ----------------------------------------------------------------------
801# USAGE: _resetLimits
802#
803# Used internally to apply automatic limits to the axes for the
804# current plot.
805# ----------------------------------------------------------------------
806itcl::body Rappture::XyResult::_resetLimits {} {
807    set g $itk_component(plot)
808
809    #
810    # HACK ALERT!
811    # Use this code to fix up the y-axis limits for the BLT graph.
812    # The auto-limits don't always work well.  We want them to be
813    # set to a "nice" number slightly above or below the min/max
814    # limits.
815    #
816    foreach axis [$g axis names] {
817        if {[info exists _limits(${axis}lin-min)]} {
818            set log [$g axis cget $axis -logscale]
819            if {$log} {
820                set min $_limits(${axis}log-min)
821                if {$min == 0} { set min 1 }
822                set max $_limits(${axis}log-max)
823                if {$max == 0} { set max 1 }
824
825                if {$min == $max} {
826                    set logmin [expr {floor(log10(abs(0.9*$min)))}]
827                    set logmax [expr {ceil(log10(abs(1.1*$max)))}]
828                } else {
829                    set logmin [expr {floor(log10(abs($min)))}]
830                    set logmax [expr {ceil(log10(abs($max)))}]
831                    if 0 {
832                    if {[string match y* $axis]} {
833                        # add a little padding
834                        set delta [expr {$logmax-$logmin}]
835                        if {$delta == 0} { set delta 1 }
836                        set logmin [expr {$logmin-0.05*$delta}]
837                        set logmax [expr {$logmax+0.05*$delta}]
838                    }
839                    }
840                }
841                if {$logmin < -300} {
842                    set min 1e-300
843                } elseif {$logmin > 300} {
844                    set min 1e+300
845                } else {
846                    set min [expr {pow(10.0,$logmin)}]
847                }
848
849                if {$logmax < -300} {
850                    set max 1e-300
851                } elseif {$logmax > 300} {
852                    set max 1e+300
853                } else {
854                    set max [expr {pow(10.0,$logmax)}]
855                }
856            } else {
857                set min $_limits(${axis}lin-min)
858                set max $_limits(${axis}lin-max)
859
860                if 0 {
861                if {[string match y* $axis]} {
862                    # add a little padding
863                    set delta [expr {$max-$min}]
864                    set min [expr {$min-0.05*$delta}]
865                    set max [expr {$max+0.05*$delta}]
866                }
867                }
868            }
869            if {$min < $max} {
870                $g axis configure $axis -min $min -max $max
871            } else {
872                $g axis configure $axis -min "" -max ""
873            }
874        } else {
875            $g axis configure $axis -min "" -max ""
876        }
877    }
878}
879
880# ----------------------------------------------------------------------
881# USAGE: _zoom reset
882#
883# Called automatically when the user clicks on one of the zoom
884# controls for this widget.  Changes the zoom for the current view.
885# ----------------------------------------------------------------------
886itcl::body Rappture::XyResult::_zoom {option args} {
887    switch -- $option {
888        reset {
889            _resetLimits
890        }
891    }
892}
893
894# ----------------------------------------------------------------------
895# USAGE: _hilite <state> <x> <y>
896#
897# Called automatically when the user brushes one of the elements
898# on the plot.  Causes the element to highlight and a tooltip to
899# pop up with element info.
900# ----------------------------------------------------------------------
901itcl::body Rappture::XyResult::_hilite {state x y} {
902    set g $itk_component(plot)
903    set elem ""
904 
905    # Peek inside of Blt_ZoomStack package to see if we're currently in the
906    # middle of a zoom selection.
907    if {[info exists ::zoomInfo($g,corner)] && $::zoomInfo($g,corner) == "B" } {
908        return;
909    }
910    set tip ""
911    if {$state == "at"} {
912        if {[$g element closest $x $y info -interpolate yes]} {
913            # for dealing with xy line plots
914            set elem $info(name)
915
916            # Some elements are generated dynamically and therefore will
917            # not have a curve object associated with them.
918            set mapx [$g element cget $elem -mapx]
919            set mapy [$g element cget $elem -mapy]
920            if {[info exists _elem2curve($elem)]} {
921                foreach {mapx mapy} [_getAxes $_elem2curve($elem)] break
922            }
923
924            # search again for an exact point -- this time don't interpolate
925            set tip ""
926            array unset info
927            if {[$g element closest $x $y info -interpolate no]
928                  && $info(name) == $elem} {
929
930                set x [$g axis transform $mapx $info(x)]
931                set y [$g axis transform $mapy $info(y)]
932               
933                if {[info exists _elem2curve($elem)]} {
934                    set curve $_elem2curve($elem)
935                    set yunits [$curve hints yunits]
936                    set xunits [$curve hints xunits]
937                } else {
938                    set xunits ""
939                    set yunits ""
940                }
941                set tip [$g element cget $elem -label]
942                set yval [_axis format y dummy $info(y)]
943                append tip "\n$yval$yunits"
944                set xval [_axis format x dummy $info(x)]
945                append tip " @ $xval$xunits"
946                set tip [string trim $tip]
947            }
948            set state 1
949        } elseif {[$g element closest $x $y info -interpolate no]} {
950            # for dealing with xy scatter plot
951            set elem $info(name)
952
953            # Some elements are generated dynamically and therefore will
954            # not have a curve object associated with them.
955            set mapx [$g element cget $elem -mapx]
956            set mapy [$g element cget $elem -mapy]
957            if {[info exists _elem2curve($elem)]} {
958                foreach {mapx mapy} [_getAxes $_elem2curve($elem)] break
959            }
960
961            set tip ""
962            set x [$g axis transform $mapx $info(x)]
963            set y [$g axis transform $mapy $info(y)]
964               
965            if {[info exists _elem2curve($elem)]} {
966                set curve $_elem2curve($elem)
967                set yunits [$curve hints yunits]
968                set xunits [$curve hints xunits]
969            } else {
970                set xunits ""
971                set yunits ""
972            }
973            set tip [$g element cget $elem -label]
974            set yval [_axis format y dummy $info(y)]
975            append tip "\n$yval$yunits"
976            set xval [_axis format x dummy $info(x)]
977            append tip " @ $xval$xunits"
978            set tip [string trim $tip]
979            set state 1
980        } else {
981            set state 0
982        }
983    }
984
985    if {$state} {
986        #
987        # Highlight ON:
988        # - activate trace
989        # - multiple axes? dim other axes
990        # - pop up tooltip about data
991        #
992        if { [$g element exists $_hilite(elem)] && $_hilite(elem) != $elem } {
993            $g element deactivate $_hilite(elem)
994            $g crosshairs configure -hide yes
995            Rappture::Tooltip::tooltip cancel
996        }
997        $g element activate $elem
998        set _hilite(elem) $elem
999
1000        set mapx [$g element cget $elem -mapx]
1001        set mapy [$g element cget $elem -mapy]
1002        if {[info exists _elem2curve($elem)]} {
1003            foreach {mapx mapy} [_getAxes $_elem2curve($elem)] break
1004        }
1005        set allx [$g x2axis use]
1006        if {[llength $allx] > 0} {
1007            lappend allx x  ;# fix main x-axis too
1008            foreach axis $allx {
1009                if {$axis == $mapx} {
1010                    $g axis configure $axis -color $itk_option(-foreground) \
1011                        -titlecolor $itk_option(-foreground)
1012                } else {
1013                    $g axis configure $axis -color $itk_option(-dimcolor) \
1014                        -titlecolor $itk_option(-dimcolor)
1015                }
1016            }
1017        }
1018        set ally [$g y2axis use]
1019        if {[llength $ally] > 0} {
1020            lappend ally y  ;# fix main y-axis too
1021            foreach axis $ally {
1022                if {$axis == $mapy} {
1023                    $g axis configure $axis -color $itk_option(-foreground) \
1024                        -titlecolor $itk_option(-foreground)
1025                } else {
1026                    $g axis configure $axis -color $itk_option(-dimcolor) \
1027                        -titlecolor $itk_option(-dimcolor)
1028                }
1029            }
1030        }
1031
1032        if {"" != $tip} {
1033            $g crosshairs configure -hide no -position @$x,$y
1034
1035            if {$x > 0.5*[winfo width $g]} {
1036                if {$x < 4} {
1037                    set tipx "-0"
1038                } else {
1039                    set tipx "-[expr {$x-4}]"  ;# move tooltip to the left
1040                }
1041            } else {
1042                if {$x < -4} {
1043                    set tipx "+0"
1044                } else {
1045                    set tipx "+[expr {$x+4}]"  ;# move tooltip to the right
1046                }
1047            }
1048            if {$y > 0.5*[winfo height $g]} {
1049                if {$y < 4} {
1050                    set tipy "-0"
1051                } else {
1052                    set tipy "-[expr {$y-4}]"  ;# move tooltip to the top
1053                }
1054            } else {
1055                if {$y < -4} {
1056                    set tipy "+0"
1057                } else {
1058                    set tipy "+[expr {$y+4}]"  ;# move tooltip to the bottom
1059                }
1060            }
1061            Rappture::Tooltip::text $g $tip
1062            Rappture::Tooltip::tooltip show $g $tipx,$tipy
1063        }
1064    } else {
1065        #
1066        # Highlight OFF:
1067        # - deactivate (color back to normal)
1068        # - put all axes back to normal color
1069        # - take down tooltip
1070        #
1071        if { [$g element exists $_hilite(elem)] } {
1072            $g element deactivate $_hilite(elem)
1073        }
1074        set allx [$g x2axis use]
1075        if {[llength $allx] > 0} {
1076            lappend allx x  ;# fix main x-axis too
1077            foreach axis $allx {
1078                $g axis configure $axis -color $itk_option(-foreground) \
1079                    -titlecolor $itk_option(-foreground)
1080            }
1081        }
1082       
1083        set ally [$g y2axis use]
1084        if {[llength $ally] > 0} {
1085            lappend ally y  ;# fix main y-axis too
1086            foreach axis $ally {
1087                $g axis configure $axis -color $itk_option(-foreground) \
1088                    -titlecolor $itk_option(-foreground)
1089            }
1090        }
1091
1092        $g crosshairs configure -hide yes
1093
1094        # only cancel in plotting area or we'll mess up axes
1095        if {[$g inside $x $y]} {
1096            Rappture::Tooltip::tooltip cancel
1097        }
1098
1099        # There is no currently highlighted element
1100        set _hilite(elem) ""
1101    }
1102}
1103
1104# ----------------------------------------------------------------------
1105# USAGE: _axis hilite <axis> <state>
1106#
1107# USAGE: _axis click <axis> <x> <y>
1108# USAGE: _axis drag <axis> <x> <y>
1109# USAGE: _axis release <axis> <x> <y>
1110#
1111# USAGE: _axis edit <axis>
1112# USAGE: _axis changed <axis> <what>
1113# USAGE: _axis format <axis> <widget> <value>
1114# USAGE: _axis scale <axis> linear|log
1115#
1116# Used internally to handle editing of the x/y axes.  The hilite
1117# operation causes the axis to light up.  The edit operation pops
1118# up a panel with editing options.  The changed operation applies
1119# changes from the panel.
1120# ----------------------------------------------------------------------
1121itcl::body Rappture::XyResult::_axis {option args} {
1122    set inner [$itk_component(hull).axes component inner]
1123
1124    switch -- $option {
1125        hilite {
1126            if {[llength $args] != 2} {
1127                error "wrong # args: should be \"_axis hilite axis state\""
1128            }
1129            set g $itk_component(plot)
1130            set axis [lindex $args 0]
1131            set state [lindex $args 1]
1132
1133            if {$state} {
1134                $g axis configure $axis \
1135                    -color $itk_option(-activecolor) \
1136                    -titlecolor $itk_option(-activecolor)
1137
1138                set x [expr {[winfo pointerx $g]+4}]
1139                set y [expr {[winfo pointery $g]+4}]
1140                Rappture::Tooltip::tooltip pending $g-$axis @$x,$y
1141            } else {
1142                $g axis configure $axis \
1143                    -color $itk_option(-foreground) \
1144                    -titlecolor $itk_option(-foreground)
1145                Rappture::Tooltip::tooltip cancel
1146            }
1147        }
1148        click {
1149            if {[llength $args] != 3} {
1150                error "wrong # args: should be \"_axis click axis x y\""
1151            }
1152            set axis [lindex $args 0]
1153            set x [lindex $args 1]
1154            set y [lindex $args 2]
1155            set g $itk_component(plot)
1156
1157            set _axis(moved) 0
1158            set _axis(click-x) $x
1159            set _axis(click-y) $y
1160            foreach {min max} [$g axis limits $axis] break
1161            set _axis(min0) $min
1162            set _axis(max0) $max
1163            Rappture::Tooltip::tooltip cancel
1164        }
1165        drag {
1166            if {[llength $args] != 3} {
1167                error "wrong # args: should be \"_axis drag axis x y\""
1168            }
1169            if {![info exists _axis(moved)]} {
1170                return  ;# must have skipped click event -- ignore
1171            }
1172            set axis [lindex $args 0]
1173            set x [lindex $args 1]
1174            set y [lindex $args 2]
1175            set g $itk_component(plot)
1176
1177            if {[info exists _axis(click-x)] && [info exists _axis(click-y)]} {
1178                foreach {x0 y0 pw ph} [$g extents plotarea] break
1179                switch -glob $axis {
1180                  x* {
1181                    set pix $x
1182                    set pix0 $_axis(click-x)
1183                    set pixmin $x0
1184                    set pixmax [expr {$x0+$pw}]
1185                  }
1186                  y* {
1187                    set pix $y
1188                    set pix0 $_axis(click-y)
1189                    set pixmin [expr {$y0+$ph}]
1190                    set pixmax $y0
1191                  }
1192                }
1193                set log [$g axis cget $axis -logscale]
1194                set min $_axis(min0)
1195                set max $_axis(max0)
1196                set dpix [expr {abs($pix-$pix0)}]
1197                set v0 [$g axis invtransform $axis $pixmin]
1198                set v1 [$g axis invtransform $axis [expr {$pixmin+$dpix}]]
1199                if {$log} {
1200                    set v0 [expr {log10($v0)}]
1201                    set v1 [expr {log10($v1)}]
1202                    set min [expr {log10($min)}]
1203                    set max [expr {log10($max)}]
1204                }
1205
1206                if {$pix > $pix0} {
1207                    set delta [expr {$v1-$v0}]
1208                } else {
1209                    set delta [expr {$v0-$v1}]
1210                }
1211                set min [expr {$min-$delta}]
1212                set max [expr {$max-$delta}]
1213                if {$log} {
1214                    set min [expr {pow(10.0,$min)}]
1215                    set max [expr {pow(10.0,$max)}]
1216                }
1217                $g axis configure $axis -min $min -max $max
1218
1219                # move axis, don't edit on release
1220                set _axis(move) 1
1221            }
1222        }
1223        release {
1224            if {[llength $args] != 3} {
1225                error "wrong # args: should be \"_axis release axis x y\""
1226            }
1227            if {![info exists _axis(moved)]} {
1228                return  ;# must have skipped click event -- ignore
1229            }
1230            set axis [lindex $args 0]
1231            set x [lindex $args 1]
1232            set y [lindex $args 2]
1233
1234            if {!$_axis(moved)} {
1235                # small movement? then treat as click -- pop up axis editor
1236                set dx [expr {abs($x-$_axis(click-x))}]
1237                set dy [expr {abs($y-$_axis(click-y))}]
1238                if {$dx < 2 && $dy < 2} {
1239                    _axis edit $axis
1240                }
1241            } else {
1242                # one last movement
1243                _axis drag $axis $x $y
1244            }
1245            catch {unset _axis}
1246        }
1247        edit {
1248            if {[llength $args] != 1} {
1249                error "wrong # args: should be \"_axis edit axis\""
1250            }
1251            set axis [lindex $args 0]
1252            set _axisPopup(current) $axis
1253
1254            # apply last value when deactivating
1255            $itk_component(hull).axes configure -deactivatecommand \
1256                [itcl::code $this _axis changed $axis focus]
1257
1258            # fix axis label controls...
1259            set label [$itk_component(plot) axis cget $axis -title]
1260            $inner.label delete 0 end
1261            $inner.label insert end $label
1262            bind $inner.label <KeyPress-Return> \
1263                [itcl::code $this _axis changed $axis label]
1264            bind $inner.label <FocusOut> \
1265                [itcl::code $this _axis changed $axis label]
1266
1267            # fix min/max controls...
1268            foreach {min max} [$itk_component(plot) axis limits $axis] break
1269            $inner.min delete 0 end
1270            $inner.min insert end $min
1271            bind $inner.min <KeyPress-Return> \
1272                [itcl::code $this _axis changed $axis min]
1273            bind $inner.min <FocusOut> \
1274                [itcl::code $this _axis changed $axis min]
1275
1276            $inner.max delete 0 end
1277            $inner.max insert end $max
1278            bind $inner.max <KeyPress-Return> \
1279                [itcl::code $this _axis changed $axis max]
1280            bind $inner.max <FocusOut> \
1281                [itcl::code $this _axis changed $axis max]
1282
1283            # fix format control...
1284            set fmts [$inner.format choices get -value]
1285            set i [lsearch -exact $fmts $_axisPopup(format-$axis)]
1286            if {$i < 0} { set i 0 }  ;# use Auto choice
1287            $inner.format value [$inner.format choices get -label $i]
1288
1289            bind $inner.format <<Value>> \
1290                [itcl::code $this _axis changed $axis format]
1291
1292            # fix scale control...
1293            if {[$itk_component(plot) axis cget $axis -logscale]} {
1294                set _axisPopup(scale) "log"
1295                $inner.format configure -state disabled
1296            } else {
1297                set _axisPopup(scale) "linear"
1298                $inner.format configure -state normal
1299            }
1300            $inner.scales.linear configure \
1301                -command [itcl::code $this _axis changed $axis scale]
1302            $inner.scales.log configure \
1303                -command [itcl::code $this _axis changed $axis scale]
1304
1305            #
1306            # Figure out where the window should pop up.
1307            #
1308            set x [winfo rootx $itk_component(plot)]
1309            set y [winfo rooty $itk_component(plot)]
1310            set w [winfo width $itk_component(plot)]
1311            set h [winfo height $itk_component(plot)]
1312            foreach {x0 y0 pw ph} [$itk_component(plot) extents plotarea] break
1313            switch -glob -- $axis {
1314                x {
1315                    set x [expr {round($x + $x0+0.5*$pw)}]
1316                    set y [expr {round($y + $y0+$ph + 0.5*($h-$y0-$ph))}]
1317                    set dir "above"
1318                }
1319                x* {
1320                    set x [expr {round($x + $x0+0.5*$pw)}]
1321                    set dir "below"
1322                    set allx [$itk_component(plot) x2axis use]
1323                    set max [llength $allx]
1324                    set i [lsearch -exact $allx $axis]
1325                    set y [expr {round($y + ($i+0.5)*$y0/double($max))}]
1326                }
1327                y {
1328                    set x [expr {round($x + 0.5*$x0)}]
1329                    set y [expr {round($y + $y0+0.5*$ph)}]
1330                    set dir "right"
1331                }
1332                y* {
1333                    set y [expr {round($y + $y0+0.5*$ph)}]
1334                    set dir "left"
1335                    set ally [$itk_component(plot) y2axis use]
1336                    set max [llength $ally]
1337                    set i [lsearch -exact $ally $axis]
1338                    set y [expr {round($y + ($i+0.5)*$y0/double($max))}]
1339                    set x [expr {round($x+$x0+$pw + ($i+0.5)*($w-$x0-$pw)/double($max))}]
1340                }
1341            }
1342            $itk_component(hull).axes activate @$x,$y $dir
1343        }
1344        changed {
1345            if {[llength $args] != 2} {
1346                error "wrong # args: should be \"_axis changed axis what\""
1347            }
1348            set axis [lindex $args 0]
1349            set what [lindex $args 1]
1350            if {$what == "focus"} {
1351                set what [focus]
1352                if {[winfo exists $what]} {
1353                    set what [winfo name $what]
1354                }
1355            }
1356
1357            switch -- $what {
1358                label {
1359                    set val [$inner.label get]
1360                    $itk_component(plot) axis configure $axis -title $val
1361                }
1362                min {
1363                    set val [$inner.min get]
1364                    if {![string is double -strict $val]} {
1365                        Rappture::Tooltip::cue $inner.min "Must be a number"
1366                        bell
1367                        return
1368                    }
1369
1370                    set max [lindex [$itk_component(plot) axis limits $axis] 1]
1371                    if {$val >= $max} {
1372                        Rappture::Tooltip::cue $inner.min "Must be <= max ($max)"
1373                        bell
1374                        return
1375                    }
1376                    catch {
1377                        # can fail in log mode
1378                        $itk_component(plot) axis configure $axis -min $val
1379                    }
1380                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1381                    $inner.min delete 0 end
1382                    $inner.min insert end $min
1383                }
1384                max {
1385                    set val [$inner.max get]
1386                    if {![string is double -strict $val]} {
1387                        Rappture::Tooltip::cue $inner.max "Should be a number"
1388                        bell
1389                        return
1390                    }
1391
1392                    set min [lindex [$itk_component(plot) axis limits $axis] 0]
1393                    if {$val <= $min} {
1394                        Rappture::Tooltip::cue $inner.max "Must be >= min ($min)"
1395                        bell
1396                        return
1397                    }
1398                    catch {
1399                        # can fail in log mode
1400                        $itk_component(plot) axis configure $axis -max $val
1401                    }
1402                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1403                    $inner.max delete 0 end
1404                    $inner.max insert end $max
1405                }
1406                format {
1407                    set fmt [$inner.format translate [$inner.format value]]
1408                    set _axisPopup(format-$axis) $fmt
1409
1410                    # force a refresh
1411                    $itk_component(plot) axis configure $axis -min \
1412                        [$itk_component(plot) axis cget $axis -min]
1413                }
1414                scale {
1415                    _axis scale $axis $_axisPopup(scale)
1416
1417                    if {$_axisPopup(scale) == "log"} {
1418                        $inner.format configure -state disabled
1419                    } else {
1420                        $inner.format configure -state normal
1421                    }
1422
1423                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1424                    $inner.min delete 0 end
1425                    $inner.min insert end $min
1426                    $inner.max delete 0 end
1427                    $inner.max insert end $max
1428                }
1429                default {
1430                    # be lenient so we can handle the "focus" case
1431                }
1432            }
1433        }
1434        format {
1435            if {[llength $args] != 3} {
1436                error "wrong # args: should be \"_axis format axis widget value\""
1437            }
1438            set axis [lindex $args 0]
1439            set value [lindex $args 2]
1440
1441            if {[$itk_component(plot) axis cget $axis -logscale]} {
1442                set fmt "%.3g"
1443            } else {
1444                set fmt $_axisPopup(format-$axis)
1445            }
1446            return [format $fmt $value]
1447        }
1448        scale {
1449            if {[llength $args] != 2} {
1450                error "wrong # args: should be \"_axis scale axis type\""
1451            }
1452            set axis [lindex $args 0]
1453            set type [lindex $args 1]
1454
1455            if {$type == "log"} {
1456                catch {$itk_component(plot) axis configure $axis -logscale 1}
1457                # leave format alone in log mode
1458                $itk_component(plot) axis configure $axis -command ""
1459            } else {
1460                catch {$itk_component(plot) axis configure $axis -logscale 0}
1461                # use special formatting for linear mode
1462                $itk_component(plot) axis configure $axis -command \
1463                    [itcl::code $this _axis format $axis]
1464            }
1465        }
1466        default {
1467            error "bad option \"$option\": should be changed, edit, hilite, or format"
1468        }
1469    }
1470}
1471
1472
1473# ----------------------------------------------------------------------
1474# USAGE: _getLineMarkerOptions <style>
1475#
1476# Used internally to create a list of configuration options specific to the
1477# axis line marker.  The input is a list of name value pairs.  Options that
1478# are not recognized are ignored.
1479# ----------------------------------------------------------------------
1480itcl::body Rappture::XyResult::_getLineMarkerOptions {style} {
1481    array set lineOptions {
1482        "-color"  "-outline"
1483        "-dashes" "-dashes"
1484        "-linecolor" "-outline"
1485        "-linewidth" "-linewidth"
1486    }
1487    set options {}
1488    foreach {name value} $style {
1489        if { [info exists lineOptions($name)] } {
1490            lappend options $lineOptions($name) $value
1491        }
1492    }
1493    return $options
1494}
1495
1496# ----------------------------------------------------------------------
1497# USAGE: _getTextMarkerOptions <style>
1498#
1499# Used internally to create a list of configuration options specific to the
1500# axis text marker.  The input is a list of name value pairs.  Options that
1501# are not recognized are ignored.
1502# ----------------------------------------------------------------------
1503itcl::body Rappture::XyResult::_getTextMarkerOptions {style} {
1504    array set textOptions {
1505        "-color"        "-outline"
1506        "-textcolor"    "-outline"
1507        "-font"         "-font"
1508        "-xoffset"      "-xoffset"
1509        "-yoffset"      "-yoffset"
1510        "-anchor"       "-anchor"
1511        "-rotate"       "-rotate"
1512    }
1513    set options {}
1514    foreach {name value} $style {
1515        if { [info exists textOptions($name)] } {
1516            lappend options $textOptions($name) $value
1517        }
1518    }
1519    return $options
1520}
1521
1522# ----------------------------------------------------------------------
1523# USAGE: _getAxes <curveObj>
1524#
1525# Used internally to figure out the axes used to plot the given
1526# <curveObj>.  Returns a list of the form {x y}, where x is the
1527# x-axis name (x, x2, x3, etc.), and y is the y-axis name.
1528# ----------------------------------------------------------------------
1529itcl::body Rappture::XyResult::_getAxes {curve} {
1530    # rebuild if needed, so we know about the axes
1531    if {[$_dispatcher ispending !rebuild]} {
1532        $_dispatcher cancel !rebuild
1533        $_dispatcher event -now !rebuild
1534    }
1535
1536    # what is the x axis?  x? x2? x3? ...
1537    set xlabel [$curve hints xlabel]
1538    if {[info exists _label2axis(x-$xlabel)]} {
1539        set mapx $_label2axis(x-$xlabel)
1540    } else {
1541        set mapx "x"
1542    }
1543
1544    # what is the y axis?  y? y2? y3? ...
1545    set ylabel [$curve hints ylabel]
1546    if {[info exists _label2axis(y-$ylabel)]} {
1547        set mapy $_label2axis(y-$ylabel)
1548    } else {
1549        set mapy "y"
1550    }
1551
1552    return [list $mapx $mapy]
1553}
1554
1555# ----------------------------------------------------------------------
1556# CONFIGURATION OPTION: -gridcolor
1557# ----------------------------------------------------------------------
1558itcl::configbody Rappture::XyResult::gridcolor {
1559    if {"" == $itk_option(-gridcolor)} {
1560        $itk_component(plot) grid off
1561    } else {
1562        $itk_component(plot) grid configure -color $itk_option(-gridcolor)
1563        $itk_component(plot) grid on
1564    }
1565}
1566
1567# ----------------------------------------------------------------------
1568# CONFIGURATION OPTION: -autocolors
1569# ----------------------------------------------------------------------
1570itcl::configbody Rappture::XyResult::autocolors {
1571    foreach c $itk_option(-autocolors) {
1572        if {[catch {winfo rgb $itk_component(hull) $c}]} {
1573            error "bad color \"$c\""
1574        }
1575    }
1576    if {$_autoColorI >= [llength $itk_option(-autocolors)]} {
1577        set _autoColorI 0
1578    }
1579}
1580
1581itcl::body Rappture::XyResult::_enterMarker { g name x y text } {
1582    _leaveMarker $g $name
1583    set id [$g marker create text \
1584                -coords [list $x $y] \
1585                -yoffset -1 \
1586                -anchor s \
1587                -text $text]
1588    set _markers($name) $id
1589}
1590
1591itcl::body Rappture::XyResult::_leaveMarker { g name } {
1592    if { [info exists _markers($name)] } {
1593        set id $_markers($name)
1594        $g marker delete $id
1595        unset _markers($name)
1596    }
1597}
Note: See TracBrowser for help on using the repository browser.