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

Last change on this file since 1040 was 1040, checked in by gah, 15 years ago

moved b64 library to rappture core

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