source: trunk/gui/scripts/resultviewer.tcl @ 468

Last change on this file since 468 was 468, checked in by mmc, 18 years ago

Fixed bugs that showed up in the nanovis viewer when you switch between
two different fields or clear the existing result viewer.

Fixed the download option for 3D fields.

File size: 15.6 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: ResultViewer - plots a collection of related results
3#
4#  This widget plots a collection of results that all represent
5#  the same quantity, but for various ranges of input values.  It
6#  is normally used as part of an Analyzer, to plot the various
7#  results selected by a ResultSet.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require Img
17
18itcl::class Rappture::ResultViewer {
19    inherit itk::Widget
20
21    itk_option define -width width Width 4i
22    itk_option define -height height Height 4i
23    itk_option define -colors colors Colors ""
24    itk_option define -clearcommand clearCommand ClearCommand ""
25    itk_option define -simulatecommand simulateCommand SimulateCommand ""
26
27    constructor {args} { # defined below }
28    destructor { # defined below }
29
30    public method add {index xmlobj path}
31    public method clear {{index ""}}
32    public method value {xmlobj}
33
34    public method plot {option args}
35    public method download {option args}
36
37    protected method _plotAdd {xmlobj {settings ""}}
38    protected method _fixScale {args}
39    protected proc _xml2data {xmlobj path}
40
41    private variable _dispatcher ""  ;# dispatchers for !events
42    private variable _mode ""        ;# current plotting mode (xy, etc.)
43    private variable _mode2widget    ;# maps plotting mode => widget
44    private variable _dataslots ""   ;# list of all data objects in this widget
45    private variable _plotlist ""    ;# list of indices plotted in _dataslots
46}
47                                                                               
48itk::usual ResultViewer {
49    keep -background -foreground -cursor -font
50}
51
52# ----------------------------------------------------------------------
53# CONSTRUCTOR
54# ----------------------------------------------------------------------
55itcl::body Rappture::ResultViewer::constructor {args} {
56    # create a dispatcher for events
57    Rappture::dispatcher _dispatcher
58    $_dispatcher register !scale
59    $_dispatcher dispatch $this !scale \
60        [itcl::code $this _fixScale]
61
62    eval itk_initialize $args
63}
64
65# ----------------------------------------------------------------------
66# DESTRUCTOR
67# ----------------------------------------------------------------------
68itcl::body Rappture::ResultViewer::destructor {} {
69    foreach slot $_dataslots {
70        foreach obj $slot {
71            itcl::delete object $obj
72        }
73    }
74}
75
76# ----------------------------------------------------------------------
77# USAGE: add <index> <xmlobj> <path>
78#
79# Adds a new result to this result viewer at the specified <index>.
80# Data is taken from the <xmlobj> object at the <path>.
81# ----------------------------------------------------------------------
82itcl::body Rappture::ResultViewer::add {index xmlobj path} {
83    set dobj [_xml2data $xmlobj $path]
84
85    #
86    # If the index doesn't exist, then fill in empty slots and
87    # make it exist.
88    #
89    for {set i [llength $_dataslots]} {$i <= $index} {incr i} {
90        lappend _dataslots ""
91    }
92    set slot [lindex $_dataslots $index]
93    lappend slot $dobj
94    set _dataslots [lreplace $_dataslots $index $index $slot]
95
96    $_dispatcher event -idle !scale
97}
98
99# ----------------------------------------------------------------------
100# USAGE: clear ?<index>?
101#
102# Clears one or all results in this result viewer.
103# ----------------------------------------------------------------------
104itcl::body Rappture::ResultViewer::clear {{index ""}} {
105    if {"" != $index} {
106        # clear one result
107        if {$index >= 0 && $index < [llength $_dataslots]} {
108            set slot [lindex $_dataslots $index]
109            foreach dobj $slot {
110                itcl::delete object $dobj
111            }
112            set _dataslots [lreplace $_dataslots $index $index ""]
113        }
114    } else {
115        # clear all results
116        plot clear
117        foreach slot $_dataslots {
118            foreach dobj $slot {
119                itcl::delete object $dobj
120            }
121        }
122        set _dataslots ""
123    }
124}
125
126# ----------------------------------------------------------------------
127# USAGE: value <xmlobj>
128#
129# Convenience method for showing a single value.  Loads the value
130# into the widget via add/clear, then immediately plots the value.
131# This makes the widget consistent with other widgets, such as
132# the DeviceEditor, etc.
133# ----------------------------------------------------------------------
134itcl::body Rappture::ResultViewer::value {xmlobj} {
135    clear
136    if {"" != $xmlobj} {
137        add 0 $xmlobj ""
138        plot add 0 ""
139    }
140}
141
142# ----------------------------------------------------------------------
143# USAGE: plot add ?<index> <settings> <index> <settings> ...?
144# USAGE: plot clear
145#
146# Used to manipulate the contents of this viewer.  The "plot clear"
147# command clears the current viewer.  Data is still stored in the
148# widget, but the results are not shown on screen.  The "plot add"
149# command adds the data at the specified <index> to the plot.  If
150# the optional <settings> are specified, then they are applied
151# to the plot; otherwise, default settings are used.
152# ----------------------------------------------------------------------
153itcl::body Rappture::ResultViewer::plot {option args} {
154    switch -- $option {
155        add {
156            foreach {index opts} $args {
157                set reset "-color autoreset"
158                set slot [lindex $_dataslots $index]
159                foreach dobj $slot {
160                    set settings ""
161                    # start with color reset, only for first object in series
162                    if {"" != $reset} {
163                        set settings $reset
164                        set reset ""
165                    }
166                    # add default settings from data object
167                    if {[catch {$dobj hints style} style] == 0} {
168                        eval lappend settings $style
169                    }
170                    # add override settings passed in here
171                    eval lappend settings $opts
172
173                    _plotAdd $dobj $settings
174                }
175            }
176        }
177        clear {
178            # clear the contents of the current mode
179            if {"" != $_mode} {
180                $_mode2widget($_mode) delete
181            }
182            set _plotlist ""
183        }
184        default {
185            error "bad option \"$option\": should be add or clear"
186        }
187    }
188}
189
190# ----------------------------------------------------------------------
191# USAGE: _plotAdd <dataobj> <settings>
192#
193# Used internally to add a <dataobj> representing some data to
194# the plot at the top of this widget.  The data is added to the
195# current plot.  Use the "clear" function to clear before adding
196# new data.
197# ----------------------------------------------------------------------
198itcl::body Rappture::ResultViewer::_plotAdd {dataobj {settings ""}} {
199    switch -- [$dataobj info class] {
200        ::Rappture::Curve {
201            set mode "xy"
202            if {![info exists _mode2widget($mode)]} {
203                set w $itk_interior.xy
204                Rappture::XyResult $w
205                set _mode2widget($mode) $w
206            }
207        }
208        ::Rappture::Field {
209            set dims [lindex [lsort [$dataobj components -dimensions]] end]
210            switch -- $dims {
211                1D {
212                    set mode "xy"
213                    if {![info exists _mode2widget($mode)]} {
214                        set w $itk_interior.xy
215                        Rappture::XyResult $w
216                        set _mode2widget($mode) $w
217                    }
218                }
219                2D {
220                    set mode "contour"
221                    if {![info exists _mode2widget($mode)]} {
222                        set w $itk_interior.contour
223                        Rappture::ContourResult $w
224                        set _mode2widget($mode) $w
225                    }
226                }
227                3D {
228                    set mode "field3D"
229                    if {![info exists _mode2widget($mode)]} {
230                        set mesh [$dataobj mesh]
231                        set fmt [expr {("" != $mesh) ? "vtk" : "nanovis"}]
232                        set w $itk_interior.field3D
233                        Rappture::Field3DResult $w -mode $fmt
234                        set _mode2widget($mode) $w
235                    }
236                }
237                default {
238                    error "can't handle [$dataobj components -dimensions] field"
239                }
240            }
241        }
242        ::Rappture::Mesh {
243            switch -- [$dataobj dimensions] {
244                2 {
245                    set mode "mesh"
246                    if {![info exists _mode2widget($mode)]} {
247                        set w $itk_interior.mesh
248                        Rappture::MeshResult $w
249                        set _mode2widget($mode) $w
250                    }
251                }
252                default {
253                    error "can't handle [$dataobj dimensions]D field"
254                }
255            }
256        }
257        ::Rappture::Table {
258            set cols [Rappture::EnergyLevels::columns $dataobj]
259            if {"" != $cols} {
260                set mode "energies"
261                if {![info exists _mode2widget($mode)]} {
262                    set w $itk_interior.energies
263                    Rappture::EnergyLevels $w
264                    set _mode2widget($mode) $w
265                }
266            }
267        }
268        ::Rappture::LibraryObj {
269            switch -- [$dataobj element -as type] {
270                string - log {
271                    set mode "log"
272                    if {![info exists _mode2widget($mode)]} {
273                        set w $itk_interior.log
274                        Rappture::TextResult $w
275                        set _mode2widget($mode) $w
276                    }
277                }
278                structure {
279                    set mode "structure"
280                    if {![info exists _mode2widget($mode)]} {
281                        set w $itk_interior.struct
282                        Rappture::DeviceResult $w
283                        set _mode2widget($mode) $w
284                    }
285                }
286                number - integer - boolean - choice {
287                    set mode "value"
288                    if {![info exists _mode2widget($mode)]} {
289                        set w $itk_interior.value
290                        Rappture::ValueResult $w
291                        set _mode2widget($mode) $w
292                    }
293                }
294            }
295        }
296        ::Rappture::Image {
297            set mode "image"
298            if {![info exists _mode2widget($mode)]} {
299                set w $itk_interior.image
300                Rappture::ImageResult $w
301                set _mode2widget($mode) $w
302            }
303        }
304        ::Rappture::Sequence {
305            set mode "sequence"
306            if {![info exists _mode2widget($mode)]} {
307                set w $itk_interior.image
308                Rappture::SequenceResult $w
309                set _mode2widget($mode) $w
310            }
311        }
312        default {
313            error "don't know how to plot <$type> data"
314        }
315    }
316
317    if {$mode != $_mode && $_mode != ""} {
318        set nactive [llength [$_mode2widget($_mode) get]]
319        if {$nactive > 0} {
320            return  ;# mixing data that doesn't mix -- ignore it!
321        }
322    }
323
324    # are we plotting in a new mode? then change widgets
325    if {$_mode2widget($mode) != [pack slaves $itk_interior]} {
326        # remove any current window
327        foreach w [pack slaves $itk_interior] {
328            pack forget $w
329        }
330        pack $_mode2widget($mode) -expand yes -fill both
331
332        set _mode $mode
333        $_dispatcher event -idle !scale
334    }
335    $_mode2widget($mode) add $dataobj $settings
336}
337
338# ----------------------------------------------------------------------
339# USAGE: _fixScale ?<eventArgs>...?
340#
341# Invoked automatically whenever a new dataset is added to fix the
342# overall scales of the viewer.  This makes the visualizer consistent
343# across all <dataobj> in this widget, so that it can plot all
344# available data.
345# ----------------------------------------------------------------------
346itcl::body Rappture::ResultViewer::_fixScale {args} {
347    if {"" != $_mode} {
348        set dlist ""
349        foreach slot $_dataslots {
350            foreach dobj $slot {
351                lappend dlist $dobj
352            }
353        }
354        eval $_mode2widget($_mode) scale $dlist
355    }
356}
357
358# ----------------------------------------------------------------------
359# USAGE: download coming
360# USAGE: download controls <downloadCommand>
361# USAGE: download now
362#
363# Clients use this method to create a downloadable representation
364# of the plot.  Returns a list of the form {ext string}, where
365# "ext" is the file extension (indicating the type of data) and
366# "string" is the data itself.
367# ----------------------------------------------------------------------
368itcl::body Rappture::ResultViewer::download {option args} {
369    if {"" == $_mode} {
370        return ""
371    }
372    return [eval $_mode2widget($_mode) download $option $args]
373}
374
375# ----------------------------------------------------------------------
376# USAGE: _xml2data <xmlobj> <path>
377#
378# Used internally to create a data object for the data at the
379# specified <path> in the <xmlobj>.
380# ----------------------------------------------------------------------
381itcl::body Rappture::ResultViewer::_xml2data {xmlobj path} {
382    set type [$xmlobj element -as type $path]
383    switch -- $type {
384        curve {
385            return [Rappture::Curve ::#auto $xmlobj $path]
386        }
387        field {
388            return [Rappture::Field ::#auto $xmlobj $path]
389        }
390        mesh {
391            return [Rappture::Mesh ::#auto $xmlobj $path]
392        }
393        table {
394            return [Rappture::Table ::#auto $xmlobj $path]
395        }
396        image {
397            return [Rappture::Image ::#auto $xmlobj $path]
398        }
399        sequence {
400            return [Rappture::Sequence ::#auto $xmlobj $path]
401        }
402        string - log {
403            return [$xmlobj element -as object $path]
404        }
405        structure {
406            return [$xmlobj element -as object $path]
407        }
408        number - integer - boolean - choice {
409            return [$xmlobj element -as object $path]
410        }
411        time - status {
412            return ""
413        }
414    }
415    error "don't know how to plot <$type> data"
416}
417
418# ----------------------------------------------------------------------
419# CONFIGURATION OPTION: -width
420# ----------------------------------------------------------------------
421itcl::configbody Rappture::ResultViewer::width {
422    set w [winfo pixels $itk_component(hull) $itk_option(-width)]
423    set h [winfo pixels $itk_component(hull) $itk_option(-height)]
424    if {$w == 0 || $h == 0} {
425        pack propagate $itk_component(hull) yes
426    } else {
427        component hull configure -width $w -height $h
428        pack propagate $itk_component(hull) no
429    }
430}
431
432# ----------------------------------------------------------------------
433# CONFIGURATION OPTION: -height
434# ----------------------------------------------------------------------
435itcl::configbody Rappture::ResultViewer::height {
436    set h [winfo pixels $itk_component(hull) $itk_option(-height)]
437    set w [winfo pixels $itk_component(hull) $itk_option(-width)]
438    if {$w == 0 || $h == 0} {
439        pack propagate $itk_component(hull) yes
440    } else {
441        component hull configure -width $w -height $h
442        pack propagate $itk_component(hull) no
443    }
444}
Note: See TracBrowser for help on using the repository browser.