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

Last change on this file since 1152 was 1152, checked in by gah, 16 years ago

xylegend fixes

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