source: trunk/gui/scripts/numberresult.tcl @ 3394

Last change on this file since 3394 was 3330, checked in by gah, 11 years ago

merge (by hand) with Rappture1.2 branch

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