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

Last change on this file since 4988 was 3813, checked in by ldelgass, 11 years ago

Fix bug in 'add' method of viewer widgets: list search for existing dataobj
entry was wrong (list and pattern transposed), causing potential duplicate
entries in dataobj list.

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