source: trunk/gui/scripts/deviceViewer1D.tcl @ 767

Last change on this file since 767 was 767, checked in by mmc, 17 years ago

Oops! Forgot to add the new -param option emitted by the ResultSet?,
so that the various viewers don't choke.

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