source: trunk/gui/scripts/xylegend.tcl @ 1132

Last change on this file since 1132 was 1132, checked in by gah, 16 years ago

edit xylegend labels

File size: 17.8 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: xylegend - X/Y plot legend.
4#
5#  This widget is a legend for an X/Y plot, meant to view line graphs produced
6#  as output from the run of a Rappture tool.  Use the "add" and
7#  "delete" methods to control the curves showing on the plot.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17
18option add *Xylegend.font \
19    -*-helvetica-medium-r-normal-*-8-* widgetDefault
20
21option add *Xylegend.Button.font \
22    -*-helvetica-medium-r-normal-*-9-* widgetDefault
23
24itcl::class ::Rappture::XyLegend {
25    inherit itk::Widget
26
27    private variable _dispatcher "" ;# dispatcher for !events
28    private variable graph_     ""
29    private variable tree_      ""
30    private variable diff_      ""
31    private variable focus_     -1
32    private variable diffelements_
33
34    constructor {args} { graph }
35    destructor {}
36
37    public method Activate {}
38    public method Average {}
39    public method Check {}
40    public method Deactivate {}
41    public method Delete { args }
42    public method Difference {}
43    public method Editor { option args }
44    public method Hide { args }
45    public method Lower { args }
46    public method Raise { args }
47    public method Rename {}
48    public method Show { args }
49    public method Toggle { args }
50    private method GetData { elem what }
51    private method Add { elem label {flags ""}}
52    private method SelectAll {}
53}
54                                                                               
55itk::usual XyLegend {
56    keep -background -foreground -cursor
57}
58
59itk::usual TreeView {
60    keep -background -foreground -cursor
61}
62
63blt::bitmap define dot1 {
64#define dot1_width 8
65#define dot1_height 8
66static unsigned char dot1_bits[] = {
67   0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};
68}
69
70# ----------------------------------------------------------------------
71# CONSTRUCTOR
72# ----------------------------------------------------------------------
73itcl::body Rappture::XyLegend::constructor { graph args } {
74    option add hull.width hull.height
75    itk_component add scrollbars {
76        Rappture::Scroller $itk_interior.scrl \
77            -xscrollmode auto -yscrollmode auto
78    }
79    set tree_ [blt::tree create]
80    itk_component add legend {
81        blt::treeview $itk_component(scrollbars).legend -linewidth 0 \
82            -bg white -selectmode multiple -width 0 \
83            -highlightthickness 0 \
84            -tree $tree_ \
85            -font "Arial 9" \
86            -flat yes -separator /
87    }
88    $itk_component(scrollbars) contents $itk_component(legend)
89    $itk_component(legend) column insert 0 "show" \
90        -text "" -weight 0.0 -pad 0 -borderwidth 0
91    $itk_component(legend) style checkbox "check" -showvalue no \
92        -onvalue 0 -offvalue 1 \
93        -boxcolor grey50 -checkcolor black -activebackground grey90
94    $itk_component(legend) column configure "treeView" -justify left \
95        -weight 1.0 -text "" -pad 0 -borderwidth 0
96    $itk_component(legend) column configure "show" -style "check" -pad {0 0} \
97        -edit yes
98    itk_component add controls {
99        frame $itk_component(hull).controls -width 100 -relief sunken -bd 2
100    }
101    set controls $itk_component(controls)
102    grid $itk_component(controls) -column 0 -row 1 -sticky nsew
103    grid rowconfigure $itk_component(hull) 1 -weight 0
104    grid rowconfigure $itk_component(hull) 0 -weight 1
105    grid $itk_component(scrollbars) -column 0 -row 0 -sticky nsew
106    set commands {
107        hide    ""
108        show    ""
109        toggle  ""
110        raise   findup
111        lower   finddn
112        average ""
113        difference ""
114        delete ""
115        rename ""
116    }
117    foreach { but icon} $commands {
118        set title [string totitle $but]
119        button $controls.$but -text $title \
120            -relief flat -pady 0 -padx 0  -font "Arial 9" \
121            -command [itcl::code $this $title]  -overrelief flat \
122            -activebackground grey90
123    }
124    grid $controls.hide       -column 0 -row 0 -sticky w
125    grid $controls.show       -column 0 -row 1 -sticky w
126    grid $controls.toggle     -column 0 -row 2 -sticky w
127    grid $controls.raise      -column 0 -row 3 -sticky w
128    grid $controls.lower      -column 0 -row 4 -sticky w
129    grid $controls.difference -column 1 -row 0 -sticky w
130    grid $controls.average    -column 1 -row 1 -sticky w
131    grid $controls.rename     -column 1 -row 2 -sticky w
132    grid $controls.delete     -column 1 -row 3 -sticky w
133
134    grid columnconfigure $controls 0  -weight 1
135    grid columnconfigure $controls 1 -weight 1
136
137    set graph_ $graph
138    set cmd [itcl::code $this Toggle current]
139    $itk_component(legend) bind CheckBoxStyle <ButtonRelease-1> \
140        [itcl::code [subst -nocommands {
141            if { [%W edit -root -test %X %Y] } {
142                %W edit -root %X %Y
143                $this Toggle [%W nearest -root %X %Y]
144                break
145            }
146        }]]
147    bind $itk_component(legend) <Enter> { focus %W }
148    $itk_component(legend) bind Entry <Control-KeyRelease-a> \
149        [itcl::code $this SelectAll]
150    $itk_component(legend) bind Entry <KeyRelease-Return> \
151        +[itcl::code $this Toggle focus]
152    $itk_component(legend) bind Entry <Escape> \
153        "$itk_component(legend) selection clearall"
154    $itk_component(legend) configure -selectcommand \
155        [itcl::code $this Check]
156
157    itk_component add editor {
158        Rappture::Editor $itk_interior.editor \
159            -activatecommand [itcl::code $this Editor activate] \
160            -validatecommand [itcl::code $this Editor validate] \
161            -applycommand [itcl::code $this Editor apply]
162    }
163    Check
164    eval itk_initialize $args
165}
166
167# ----------------------------------------------------------------------
168# DESTRUCTOR
169# ----------------------------------------------------------------------
170itcl::body Rappture::XyLegend::destructor {} {
171    Deactivate
172}
173
174itcl::body Rappture::XyLegend::Add { elem label {flags ""} } {
175    set hide [$graph_ element cget $elem -hide]
176    set im [image create photo]
177    $graph_ legend icon $elem $im
178    set data(show) $hide
179    set data(delete) [expr { $flags == "-delete" }]
180    set node [$tree_ insert root -at 0 -label $elem -data [array get data]]
181    $itk_component(legend) entry configure $node -label $label -icon $im \
182        -activeicon $im
183    return $node
184}
185
186# ----------------------------------------------------------------------
187# USAGE: activate <curve> ?<settings>?
188#
189# Clients use this to add a curve to the plot.  The optional <settings>
190# are used to configure the plot.  Allowed settings are -color,
191# -brightness, -width, -linestyle and -raise.
192# ----------------------------------------------------------------------
193itcl::body Rappture::XyLegend::Activate {} {
194    foreach elem [$graph_ element show] {
195        set label [$graph_ element cget $elem -label]
196        if { $label == "" } {
197            continue
198        }
199        Add $elem $label
200    }
201    $itk_component(legend) open -recurse root
202}
203
204itcl::body Rappture::XyLegend::Deactivate {} {
205    foreach node [$tree_ children root] {
206        $tree_ delete $node
207    }
208}
209
210itcl::body Rappture::XyLegend::Hide { args } {
211    if { $args == "" } {
212        set nodes [$itk_component(legend) curselection]
213    } else {
214        set nodes $args
215    }
216    foreach node $nodes {
217        set elem [$tree_ label $node]
218        if { ![$graph_ element exists $elem] } {
219            continue
220        }
221        $graph_ element configure $elem -hide yes
222        $tree_ set $node "show" 1
223    }
224}
225
226itcl::body Rappture::XyLegend::Show { args } {
227    if { $args == "" } {
228        set nodes [$itk_component(legend) curselection]
229    } else {
230        set nodes $args
231    }
232    foreach node $nodes {
233        set elem [$tree_ label $node]
234        if { ![$graph_ element exists $elem] } {
235            continue
236        }
237        $graph_ element configure $elem -hide no
238        $tree_ set $node "show" 0
239    }
240}
241
242itcl::body Rappture::XyLegend::Toggle { args } {
243    if { $args == "" } {
244        set nodes [$itk_component(legend) curselection]
245    } else {
246        set nodes $args
247    }
248    foreach node $nodes {
249        set node [$itk_component(legend) index $node]
250        set elem [$tree_ label $node]
251        if { ![$graph_ element exists $elem] } {
252            continue
253        }
254        set hide [$graph_ element cget $elem -hide]
255        set hide [expr $hide==0]
256        $tree_ set $node "show" $hide
257        $graph_ element configure $elem -hide $hide
258    }
259}
260
261itcl::body Rappture::XyLegend::Raise { args } {
262    if { $args == "" } {
263        set nodes [$itk_component(legend) curselection]
264    } else {
265        set nodes $args
266    }
267    set elements {}
268    foreach node $nodes {
269        set elem [$tree_ label $node]
270        set found($elem) 1
271        set elements [linsert $elements 0 $elem]
272    }
273    foreach elem $elements {
274        $tree_ move [$tree_ index $elem] 0 -at 0
275    }
276    set list {}
277    foreach elem [$graph_ element show] {
278        if { [info exists found($elem)] }  {
279            continue
280        }
281        lappend list $elem
282    }
283    $graph_ element show [concat $list $elements]
284}
285
286itcl::body Rappture::XyLegend::Lower { args } {
287    if { $args == "" } {
288        set nodes [$itk_component(legend) curselection]
289    } else {
290        set nodes $args
291    }
292    set elements {}
293    foreach node $nodes {
294        set elem [$tree_ label $node]
295        set found($elem) 1
296        set elements [linsert $elements 0 $elem]
297    }
298    set pos [$tree_ degree 0]
299
300    foreach elem $elements {
301        incr pos -1
302        $tree_ move [$tree_ index $elem] 0 -at $pos
303    }
304
305    set list {}
306    foreach elem [$graph_ element show] {
307        if { [info exists found($elem)] }  {
308            continue
309        }
310        lappend list $elem
311    }
312    $graph_ element show [concat $elements $list]
313}
314
315itcl::body Rappture::XyLegend::Delete { args } {
316    if { $args == "" } {
317        set nodes [$itk_component(legend) curselection]
318    } else {
319        set nodes $args
320    }
321    set elements {}
322    set delnodes {}
323    foreach node $nodes {
324        if { ![$tree_ get $node "delete" 0] } {
325            continue
326        }
327        set elem [$tree_ label $node]
328        lappend elements $elem
329        lappend delnodes $node
330        if { $diff_ != "" && [info exists diffelements_($elem)] } {
331            $graph_ element delete $diff_
332            array unset diffelements_
333            set diff_ ""
334        }
335    }
336    if { [llength $delnodes] > 0 } {
337        eval $tree_ delete $delnodes
338    }
339    $itk_component(legend) selection clearall
340    eval $graph_ element delete $elements
341}
342
343itcl::body Rappture::XyLegend::Check {} {
344    set nodes [$itk_component(legend) curselection]
345    foreach n { hide show toggle raise lower
346        rename average difference delete } {
347        $itk_component(controls).$n configure -state disabled
348    }
349    foreach node $nodes {
350        if { [$tree_ get $node "delete" 0] } {
351            $itk_component(controls).delete configure -state normal
352            break
353        }
354    }
355    if { [$itk_component(legend) index focus] != -1 } {
356            $itk_component(controls).rename configure -state normal
357    }
358    if { [$tree_ degree 0] > 1  && [llength $nodes] > 0 } {
359        foreach n { raise lower } {
360            $itk_component(controls).$n configure -state normal
361        }
362    }
363    switch -- [llength $nodes] {
364        0 {
365        }
366        1 {
367            foreach n { hide show toggle } {
368                $itk_component(controls).$n configure -state normal
369            }
370        }
371        2 {
372            foreach n { hide show toggle difference } {
373                $itk_component(controls).$n configure -state normal
374            }
375        }
376        default {
377            foreach n { hide show toggle } {
378                $itk_component(controls).$n configure -state normal
379            }
380        }
381    }
382}
383
384itcl::body Rappture::XyLegend::GetData { elem what } {
385    set y [$graph_ element cget $elem $what]
386    if { [blt::vector names $y] == $y } {
387        set y [$y range 0 end]
388    }
389    return $y
390}
391
392itcl::body Rappture::XyLegend::Average {} {
393    set nodes [$itk_component(legend) curselection]
394    if { $nodes == "" } {
395        return
396    }
397    set elements {}
398    set sum [blt::vector create \#auto -command ""]
399
400    set xcoords [blt::vector create \#auto -command ""]
401    set ycoords [blt::vector create \#auto -command ""]
402
403    # Step 1. Get the x-values for each curve, then sort them to get the
404    #         unique values.
405
406    foreach node $nodes {
407        set elem [$tree_ label $node]
408        $xcoords append [GetData $elem -x]
409        set elements [linsert $elements 0 $elem]
410    }
411    # Sort the abscissas keeping unique values.
412    $xcoords sort -uniq
413
414    # Step 2. Now for each curve, generate a cubic spline of that curve
415    #         and interpolate to get the corresponding y-values for each
416    #         abscissa.  Normally the abscissa are the same, so we're
417    #         interpolation the knots.
418
419    set x [blt::vector create \#auto -command ""]
420    set y [blt::vector create \#auto -command ""]
421    $sum length [$xcoords length]
422
423    foreach node $nodes {
424        set elem [$tree_ label $node]
425        $x set [GetData $elem -x]
426        $y set [GetData $elem -y]
427        blt::spline natural $x $y $xcoords $ycoords
428
429        # Sum the interpolated y-coordinate values.
430        $sum expr "$sum + $ycoords"
431    }
432    blt::vector destroy $x $y
433
434    # Get the average
435    $sum expr "$sum / [llength $elements]"
436
437    # Step 3.  Create a new curve which is the average. Append it to the
438    #          the end.
439
440    set count 0
441    while {[$graph_ element exists avg$count] } {
442        incr count
443    }
444    set elements [lsort -dictionary $elements]
445    set name "avg$count"
446    set label "Avg. [join $elements ,]"
447
448    # Don't use the vector because we don't know when it will be cleaned up.
449
450    $graph_ element create $name -label $label -x [$xcoords range 0 end]\
451        -y [$sum range 0 end] -symbol scross
452    blt::vector destroy $xcoords $ycoords $sum
453    set node [Add $name $label -delete]
454    Raise $node
455}
456
457itcl::body Rappture::XyLegend::Difference {} {
458    set nodes [$itk_component(legend) curselection]
459    set elements {}
460    set sum [blt::vector create \#auto -command ""]
461
462    set xcoords [blt::vector create \#auto -command ""]
463    set ycoords [blt::vector create \#auto -command ""]
464
465    # Step 1. Get the x-values for each curve, then sort them to get the
466    #         unique values.
467
468    foreach node $nodes {
469        set elem [$tree_ label $node]
470        $xcoords append [GetData $elem -x]
471        set elements [linsert $elements 0 $elem]
472    }
473    # Sort the abscissas keeping unique values.
474    $xcoords sort -uniq
475
476    # Step 2. Now for each curve, generate a cubic spline of that curve
477    #         and interpolate to get the corresponding y-values for each
478    #         abscissa.  Normally the abscissa are the same, so we're
479    #         interpolation the knots.
480
481    set x [blt::vector create \#auto -command ""]
482    set y [blt::vector create \#auto -command ""]
483    $sum length [$xcoords length]
484
485    foreach node $nodes {
486        set elem [$tree_ label $node]
487        $x set [GetData $elem -x]
488        $y set [GetData $elem -y]
489        blt::spline natural $x $y $xcoords $ycoords
490
491        # Sum the interpolated y-coordinate values.
492        $sum expr "fabs($ycoords - $sum)"
493    }
494    blt::vector destroy $x $y
495
496    # Get the average
497    #$sum expr "$sum / [llength $elements]"
498
499    # Step 3.  Create a new curve which is the average. Append it to the
500    #          the end.
501
502    set count 0
503    while {[$graph_ element exists diff$count] } {
504        incr count
505    }
506    set elements [lsort -dictionary $elements]
507    set name "diff$count"
508    set label "Diff. [join $elements ,]"
509
510    # Don't use the vector because we don't know when it will be cleaned up.
511
512    $graph_ element create $name -label $label -x [$xcoords range 0 end] \
513        -y [$sum range 0 end]
514    blt::vector destroy $xcoords $ycoords $sum
515    set node [Add $name $label]
516    Raise $node
517}
518
519itcl::body Rappture::XyLegend::Difference {} {
520
521    if { $diff_ != "" } {
522        $graph_ marker delete $diff_
523        set diff_ ""
524    }
525    set nodes [$itk_component(legend) curselection]
526    set elem1 [$tree_ label [lindex $nodes 0]]
527    set elem2 [$tree_ label [lindex $nodes 1]]
528    if { [info exists diffelements_($elem1)] &&
529         [info exists diffelements_($elem2)] } {
530        array unset diffelements_
531        return
532    }
533    array unset diffelements_
534    set x [blt::vector create \#auto -command ""]
535    set y [blt::vector create \#auto -command ""]
536    set m [blt::vector create \#auto -command ""]
537
538    $x append [GetData $elem1 -x]
539    $y append [GetData $elem1 -y]
540    $x sort -reverse $y
541    $x append [GetData $elem2 -x]
542    $y append [GetData $elem2 -y]
543    $m merge $x $y
544    set diff_ [$graph_ marker create polygon \
545                   -coords [$m range 0 end] \
546                   -elem $elem1 \
547                   -stipple dot1 \
548                   -outline "" -fill blue4 ]
549    blt::vector destroy $m $x $y
550    set diffelements_($elem1) 1
551    set diffelements_($elem2) 1
552}
553
554
555itcl::body Rappture::XyLegend::SelectAll { } {
556    foreach node [$tree_ children 0] {
557        $itk_component(legend) selection set $node
558    } 
559}
560
561itcl::body Rappture::XyLegend::Rename {} {
562    Editor popup
563}
564
565# ----------------------------------------------------------------------
566# USAGE: Editor popup
567# USAGE: Editor activate
568# USAGE: Editor validate <value>
569# USAGE: Editor apply <value>
570# USAGE: Editor menu <rootx> <rooty>
571#
572# Used internally to handle the various functions of the pop-up
573# editor for the value of this gauge.
574# ----------------------------------------------------------------------
575itcl::body Rappture::XyLegend::Editor {option args} {
576    switch -- $option {
577        popup {
578            $itk_component(editor) activate
579        }
580        activate {
581            set focus_ [$itk_component(legend) index focus]
582            if { $focus_ == -1 } {
583                return;
584            }
585            set label [$itk_component(legend) entry cget $focus_ -label]
586            foreach { l r t b } [$itk_component(legend) bbox $focus_] break
587            set info(text) $label
588            set info(x) [expr $l + [winfo rootx $itk_component(legend)]]
589            set info(y) [expr $r + [winfo rooty $itk_component(legend)]]
590            set info(w) [expr $r - $l]
591            set info(h) [expr $b - $t]
592            return [array get info]
593        }
594        validate {
595            if {[llength $args] != 1} {
596                error "wrong # args: should be \"editor validate value\""
597            }
598        }
599        apply {
600            if {[llength $args] != 1} {
601                error "wrong # args: should be \"editor apply value\""
602            }
603            set label [lindex $args 0]
604            $itk_component(legend) entry configure $focus_ -label $label
605        }
606        menu {
607            eval tk_popup $itk_component(emenu) $args
608        }
609        default {
610            error "bad option \"$option\": should be popup, activate, validate, apply, and menu"
611        }
612    }
613}
Note: See TracBrowser for help on using the repository browser.