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

Last change on this file since 3021 was 2977, checked in by gah, 12 years ago

fix multiple for about icons for 2 page tools with manual-resim set

File size: 22.1 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
16
17itcl::class Rappture::ResultViewer {
18    inherit itk::Widget
19
20    itk_option define -width width Width 4i
21    itk_option define -height height Height 4i
22    itk_option define -colors colors Colors ""
23    itk_option define -clearcommand clearCommand ClearCommand ""
24    itk_option define -simulatecommand simulateCommand SimulateCommand ""
25
26    constructor {args} { # defined below }
27    destructor { # defined below }
28
29    public method add {index xmlobj path}
30    public method clear {{index ""}}
31    public method value {xmlobj}
32
33    public method plot {option args}
34    public method download {option args}
35
36    protected method _plotAdd {xmlobj {settings ""}}
37    protected method _fixScale {args}
38    protected method _xml2data {xmlobj path}
39    protected method _cleanIndex {index}
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 _xml2data       ;# maps xmlobj => data obj 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 index [_cleanIndex $index]
84    set dobj [_xml2data $xmlobj $path]
85
86    #
87    # If the index doesn't exist, then fill in empty slots and
88    # make it exist.
89    #
90    for {set i [llength $_dataslots]} {$i <= $index} {incr i} {
91        lappend _dataslots ""
92    }
93    set slot [lindex $_dataslots $index]
94    lappend slot $dobj
95    set _dataslots [lreplace $_dataslots $index $index $slot]
96
97    $_dispatcher event -idle !scale
98}
99
100# ----------------------------------------------------------------------
101# USAGE: clear ?<index>|<xmlobj>?
102#
103# Clears one or all results in this result viewer.  If a particular
104# <index> is specified, then all data objects at that index are
105# deleted.  If a particular <xmlobj> is specified, then all data
106# objects related to that <xmlobj> are removed--regardless of whether
107# they reside at one or more indices.
108# ----------------------------------------------------------------------
109itcl::body Rappture::ResultViewer::clear {{index ""}} {
110    if {$index ne ""} {
111        # clear one result
112        if {[catch {_cleanIndex $index} i] == 0} {
113            if {$i >= 0 && $i < [llength $_dataslots]} {
114                set slot [lindex $_dataslots $i]
115                foreach dobj $slot {
116                    itcl::delete object $dobj
117                }
118                set _dataslots [lreplace $_dataslots $i $i ""]
119                $_dispatcher event -idle !scale
120            }
121        } else {
122            foreach key [array names _xml2data $index-*] {
123                set dobj $_xml2data($key)
124
125                # search for and remove all references to this data object
126                for {set n 0} {$n < [llength $_dataslots]} {incr n} {
127                    set slot [lindex $_dataslots $n]
128                    set pos [lsearch -exact $slot $dobj]
129                    if {$pos >= 0} {
130                        set slot [lreplace $slot $pos $pos]
131                        set _dataslots [lreplace $_dataslots $n $n $slot]
132                        $_dispatcher event -idle !scale
133                    }
134                }
135
136                # destroy the object and forget it
137                itcl::delete object $dobj
138                unset _xml2data($key)
139            }
140        }
141    } else {
142        # clear all results
143        plot clear
144        foreach slot $_dataslots {
145            foreach dobj $slot {
146                itcl::delete object $dobj
147            }
148        }
149        set _dataslots ""
150        catch {unset _xml2data}
151    }
152}
153
154# ----------------------------------------------------------------------
155# USAGE: value <xmlobj>
156#
157# Convenience method for showing a single value.  Loads the value
158# into the widget via add/clear, then immediately plots the value.
159# This makes the widget consistent with other widgets, such as
160# the DeviceEditor, etc.
161# ----------------------------------------------------------------------
162itcl::body Rappture::ResultViewer::value {xmlobj} {
163    clear
164    if {"" != $xmlobj} {
165        add 0 $xmlobj ""
166        plot add 0 ""
167    }
168}
169
170# ----------------------------------------------------------------------
171# USAGE: plot add ?<simnum> <settings> <simnum> <settings> ...?
172# USAGE: plot clear
173#
174# Used to manipulate the contents of this viewer.  The "plot clear"
175# command clears the current viewer.  Data is still stored in the
176# widget, but the results are not shown on screen.  The "plot add"
177# command adds the data at the specified <simnum> to the plot.  Each
178# <simnum> is the simulation number, like "#1", "#2", "#3", etc.  If
179# the optional <settings> are specified, then they are applied
180# to the plot; otherwise, default settings are used.
181# ----------------------------------------------------------------------
182itcl::body Rappture::ResultViewer::plot {option args} {
183    switch -- $option {
184        add {
185            set params ""
186            foreach {index opts} $args {
187                if {$index == "params"} {
188                    set params $opts
189                    continue
190                }
191
192                set index [_cleanIndex $index]
193                set reset "-color autoreset"
194                set slot [lindex $_dataslots $index]
195                foreach dobj $slot {
196                    set settings ""
197                    # start with color reset, only for first object in series
198                    if {"" != $reset} {
199                        set settings $reset
200                        set reset ""
201                    }
202                    # add default settings from data object
203                    if {[catch {$dobj hints style} style] == 0} {
204                        eval lappend settings $style
205                    }
206                    if {[catch {$dobj hints type} type] == 0} {
207                        if {"" != $type} {
208                            eval lappend settings "-type $type"
209                        }
210                    }
211                    # add override settings passed in here
212                    eval lappend settings $opts
213                    _plotAdd $dobj $settings
214                }
215            }
216            if {"" != $params && "" != $_mode} {
217                eval $_mode2widget($_mode) parameters $params
218            }
219        }
220        clear {
221            # clear the contents of the current mode
222            if {"" != $_mode} {
223                $_mode2widget($_mode) delete
224            }
225        }
226        default {
227            error "bad option \"$option\": should be add or clear"
228        }
229    }
230}
231
232# ----------------------------------------------------------------------
233# USAGE: _plotAdd <dataobj> <settings>
234#
235# Used internally to add a <dataobj> representing some data to
236# the plot at the top of this widget.  The data is added to the
237# current plot.  Use the "clear" function to clear before adding
238# new data.
239# ----------------------------------------------------------------------
240itcl::body Rappture::ResultViewer::_plotAdd {dataobj {settings ""}} {
241    switch -- [$dataobj info class] {
242        ::Rappture::DataTable {
243            set mode "datatable"
244            if {![info exists _mode2widget($mode)]} {
245                set w $itk_interior.datatable
246                Rappture::DataTableResult $w
247                set _mode2widget($mode) $w
248            }
249        }
250        ::Rappture::Drawing {
251            set mode "vtkviewer"
252            if {![info exists _mode2widget($mode)]} {
253                set servers [Rappture::VisViewer::GetServerList "vtkvis"]
254                set w $itk_interior.vtkviewer
255                Rappture::VtkViewer $w $servers
256                set _mode2widget($mode) $w
257            }
258        }
259        ::Rappture::Histogram {
260            set mode "histogram"
261            if {![info exists _mode2widget($mode)]} {
262                set w $itk_interior.histogram
263                Rappture::HistogramResult $w
264                set _mode2widget($mode) $w
265            }
266        }
267        ::Rappture::Curve {
268            set type [$dataobj hints type]
269            set mode "xy"
270            if { $type == "bars" } {
271                if {![info exists _mode2widget($mode)]} {
272                    set w $itk_interior.xy
273                    Rappture::BarchartResult $w
274                    set _mode2widget($mode) $w
275                }
276            } else {
277                if {![info exists _mode2widget($mode)]} {
278                    set w $itk_interior.xy
279                    Rappture::XyResult $w
280                    set _mode2widget($mode) $w
281                }
282            }
283        }
284        ::Rappture::Field {
285            set dims [lindex [lsort [$dataobj components -dimensions]] end]
286            switch -- $dims {
287                1D {
288                    set mode "xy"
289                    if {![info exists _mode2widget($mode)]} {
290                        set w $itk_interior.xy
291                        Rappture::XyResult $w
292                        set _mode2widget($mode) $w
293                    }
294                }
295                2D {
296                    set mode "contour"
297                    if {![info exists _mode2widget($mode)]} {
298                        global env
299                        if { [$dataobj isunirect2d] } {
300                            if { [$dataobj hints type] == "contour" } {
301                                set resultMode "vtkcontour"
302                            } elseif { [info exists env(VTKHEIGHTMAP)] } {
303                                set resultMode "vtkheightmap"
304                            } else {
305                                set resultMode "heightmap"
306                            }
307                        } elseif { [info exists env(VTKCONTOUR)] } {
308                            set resultMode "vtkcontour"
309                        } else {
310                            set resultMode "vtk"
311                        }
312                        set extents [$dataobj extents]
313                        if { $extents > 1 } {
314                            set resultMode "flowvis"
315                        }
316                        set w $itk_interior.contour
317                        if { ![winfo exists $w] } {
318                            Rappture::Field2DResult $w -mode $resultMode
319                        }
320                        set _mode2widget($mode) $w
321                    }
322                }
323                3D {
324                    set mode "field3D"
325                    if {![info exists _mode2widget($mode)]} {
326                        switch -- [$dataobj type] {
327                            "vtk" {
328                                set fmt "vtk"
329                            }
330                            "points-on-mesh" {
331                                set mesh [$dataobj mesh]
332                                set fmt [expr {("" != $mesh) ? "vtk" : "nanovis"}]
333                                set extents [$dataobj extents]
334                                if { $extents > 1 } {
335                                    set fmt "flowvis"
336                                }
337                            }
338                            "opendx" - "dx" {
339                                set fmt "nanovis"
340                                set extents [$dataobj extents]
341                                if { $extents > 1 } {
342                                    set fmt "flowvis"
343                                }
344                            }
345                            "vtkvolume" {
346                                set fmt "vtkvolume"
347                            }
348                            "vtkstreamlines" {
349                                set fmt "vtkstreamlines"
350                            }
351                        }
352                        set w $itk_interior.field3D
353                        Rappture::Field3DResult $w -mode $fmt
354                        set _mode2widget($mode) $w
355                    }
356                }
357                default {
358                    error "can't handle \"$dims\" dimension field"
359                }
360            }
361        }
362        ::Rappture::Mesh {
363            switch -- [$dataobj dimensions] {
364                2 {
365                    set mode "mesh"
366                    if {![info exists _mode2widget($mode)]} {
367                        set w $itk_interior.mesh
368                        Rappture::MeshResult $w
369                        set _mode2widget($mode) $w
370                    }
371                }
372                default {
373                    error "can't handle [$dataobj dimensions]D field"
374                }
375            }
376        }
377        ::Rappture::Table {
378            set cols [Rappture::EnergyLevels::columns $dataobj]
379            if {"" != $cols} {
380                set mode "energies"
381                if {![info exists _mode2widget($mode)]} {
382                    set w $itk_interior.energies
383                    Rappture::EnergyLevels $w
384                    set _mode2widget($mode) $w
385                }
386            }
387        }
388        ::Rappture::LibraryObj {
389            switch -- [$dataobj element -as type] {
390                string - log {
391                    set mode "log"
392                    if {![info exists _mode2widget($mode)]} {
393                        set w $itk_interior.log
394                        Rappture::TextResult $w
395                        set _mode2widget($mode) $w
396                    }
397                }
398                structure {
399                    set mode "structure"
400                    if {![info exists _mode2widget($mode)]} {
401                        set w $itk_interior.struct
402                        Rappture::DeviceResult $w
403                        set _mode2widget($mode) $w
404                    }
405                }
406                number - integer {
407                    set mode "number"
408                    if {![info exists _mode2widget($mode)]} {
409                        set w $itk_interior.number
410                        Rappture::NumberResult $w
411                        set _mode2widget($mode) $w
412                    }
413                }
414                boolean - choice {
415                    set mode "value"
416                    if {![info exists _mode2widget($mode)]} {
417                        set w $itk_interior.value
418                        Rappture::ValueResult $w
419                        set _mode2widget($mode) $w
420                    }
421                }
422            }
423        }
424        ::Rappture::Image {
425            set mode "image"
426            if {![info exists _mode2widget($mode)]} {
427                set w $itk_interior.image
428                Rappture::ImageResult $w
429                set _mode2widget($mode) $w
430            }
431        }
432        ::Rappture::Sequence {
433            set mode "sequence"
434            if {![info exists _mode2widget($mode)]} {
435                set w $itk_interior.image
436                Rappture::SequenceResult $w
437                set _mode2widget($mode) $w
438            }
439        }
440        default {
441            error "don't know how to plot <$type> data [$dataobj info class]"
442        }
443    }
444
445    if {$mode != $_mode && $_mode != ""} {
446        set nactive [llength [$_mode2widget($_mode) get]]
447        if {$nactive > 0} {
448            return  ;# mixing data that doesn't mix -- ignore it!
449        }
450    }
451
452    # Are we plotting in a new mode? then change widgets
453    if {$_mode2widget($mode) != [pack slaves $itk_interior]} {
454        # remove any current window
455        foreach w [pack slaves $itk_interior] {
456            pack forget $w
457        }
458        pack $_mode2widget($mode) -expand yes -fill both
459
460        set _mode $mode
461        $_dispatcher event -idle !scale
462    }
463    $_mode2widget($mode) add $dataobj $settings
464}
465
466# ----------------------------------------------------------------------
467# USAGE: _fixScale ?<eventArgs>...?
468#
469# Invoked automatically whenever a new dataset is added to fix the
470# overall scales of the viewer.  This makes the visualizer consistent
471# across all <dataobj> in this widget, so that it can plot all
472# available data.
473# ----------------------------------------------------------------------
474itcl::body Rappture::ResultViewer::_fixScale {args} {
475    if {"" != $_mode} {
476        set dlist ""
477        foreach slot $_dataslots {
478            foreach dobj $slot {
479                lappend dlist $dobj
480            }
481        }
482        eval $_mode2widget($_mode) scale $dlist
483    }
484}
485
486# ----------------------------------------------------------------------
487# USAGE: download coming
488# USAGE: download controls <downloadCommand>
489# USAGE: download now
490#
491# Clients use this method to create a downloadable representation
492# of the plot.  Returns a list of the form {ext string}, where
493# "ext" is the file extension (indicating the type of data) and
494# "string" is the data itself.
495# ----------------------------------------------------------------------
496itcl::body Rappture::ResultViewer::download {option args} {
497    if {"" == $_mode} {
498        return ""
499    }
500    return [eval $_mode2widget($_mode) download $option $args]
501}
502
503# ----------------------------------------------------------------------
504# USAGE: _xml2data <xmlobj> <path>
505#
506# Used internally to create a data object for the data at the
507# specified <path> in the <xmlobj>.
508# ----------------------------------------------------------------------
509itcl::body Rappture::ResultViewer::_xml2data {xmlobj path} {
510    if {[info exists _xml2data($xmlobj-$path)]} {
511        return $_xml2data($xmlobj-$path)
512    }
513
514    set type [$xmlobj element -as type $path]
515    switch -- $type {
516        curve {
517            set dobj [Rappture::Curve ::#auto $xmlobj $path]
518        }
519        datatable {
520            set dobj [Rappture::DataTable ::#auto $xmlobj $path]
521        }
522        histogram {
523            set dobj [Rappture::Histogram ::#auto $xmlobj $path]
524        }
525        field {
526            set dobj [Rappture::Field ::#auto $xmlobj $path]
527        }
528        mesh {
529            set dobj [Rappture::Mesh ::#auto $xmlobj $path]
530        }
531        table {
532            set dobj [Rappture::Table ::#auto $xmlobj $path]
533        }
534        image {
535            set dobj [Rappture::Image ::#auto $xmlobj $path]
536        }
537        sequence {
538            set dobj [Rappture::Sequence ::#auto $xmlobj $path]
539        }
540        string - log {
541            set dobj [$xmlobj element -as object $path]
542        }
543        structure {
544            set dobj [$xmlobj element -as object $path]
545        }
546        number - integer - boolean - choice {
547            set dobj [$xmlobj element -as object $path]
548        }
549        drawing3d - drawing {
550            set dobj [Rappture::Drawing ::#auto $xmlobj $path]
551        }
552        time - status {
553            set dobj ""
554        }
555        default {
556            error "don't know how to plot <$type> data path=$path"
557        }
558    }
559
560    # store the mapping xmlobj=>dobj so we can find this result later
561    if {$dobj ne ""} {
562        set _xml2data($xmlobj-$path) $dobj
563    }
564    return $dobj
565}
566
567# ----------------------------------------------------------------------
568# USAGE: _cleanIndex <index>
569#
570# Used internally to create a data object for the data at the
571# specified <path> in the <xmlobj>.
572# ----------------------------------------------------------------------
573itcl::body Rappture::ResultViewer::_cleanIndex {index} {
574    set index [lindex $index 0]
575    if {[regexp {^#([0-9]+)} $index match num]} {
576        return [expr {$num-1}]  ;# start from 0 instead of 1
577    } elseif {[string is integer -strict $index]} {
578        return $index
579    }
580    error "bad plot index \"$index\": should be 0,1,2,... or #1,#2,#3,..."
581}
582
583# ----------------------------------------------------------------------
584# CONFIGURATION OPTION: -width
585# ----------------------------------------------------------------------
586itcl::configbody Rappture::ResultViewer::width {
587    set w [winfo pixels $itk_component(hull) $itk_option(-width)]
588    set h [winfo pixels $itk_component(hull) $itk_option(-height)]
589    if {$w == 0 || $h == 0} {
590        pack propagate $itk_component(hull) yes
591    } else {
592        component hull configure -width $w -height $h
593        pack propagate $itk_component(hull) no
594    }
595}
596
597# ----------------------------------------------------------------------
598# CONFIGURATION OPTION: -height
599# ----------------------------------------------------------------------
600itcl::configbody Rappture::ResultViewer::height {
601    set h [winfo pixels $itk_component(hull) $itk_option(-height)]
602    set w [winfo pixels $itk_component(hull) $itk_option(-width)]
603    if {$w == 0 || $h == 0} {
604        pack propagate $itk_component(hull) yes
605    } else {
606        component hull configure -width $w -height $h
607        pack propagate $itk_component(hull) no
608    }
609}
Note: See TracBrowser for help on using the repository browser.