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

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