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

Last change on this file since 6372 was 6372, checked in by dkearney, 8 years ago

adding multichoice widget from the multichoice branch

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