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

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

Fix for problem viewing structures in app-mgnanowirefet. The icon on
the first tab wasn't coming up properly. It was contained within a
<structure> element, and the code for resizing structures wasn't being
handled properly.

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