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

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

Added popup options for the "download" button. Right now this works
only for <curve> objects. You can select between CSV and PDF output.
Will add other formats later.

Fixed a few "after cancel" errors that were happening when you switch
between inputs in the structure demo.

Fixed the colors and fonts for the new bug report window.

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 mode [expr {("" != $mesh) ? "vtk" : "nanovis"}]
232                        set w $itk_interior.field3D
233                        Rappture::Field3DResult $w -mode $mode
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.