source: trunk/gui/scripts/historesult.tcl @ 1742

Last change on this file since 1742 was 1742, checked in by mmc, 14 years ago

Added preview of outputs to the irappture builder. Fixed a problem
with the file chooser for the "save as" dialog. Added a distinction
between errors and warnings when looking for problems in the tool
definition.

Fixed a few problems in RapptureGUI affecting the scroller and the
way it refreshes itself when the frame within it suddenly shrinks.
Fixed the histogram to avoid errors when the histogram data is
empty. Fixed the analyzer so you can create it without setting
the -notebookpage option. All of these errors showed up in the
irappture builder.

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