source: branches/1.3/gui/scripts/numberresult.tcl @ 4693

Last change on this file since 4693 was 3844, checked in by ldelgass, 11 years ago

Sync with trunk. Branch now differs only from trunk by r3722 (branch is version
1.3, trunk is version 1.4)

File size: 56.0 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    private method ResetLegend {}
98
99    private variable _dispatcher "" ;# dispatcher for !events
100
101    private variable _dlist ""     ;# list of data objects
102    private variable _dataobj2color   ;# maps data object => plotting color
103    private variable _dataobj2width   ;# maps data object => line width
104    private variable _dataobj2raise   ;# maps data object => raise flag 0/1
105    private variable _dataobj2desc    ;# maps data object => description of data
106    private variable _dataobj2param   ;# maps data object => x-axis value
107    private variable _elem2dataobj    ;# maps graph element => data object
108    private variable _label2axis   ;# maps axis label => axis ID
109    private variable _params ""    ;# complete description of x-axis
110    private variable _xval2label   ;# maps x-axis value => tick label
111    private variable _xlabels 0    ;# non-zero => use _xval2label
112    private variable _limits       ;# axis limits:  x-min, x-max, etc.
113
114    private variable _hilite       ;# info for element currently highlighted
115    private variable _axis         ;# info for axis manipulations
116    private variable _axisPopup    ;# info for axis being edited in popup
117    common _downloadPopup          ;# download options from popup
118}
119
120itk::usual NumberResult {
121    keep -background -foreground -cursor -font
122}
123
124# ----------------------------------------------------------------------
125# CONSTRUCTOR
126# ----------------------------------------------------------------------
127itcl::body Rappture::NumberResult::constructor {args} {
128    Rappture::dispatcher _dispatcher
129    $_dispatcher register !rebuild
130    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
131
132    array set _downloadPopup {
133        format csv
134    }
135
136    option add hull.width hull.height
137    pack propagate $itk_component(hull) no
138
139    itk_component add main {
140        Rappture::SidebarFrame $itk_interior.main
141    }
142    pack $itk_component(main) -expand yes -fill both
143    set f [$itk_component(main) component controls]
144
145    itk_component add reset {
146        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
147            -highlightthickness 0 \
148            -image [Rappture::icon reset-view] \
149            -command [itcl::code $this Zoom reset]
150    } {
151        usual
152        ignore -borderwidth -highlightthickness
153    }
154    pack $itk_component(reset) -padx 4 -pady 2 -anchor e
155    Rappture::Tooltip::for $itk_component(reset) \
156        "Reset the view to the default zoom level"
157
158    set f [$itk_component(main) component frame]
159    itk_component add plot {
160        blt::graph $f.plot \
161            -highlightthickness 0 -plotpadx 0 -plotpady 4 \
162            -rightmargin 10
163    } {
164        keep -background -foreground -cursor -font
165    }
166    pack $itk_component(plot) -expand yes -fill both
167
168    $itk_component(plot) pen configure activeLine \
169        -symbol square -pixels 3 -linewidth 2 \
170        -outline black -fill red -color black
171
172    # Add bindings so you can mouse over points to see values:
173    bind $itk_component(plot) <Motion> \
174        [itcl::code $this Hilite at %x %y]
175    bind $itk_component(plot) <Leave> \
176        [itcl::code $this Hilite off %x %y]
177
178    #
179    # Add support for editing axes:
180    #
181    Rappture::Balloon $itk_component(hull).axes -title "Axis Options"
182    set inner [$itk_component(hull).axes component inner]
183
184    label $inner.labell -text "Label:"
185    entry $inner.label -width 15 -highlightbackground $itk_option(-background)
186    grid $inner.labell -row 1 -column 0 -sticky e
187    grid $inner.label -row 1 -column 1 -sticky ew -pady 4
188
189    label $inner.minl -text "Minimum:"
190    entry $inner.min -width 15 -highlightbackground $itk_option(-background)
191    grid $inner.minl -row 2 -column 0 -sticky e
192    grid $inner.min -row 2 -column 1 -sticky ew -pady 4
193
194    label $inner.maxl -text "Maximum:"
195    entry $inner.max -width 15 -highlightbackground $itk_option(-background)
196    grid $inner.maxl -row 3 -column 0 -sticky e
197    grid $inner.max -row 3 -column 1 -sticky ew -pady 4
198
199    label $inner.formatl -text "Format:"
200    Rappture::Combobox $inner.format -width 15 -editable no
201    $inner.format choices insert end \
202        "%.6g"  "Auto"         \
203        "%.0f"  "X"          \
204        "%.1f"  "X.X"          \
205        "%.2f"  "X.XX"         \
206        "%.3f"  "X.XXX"        \
207        "%.6f"  "X.XXXXXX"     \
208        "%.1e"  "X.Xe+XX"      \
209        "%.2e"  "X.XXe+XX"     \
210        "%.3e"  "X.XXXe+XX"    \
211        "%.6e"  "X.XXXXXXe+XX"
212    grid $inner.formatl -row 4 -column 0 -sticky e
213    grid $inner.format -row 4 -column 1 -sticky ew -pady 4
214
215    label $inner.scalel -text "Scale:"
216    frame $inner.scales
217    radiobutton $inner.scales.linear -text "Linear" \
218        -variable [itcl::scope _axisPopup(scale)] -value "linear"
219    pack $inner.scales.linear -side left
220    radiobutton $inner.scales.log -text "Logarithmic" \
221        -variable [itcl::scope _axisPopup(scale)] -value "log"
222    pack $inner.scales.log -side left
223    grid $inner.scalel -row 5 -column 0 -sticky e
224    grid $inner.scales -row 5 -column 1 -sticky ew -pady 4
225
226    foreach axis {x y} {
227        set _axisPopup(format-$axis) "%.6g"
228    }
229    Axis scale x linear
230    Axis scale y linear
231
232    $itk_component(plot) legend configure -hide yes
233
234    #
235    # Add legend for editing hidden/elements:
236    #
237    set inner [$itk_component(main) insert end \
238        -title "Legend" \
239        -icon [Rappture::icon wrench]]
240    $inner configure -borderwidth 4
241
242    itk_component add legend {
243        Rappture::XyLegend $inner.legend $itk_component(plot)
244    }
245    pack $itk_component(legend) -expand yes -fill both
246    after idle [itcl::code $this ResetLegend]
247
248    # quick-and-dirty zoom functionality, for now...
249    Blt_ZoomStack $itk_component(plot)
250    eval itk_initialize $args
251    set _hilite(elem) ""
252}
253
254# ----------------------------------------------------------------------
255# DESTRUCTOR
256# ----------------------------------------------------------------------
257itcl::body Rappture::NumberResult::destructor {} {
258}
259
260# ----------------------------------------------------------------------
261# USAGE: add <dataobj> ?<settings>?
262#
263# Clients use this to add a data object to the plot.  The optional
264# <settings> are used to configure the plot.  Allowed settings are
265# -color, -brightness, -width, -linestyle and -raise.
266# ----------------------------------------------------------------------
267itcl::body Rappture::NumberResult::add {dataobj {settings ""}} {
268    array set params {
269        -color auto
270        -brightness 0
271        -width 1
272        -type "line"
273        -raise 0
274        -linestyle solid
275        -description ""
276        -param ""
277    }
278    array set params $settings
279
280    # if type is set to "scatter", then override the width
281    if {"scatter" == $params(-type)} {
282        set params(-width) 0
283    }
284
285    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
286        set params(-color) #0000ff
287    }
288
289    # if -brightness is set, then update the color
290    if {$params(-brightness) != 0} {
291        set params(-color) [Rappture::color::brightness \
292            $params(-color) $params(-brightness)]
293
294        set bg [$itk_component(plot) cget -plotbackground]
295        foreach {h s v} [Rappture::color::RGBtoHSV $bg] break
296        if {$v > 0.5} {
297            set params(-color) [Rappture::color::brightness_max \
298                $params(-color) 0.8]
299        } else {
300            set params(-color) [Rappture::color::brightness_min \
301                $params(-color) 0.2]
302        }
303    }
304
305    set pos [lsearch -exact $_dlist $dataobj]
306    if {$pos < 0} {
307        lappend _dlist $dataobj
308        set _dataobj2color($dataobj) $params(-color)
309        set _dataobj2width($dataobj) $params(-width)
310        set _dataobj2raise($dataobj) $params(-raise)
311        set _dataobj2desc($dataobj) $params(-description)
312        set _dataobj2param($dataobj) $params(-param)
313
314        $_dispatcher event -idle !rebuild
315    }
316}
317
318# ----------------------------------------------------------------------
319# USAGE: get
320#
321# Clients use this to query the list of objects being plotted, in
322# order from bottom to top of this result.
323# ----------------------------------------------------------------------
324itcl::body Rappture::NumberResult::get {} {
325    # put the dataobj list in order according to -raise options
326    set dlist $_dlist
327    foreach obj $dlist {
328        if {[info exists _dataobj2raise($obj)] && $_dataobj2raise($obj)} {
329            set i [lsearch -exact $dlist $obj]
330            if {$i >= 0} {
331                set dlist [lreplace $dlist $i $i]
332                lappend dlist $obj
333            }
334        }
335    }
336    return $dlist
337}
338
339# ----------------------------------------------------------------------
340# USAGE: delete ?<dataobj2> <dataobj2> ...?
341#
342# Clients use this to delete a data object from the plot.  If no
343# objects are specified, then all objects are deleted.
344# ----------------------------------------------------------------------
345itcl::body Rappture::NumberResult::delete {args} {
346    if {[llength $args] == 0} {
347        set args $_dlist
348    }
349
350    # delete all specified data objects
351    set changed 0
352    foreach dataobj $args {
353        set pos [lsearch -exact $_dlist $dataobj]
354        if {$pos >= 0} {
355            set _dlist [lreplace $_dlist $pos $pos]
356            catch {unset _dataobj2color($dataobj)}
357            catch {unset _dataobj2width($dataobj)}
358            catch {unset _dataobj2raise($dataobj)}
359            catch {unset _dataobj2desc($dataobj)}
360            catch {unset _dataobj2param($dataobj)}
361            foreach elem [array names _elem2dataobj] {
362                if {$_elem2dataobj($elem) == $dataobj} {
363                    unset _elem2dataobj($elem)
364                }
365            }
366            set changed 1
367        }
368    }
369
370    # if anything changed, then rebuild the plot
371    if {$changed} {
372        $_dispatcher event -idle !rebuild
373    }
374    # Nothing left? then start over with auto colors
375    if {[llength $_dlist] == 0} {
376        set _autoColorI 0
377    }
378}
379
380# ----------------------------------------------------------------------
381# USAGE: scale ?<dataobj1> <dataobj2> ...?
382#
383# Sets the default limits for the overall plot according to the
384# limits of the data for all of the given data objects.  This
385# accounts for all objects--even those not showing on the screen.
386# Because of this, the limits are appropriate for all objects as
387# the user scans through data in the ResultSet viewer.
388# ----------------------------------------------------------------------
389itcl::body Rappture::NumberResult::scale {args} {
390    set allx [$itk_component(plot) x2axis use]
391    lappend allx x  ;# fix main x-axis too
392    foreach axis $allx {
393        Axis scale $axis linear
394    }
395
396    set ally [$itk_component(plot) y2axis use]
397    lappend ally y  ;# fix main y-axis too
398    foreach axis $ally {
399        Axis scale $axis linear
400    }
401
402    catch {unset _limits}
403    foreach dataobj $args {
404        # find the axes for this data object (e.g., {x y2})
405        foreach {map(x) map(y)} [GetAxes $dataobj] break
406
407        foreach axis {x y} {
408            # get defaults for both linear and log scales
409            foreach type {lin log} {
410                # store results -- ex: _limits(x2log-min)
411                set id $map($axis)$type
412                if {$axis == "x"} {
413                    set min [set max ""]
414                    foreach {xlab xval} [lrange $_params 1 end] {
415                        if {$type == "log"} {
416                            set xval [expr {abs($xval)}]
417                            if {$xval == 0} {
418                                continue
419                            }
420                        }
421                        if {"" == $min} {
422                            set min [set max $xval]
423                        } else {
424                            if {$xval < $min} { set min $xval }
425                            if {$xval > $max} { set max $xval }
426                        }
427                    }
428                } else {
429                    set min [set max [GetValue $dataobj y]]
430                }
431
432                if {"" != $min && "" != $max} {
433                    if {![info exists _limits($id-min)]} {
434                        set _limits($id-min) $min
435                        set _limits($id-max) $max
436                    } else {
437                        if {$min < $_limits($id-min)} {
438                            set _limits($id-min) $min
439                        }
440                        if {$max > $_limits($id-max)} {
441                            set _limits($id-max) $max
442                        }
443                    }
444                }
445            }
446        }
447    }
448    ResetLimits
449}
450
451# ----------------------------------------------------------------------
452# USAGE: parameters <title> <label1> <value1> <label2> <value2> ...
453#
454# Called when the resultset changes to convey information about the
455# current set of results being displayed.  The <title> is the name
456# of the parameter being explored, and each <label>/<value> represents
457# a data point.
458# ----------------------------------------------------------------------
459itcl::body Rappture::NumberResult::parameters {args} {
460    set _params $args
461
462    #
463    # Fix up the x-axis limits for the primary axis.
464    #
465    foreach type {lin log} {
466        # store results -- ex: _limits(x2log-min)
467        set id x$type
468
469        set min [set max ""]
470        foreach {xlab xval} [lrange $_params 1 end] {
471            if {$type == "log"} {
472                set xval [expr {abs($xval)}]
473                if {$xval == 0} {
474                    continue
475                }
476            }
477            if {"" == $min} {
478                set min [set max $xval]
479            } else {
480                if {$xval < $min} { set min $xval }
481                if {$xval > $max} { set max $xval }
482            }
483        }
484
485        if {"" != $min && "" != $max} {
486            set _limits($id-min) $min
487            set _limits($id-max) $max
488        }
489    }
490    ResetLimits
491
492    #
493    # Figure out a good set of ticks for the x-axis.
494    # If this is a numeric axis, then labels will be like
495    #   0.3V <=> 0.3, 0.4V <=> 0.4, etc.
496    # Otherwise, they will be more like
497    #   red <=> 0, green <=> 1, etc.
498    #
499    set havenums 1
500    catch {unset _xval2label}
501    foreach {xlab xval} [lrange $_params 1 end] {
502        set _xval2label($xval) $xlab
503        if {![string match $xval* $xlab]} {
504            set havenums 0
505        }
506    }
507    if {$havenums} {
508        set _xlabels 0
509        $itk_component(plot) xaxis configure -command "" -majorticks ""
510        if {![$itk_component(plot) axis cget x -logscale]} {
511            $itk_component(plot) xaxis configure \
512                -command [itcl::code $this Axis format x]
513        }
514    } else {
515        set _xlabels 1
516        $itk_component(plot) xaxis configure \
517            -command [itcl::code $this Axis format x] \
518            -majorticks [lsort -real [array names _xval2label]]
519    }
520
521    $_dispatcher event -idle !rebuild
522}
523
524# ----------------------------------------------------------------------
525# USAGE: download coming
526# USAGE: download controls <downloadCommand>
527# USAGE: download now
528#
529# Clients use this method to create a downloadable representation
530# of the plot.  Returns a list of the form {ext string}, where
531# "ext" is the file extension (indicating the type of data) and
532# "string" is the data itself.
533# ----------------------------------------------------------------------
534itcl::body Rappture::NumberResult::download {option args} {
535    switch $option {
536        coming {
537            # nothing to do
538        }
539        controls {
540            set popup .numberresultdownload
541            if {![winfo exists .numberresultdownload]} {
542                # if we haven't created the popup yet, do it now
543                Rappture::Balloon $popup \
544                    -title "[Rappture::filexfer::label downloadWord] as..."
545                set inner [$popup component inner]
546                label $inner.summary -text "" -anchor w
547                pack $inner.summary -side top
548                radiobutton $inner.csv -text "Data as Comma-Separated Values" \
549                    -variable Rappture::NumberResult::_downloadPopup(format) \
550                    -value csv
551                pack $inner.csv -anchor w
552                radiobutton $inner.image -text "Image (PS/PDF/PNG/JPEG)" \
553                    -variable Rappture::NumberResult::_downloadPopup(format) \
554                    -value image
555                pack $inner.image -anchor w
556                button $inner.go -text [Rappture::filexfer::label download] \
557                    -command [lindex $args 0]
558                pack $inner.go -pady 4
559            } else {
560                set inner [$popup component inner]
561            }
562            set num [llength [get]]
563            set num [expr {($num == 1) ? "1 result" : "$num results"}]
564            $inner.summary configure -text "[Rappture::filexfer::label downloadWord] $num in the following format:"
565            update idletasks ;# fix initial sizes
566            return $popup
567        }
568        now {
569            set popup .numberresultdownload
570            if {[winfo exists .numberresultdownload]} {
571                $popup deactivate
572            }
573            switch -- $_downloadPopup(format) {
574                csv {
575                    # March through the values in order and report
576                    # all data points
577                    set csvdata ""
578                    set xtitle [$itk_component(plot) xaxis cget -title]
579                    set ytitle [$itk_component(plot) yaxis cget -title]
580                   
581                    set desc ""
582                    set dataobj [lindex [get] end]
583                   
584                    # the "Simulation" axis shows all values
585                    # -- no need for assumptions
586                    if {$xtitle != "Simulation"
587                        && [info exists _dataobj2desc($dataobj)]} {
588                        foreach line [split $_dataobj2desc($dataobj) \n] {
589                            # skip the current axis and the Simulation axis
590                            # Other values show assumptions about values reported
591                            if {[string match "$xtitle =*" $line]
592                                || [string match "Simulation =*" $line]} {
593                                continue
594                            }
595                            set indent [expr {("" == $desc) ? "for:" : "    "}]
596                            append desc " $indent $line\n"
597                        }
598                    }
599                    if {[string length $desc] > 0} {
600                        append csvdata "[string repeat - 60]\n"
601                        append csvdata $desc
602                        append csvdata "[string repeat - 60]\n"
603                    }
604                   
605                    append csvdata "$xtitle, $ytitle\n"
606                    foreach xval [lsort -real [array names _xval2label]] {
607                        set dataobj ""
608                        set param [list $_xval2label($xval) $xval]
609                        foreach obj $_dlist {
610                            if {[info exists _dataobj2param($obj)]
611                                && [string equal $_dataobj2param($obj) $param]} {
612                                set dataobj $obj
613                                break
614                            }
615                        }
616                        if {"" != $dataobj} {
617                            set yval [$dataobj get current]
618                            append csvdata "$_xval2label($xval), $yval\n"
619                        }
620                    }
621                    return [list .txt $csvdata]
622                }
623                image {
624                    set popup .numberresultprintdownload
625                    if { ![winfo exists $popup] } {
626                        # Create a popup for the print dialog
627                        Rappture::Balloon $popup -title "Save as image..."
628                        set inner [$popup component inner]
629                        # Create the print dialog widget and add it to the
630                        # the balloon popup.
631                        Rappture::XyPrint $inner.print
632                        $popup configure \
633                            -deactivatecommand [list $inner.print reset]
634                        blt::table $inner 0,0 $inner.print -fill both
635                    }
636                    update
637                    # Activate the popup and call for the output.
638                    foreach { widget toolName plotName } $args break
639                    $popup activate $widget left
640                    set inner [$popup component inner]
641                    set output [$inner.print print $itk_component(plot) \
642                                    $toolName $plotName]
643                    $popup deactivate
644                    return $output
645                }
646            }
647        }
648        default {
649            error "bad option \"$option\": should be coming, controls, now"
650        }
651    }
652}
653
654# ----------------------------------------------------------------------
655# USAGE: Rebuild
656#
657# Called automatically whenever something changes that affects the
658# data in the widget.  Clears any existing data and rebuilds the
659# widget to display new data.
660# ----------------------------------------------------------------------
661itcl::body Rappture::NumberResult::Rebuild {} {
662    set g $itk_component(plot)
663
664    # first clear out the widget
665    eval $g element delete [$g element names]
666    eval $g marker delete [$g marker names]
667    foreach axis [$g axis names] {
668        $g axis configure $axis -hide yes -checklimits no
669    }
670    # Presumably you want at least an X-axis and Y-axis displayed.
671    $g xaxis configure -hide no
672    $g yaxis configure -hide no
673    catch {unset _label2axis}
674
675    #
676    # Scan through all objects and create a list of all axes.
677    # The first x-axis gets mapped to "x".  The second, to "x2".
678    # Beyond that, we must create new axes "x3", "x4", etc.
679    # We do the same for y.
680    #
681    set anum(x) 0
682    set anum(y) 0
683    foreach dataobj [get] {
684        foreach ax {x y} {
685            set label [GetInfo about.label $dataobj ${ax}]
686            if {"" != $label} {
687                if {![info exists _label2axis($ax-$label)]} {
688                    switch [incr anum($ax)] {
689                        1 { set axis $ax }
690                        2 { set axis ${ax}2 }
691                        default {
692                            set axis $ax$anum($ax)
693                            catch {$g axis create $axis}
694                        }
695                    }
696                    $g axis configure $axis -title $label -hide no \
697                        -checklimits no
698                    set _label2axis($ax-$label) $axis
699
700                    # if this axis has a description, add it as a tooltip
701                    set desc [string trim [GetInfo about.description $dataobj ${ax}]]
702                    Rappture::Tooltip::text $g-$axis $desc
703                }
704            }
705        }
706    }
707
708    #
709    # All of the extra axes get mapped to the x2/y2 (top/right)
710    # position.
711    #
712    set all ""
713    foreach ax {x y} {
714        lappend all $ax
715
716        set extra ""
717        for {set i 2} {$i <= $anum($ax)} {incr i} {
718            lappend extra ${ax}$i
719        }
720        eval lappend all $extra
721        $g ${ax}2axis use $extra
722        if {$ax == "y"} {
723            $g configure -rightmargin [expr {($extra == "") ? 10 : 0}]
724        }
725    }
726
727    foreach axis $all {
728        set _axisPopup(format-$axis) "%.6g"
729
730        $g axis bind $axis <Enter> \
731            [itcl::code $this Axis hilite $axis on]
732        $g axis bind $axis <Leave> \
733            [itcl::code $this Axis hilite $axis off]
734        $g axis bind $axis <ButtonPress-1> \
735            [itcl::code $this Axis click $axis %x %y]
736        $g axis bind $axis <B1-Motion> \
737            [itcl::code $this Axis drag $axis %x %y]
738        $g axis bind $axis <ButtonRelease-1> \
739            [itcl::code $this Axis release $axis %x %y]
740        $g axis bind $axis <KeyPress> \
741            [list ::Rappture::Tooltip::tooltip cancel]
742    }
743
744    #
745    # Plot all of the data objects.
746    #
747    set count 0
748    foreach dataobj $_dlist {
749        set label [$dataobj get about.label]
750        foreach {mapx mapy} [GetAxes $dataobj] break
751
752        foreach {xv yv} [GetValue $dataobj] break
753
754        if {[info exists _dataobj2color($dataobj)]} {
755            set color $_dataobj2color($dataobj)
756        } else {
757            set color [$dataobj get about.color]
758            if {"" == $color} {
759                set color $itk_option(-activecolor)
760            }
761        }
762
763        set sym square
764        set pixels 6
765
766        set elem "elem[incr count]"
767        set _elem2dataobj($elem) $dataobj
768        $g element create $elem -x $xv -y $yv \
769            -symbol $sym -pixels $pixels -label $label \
770            -color $color -mapx $mapx -mapy $mapy
771    }
772}
773
774# ----------------------------------------------------------------------
775# USAGE: ResetLimits
776#
777# Used internally to apply automatic limits to the axes for the
778# current plot.
779# ----------------------------------------------------------------------
780itcl::body Rappture::NumberResult::ResetLimits {} {
781    set g $itk_component(plot)
782
783    #
784    # HACK ALERT!
785    # Use this code to fix up the y-axis limits for the BLT graph.
786    # The auto-limits don't always work well.  We want them to be
787    # set to a "nice" number slightly above or below the min/max
788    # limits.
789    #
790    foreach axis [$g axis names] {
791        if {[info exists _limits(${axis}lin-min)]} {
792            set log [$g axis cget $axis -logscale]
793            if {$log} {
794                set min $_limits(${axis}log-min)
795                if {$min == 0} { set min 1 }
796                set max $_limits(${axis}log-max)
797                if {$max == 0} { set max 1 }
798
799                if {$min == $max} {
800                    set logmin [expr {floor(log10(abs(0.9*$min)))}]
801                    set logmax [expr {ceil(log10(abs(1.1*$max)))}]
802                } else {
803                    set logmin [expr {floor(log10(abs($min)))}]
804                    set logmax [expr {ceil(log10(abs($max)))}]
805                    if {[string match y* $axis]} {
806                        # add a little padding
807                        set delta [expr {$logmax-$logmin}]
808                        if {$delta == 0} { set delta 1 }
809                        set logmin [expr {$logmin-0.05*$delta}]
810                        set logmax [expr {$logmax+0.05*$delta}]
811                    }
812                }
813                if {$logmin < -300} {
814                    set min 1e-300
815                } elseif {$logmin > 300} {
816                    set min 1e+300
817                } else {
818                    set min [expr {pow(10.0,$logmin)}]
819                }
820
821                if {$logmax < -300} {
822                    set max 1e-300
823                } elseif {$logmax > 300} {
824                    set max 1e+300
825                } else {
826                    set max [expr {pow(10.0,$logmax)}]
827                }
828            } else {
829                set min $_limits(${axis}lin-min)
830                set max $_limits(${axis}lin-max)
831
832                # add a little padding
833                set delta [expr {$max-$min}]
834                set min [expr {$min-0.05*$delta}]
835                set max [expr {$max+0.05*$delta}]
836            }
837            if {$min < $max} {
838                $g axis configure $axis -min $min -max $max
839            } else {
840                $g axis configure $axis -min "" -max ""
841            }
842        } else {
843            $g axis configure $axis -min "" -max ""
844        }
845    }
846}
847
848# ----------------------------------------------------------------------
849# USAGE: Zoom reset
850#
851# Called automatically when the user clicks on one of the zoom
852# controls for this widget.  Changes the zoom for the current view.
853# ----------------------------------------------------------------------
854itcl::body Rappture::NumberResult::Zoom {option args} {
855    switch -- $option {
856        reset {
857            ResetLimits
858            Rappture::Logger::log number zoom -reset
859        }
860    }
861}
862
863# ----------------------------------------------------------------------
864# USAGE: Hilite <state> <x> <y>
865#
866# Called automatically when the user brushes one of the elements
867# on the plot.  Causes the element to highlight and a tooltip to
868# pop up with element info.
869# ----------------------------------------------------------------------
870itcl::body Rappture::NumberResult::Hilite {state x y} {
871    set g $itk_component(plot)
872    set elem ""
873
874    # Peek inside of Blt_ZoomStack package to see if we're currently in the
875    # middle of a zoom selection.
876    if {[info exists ::zoomInfo($g,corner)] && $::zoomInfo($g,corner) == "B" } {
877        return;
878    }
879    set tip ""
880    if {$state == "at"} {
881        if {[$g element closest $x $y info -interpolate yes]} {
882            # for dealing with xy line plots
883            set elem $info(name)
884            # Some elements are generated dynamically and therefore will
885            # not have a data object associated with them.
886            set mapx [$g element cget $elem -mapx]
887            set mapy [$g element cget $elem -mapy]
888            if {[info exists _elem2dataobj($elem)]} {
889                foreach {mapx mapy} [GetAxes $_elem2dataobj($elem)] break
890            }
891
892            # search again for an exact point -- this time don't interpolate
893            set tip ""
894            if {[$g element closest $x $y info -interpolate no]
895                  && $info(name) == $elem} {
896                set x [$g axis transform $mapx $info(x)]
897                set y [$g axis transform $mapy $info(y)]
898
899                if {[info exists _elem2dataobj($elem)]} {
900                    set dataobj $_elem2dataobj($elem)
901                    set tip [GetInfo about.label $dataobj y]
902                    if {[info exists info(y)]} {
903                        set val [Axis format y dummy $info(y)]
904                        set units [GetInfo units $dataobj y]
905                        append tip "\n$val$units"
906
907                        if {[info exists _dataobj2param($dataobj)]} {
908                            set val [lindex $_dataobj2param($dataobj) 0]
909                            append tip " @ $val"
910                        }
911                    }
912                    set tip [string trim $tip]
913                }
914            }
915            set state 1
916        } elseif {[$g element closest $x $y info -interpolate no]} {
917            # for dealing with xy scatter plot
918            set elem $info(name)
919            foreach {mapx mapy} [GetAxes $_elem2dataobj($elem)] break
920
921            # search again for an exact point -- this time don't interpolate
922            set tip ""
923            if {$info(name) == $elem} {
924                set x [$g axis transform $mapx $info(x)]
925                set y [$g axis transform $mapy $info(y)]
926
927                if {[info exists _elem2dataobj($elem)]} {
928                    set dataobj $_elem2dataobj($elem)
929                    set tip [GetInfo about.label $dataobj y]
930                    if {[info exists info(y)]} {
931                        set val [Axis format y dummy $info(y)]
932                        set units [GetInfo units $dataobj y]
933                        append tip "\n$val$units"
934
935                        if {[info exists _dataobj2param($dataobj)]} {
936                            set val [lindex $_dataobj2param($dataobj) 0]
937                            append tip " @ $val"
938                        }
939                    }
940                    set tip [string trim $tip]
941                }
942            }
943            set state 1
944        } else {
945            set state 0
946        }
947    }
948
949    if {$state} {
950        #
951        # Highlight ON:
952        # - activate trace
953        # - multiple axes? dim other axes
954        # - pop up tooltip about data
955        if {$_hilite(elem) ne ""} {
956            $g element deactivate $_hilite(elem)
957        }
958        $g crosshairs configure -hide yes
959        Rappture::Tooltip::tooltip cancel
960
961        $g element activate $elem
962        set _hilite(elem) $elem
963
964        set dlist [$g element show]
965        set i [lsearch -exact $dlist $elem]
966        if {$i >= 0} {
967            set dlist [lreplace $dlist $i $i]
968            lappend dlist $elem
969            $g element show $dlist
970        }
971
972        foreach {mapx mapy} [GetAxes $_elem2dataobj($elem)] break
973
974        set allx [$g x2axis use]
975        if {[llength $allx] > 0} {
976            lappend allx x  ;# fix main x-axis too
977            foreach axis $allx {
978                if {$axis == $mapx} {
979                    $g axis configure $axis -color $itk_option(-foreground) \
980                        -titlecolor $itk_option(-foreground)
981                } else {
982                    $g axis configure $axis -color $itk_option(-dimcolor) \
983                        -titlecolor $itk_option(-dimcolor)
984                }
985            }
986        }
987        set ally [$g y2axis use]
988        if {[llength $ally] > 0} {
989            lappend ally y  ;# fix main y-axis too
990            foreach axis $ally {
991                if {$axis == $mapy} {
992                    $g axis configure $axis -color $itk_option(-foreground) \
993                        -titlecolor $itk_option(-foreground)
994                } else {
995                    $g axis configure $axis -color $itk_option(-dimcolor) \
996                        -titlecolor $itk_option(-dimcolor)
997                }
998            }
999        }
1000
1001        if {"" != $tip} {
1002            $g crosshairs configure -hide no -position @$x,$y
1003
1004            if {$x > 0.5*[winfo width $g]} {
1005                if {$x < 4} {
1006                    set tipx "-0"
1007                } else {
1008                    set tipx "-[expr {$x-4}]"  ;# move tooltip to the left
1009                }
1010            } else {
1011                if {$x < -4} {
1012                    set tipx "+0"
1013                } else {
1014                    set tipx "+[expr {$x+4}]"  ;# move tooltip to the right
1015                }
1016            }
1017            if {$y > 0.5*[winfo height $g]} {
1018                if {$y < 4} {
1019                    set tipy "-0"
1020                } else {
1021                    set tipy "-[expr {$y-4}]"  ;# move tooltip to the top
1022                }
1023            } else {
1024                if {$y < -4} {
1025                    set tipy "+0"
1026                } else {
1027                    set tipy "+[expr {$y+4}]"  ;# move tooltip to the bottom
1028                }
1029            }
1030            Rappture::Tooltip::text $g $tip
1031            Rappture::Tooltip::tooltip show $g $tipx,$tipy
1032            Rappture::Logger::log tooltip -for "number probe -- [string map [list \n " // "] $tip]"
1033        }
1034    } else {
1035        #
1036        # Highlight OFF:
1037        # - deactivate (color back to normal)
1038        # - put all axes back to normal color
1039        # - take down tooltip
1040        #
1041        if {$_hilite(elem) ne ""} {
1042            $g element deactivate $_hilite(elem)
1043        }
1044
1045        set allx [$g x2axis use]
1046        if {[llength $allx] > 0} {
1047            lappend allx x  ;           # fix main x-axis too
1048            foreach axis $allx {
1049                $g axis configure $axis -color $itk_option(-foreground) \
1050                    -titlecolor $itk_option(-foreground)
1051            }
1052        }
1053
1054        set ally [$g y2axis use]
1055        if {[llength $ally] > 0} {
1056            lappend ally y  ;           # fix main y-axis too
1057            foreach axis $ally {
1058                $g axis configure $axis -color $itk_option(-foreground) \
1059                    -titlecolor $itk_option(-foreground)
1060            }
1061        }
1062
1063        $g crosshairs configure -hide yes
1064
1065        # only cancel in plotting area or we'll mess up axes
1066        if {[$g inside $x $y]} {
1067            Rappture::Tooltip::tooltip cancel
1068        }
1069
1070        # there is no currently highlighted element
1071        set _hilite(elem) ""
1072    }
1073}
1074
1075# ----------------------------------------------------------------------
1076# USAGE: Axis hilite <axis> <state>
1077#
1078# USAGE: Axis click <axis> <x> <y>
1079# USAGE: Axis drag <axis> <x> <y>
1080# USAGE: Axis release <axis> <x> <y>
1081#
1082# USAGE: Axis edit <axis>
1083# USAGE: Axis changed <axis> <what>
1084# USAGE: Axis format <axis> <widget> <value>
1085# USAGE: Axis scale <axis> linear|log
1086#
1087# Used internally to handle editing of the x/y axes.  The hilite
1088# operation causes the axis to light up.  The edit operation pops
1089# up a panel with editing options.  The changed operation applies
1090# changes from the panel.
1091# ----------------------------------------------------------------------
1092itcl::body Rappture::NumberResult::Axis {option args} {
1093    set inner [$itk_component(hull).axes component inner]
1094
1095    switch -- $option {
1096        hilite {
1097            if {[llength $args] != 2} {
1098                error "wrong # args: should be \"Axis hilite axis state\""
1099            }
1100            set g $itk_component(plot)
1101            set axis [lindex $args 0]
1102            set state [lindex $args 1]
1103
1104            if {$state} {
1105                $g axis configure $axis \
1106                    -color $itk_option(-activecolor) \
1107                    -titlecolor $itk_option(-activecolor)
1108
1109                set x [expr {[winfo pointerx $g]+4}]
1110                set y [expr {[winfo pointery $g]+4}]
1111                Rappture::Tooltip::tooltip pending $g-$axis @$x,$y
1112            } else {
1113                $g axis configure $axis \
1114                    -color $itk_option(-foreground) \
1115                    -titlecolor $itk_option(-foreground)
1116                Rappture::Tooltip::tooltip cancel
1117            }
1118        }
1119        click {
1120            if {[llength $args] != 3} {
1121                error "wrong # args: should be \"Axis click axis x y\""
1122            }
1123            set axis [lindex $args 0]
1124            set x [lindex $args 1]
1125            set y [lindex $args 2]
1126            set g $itk_component(plot)
1127
1128            set _axis(moved) 0
1129            set _axis(click-x) $x
1130            set _axis(click-y) $y
1131            foreach {min max} [$g axis limits $axis] break
1132            set _axis(min0) $min
1133            set _axis(max0) $max
1134            Rappture::Tooltip::tooltip cancel
1135        }
1136        drag {
1137            if {[llength $args] != 3} {
1138                error "wrong # args: should be \"Axis drag axis x y\""
1139            }
1140            if {![info exists _axis(moved)]} {
1141                return  ;# must have skipped click event -- ignore
1142            }
1143            set axis [lindex $args 0]
1144            set x [lindex $args 1]
1145            set y [lindex $args 2]
1146            set g $itk_component(plot)
1147
1148            if {[info exists _axis(click-x)] && [info exists _axis(click-y)]} {
1149                foreach {x0 y0 pw ph} [$g extents plotarea] break
1150                switch -glob $axis {
1151                  x* {
1152                    set pix $x
1153                    set pix0 $_axis(click-x)
1154                    set pixmin $x0
1155                    set pixmax [expr {$x0+$pw}]
1156                  }
1157                  y* {
1158                    set pix $y
1159                    set pix0 $_axis(click-y)
1160                    set pixmin [expr {$y0+$ph}]
1161                    set pixmax $y0
1162                  }
1163                }
1164                set log [$g axis cget $axis -logscale]
1165                set min $_axis(min0)
1166                set max $_axis(max0)
1167                set dpix [expr {abs($pix-$pix0)}]
1168                set v0 [$g axis invtransform $axis $pixmin]
1169                set v1 [$g axis invtransform $axis [expr {$pixmin+$dpix}]]
1170                if {$log} {
1171                    set v0 [expr {log10($v0)}]
1172                    set v1 [expr {log10($v1)}]
1173                    set min [expr {log10($min)}]
1174                    set max [expr {log10($max)}]
1175                }
1176
1177                if {$pix > $pix0} {
1178                    set delta [expr {$v1-$v0}]
1179                } else {
1180                    set delta [expr {$v0-$v1}]
1181                }
1182                set min [expr {$min-$delta}]
1183                set max [expr {$max-$delta}]
1184                if {$log} {
1185                    set min [expr {pow(10.0,$min)}]
1186                    set max [expr {pow(10.0,$max)}]
1187                }
1188                $g axis configure $axis -min $min -max $max
1189
1190                # move axis, don't edit on release
1191                set _axis(move) 1
1192            }
1193        }
1194        release {
1195            if {[llength $args] != 3} {
1196                error "wrong # args: should be \"Axis release axis x y\""
1197            }
1198            if {![info exists _axis(moved)]} {
1199                return  ;# must have skipped click event -- ignore
1200            }
1201            set axis [lindex $args 0]
1202            set x [lindex $args 1]
1203            set y [lindex $args 2]
1204
1205            if {!$_axis(moved)} {
1206                # small movement? then treat as click -- pop up axis editor
1207                set dx [expr {abs($x-$_axis(click-x))}]
1208                set dy [expr {abs($y-$_axis(click-y))}]
1209                if {$dx < 2 && $dy < 2} {
1210                    Axis edit $axis
1211                    return
1212                }
1213            }
1214            # one last movement
1215            Axis drag $axis $x $y
1216
1217            # log this change
1218            Rappture::Logger::log number axis $axis \
1219                -drag [$itk_component(plot) axis limits $axis]
1220
1221            catch {unset _axis}
1222        }
1223        edit {
1224            if {[llength $args] != 1} {
1225                error "wrong # args: should be \"Axis edit axis\""
1226            }
1227            set axis [lindex $args 0]
1228            set _axisPopup(current) $axis
1229
1230            # apply last value when deactivating
1231            $itk_component(hull).axes configure -deactivatecommand \
1232                [itcl::code $this Axis changed $axis focus]
1233
1234            # fix axis label controls...
1235            set label [$itk_component(plot) axis cget $axis -title]
1236            $inner.label delete 0 end
1237            $inner.label insert end $label
1238            bind $inner.label <Return> \
1239                [itcl::code $this Axis changed $axis label]
1240            bind $inner.label <KP_Enter> \
1241                [itcl::code $this Axis changed $axis label]
1242            bind $inner.label <FocusOut> \
1243                [itcl::code $this Axis changed $axis label]
1244
1245            # fix min/max controls...
1246            foreach {min max} [$itk_component(plot) axis limits $axis] break
1247            $inner.min delete 0 end
1248            $inner.min insert end $min
1249            bind $inner.min <Return> \
1250                [itcl::code $this Axis changed $axis min]
1251            bind $inner.min <KP_Enter> \
1252                [itcl::code $this Axis changed $axis min]
1253            bind $inner.min <FocusOut> \
1254                [itcl::code $this Axis changed $axis min]
1255
1256            $inner.max delete 0 end
1257            $inner.max insert end $max
1258            bind $inner.max <Return> \
1259                [itcl::code $this Axis changed $axis max]
1260            bind $inner.max <KP_Enter> \
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}
1560
1561#
1562# ResetLegend --
1563#
1564itcl::body Rappture::NumberResult::ResetLegend {} {
1565    set g $itk_component(plot)
1566    update idletasks
1567    $itk_component(legend) reset [$g element show]
1568}
Note: See TracBrowser for help on using the repository browser.