source: branches/blt4/gui/scripts/historesult.tcl @ 1897

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