source: branches/1.2/gui/scripts/resultviewer.tcl @ 3652

Last change on this file since 3652 was 3573, checked in by gah, 11 years ago

improve warning messages

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