source: branches/blt4/gui/scripts/deviceViewer1D.tcl @ 1650

Last change on this file since 1650 was 1650, checked in by gah, 14 years ago
File size: 27.1 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: deviceViewer1D - visualizer for 1D device geometries
3#
4#  This widget is a simple visualizer for 1D devices.  It takes the
5#  Rappture XML representation for a 1D device and draws various
6#  facets of the data.  Each facet shows the physical layout along
7#  with some other quantity.  The "Electrical" facet shows electrical
8#  contacts.  The "Doping" facet shows the doping profile, and so
9#  forth.
10# ======================================================================
11#  AUTHOR:  Michael McLennan, Purdue University
12#  Copyright (c) 2004-2005  Purdue Research Foundation
13#
14#  See the file "license.terms" for information on usage and
15#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16# ======================================================================
17package require Itk
18#package require Img
19package require BLT
20
21option add *DeviceViewer1D.padding 4 widgetDefault
22option add *DeviceViewer1D.deviceSize 0.25i widgetDefault
23option add *DeviceViewer1D.deviceOutline black widgetDefault
24option add *DeviceViewer1D*graph.width 3i widgetDefault
25option add *DeviceViewer1D*graph.height 2i widgetDefault
26
27itcl::class Rappture::DeviceViewer1D {
28    inherit itk::Widget
29
30    itk_option define -device device Device ""
31
32    constructor {owner args} { # defined below }
33    destructor { # defined below }
34
35    public method add {dataobj {settings ""}}
36    public method get {}
37    public method delete {args}
38    public method parameters {title args} { # do nothing }
39
40    public method controls {option args}
41    public method download {option args}
42                                                                               
43    protected method _loadDevice {}
44    protected method _loadParameters {frame path}
45    protected method _changeTabs { {index -1} }
46    protected method _fixSize {}
47    protected method _fixAxes {}
48    protected method _align {}
49
50    protected method _marker {option {name ""} {path ""}}
51
52    protected method _controlCreate {container libObj path}
53    protected method _controlSet {widget libObj path}
54
55    private variable _owner ""      ;# thing managing this control
56    private variable _dlist ""      ;# list of dataobj objects
57    private variable _dobj2raise    ;# maps dataobj => raise flag
58    private variable _device ""     ;# XML library with <structure>
59    private variable _tab2fields    ;# maps tab name => list of fields
60    private variable _field2parm    ;# maps field path => parameter name
61    private variable _units ""      ;# units for field being edited
62    private variable _marker        ;# marker currently being edited
63}
64                                                                               
65itk::usual DeviceViewer1D {
66}
67
68# ----------------------------------------------------------------------
69# CONSTRUCTOR
70# ----------------------------------------------------------------------
71itcl::body Rappture::DeviceViewer1D::constructor {owner args} {
72    set _owner $owner
73
74    pack propagate $itk_component(hull) no
75
76    itk_component add tabs {
77        blt::tabset $itk_interior.tabs -outerborderwidth 0 -outerrelief flat \
78            -side bottom -tearoff 0 \
79            -selectcommand [itcl::code $this _changeTabs]
80    } {
81        ignore -activebackground -activeforeground
82        keep -background -cursor -font
83        rename -highlightbackground -background background Background
84        keep -highlightcolor -highlightthickness
85        keep -background -foreground
86        rename -selectbackground -background background Background
87        rename -selectforeground -foreground foreground Foreground
88    }
89    pack $itk_component(tabs) -expand yes -fill both
90
91    itk_component add -protected inner {
92        frame $itk_component(tabs).inner
93    }
94
95    itk_component add top {
96        frame $itk_component(inner).top
97    }
98    pack $itk_component(top) -fill x
99
100    itk_component add layout {
101        Rappture::DeviceLayout1D $itk_component(inner).layout
102    }
103    pack $itk_component(layout) -side top -fill x -pady 4
104
105    itk_component add graph {
106        blt::graph $itk_component(inner).graph \
107            -highlightthickness 0 -plotpadx 0 -plotpady 0
108    } {
109        keep -background -foreground -cursor -font
110    }
111    pack $itk_component(graph) -expand yes -fill both
112    $itk_component(graph) legend configure -hide yes
113
114    bind $itk_component(graph) <Configure> "
115        [list after cancel [list catch [itcl::code $this _align]]]
116        [list after 100 [list catch [itcl::code $this _align]]]
117    "
118
119    itk_component add geditor {
120        Rappture::Editor $itk_component(graph).editor \
121            -activatecommand [itcl::code $this _marker activate] \
122            -validatecommand [itcl::code $this _marker validate] \
123            -applycommand [itcl::code $this _marker apply]
124    }
125
126    itk_component add devcntls {
127        Rappture::Notebook $itk_component(inner).devcntls
128    }
129    pack $itk_component(devcntls) -side bottom -fill x
130
131    eval itk_initialize $args
132
133    after cancel [list catch [itcl::code $this _fixSize]]
134    after idle [list catch [itcl::code $this _fixSize]]
135}
136
137# ----------------------------------------------------------------------
138# DESTRUCTOR
139# ----------------------------------------------------------------------
140itcl::body Rappture::DeviceViewer1D::destructor {} {
141    set _device ""
142    foreach name [array names _tab2fields] {
143        eval itcl::delete object $_tab2fields($name)
144    }
145    after cancel [list catch [itcl::code $this _fixAxes]]
146    after cancel [list catch [itcl::code $this _align]]
147    after cancel [list catch [itcl::code $this _loadDevice]]
148}
149
150# ----------------------------------------------------------------------
151# USAGE: add <dataobj> ?<settings>?
152#
153# Clients use this to add a data object to the plot.  The optional
154# <settings> are used to configure the plot.  Allowed settings are
155# -color, -brightness, -width, -linestyle, and -raise. Only
156# -brightness and -raise do anything.
157# ----------------------------------------------------------------------
158itcl::body Rappture::DeviceViewer1D::add {dataobj {settings ""}} {
159    array set params {
160        -color auto
161        -brightness 0
162        -width 1
163        -raise 0
164        -linestyle solid
165        -description ""
166        -param ""
167    }
168    foreach {opt val} $settings {
169        if {![info exists params($opt)]} {
170            error "bad settings \"$opt\": should be [join [lsort [array names params]] {, }]"
171        }
172        set params($opt) $val
173    }
174 
175    set pos [lsearch -exact $dataobj $_dlist]
176
177    if {$pos < 0} {
178        if {![Rappture::library isvalid $dataobj]} {
179            error "bad value \"$dataobj\": should be Rappture::library object"
180        }
181
182        lappend _dlist $dataobj
183        set _dobj2raise($dataobj) $params(-raise)
184
185        after cancel [list catch [itcl::code $this _loadDevice]]
186        after idle [list catch [itcl::code $this _loadDevice]]
187    }
188}
189
190# ----------------------------------------------------------------------
191# USAGE: get
192#
193# Clients use this to query the list of objects being plotted, in
194# order from bottom to top of this result.
195# ----------------------------------------------------------------------
196itcl::body Rappture::DeviceViewer1D::get {} {
197    # put the dataobj list in order according to -raise options
198    set dlist $_dlist
199    foreach obj $dlist {
200        if {[info exists _dobj2raise($obj)] && $_dobj2raise($obj)} {
201            set i [lsearch -exact $dlist $obj]
202            if {$i >= 0} {
203                set dlist [lreplace $dlist $i $i]
204                lappend dlist $obj
205            }
206        }
207    }
208    return $dlist
209}
210
211# ----------------------------------------------------------------------
212# USAGE: delete ?<dataobj> <dataobj> ...?
213#
214# Clients use this to delete a dataobj from the plot. If no dataobjs
215# are specified, then all dataobjs are deleted.
216# ----------------------------------------------------------------------
217itcl::body Rappture::DeviceViewer1D::delete {args} {
218    if {[llength $args] == 0} {
219        set args $_dlist
220    }
221
222    # delete all specified dataobjs
223    set changed 0
224    foreach dataobj $args {
225        set pos [lsearch -exact $_dlist $dataobj]
226        if {$pos >= 0} {
227            set _dlist [lreplace $_dlist $pos $pos]
228            catch {unset _dobj2raise($dataobj)}
229            set changed 1
230        }
231    }
232
233    # if anything changed, then rebuild the plot
234    if {$changed} {
235        after cancel [list catch [itcl::code $this _loadDevice]]
236        after idle [list catch [itcl::code $this _loadDevice]]
237    }
238}
239
240# ----------------------------------------------------------------------
241# USAGE: controls insert <pos> <xmlobj> <path>
242#
243# Clients use this to add a control to the internal panels of this
244# widget.  Such controls are usually placed at the top of the widget,
245# but if possible, they are integrated directly onto the device
246# layout or the field area.
247# ----------------------------------------------------------------------
248itcl::body Rappture::DeviceViewer1D::controls {option args} {
249    switch -- $option {
250        insert {
251            if {[llength $args] != 3} {
252                error "wrong # args: should be \"controls insert pos xmlobj path\""
253            }
254            set pos [lindex $args 0]
255            set xmlobj [lindex $args 1]
256            set path [lindex $args 2]
257            if {[string match *structure.parameters* $path]} {
258            } elseif {[string match structure.components* $path]} {
259                $itk_component(layout) controls insert $pos $xmlobj $path
260            }
261        }
262        default {
263            error "bad option \"$option\": should be insert"
264        }
265    }
266}
267
268
269# ----------------------------------------------------------------------
270# USAGE: download coming
271# USAGE: download controls <downloadCommand>
272# USAGE: download now
273#
274# Clients use this method to create a downloadable representation
275# of the plot.  Returns a list of the form {ext string}, where
276# "ext" is the file extension (indicating the type of data) and
277# "string" is the data itself.
278# ----------------------------------------------------------------------
279itcl::body Rappture::DeviceViewer1D::download {option args} {
280    switch $option {
281        coming {
282            # nothing to do
283        }
284        controls {
285            # no controls for this download yet
286            return ""
287        }
288        now {
289            return ""  ;# not implemented yet!
290        }
291        default {
292            error "bad option \"$option\": should be coming, controls, now"
293        }
294    }
295}
296
297# ----------------------------------------------------------------------
298# USAGE: _loadDevice
299#
300# Used internally to search for fields and create corresponding
301# tabs whenever a device is installed into this viewer.
302# ----------------------------------------------------------------------
303itcl::body Rappture::DeviceViewer1D::_loadDevice {} {
304    set _device [lindex [get] end]
305
306    #
307    # Release any info left over from the last device.
308    #
309    foreach name [array names _tab2fields] {
310        eval itcl::delete object $_tab2fields($name)
311    }
312    catch {unset _tab2fields}
313    catch {unset _field2parm}
314
315    if {[winfo exists $itk_component(top).cntls]} {
316        $itk_component(top).cntls delete 0 end
317    }
318
319    #
320    # Scan through the current device and extract the list of
321    # fields.  Create a tab for each field.
322    #
323    if {$_device != ""} {
324        foreach nn [$_device children fields] {
325            set name [$_device get fields.$nn.about.label]
326            if {$name == ""} {
327                set name $nn
328            }
329
330            set fobj [Rappture::Field ::#auto $_device fields.$nn]
331            lappend _tab2fields($name) $fobj
332        }
333    }
334    set tabs [lsort [array names _tab2fields]]
335
336    if {[$itk_component(tabs) size] > 0} {
337        $itk_component(tabs) delete all
338    }
339
340    if {[llength $tabs] <= 0} {
341        #
342        # No fields?  Then we don't need to bother with tabs.
343        # Just pack the inner frame directly.  If there are no
344        # fields, get rid of the graph.
345        #
346        pack $itk_component(inner) -expand yes -fill both
347        if {[llength $tabs] > 0} {
348            pack $itk_component(graph) -expand yes -fill both
349        } else {
350            pack forget $itk_component(graph)
351            $itk_component(layout) configure -leftmargin 0 -rightmargin 0
352        }
353    } else {
354        #
355        # Two or more fields?  Then create a tab for each field
356        # and select the first one by default.  Make sure the
357        # graph is packed.
358        #
359        pack forget $itk_component(inner)
360        pack $itk_component(graph) -expand yes -fill both
361
362        foreach name $tabs {
363            $itk_component(tabs) insert end $name
364#               -activebackground $itk_option(-background)
365        }
366        $itk_component(tabs) select 0
367    }
368
369    #
370    # Scan through and look for any parameters in the <structure>.
371    # Register any parameters associated with fields, so we can
372    # add them as active controls whenever we install new fields.
373    # Create controls for any remaining parameters, so the user
374    # can see that there's something to adjust.
375    #
376    if {$_device != ""} {
377        _loadParameters $itk_component(top) parameters
378    }
379
380    #
381    # Install the first tab
382    #
383    after idle [list catch [itcl::code $this _changeTabs 0]]
384    #
385    # Fix the right margin of the graph so that it has enough room
386    # to display the right-hand edge of the device.
387    #
388    $itk_component(graph) configure \
389        -rightmargin [$itk_component(layout) extents bar3D]
390
391    after cancel [list catch [itcl::code $this _fixSize]]
392    after idle [list catch [itcl::code $this _fixSize]]
393}
394
395# ----------------------------------------------------------------------
396# USAGE: _loadParameters <frame> <path>
397#
398# Used internally in _loadDevice to load child parameters at the
399# specified <path> into the <frame>.  If any of the children are
400# groups, then this is called recursively to fill in the group
401# children.
402# ----------------------------------------------------------------------
403itcl::body Rappture::DeviceViewer1D::_loadParameters {frame path} {
404    foreach cname [$_device children $path] {
405        set handled 0
406        set type [$_device element -as type $path.$cname]
407        if {$type == "about"} {
408            continue
409        }
410        if {$type == "number"} {
411            set name [$_device element -as id $path.$cname]
412
413            # look for a field that uses this parameter
414            set found ""
415            foreach fname [$_device children fields] {
416                foreach comp [$_device children fields.$fname] {
417                    set v [$_device get fields.$fname.$comp.constant]
418                    if {[string equal $v $name]} {
419                        set found "fields.$fname.$comp"
420                        break
421                    }
422                }
423                if {"" != $found} break
424            }
425
426            if {"" != $found} {
427                set _field2parm($found) $name
428                set handled 1
429            }
430        }
431
432        #
433        # Any parameter that was not handled above should be handled
434        # here, by adding it to a control panel above the device
435        # layout area.
436        #
437        if {!$handled} {
438            if {![winfo exists $frame.cntls]} {
439                Rappture::Controls $frame.cntls $_owner
440                pack $frame.cntls -expand yes -fill both
441            }
442            $frame.cntls insert end $path.$cname
443
444            #
445            # If this is a group, then we must add its children
446            # recursively.
447            #
448            if {$type == "group"} {
449                set gr [$frame.cntls control -value end]
450                _loadParameters [$gr component inner] $path.$cname
451            }
452        }
453    }
454}
455
456# ----------------------------------------------------------------------
457# USAGE: _changeTabs
458#
459# Used internally to change the field being displayed whenever a new
460# tab is selected.
461# ----------------------------------------------------------------------
462itcl::body Rappture::DeviceViewer1D::_changeTabs { {index -1} } {
463    set graph $itk_component(graph)
464
465    #
466    # Figure out which tab is selected and make the inner frame
467    # visible in that tab.
468    #
469    if {$index == -1} {
470        set index [$itk_component(tabs) index selected]
471    }
472    if {$index == -1} {
473        set name [lindex [array names _tab2fields] 0]
474        if { $name == "" } {
475            if { [$itk_component(tabs) size] > 0 } {
476                $itk_component(tabs) select 0
477                set name [$itk_component(tabs) id 0]
478            }
479        } else {
480            $itk_component(tabs) select $name
481        }
482    } else {
483        set name [$itk_component(tabs) id $index]
484        $itk_component(tabs) tab configure $name \
485            -window $itk_component(inner) -fill both
486    }
487
488    #
489    # Update the graph to show the current field.
490    #
491    eval $graph element delete [$graph element names]
492    eval $graph marker delete [$graph marker names]
493
494    foreach {zmin zmax} [$itk_component(layout) limits] { break }
495    if {$zmin != "" && $zmin < $zmax} {
496        $graph axis configure x -min $zmin -max $zmax
497    }
498
499    if {$_device != ""} {
500        set units [$_device get units]
501        if {$units != "arbitrary"} {
502            $graph axis configure x -hide no -title "Position ($units)"
503        } else {
504            $graph axis configure x -hide yes
505        }
506    } else {
507        $graph axis configure x -hide no -title "Position"
508    }
509    $graph axis configure x -checklimits no
510
511    # turn on auto limits
512    $graph axis configure y -min "" -max "" -checklimits no
513
514    set flist ""
515    if {[info exists _tab2fields($name)]} {
516        set flist $_tab2fields($name)
517    }
518
519    set n 0
520    foreach fobj $flist {
521        catch {unset hints}
522        array set hints [$fobj hints]
523
524        if {[info exists hints(units)]} {
525            set _units $hints(units)
526            $graph axis configure y -title "$name ($hints(units))"
527        } else {
528            set _units ""
529            $graph axis configure y -title $name
530        }
531
532        if {[info exists hints(scale)]
533              && [string match log* $hints(scale)]} {
534            $graph axis configure y -logscale yes
535        } else {
536            $graph axis configure y -logscale no
537        }
538
539        foreach comp [$fobj components] {
540            # can only handle 1D meshes here
541            if {[$fobj components -dimensions $comp] != "1D"} {
542                continue
543            }
544
545            set elem "elem[incr n]"
546            set xv [$fobj mesh $comp]
547            set yv [$fobj values $comp]
548
549            $graph element create $elem -x $xv -y $yv \
550                -color black -symbol "" -linewidth 2
551
552            if {[info exists hints(color)]} {
553                $graph element configure $elem -color $hints(color)
554            }
555
556            foreach {path x y val} [$fobj controls get $comp] {
557                if {$path != ""} {
558                    set id "control[incr n]"
559                    $graph marker create text -coords [list $x $y] \
560                        -text $val -anchor s -name $id -background ""
561                    $graph marker bind $id <Enter> \
562                        [itcl::code $this _marker enter $id]
563                    $graph marker bind $id <Leave> \
564                        [itcl::code $this _marker leave $id]
565                    $graph marker bind $id <ButtonPress> \
566                        [itcl::code $this _marker edit $id $fobj/$path]
567                }
568            }
569        }
570    }
571
572    # let the widget settle, then fix the axes to "nice" values
573    after cancel [list catch [itcl::code $this _fixAxes]]
574    after 100 [list catch [itcl::code $this _fixAxes]]
575}
576
577# ----------------------------------------------------------------------
578# USAGE: _fixSize
579#
580# Used internally to fix the overall size of this widget based on
581# the various parts inside.  Sets the requested width/height of the
582# widget so that it is big enough to display the device and its
583# fields.
584# ----------------------------------------------------------------------
585itcl::body Rappture::DeviceViewer1D::_fixSize {} {
586    update idletasks
587    set w [winfo reqwidth $itk_component(tabs)]
588    set h [winfo reqheight $itk_component(tabs)]
589    component hull configure -width $w -height $h
590}
591
592# ----------------------------------------------------------------------
593# USAGE: _fixAxes
594#
595# Used internally to adjust the y-axis limits of the graph to "nice"
596# values, so that any control marker associated with the value,
597# for example, remains on screen.
598# ----------------------------------------------------------------------
599itcl::body Rappture::DeviceViewer1D::_fixAxes {} {
600    set graph $itk_component(graph)
601    if {![winfo ismapped $graph]} {
602        after cancel [list catch [itcl::code $this _fixAxes]]
603        after 100 [list catch [itcl::code $this _fixAxes]]
604        return
605    }
606
607    #
608    # HACK ALERT!
609    # Use this code to fix up the y-axis limits for the BLT graph.
610    # The auto-limits don't always work well.  We want them to be
611    # set to a "nice" number slightly above or below the min/max
612    # limits.
613    #
614    set log [$graph axis cget y -logscale]
615    $graph axis configure y -min "" -max ""
616    foreach {min max} [$graph axis limits y] { break }
617
618    if {$log} {
619        set min [expr {0.9*$min}]
620        set max [expr {1.1*$max}]
621    } else {
622        if {$min > 0} {
623            set min [expr {0.95*$min}]
624        } else {
625            set min [expr {1.05*$min}]
626        }
627        if {$max > 0} {
628            set max [expr {1.05*$max}]
629        } else {
630            set max [expr {0.95*$max}]
631        }
632    }
633
634    # bump up the max so that it's big enough to show control markers
635    set fnt $itk_option(-font)
636    set h [expr {[font metrics $fnt -linespace] + 5}]
637    foreach mname [$graph marker names] {
638        set xy [$graph marker cget $mname -coord]
639        foreach {x y} [eval $graph transform $xy] { break }
640        set y [expr {$y-$h}]  ;# find top of text in pixels
641        foreach {x y} [eval $graph invtransform [list 0 $y]] { break }
642        if {$y > $max} { set max $y }
643    }
644
645    if {$log} {
646        set min [expr {pow(10.0,floor(log10($min)))}]
647        set max [expr {pow(10.0,ceil(log10($max)))}]
648    } else {
649        set min [expr {0.1*floor(10*$min)}]
650        set max [expr {0.1*ceil(10*$max)}]
651    }
652
653    $graph axis configure y -min $min -max $max
654
655    after cancel [list catch [itcl::code $this _align]]
656    after 100 [list catch [itcl::code $this _align]]
657}
658
659# ----------------------------------------------------------------------
660# USAGE: _align
661#
662# Used internally to align the margins of the device layout and the
663# graph, so that two areas line up.
664# ----------------------------------------------------------------------
665itcl::body Rappture::DeviceViewer1D::_align {} {
666    set graph $itk_component(graph)
667
668    #
669    # Set the left/right margins of the layout so that it aligns
670    # with the graph.  Set the right margin of the graph so it
671    # it is big enough to show the 3D edge of the device that
672    # hangs over on the right-hand side.
673    #
674    update
675    foreach {xmin xmax} [$graph axis limits x] { break }
676    set lm [$graph xaxis transform $xmin]
677    $itk_component(layout) configure -leftmargin $lm
678
679    set w [winfo width $graph]
680    set rm [expr {$w-[$graph xaxis transform $xmax]}]
681    $itk_component(layout) configure -rightmargin $rm
682}
683
684# ----------------------------------------------------------------------
685# USAGE: _marker enter <name>
686# USAGE: _marker leave <name>
687# USAGE: _marker edit <name> <path>
688# USAGE: _marker activate
689# USAGE: _marker validate <value>
690# USAGE: _marker apply <value>
691#
692# Used internally to manipulate the control markers draw on the
693# graph for a field.
694# ----------------------------------------------------------------------
695itcl::body Rappture::DeviceViewer1D::_marker {option {name ""} {path ""}} {
696    switch -- $option {
697        enter {
698            $itk_component(graph) marker configure $name -background #e5e5e5
699            #
700            # BE CAREFUL:  Need an update here to force the graph to
701            #   refresh itself or else a subsequent click on the
702            #   marker will ignore the text that was recently changed,
703            #   and fail to generate a <ButtonPress> event!
704            #
705            update idletasks
706        }
707        leave {
708            $itk_component(graph) marker configure $name -background ""
709            #
710            # BE CAREFUL:  Need an update here to force the graph to
711            #   refresh itself or else a subsequent click on the
712            #   marker will ignore the text that was recently changed,
713            #   and fail to generate a <ButtonPress> event!
714            #
715            update idletasks
716        }
717        edit {
718            set _marker(name) $name
719            set _marker(fobj) [lindex [split $path /] 0]
720            set _marker(path) [lindex [split $path /] 1]
721            $itk_component(geditor) activate
722        }
723        activate {
724            set g $itk_component(graph)
725            set val [$g marker cget $_marker(name) -text]
726            foreach {x y} [$g marker cget $_marker(name) -coords] { break }
727            foreach {x y} [$g transform $x $y] { break }
728            set x [expr {$x + [winfo rootx $g] - 4}]
729            set y [expr {$y + [winfo rooty $g] - 5}]
730
731            set fnt $itk_option(-font)
732            set h [expr {[font metrics $fnt -linespace] + 2}]
733            set w [expr {[font measure $fnt $val] + 5}]
734
735            return [list text $val \
736                x [expr {$x-$w/2}] \
737                y [expr {$y-$h}] \
738                w $w \
739                h $h]
740        }
741        validate {
742            if {$_units != ""} {
743                if {[catch {Rappture::Units::convert $name \
744                        -context $_units -to $_units} result] != 0} {
745                    if {[regexp {^bad.*: +(.)(.+)} $result match first tail]
746                          || [regexp {(.)(.+)} $result match first tail]} {
747                        set result "[string toupper $first]$tail"
748                    }
749                    bell
750                    Rappture::Tooltip::cue $itk_component(geditor) $result
751                    return 0
752                }
753            }
754            if {[catch {$_marker(fobj) controls validate $_marker(path) $name} result]} {
755                bell
756                Rappture::Tooltip::cue $itk_component(geditor) $result
757                return 0
758            }
759            return 1
760        }
761        apply {
762            if {$_units != ""} {
763                catch {Rappture::Units::convert $name \
764                    -context $_units -to $_units} value
765            } else {
766                set value $name
767            }
768
769            $_marker(fobj) controls put $_marker(path) $value
770            $_owner changed $_marker(path)
771            event generate $itk_component(hull) <<Edit>>
772
773            _changeTabs
774        }
775    }
776}
777
778# ----------------------------------------------------------------------
779# USAGE: _controlCreate <container> <libObj> <path>
780#
781# Used internally to create a gauge widget and pack it into the
782# given <container>.  When the gauge is set, it updates the value
783# for the <path> in the <libObj>.
784# ----------------------------------------------------------------------
785itcl::body Rappture::DeviceViewer1D::_controlCreate {container libObj path} {
786    set presets ""
787    foreach pre [$libObj children -type preset $path] {
788        lappend presets \
789            [$libObj get $path.$pre.value] \
790            [$libObj get $path.$pre.label]
791    }
792
793    set type Rappture::Gauge
794    set units [$libObj get $path.units]
795    if {$units != ""} {
796        set desc [Rappture::Units::description $units]
797        if {[string match temperature* $desc]} {
798            set type Rappture::TemperatureGauge
799        }
800    }
801
802    set counter 0
803    set w "$container.gauge[incr counter]"
804    while {[winfo exists $w]} {
805        set w "$container.gauge[incr counter]"
806    }
807
808    # create the widget
809    $type $w -units $units -presets $presets
810    pack $w -side top -anchor w
811    bind $w <<Value>> [itcl::code $this _controlSet $w $libObj $path]
812
813    set min [$libObj get $path.min]
814    if {"" != $min} { $w configure -minvalue $min }
815
816    set max [$libObj get $path.max]
817    if {"" != $max} { $w configure -maxvalue $max }
818
819    set str [$libObj get $path.default]
820    if {$str != ""} { $w value $str }
821
822    if {$type == "Rappture::Gauge" && "" != $min && "" != $max} {
823        set color [$libObj get $path.color]
824        if {$color == ""} {
825            set color blue
826        }
827        if {$units != ""} {
828            set min [Rappture::Units::convert $min -to $units -units off]
829            set max [Rappture::Units::convert $max -to $units -units off]
830        }
831        $w configure -spectrum [Rappture::Spectrum ::#auto [list \
832            $min white $max $color] -units $units]
833    }
834
835    set str [$libObj get $path.label]
836    if {$str != ""} {
837        set help [$libObj get $path.help]
838        if {"" != $help} {
839            append str "\n$help"
840        }
841        if {$units != ""} {
842            set desc [Rappture::Units::description $units]
843            append str "\n(units of $desc)"
844        }
845        Rappture::Tooltip::for $w $str
846    }
847
848    set str [$libObj get $path.icon]
849    if {$str != ""} {
850        $w configure -image [image create picture -data $str]
851    }
852}
853
854# ----------------------------------------------------------------------
855# USAGE: _controlSet <widget> <libObj> <path>
856#
857# Invoked automatically whenever an internal control changes value.
858# Queries the new value for the control and assigns the value to the
859# given <path> on the XML object <libObj>.
860# ----------------------------------------------------------------------
861itcl::body Rappture::DeviceViewer1D::_controlSet {widget libObj path} {
862    set newval [$widget value]
863    $libObj put $path.current $newval
864    event generate $itk_component(hull) <<Edit>>
865}
866
867# ----------------------------------------------------------------------
868# CONFIGURATION OPTION: -device
869#
870# Set to the Rappture::Library object representing the device being
871# displayed in the viewer.  If set to "", the viewer is cleared to
872# display nothing.
873# ----------------------------------------------------------------------
874itcl::configbody Rappture::DeviceViewer1D::device {
875    if {$itk_option(-device) != ""} {
876        if {![Rappture::library isvalid $itk_option(-device)]} {
877            error "bad value \"$itk_option(-device)\": should be Rappture::Library"
878        }
879    }
880
881    delete
882    if {"" != $itk_option(-device)} {
883        add $itk_option(-device)
884    }
885    _loadDevice
886}
887
888
Note: See TracBrowser for help on using the repository browser.