source: trunk/gui/scripts/histogramresult.tcl @ 3789

Last change on this file since 3789 was 3764, checked in by gah, 11 years ago

fix: change in XyLegend::reset method

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