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

Last change on this file since 1681 was 1650, checked in by gah, 15 years ago
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]
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 {[info exists _histo2color($xydata)]} {
630            set color $_histo2color($xydata)
631        } else {
632            set color [$xydata hints color]
633            if {"" == $color} {
634                set color black
635            }
636        }
637       
638        if {[info exists _histo2width($xydata)]} {
639            set lwidth $_histo2width($xydata)
640        } else {
641            set lwidth 2
642        }
643
644        if {[info exists _histo2dashes($xydata)]} {
645            set dashes $_histo2dashes($xydata)
646        } else {
647            set dashes ""
648        }
649        if {([$xv length] <= 1) || ($lwidth == 0)} {
650            set sym square
651            set pixels 2
652        } else {
653            set sym ""
654            set pixels 6
655        }
656        if { 0 } {
657        set elem "elem[incr count]"
658        set _elem2histo($elem) $xydata
659        $g line create $elem -x $xv -y $yv \
660            -symbol $sym -pixels $pixels -linewidth $lwidth -label $label \
661            -color $color -dashes $dashes -smooth natural \
662            -mapx $mapx -mapy $mapy
663        }
664        # Compute default bar width for histogram elements.
665        if { [$zv length] == [$xv length] } {
666            foreach x [$xv range 0 end] \
667                    y [$yv range 0 end] \
668                    z [$zv range 0 end] {
669                set elem "elem[incr count]"
670                set _elem2histo($elem) $xydata
671                $g element create $elem -x $x -y $y -barwidth $z \
672                    -label $label -foreground $color \
673                    -mapx $mapx -mapy $mapy
674            }
675        } else {
676            set r [blt::vector expr {max($xv) - min($xv)}]
677            set z [expr {$r / ([$xv length]-1) * 0.8}]
678            foreach x [$xv range 0 end] y [$yv range 0 end] {
679                set elem "elem[incr count]"
680                set _elem2histo($elem) $xydata
681                $g element create $elem -x $x -y $y -barwidth $z \
682                    -label $label -foreground $color \
683                    -mapx $mapx -mapy $mapy
684            }
685        }
686
687        #
688        # Create text/line markers for each *axis.marker specified.
689        #
690        foreach m [$xydata xmarkers] {
691            foreach {at label style} $m break
692            set yv [$xydata heights]
693            set min [blt::vector expr min($yv)]
694            set max [blt::vector expr max($yv)]
695            set id [$g marker create line -coords [list $at $min $at $max]]
696            $g marker bind $id <Enter> \
697                [itcl::code $this _enterMarker $g x-$label $at $min $at]
698            $g marker bind $id <Leave> \
699                [itcl::code $this _leaveMarker $g x-$label]
700            set options [_getLineMarkerOptions $style]
701            if { $options != "" } {
702                eval $g marker configure $id $options
703            }
704            if { $label != "" } {
705                set id [$g marker create text -anchor w -xoffset 5 \
706                        -text $label -coords [list $at $max]]
707                set options [_getTextMarkerOptions $style]
708                if { $options != "" } {
709                    eval $g marker configure $id $options
710                }
711            }
712        }
713        foreach m [$xydata ymarkers] {
714            foreach {at label style} $m break
715            set xv [$xydata widths]
716            set min [blt::vector expr min($xv)]
717            set max [blt::vector expr max($xv)]
718            set id [$g marker create line -coords [list $min $at $max $at]]
719            $g marker bind $id <Enter> \
720                [itcl::code $this _enterMarker $g y-$label $at $min $at]
721            $g marker bind $id <Leave> \
722                [itcl::code $this _leaveMarker $g y-$label]
723            set options [_getLineMarkerOptions $style]
724            if { $options != "" } {
725                eval $g marker configure $id $options
726            }
727            if { $label != "" } {
728                set id [$g marker create text -anchor se -yoffset 5 \
729                        -text $label -coords [list $max $at]]
730                set options [_getTextMarkerOptions $style]
731                if { $options != "" } {
732                    eval $g marker configure $id $options
733                }
734            }
735        }
736    }
737}
738
739# ----------------------------------------------------------------------
740# USAGE: _resetLimits
741#
742# Used internally to apply automatic limits to the axes for the
743# current plot.
744# ----------------------------------------------------------------------
745itcl::body Rappture::HistogramResult::_resetLimits {} {
746    set g $itk_component(plot)
747
748    #
749    # HACK ALERT!
750    # Use this code to fix up the y-axis limits for the BLT barchart.
751    # The auto-limits don't always work well.  We want them to be
752    # set to a "nice" number slightly above or below the min/max
753    # limits.
754    #
755    foreach axis [$g axis names] {
756        if {[info exists _limits(${axis}lin-min)]} {
757            set log [$g axis cget $axis -logscale]
758            if {$log} {
759                set min $_limits(${axis}log-min)
760                if {$min == 0} { set min 1 }
761                set max $_limits(${axis}log-max)
762                if {$max == 0} { set max 1 }
763
764                if {$min == $max} {
765                    set logmin [expr {floor(log10(abs(0.9*$min)))}]
766                    set logmax [expr {ceil(log10(abs(1.1*$max)))}]
767                } else {
768                    set logmin [expr {floor(log10(abs($min)))}]
769                    set logmax [expr {ceil(log10(abs($max)))}]
770                    if {[string match y* $axis]} {
771                        # add a little padding
772                        set delta [expr {$logmax-$logmin}]
773                        if {$delta == 0} { set delta 1 }
774                        set logmin [expr {$logmin-0.05*$delta}]
775                        set logmax [expr {$logmax+0.05*$delta}]
776                    }
777                }
778                if {$logmin < -300} {
779                    set min 1e-300
780                } elseif {$logmin > 300} {
781                    set min 1e+300
782                } else {
783                    set min [expr {pow(10.0,$logmin)}]
784                }
785
786                if {$logmax < -300} {
787                    set max 1e-300
788                } elseif {$logmax > 300} {
789                    set max 1e+300
790                } else {
791                    set max [expr {pow(10.0,$logmax)}]
792                }
793            } else {
794                set min $_limits(${axis}lin-min)
795                set max $_limits(${axis}lin-max)
796                if {[string match y* $axis]} {
797                    # add a little padding
798                    set delta [expr {$max-$min}]
799                    set min [expr {$min-0.05*$delta}]
800                    set max [expr {$max+0.05*$delta}]
801                }
802            }
803            if {$min < $max} {
804                $g axis configure $axis -min $min -max $max
805            } else {
806                $g axis configure $axis -min "" -max ""
807            }
808        } else {
809            $g axis configure $axis -min "" -max ""
810        }
811    }
812}
813
814# ----------------------------------------------------------------------
815# USAGE: _zoom reset
816#
817# Called automatically when the user clicks on one of the zoom
818# controls for this widget.  Changes the zoom for the current view.
819# ----------------------------------------------------------------------
820itcl::body Rappture::HistogramResult::_zoom {option args} {
821    switch -- $option {
822        reset {
823            _resetLimits
824        }
825    }
826}
827
828# ----------------------------------------------------------------------
829# USAGE: _hilite <state> <x> <y>
830#
831# Called automatically when the user brushes one of the elements
832# on the plot.  Causes the element to highlight and a tooltip to
833# pop up with element info.
834# ----------------------------------------------------------------------
835itcl::body Rappture::HistogramResult::_hilite {state x y} {
836    set g $itk_component(plot)
837    set elem ""
838    if {$state == "at"} {
839        if {[$g element closest $x $y info -interpolate yes]} {
840            # for dealing with xy line plots
841            set elem $info(name)
842            foreach {mapx mapy} [_getAxes $_elem2histo($elem)] break
843
844            # search again for an exact point -- this time don't interpolate
845            set tip ""
846            if {[$g element closest $x $y info -interpolate no]
847                  && $info(name) == $elem} {
848                set x [$g axis transform $mapx $info(x)]
849                set y [$g axis transform $mapy $info(y)]
850
851                if {[info exists _elem2histo($elem)]} {
852                    set histogram $_elem2histo($elem)
853                    set tip [$histogram hints tooltip]
854                    if {[info exists info(y)]} {
855                        set val [_axis format y dummy $info(y)]
856                        set units [$histogram hints yunits]
857                        append tip "\n$val$units"
858
859                        if {[info exists info(x)]} {
860                            set val [_axis format x dummy $info(x)]
861                            set units [$histogram hints xunits]
862                            append tip " @ $val$units"
863                        }
864                    }
865                    set tip [string trim $tip]
866                }
867            }
868            set state 1
869        } elseif {[$g element closest $x $y info -interpolate no]} {
870            # for dealing with xy scatter plot
871            set elem $info(name)
872            foreach {mapx mapy} [_getAxes $_elem2histo($elem)] break
873
874            # search again for an exact point -- this time don't interpolate
875            set tip ""
876            if {$info(name) == $elem} {
877                set x [$g axis transform $mapx $info(x)]
878                set y [$g axis transform $mapy $info(y)]
879
880                if {[info exists _elem2histo($elem)]} {
881                    set histogram $_elem2histo($elem)
882                    set tip [$histogram hints tooltip]
883                    if {[info exists info(y)]} {
884                        set val [_axis format y dummy $info(y)]
885                        set units [$histogram hints yunits]
886                        append tip "\n$val$units"
887
888                        if {[info exists info(x)]} {
889                            set val [_axis format x dummy $info(x)]
890                            set units [$histogram hints xunits]
891                            append tip " @ $val$units"
892                        }
893                    }
894                    set tip [string trim $tip]
895                }
896            }
897            set state 1
898        } else {
899            set state 0
900        }
901    }
902
903    if {$state} {
904        #
905        # Highlight ON:
906        # - activate trace
907        # - multiple axes? dim other axes
908        # - pop up tooltip about data
909        #
910        if {$_hilite(elem) != "" && $_hilite(elem) != $elem} {
911            $g element deactivate $_hilite(elem)
912            $g crosshairs configure -hide yes
913            Rappture::Tooltip::tooltip cancel
914        }
915        $g element activate $elem
916        set _hilite(elem) $elem
917
918        set dlist [$g element show]
919        set i [lsearch -exact $dlist $elem]
920        if {$i >= 0} {
921            set dlist [lreplace $dlist $i $i]
922            lappend dlist $elem
923            $g element show $dlist
924        }
925
926        foreach {mapx mapy} [_getAxes $_elem2histo($elem)] break
927
928        set allx [$g x2axis use]
929        if {[llength $allx] > 0} {
930            lappend allx x  ;# fix main x-axis too
931            foreach axis $allx {
932                if {$axis == $mapx} {
933                    $g axis configure $axis -color $itk_option(-foreground) \
934                        -titlecolor $itk_option(-foreground)
935                } else {
936                    $g axis configure $axis -color $itk_option(-dimcolor) \
937                        -titlecolor $itk_option(-dimcolor)
938                }
939            }
940        }
941        set ally [$g y2axis use]
942        if {[llength $ally] > 0} {
943            lappend ally y  ;# fix main y-axis too
944            foreach axis $ally {
945                if {$axis == $mapy} {
946                    $g axis configure $axis -color $itk_option(-foreground) \
947                        -titlecolor $itk_option(-foreground)
948                } else {
949                    $g axis configure $axis -color $itk_option(-dimcolor) \
950                        -titlecolor $itk_option(-dimcolor)
951                }
952            }
953        }
954
955        if {"" != $tip} {
956            $g crosshairs configure -hide no -position @$x,$y
957
958            if {$x > 0.5*[winfo width $g]} {
959                if {$x < 4} {
960                    set tipx "-0"
961                } else {
962                    set tipx "-[expr {$x-4}]"  ;# move tooltip to the left
963                }
964            } else {
965                if {$x < -4} {
966                    set tipx "+0"
967                } else {
968                    set tipx "+[expr {$x+4}]"  ;# move tooltip to the right
969                }
970            }
971            if {$y > 0.5*[winfo height $g]} {
972                if {$y < 4} {
973                    set tipy "-0"
974                } else {
975                    set tipy "-[expr {$y-4}]"  ;# move tooltip to the top
976                }
977            } else {
978                if {$y < -4} {
979                    set tipy "+0"
980                } else {
981                    set tipy "+[expr {$y+4}]"  ;# move tooltip to the bottom
982                }
983            }
984            Rappture::Tooltip::text $g $tip
985            Rappture::Tooltip::tooltip show $g $tipx,$tipy
986        }
987    } else {
988        #
989        # Highlight OFF:
990        # - deactivate (color back to normal)
991        # - put all axes back to normal color
992        # - take down tooltip
993        #
994        if {"" != $_hilite(elem)} {
995            $g element deactivate $_hilite(elem)
996
997            set allx [$g x2axis use]
998            if {[llength $allx] > 0} {
999                lappend allx x  ;# fix main x-axis too
1000                foreach axis $allx {
1001                    $g axis configure $axis -color $itk_option(-foreground) \
1002                        -titlecolor $itk_option(-foreground)
1003                }
1004            }
1005
1006            set ally [$g y2axis use]
1007            if {[llength $ally] > 0} {
1008                lappend ally y  ;# fix main y-axis too
1009                foreach axis $ally {
1010                    $g axis configure $axis -color $itk_option(-foreground) \
1011                        -titlecolor $itk_option(-foreground)
1012                }
1013            }
1014        }
1015
1016        $g crosshairs configure -hide yes
1017
1018        # only cancel in plotting area or we'll mess up axes
1019        if {[$g inside $x $y]} {
1020            Rappture::Tooltip::tooltip cancel
1021        }
1022
1023        # there is no currently highlighted element
1024        set _hilite(elem) ""
1025    }
1026}
1027
1028# ----------------------------------------------------------------------
1029# USAGE: _axis hilite <axis> <state>
1030#
1031# USAGE: _axis click <axis> <x> <y>
1032# USAGE: _axis drag <axis> <x> <y>
1033# USAGE: _axis release <axis> <x> <y>
1034#
1035# USAGE: _axis edit <axis>
1036# USAGE: _axis changed <axis> <what>
1037# USAGE: _axis format <axis> <widget> <value>
1038# USAGE: _axis scale <axis> linear|log
1039#
1040# Used internally to handle editing of the x/y axes.  The hilite
1041# operation causes the axis to light up.  The edit operation pops
1042# up a panel with editing options.  The changed operation applies
1043# changes from the panel.
1044# ----------------------------------------------------------------------
1045itcl::body Rappture::HistogramResult::_axis {option args} {
1046    set inner [$itk_component(hull).axes component inner]
1047
1048    switch -- $option {
1049        hilite {
1050            if {[llength $args] != 2} {
1051                error "wrong # args: should be \"_axis hilite axis state\""
1052            }
1053            set g $itk_component(plot)
1054            set axis [lindex $args 0]
1055            set state [lindex $args 1]
1056
1057            if {$state} {
1058                $g axis configure $axis \
1059                    -color $itk_option(-activecolor) \
1060                    -titlecolor $itk_option(-activecolor)
1061
1062                set x [expr {[winfo pointerx $g]+4}]
1063                set y [expr {[winfo pointery $g]+4}]
1064                Rappture::Tooltip::tooltip pending $g-$axis @$x,$y
1065            } else {
1066                $g axis configure $axis \
1067                    -color $itk_option(-foreground) \
1068                    -titlecolor $itk_option(-foreground)
1069                Rappture::Tooltip::tooltip cancel
1070            }
1071        }
1072        click {
1073            if {[llength $args] != 3} {
1074                error "wrong # args: should be \"_axis click axis x y\""
1075            }
1076            set axis [lindex $args 0]
1077            set x [lindex $args 1]
1078            set y [lindex $args 2]
1079            set g $itk_component(plot)
1080
1081            set _axis(moved) 0
1082            set _axis(click-x) $x
1083            set _axis(click-y) $y
1084            foreach {min max} [$g axis limits $axis] break
1085            set _axis(min0) $min
1086            set _axis(max0) $max
1087            Rappture::Tooltip::tooltip cancel
1088        }
1089        drag {
1090            if {[llength $args] != 3} {
1091                error "wrong # args: should be \"_axis drag axis x y\""
1092            }
1093            if {![info exists _axis(moved)]} {
1094                return  ;# must have skipped click event -- ignore
1095            }
1096            set axis [lindex $args 0]
1097            set x [lindex $args 1]
1098            set y [lindex $args 2]
1099            set g $itk_component(plot)
1100
1101            if {[info exists _axis(click-x)] && [info exists _axis(click-y)]} {
1102                foreach {x0 y0 pw ph} [$g extents plotarea] break
1103                switch -glob $axis {
1104                  x* {
1105                    set pix $x
1106                    set pix0 $_axis(click-x)
1107                    set pixmin $x0
1108                    set pixmax [expr {$x0+$pw}]
1109                  }
1110                  y* {
1111                    set pix $y
1112                    set pix0 $_axis(click-y)
1113                    set pixmin [expr {$y0+$ph}]
1114                    set pixmax $y0
1115                  }
1116                }
1117                set log [$g axis cget $axis -logscale]
1118                set min $_axis(min0)
1119                set max $_axis(max0)
1120                set dpix [expr {abs($pix-$pix0)}]
1121                set v0 [$g axis invtransform $axis $pixmin]
1122                set v1 [$g axis invtransform $axis [expr {$pixmin+$dpix}]]
1123                if {$log} {
1124                    set v0 [expr {log10($v0)}]
1125                    set v1 [expr {log10($v1)}]
1126                    set min [expr {log10($min)}]
1127                    set max [expr {log10($max)}]
1128                }
1129
1130                if {$pix > $pix0} {
1131                    set delta [expr {$v1-$v0}]
1132                } else {
1133                    set delta [expr {$v0-$v1}]
1134                }
1135                set min [expr {$min-$delta}]
1136                set max [expr {$max-$delta}]
1137                if {$log} {
1138                    set min [expr {pow(10.0,$min)}]
1139                    set max [expr {pow(10.0,$max)}]
1140                }
1141                $g axis configure $axis -min $min -max $max
1142
1143                # move axis, don't edit on release
1144                set _axis(move) 1
1145            }
1146        }
1147        release {
1148            if {[llength $args] != 3} {
1149                error "wrong # args: should be \"_axis release axis x y\""
1150            }
1151            if {![info exists _axis(moved)]} {
1152                return  ;# must have skipped click event -- ignore
1153            }
1154            set axis [lindex $args 0]
1155            set x [lindex $args 1]
1156            set y [lindex $args 2]
1157
1158            if {!$_axis(moved)} {
1159                # small movement? then treat as click -- pop up axis editor
1160                set dx [expr {abs($x-$_axis(click-x))}]
1161                set dy [expr {abs($y-$_axis(click-y))}]
1162                if {$dx < 2 && $dy < 2} {
1163                    _axis edit $axis
1164                }
1165            } else {
1166                # one last movement
1167                _axis drag $axis $x $y
1168            }
1169            catch {unset _axis}
1170        }
1171        edit {
1172            if {[llength $args] != 1} {
1173                error "wrong # args: should be \"_axis edit axis\""
1174            }
1175            set axis [lindex $args 0]
1176            set _axisPopup(current) $axis
1177
1178            # apply last value when deactivating
1179            $itk_component(hull).axes configure -deactivatecommand \
1180                [itcl::code $this _axis changed $axis focus]
1181
1182            # fix axis label controls...
1183            set label [$itk_component(plot) axis cget $axis -title]
1184            $inner.label delete 0 end
1185            $inner.label insert end $label
1186            bind $inner.label <KeyPress-Return> \
1187                [itcl::code $this _axis changed $axis label]
1188            bind $inner.label <FocusOut> \
1189                [itcl::code $this _axis changed $axis label]
1190
1191            # fix min/max controls...
1192            foreach {min max} [$itk_component(plot) axis limits $axis] break
1193            $inner.min delete 0 end
1194            $inner.min insert end $min
1195            bind $inner.min <KeyPress-Return> \
1196                [itcl::code $this _axis changed $axis min]
1197            bind $inner.min <FocusOut> \
1198                [itcl::code $this _axis changed $axis min]
1199
1200            $inner.max delete 0 end
1201            $inner.max insert end $max
1202            bind $inner.max <KeyPress-Return> \
1203                [itcl::code $this _axis changed $axis max]
1204            bind $inner.max <FocusOut> \
1205                [itcl::code $this _axis changed $axis max]
1206
1207            # fix format control...
1208            set fmts [$inner.format choices get -value]
1209            set i [lsearch -exact $fmts $_axisPopup(format-$axis)]
1210            if {$i < 0} { set i 0 }  ;# use Auto choice
1211            $inner.format value [$inner.format choices get -label $i]
1212
1213            bind $inner.format <<Value>> \
1214                [itcl::code $this _axis changed $axis format]
1215
1216            # I removed the code for fixing the axis menus scale controls.
1217            # This needs to be added back when different menus are available
1218            # for each axis.
1219
1220            #
1221            # Figure out where the window should pop up.
1222            #
1223            set x [winfo rootx $itk_component(plot)]
1224            set y [winfo rooty $itk_component(plot)]
1225            set w [winfo width $itk_component(plot)]
1226            set h [winfo height $itk_component(plot)]
1227            foreach {x0 y0 pw ph} [$itk_component(plot) extents plotarea] break
1228            switch -glob -- $axis {
1229                x {
1230                    set x [expr {round($x + $x0+0.5*$pw)}]
1231                    set y [expr {round($y + $y0+$ph + 0.5*($h-$y0-$ph))}]
1232                    set dir "above"
1233                }
1234                x* {
1235                    set x [expr {round($x + $x0+0.5*$pw)}]
1236                    set dir "below"
1237                    set allx [$itk_component(plot) x2axis use]
1238                    set max [llength $allx]
1239                    set i [lsearch -exact $allx $axis]
1240                    set y [expr {round($y + ($i+0.5)*$y0/double($max))}]
1241                }
1242                y {
1243                    set x [expr {round($x + 0.5*$x0)}]
1244                    set y [expr {round($y + $y0+0.5*$ph)}]
1245                    set dir "right"
1246                }
1247                y* {
1248                    set y [expr {round($y + $y0+0.5*$ph)}]
1249                    set dir "left"
1250                    set ally [$itk_component(plot) y2axis use]
1251                    set max [llength $ally]
1252                    set i [lsearch -exact $ally $axis]
1253                    set y [expr {round($y + ($i+0.5)*$y0/double($max))}]
1254                    set x [expr {round($x+$x0+$pw + ($i+0.5)*($w-$x0-$pw)/double($max))}]
1255                }
1256            }
1257            $itk_component(hull).axes activate @$x,$y $dir
1258        }
1259        changed {
1260            if {[llength $args] != 2} {
1261                error "wrong # args: should be \"_axis changed axis what\""
1262            }
1263            set axis [lindex $args 0]
1264            set what [lindex $args 1]
1265            if {$what == "focus"} {
1266                set what [focus]
1267                if {[winfo exists $what]} {
1268                    set what [winfo name $what]
1269                }
1270            }
1271
1272            switch -- $what {
1273                label {
1274                    set val [$inner.label get]
1275                    $itk_component(plot) axis configure $axis -title $val
1276                }
1277                min {
1278                    set val [$inner.min get]
1279                    if {![string is double -strict $val]} {
1280                        Rappture::Tooltip::cue $inner.min "Must be a number"
1281                        bell
1282                        return
1283                    }
1284
1285                    set max [lindex [$itk_component(plot) axis limits $axis] 1]
1286                    if {$val >= $max} {
1287                        Rappture::Tooltip::cue $inner.min "Must be <= max ($max)"
1288                        bell
1289                        return
1290                    }
1291                    catch {
1292                        # can fail in log mode
1293                        $itk_component(plot) axis configure $axis -min $val
1294                    }
1295                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1296                    $inner.min delete 0 end
1297                    $inner.min insert end $min
1298                }
1299                max {
1300                    set val [$inner.max get]
1301                    if {![string is double -strict $val]} {
1302                        Rappture::Tooltip::cue $inner.max "Should be a number"
1303                        bell
1304                        return
1305                    }
1306
1307                    set min [lindex [$itk_component(plot) axis limits $axis] 0]
1308                    if {$val <= $min} {
1309                        Rappture::Tooltip::cue $inner.max "Must be >= min ($min)"
1310                        bell
1311                        return
1312                    }
1313                    catch {
1314                        # can fail in log mode
1315                        $itk_component(plot) axis configure $axis -max $val
1316                    }
1317                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1318                    $inner.max delete 0 end
1319                    $inner.max insert end $max
1320                }
1321                format {
1322                    set fmt [$inner.format translate [$inner.format value]]
1323                    set _axisPopup(format-$axis) $fmt
1324
1325                    # force a refresh
1326                    $itk_component(plot) axis configure $axis -min \
1327                        [$itk_component(plot) axis cget $axis -min]
1328                }
1329                scale {
1330                    _axis scale $axis $_axisPopup(scale)
1331
1332                    if {$_axisPopup(scale) == "log"} {
1333                        $inner.format configure -state disabled
1334                    } else {
1335                        $inner.format configure -state normal
1336                    }
1337
1338                    foreach {min max} [$itk_component(plot) axis limits $axis] break
1339                    $inner.min delete 0 end
1340                    $inner.min insert end $min
1341                    $inner.max delete 0 end
1342                    $inner.max insert end $max
1343                }
1344                default {
1345                    # be lenient so we can handle the "focus" case
1346                }
1347            }
1348        }
1349        format {
1350            if {[llength $args] != 3} {
1351                error "wrong # args: should be \"_axis format axis widget value\""
1352            }
1353            set axis [lindex $args 0]
1354            set value [lindex $args 2]
1355
1356            if {[$itk_component(plot) axis cget $axis -logscale]} {
1357                set fmt "%.3g"
1358            } else {
1359                set fmt $_axisPopup(format-$axis)
1360            }
1361            return [format $fmt $value]
1362        }
1363        scale {
1364            if {[llength $args] != 2} {
1365                error "wrong # args: should be \"_axis scale axis type\""
1366            }
1367            set axis [lindex $args 0]
1368            set type [lindex $args 1]
1369
1370            if {$type == "log"} {
1371                catch {$itk_component(plot) axis configure $axis -logscale 1}
1372                # leave format alone in log mode
1373                $itk_component(plot) axis configure $axis -command ""
1374            } else {
1375                catch {$itk_component(plot) axis configure $axis -logscale 0}
1376                # use special formatting for linear mode
1377                $itk_component(plot) axis configure $axis -command \
1378                    [itcl::code $this _axis format $axis]
1379            }
1380        }
1381        default {
1382            error "bad option \"$option\": should be changed, edit, hilite, or format"
1383        }
1384    }
1385}
1386
1387# ----------------------------------------------------------------------
1388# USAGE: _getLineMarkerOptions <style>
1389#
1390# Used internally to create a list of configuration options specific to the
1391# axis line marker.  The input is a list of name value pairs.  Options that
1392# are not recognized are ignored.
1393# ----------------------------------------------------------------------
1394itcl::body Rappture::HistogramResult::_getLineMarkerOptions {style} {
1395    array set lineOptions {
1396        "-color"  "-outline"
1397        "-dashes" "-dashes"
1398        "-linecolor" "-outline"
1399        "-linewidth" "-linewidth"
1400    }
1401    set options {}
1402    foreach {name value} $style {
1403        if { [info exists lineOptions($name)] } {
1404            lappend options $lineOptions($name) $value
1405        }
1406    }
1407    return $options
1408}
1409
1410# ----------------------------------------------------------------------
1411# USAGE: _getTextMarkerOptions <style>
1412#
1413# Used internally to create a list of configuration options specific to the
1414# axis text marker.  The input is a list of name value pairs.  Options that
1415# are not recognized are ignored.
1416# ----------------------------------------------------------------------
1417itcl::body Rappture::HistogramResult::_getTextMarkerOptions {style} {
1418    array set textOptions {
1419        "-color"  "-outline"
1420        "-textcolor"  "-outline"
1421        "-font"   "-font"
1422        "-xoffset" "-xoffset"
1423        "-yoffset" "-yoffset"
1424        "-anchor" "-anchor"
1425    }
1426    set options {}
1427    foreach {name value} $style {
1428        if { [info exists textOptions($name)] } {
1429            lappend options $textOptions($name) $value
1430        }
1431    }
1432    return $options
1433}
1434
1435# ----------------------------------------------------------------------
1436# USAGE: _getAxes <histoObj>
1437#
1438# Used internally to figure out the axes used to plot the given
1439# <histoObj>.  Returns a list of the form {x y}, where x is the
1440# x-axis name (x, x2, x3, etc.), and y is the y-axis name.
1441# ----------------------------------------------------------------------
1442itcl::body Rappture::HistogramResult::_getAxes {xydata} {
1443    # rebuild if needed, so we know about the axes
1444    if {[$_dispatcher ispending !rebuild]} {
1445        $_dispatcher cancel !rebuild
1446        $_dispatcher event -now !rebuild
1447    }
1448
1449    # what is the x axis?  x? x2? x3? ...
1450    set xlabel [$xydata hints xlabel]
1451    if {[info exists _label2axis(x-$xlabel)]} {
1452        set mapx $_label2axis(x-$xlabel)
1453    } else {
1454        set mapx "x"
1455    }
1456
1457    # what is the y axis?  y? y2? y3? ...
1458    set ylabel [$xydata hints ylabel]
1459    if {[info exists _label2axis(y-$ylabel)]} {
1460        set mapy $_label2axis(y-$ylabel)
1461    } else {
1462        set mapy "y"
1463    }
1464
1465    return [list $mapx $mapy]
1466}
1467
1468# ----------------------------------------------------------------------
1469# CONFIGURATION OPTION: -gridcolor
1470# ----------------------------------------------------------------------
1471itcl::configbody Rappture::HistogramResult::gridcolor {
1472    if { "" == $itk_option(-gridcolor) } {
1473        foreach axis [$itk_component(plot) axis names] {
1474            $itk_component(plot) axis configure $axis -grid off
1475        }
1476    } else {
1477        foreach axis [$itk_component(plot) axis names] {
1478            $itk_component(plot) axis configure $axis \
1479                -gridcolor $itk_option(-gridcolor) -grid on
1480        }
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.