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

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

fix drawing default coordinates, rewrite of xyresult scaling, sync with trunk

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