source: branches/1.4/gui/scripts/histogramresult.tcl @ 5671

Last change on this file since 5671 was 5660, checked in by ldelgass, 9 years ago

Merge r5657:5659 from trunk (whitespace/style)

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