source: trunk/gui/scripts/energyLevels.tcl @ 9

Last change on this file since 9 was 9, checked in by mmc, 16 years ago

Massive changes across the entire toolkit. Rearranged the
XML description to agree better with new documentation and
conventions.

Added a small start of Rappture.interface and Rappture.number
in the python directory. This is the new way of doing Rappture--
by declaring variables directly in the program, not using XML
directly at all.

File size: 15.9 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: EnergyLevels - visualizer for discrete energy levels
3#
4#  This widget is a simple visualizer for a set of quantized energy
5#  levels, as you might find for a molecule or a quantum well.  It
6#  takes the Rappture XML representation for a <table> and extracts
7#  values from the "energy" column, then plots those energies on a
8#  graph.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004  Purdue Research Foundation, West Lafayette, IN
12# ======================================================================
13package require Itk
14package require BLT
15
16option add *EnergyLevels.width 4i widgetDefault
17option add *EnergyLevels.height 4i widgetDefault
18option add *EnergyLevels.levelColor blue widgetDefault
19option add *EnergyLevels.levelTextForeground blue widgetDefault
20option add *EnergyLevels.levelTextBackground #d9d9d9 widgetDefault
21
22option add *EnergyLevels.font \
23    -*-helvetica-medium-r-normal-*-*-120-* widgetDefault
24
25option add *EnergyLevels.detailFont \
26    -*-helvetica-medium-r-normal-*-*-100-* widgetDefault
27
28itcl::class Rappture::EnergyLevels {
29    inherit itk::Widget
30
31    itk_option define -layout layout Layout ""
32    itk_option define -output output Output ""
33    itk_option define -levelcolor levelColor LevelColor ""
34    itk_option define -leveltextforeground levelTextForeground Foreground ""
35    itk_option define -leveltextbackground levelTextBackground Background ""
36
37    constructor {args} { # defined below }
38    destructor { # defined below }
39
40    protected method _render {}
41    protected method _adjust {what val}
42    protected method _getColumn {name}
43    protected method _getUnits {name}
44    protected method _getMidPt {elist pos}
45}
46
47itk::usual EnergyLevels {
48}
49
50# ----------------------------------------------------------------------
51# CONSTRUCTOR
52# ----------------------------------------------------------------------
53itcl::body Rappture::EnergyLevels::constructor {args} {
54    itk_option add hull.width hull.height
55    pack propagate $itk_component(hull) no
56
57    #
58    # Add label for the title.
59    #
60    itk_component add title {
61        label $itk_interior.title
62    }
63    pack $itk_component(title) -side top
64
65
66    itk_component add cntls {
67        frame $itk_interior.cntls
68    }
69    pack $itk_component(cntls) -side right -fill y
70    grid rowconfigure $itk_component(cntls) 0 -weight 1
71    grid rowconfigure $itk_component(cntls) 1 -minsize 10
72    grid rowconfigure $itk_component(cntls) 2 -weight 1
73
74    #
75    # Add MORE/FEWER levels control for TOP of graph
76    #
77    itk_component add upperE {
78        frame $itk_component(cntls).upperE
79    }
80
81    itk_component add upperEmore {
82        label $itk_component(upperE).morel -text "More"
83    } {
84        usual
85        rename -font -detailfont detailFont Font
86    }
87    pack $itk_component(upperEmore) -side top
88
89    itk_component add upperEfewer {
90        label $itk_component(upperE).fewerl -text "Fewer"
91    } {
92        usual
93        rename -font -detailfont detailFont Font
94    }
95    pack $itk_component(upperEfewer) -side bottom
96
97    itk_component add upperEcntl {
98        scale $itk_component(upperE).cntl -orient vertical -showvalue 0 \
99            -command [itcl::code $this _adjust upper]
100    }
101    pack $itk_component(upperEcntl) -side top -fill y
102
103    #
104    # Add MORE/FEWER levels control for BOTTOM of graph
105    #
106    itk_component add lowerE {
107        frame $itk_component(cntls).lowerE
108    }
109
110    itk_component add lowerEmore {
111        label $itk_component(lowerE).morel -text "More"
112    } {
113        usual
114        rename -font -detailfont detailFont Font
115    }
116    pack $itk_component(lowerEmore) -side bottom
117
118    itk_component add lowerEfewer {
119        label $itk_component(lowerE).fewerl -text "Fewer"
120    } {
121        usual
122        rename -font -detailfont detailFont Font
123    }
124    pack $itk_component(lowerEfewer) -side top
125
126    itk_component add lowerEcntl {
127        scale $itk_component(lowerE).cntl -orient vertical -showvalue 0 \
128            -command [itcl::code $this _adjust lower]
129    }
130    pack $itk_component(lowerEcntl) -side top -fill y
131
132    #
133    # Add graph showing levels
134    #
135    itk_component add graph {
136        blt::graph $itk_interior.graph \
137            -highlightthickness 0 -plotpadx 0 -plotpady 0 \
138            -width 3i -height 3i
139    } {
140        keep -background -foreground -cursor -font
141    }
142    pack $itk_component(graph) -expand yes -fill both
143    $itk_component(graph) legend configure -hide yes
144
145    eval itk_initialize $args
146}
147
148# ----------------------------------------------------------------------
149# DESTRUCTOR
150# ----------------------------------------------------------------------
151itcl::body Rappture::EnergyLevels::destructor {} {
152}
153
154# ----------------------------------------------------------------------
155# USAGE: _render
156#
157# Used internally to load a list of energy levels from a <table> within
158# the -output XML object.  The -layout object indicates how information
159# should be extracted from the table.  The <layout> should have an
160# <energies> tag and perhaps a <labels> tag, which indicates the table
161# and the column within the table containing the energies.
162# ----------------------------------------------------------------------
163itcl::body Rappture::EnergyLevels::_render {} {
164    #
165    # Clear any information shown in the graph.
166    #
167    set graph $itk_component(graph)
168    eval $graph element delete [$graph element names]
169    eval $graph marker delete [$graph marker names]
170
171    #
172    # Plug in the title from the layout
173    #
174    set title ""
175    if {$itk_option(-layout) != ""} {
176        set title [$itk_option(-layout) get label]
177    }
178    if {"" != $title} {
179        pack $itk_component(title) -side top -before $graph
180        $itk_component(title) configure -text $title
181    } else {
182        pack forget $itk_component(title)
183    }
184
185    #
186    # Look through the layout and figure out what to extract
187    # from the table.
188    #
189    set elist [_getColumn energies]
190    if {[llength $elist] == 0} {
191        return
192    }
193    set units [_getUnits energies]
194
195    set llist [_getColumn names]
196    if {[llength $llist] == 0} {
197        # no labels? then invent some
198        set i 0
199        foreach name $elist {
200            lappend llist "E$i"
201            incr i
202        }
203    }
204
205    #
206    # Update the graph to show the current set of levels.
207    #
208    set n 0
209    set nlumo -1
210    set emax ""
211    set emin ""
212    set ehomo ""
213    set elumo ""
214    foreach eval $elist lval $llist {
215        if {$lval == "HOMO"} {
216            set ehomo $eval
217            set lval "HOMO = $eval $units"
218            set nlumo [expr {$n+1}]
219        } elseif {$lval == "LUMO" || $n == $nlumo} {
220            set elumo $eval
221            set lval "LUMO = $eval $units"
222        } else {
223            set lval ""
224        }
225
226        set elem "elem[incr n]"
227        $graph element create $elem \
228            -xdata {0 1} -ydata [list $eval $eval] \
229            -color $itk_option(-levelcolor) -symbol "" -linewidth 1
230
231        if {$lval != ""} {
232            $graph marker create text -coords [list 0.5 $eval] \
233                -text $lval -anchor c \
234                -foreground $itk_option(-leveltextforeground) \
235                -background $itk_option(-leveltextbackground)
236        }
237
238        if {$emax == ""} {
239            set emax $eval
240            set emin $eval
241        } else {
242            if {$eval > $emax} {set emax $eval}
243            if {$eval < $emin} {set emin $eval}
244        }
245    }
246    $graph xaxis configure -min 0 -max 1 -showticks off -linewidth 0
247    if {$units != ""} {
248        $graph yaxis configure -title "Energy ($units)"
249    } else {
250        $graph yaxis configure -title "Energy"
251    }
252
253    # bump the limits so they are big enough to show labels
254    set fnt $itk_option(-font)
255    set h [expr {0.5*([font metrics $fnt -linespace] + 5)}]
256    set emin [expr {$emin-($emax-$emin)*$h/150.0}]
257    set emax [expr {$emax+($emax-$emin)*$h/150.0}]
258    $graph yaxis configure -min $emin -max $emax
259
260    #
261    # If we found HOMO/LUMO levels, then add the band gap at
262    # that point.  Also, fix the controls for energy range.
263    #
264    if {$ehomo != "" && $elumo != ""} {
265        set id [$graph marker create line \
266            -coords [list 0.2 $elumo 0.2 $ehomo]]
267        $graph marker after $id
268
269        set egap [expr {$elumo-$ehomo}]
270        set emid [expr {0.5*($ehomo+$elumo)}]
271        $graph marker create text \
272            -coords [list 0.21 $emid] -background "" \
273            -text "Eg = [format %.2g $egap] $units" -anchor w
274
275        # fix the limits for the lower scale
276        set elim [_getMidPt $elist [expr {$nlumo-1}]]
277        if {"" != $elim} {
278            $itk_component(lowerEcntl) configure -from $elim -to $emin \
279                -resolution [expr {0.02*($elim-$emin)}]
280            grid $itk_component(lowerE) -row 2 -column 0 -sticky ns
281
282            set e0 [_getMidPt $elist [expr {$nlumo-3}]]
283            if {"" != $e0} {
284                $itk_component(lowerEcntl) set $e0
285            } else {
286                $itk_component(lowerEcntl) set $elim
287            }
288        } else {
289            grid forget $itk_component(lowerE)
290        }
291
292        # fix the limits for the upper scale
293        set elim [_getMidPt $elist [expr {$nlumo+1}]]
294        if {"" != $elim} {
295            $itk_component(upperEcntl) configure -from $emax -to $elim \
296                -resolution [expr {0.02*($emax-$elim)}]
297            grid $itk_component(upperE) -row 0 -column 0 -sticky ns
298
299            set e0 [_getMidPt $elist [expr {$nlumo+3}]]
300            if {"" != $e0} {
301                $itk_component(upperEcntl) set $e0
302            } else {
303                $itk_component(upperEcntl) set $elim
304            }
305        } else {
306            grid forget $itk_component(upperE)
307        }
308    } else {
309        grid forget $itk_component(upperE)
310        grid forget $itk_component(lowerE)
311    }
312}
313
314# ----------------------------------------------------------------------
315# USAGE: _adjust <what> <val>
316#
317# Used internally to adjust the upper/lower limits of the graph
318# as the user drags the slider from "More" to "Fewer".  Sets
319# the specified limit to the given value.
320# ----------------------------------------------------------------------
321itcl::body Rappture::EnergyLevels::_adjust {what val} {
322    switch -- $what {
323        upper {
324            $itk_component(graph) yaxis configure -max $val
325        }
326        lower {
327            $itk_component(graph) yaxis configure -min $val
328        }
329        default {
330            error "bad limit \"$what\": should be upper or lower"
331        }
332    }
333}
334
335# ----------------------------------------------------------------------
336# USAGE: _getColumn <name>
337#
338# Used internally to load a list of energy levels from a <table> within
339# the -output XML object.  The -layout object indicates how information
340# should be extracted from the table.  The <layout> should have an
341# <energies> tag and perhaps a <labels> tag, which indicates the table
342# and the column within the table containing the energies.
343# ----------------------------------------------------------------------
344itcl::body Rappture::EnergyLevels::_getColumn {name} {
345    if {$itk_option(-layout) == "" || $itk_option(-output) == ""} {
346        return
347    }
348
349    #
350    # Figure out which column in which table contains the data.
351    # Then, find that table and extract the column.  Figure out
352    # the position of the column from the list of all column names.
353    #
354    set table [$itk_option(-layout) get $name.table]
355    set col [$itk_option(-layout) get $name.column]
356
357    set clist ""
358    foreach c [$itk_option(-output) children -type column $table] {
359        lappend clist [$itk_option(-output) get $table.$c.label]
360    }
361    set ipos [lsearch $clist $col]
362    if {$ipos < 0} {
363        return  ;# can't find data -- bail out!
364    }
365
366    set units [$itk_option(-output) get $table.column$ipos.units]
367
368    set rlist ""
369    foreach line [split [$itk_option(-output) get $table.data] "\n"] {
370        if {"" != [string trim $line]} {
371            set val [lindex $line $ipos]
372
373            if {$units != ""} {
374                set val [Rappture::Units::convert $val \
375                    -context $units -to $units -units off]
376            }
377            lappend rlist $val
378        }
379    }
380    return $rlist
381}
382
383# ----------------------------------------------------------------------
384# USAGE: _getUnits <name>
385#
386# Used internally to extract the units from a <table> within the
387# -output XML object.  The -layout object indicates how information
388# should be extracted from the table.  The <layout> should have an
389# <energies> tag and perhaps a <labels> tag, which indicates the table
390# and the column within the table containing the units.
391# ----------------------------------------------------------------------
392itcl::body Rappture::EnergyLevels::_getUnits {name} {
393    if {$itk_option(-layout) == "" || $itk_option(-output) == ""} {
394        return
395    }
396
397    #
398    # Figure out which column in which table contains the data.
399    # Then, find that table and extract the column.  Figure out
400    # the position of the column from the list of all column names.
401    #
402    set table [$itk_option(-layout) get $name.table]
403    set col [$itk_option(-layout) get $name.column]
404
405    set clist ""
406    foreach c [$itk_option(-output) children -type column $table] {
407        lappend clist [$itk_option(-output) get $table.$c.label]
408    }
409    set ipos [lsearch $clist $col]
410    if {$ipos < 0} {
411        return  ;# can't find data -- bail out!
412    }
413
414    return [$itk_option(-output) get $table.column$ipos.units]
415}
416
417# ----------------------------------------------------------------------
418# USAGE: _getMidPt <elist> <pos>
419#
420# Used internally to compute the midpoint between two energy levels
421# at <pos> and <pos-1> in the <elist>.  Returns a number representing
422# the mid-point (average value) or "" if the levels involved do
423# no exist in <elist>.
424# ----------------------------------------------------------------------
425itcl::body Rappture::EnergyLevels::_getMidPt {elist pos} {
426    if {$pos < [llength $elist] && $pos > 1} {
427        set e1 [lindex $elist $pos]
428        set e0 [lindex $elist [expr {$pos-1}]]
429        return [expr {0.5*($e0+$e1)}]
430    }
431    return ""
432}
433
434# ----------------------------------------------------------------------
435# OPTION: -layout
436# ----------------------------------------------------------------------
437itcl::configbody Rappture::EnergyLevels::layout {
438    if {$itk_option(-layout) != ""
439          && ![Rappture::library isvalid $itk_option(-layout)]} {
440        error "bad value \"$itk_option(-layout)\": should be Rappture::library object"
441    }
442    after idle [itcl::code $this _render]
443}
444
445# ----------------------------------------------------------------------
446# OPTION: -output
447# ----------------------------------------------------------------------
448itcl::configbody Rappture::EnergyLevels::output {
449    if {$itk_option(-output) != ""
450          && ![Rappture::library isvalid $itk_option(-output)]} {
451        error "bad value \"$itk_option(-output)\": should be Rappture::library object"
452    }
453    after cancel [itcl::code $this _render]
454    after idle [itcl::code $this _render]
455}
456
457# ----------------------------------------------------------------------
458# OPTION: -levelColor
459# ----------------------------------------------------------------------
460itcl::configbody Rappture::EnergyLevels::levelcolor {
461    after cancel [itcl::code $this _render]
462    after idle [itcl::code $this _render]
463}
464
465# ----------------------------------------------------------------------
466# OPTION: -leveltextforeground
467# ----------------------------------------------------------------------
468itcl::configbody Rappture::EnergyLevels::leveltextforeground {
469    after cancel [itcl::code $this _render]
470    after idle [itcl::code $this _render]
471}
472
473# ----------------------------------------------------------------------
474# OPTION: -leveltextbackground
475# ----------------------------------------------------------------------
476itcl::configbody Rappture::EnergyLevels::leveltextbackground {
477    after cancel [itcl::code $this _render]
478    after idle [itcl::code $this _render]
479}
Note: See TracBrowser for help on using the repository browser.