source: branches/uq/gui/scripts/numberresult.tcl @ 5679

Last change on this file since 5679 was 5679, checked in by ldelgass, 9 years ago

Full merge 1.3 branch to uq branch to sync. Fixed partial subdirectory merge
by removing mergeinfo from lang/python/Rappture directory.

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