source: branches/blt4/gui/scripts/barresult.tcl @ 1923

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