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

Last change on this file since 3515 was 3454, checked in by gah, 11 years ago

add KP_Enter to Return bindings. Implement OK handler in visviewer base class. Fix setting # of isolines while running a sequence of heightmaps/contours

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