source: trunk/gui/scripts/analyzer.tcl @ 16

Last change on this file since 16 was 13, checked in by mmc, 19 years ago

Many improvements, including a new energy level viewer
for Huckel-IV. Added support for a new <boolean> type.
Fixed the cloud/field stuff so that when a cloud is 1D,
it reverts to BLT vectors so it will plot correctly.
Fixed the install script to work better on Windows.

File size: 25.4 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: analyzer - output area for Rappture
3#
4#  This widget acts as the output side of a Rappture application.
5#  When the input has changed, it displays a Simulate button that
6#  launches the simulation.  When a simulation is running, this
7#  area shows status.  When it is finished, the results appear
8#  in place of the button, according to the specs in the <analyze>
9#  XML data.
10# ======================================================================
11#  AUTHOR:  Michael McLennan, Purdue University
12#  Copyright (c) 2004-2005
13#  Purdue Research Foundation, West Lafayette, IN
14# ======================================================================
15package require Itk
16
17option add *Analyzer.width 5i widgetDefault
18option add *Analyzer.height 5i widgetDefault
19option add *Analyzer.simControl "auto" widgetDefault
20option add *Analyzer.simControlBackground "" widgetDefault
21option add *Analyzer.simControlOutline gray widgetDefault
22option add *Analyzer.simControlActiveBackground #ffffcc widgetDefault
23option add *Analyzer.simControlActiveOutline black widgetDefault
24
25option add *Analyzer.font \
26    -*-helvetica-medium-r-normal-*-*-120-* widgetDefault
27option add *Analyzer.textFont \
28    -*-helvetica-medium-r-normal-*-*-120-* widgetDefault
29option add *Analyzer.boldTextFont \
30    -*-helvetica-bold-r-normal-*-*-120-* widgetDefault
31
32itcl::class Rappture::Analyzer {
33    inherit itk::Widget
34
35    itk_option define -textfont textFont Font ""
36    itk_option define -boldtextfont boldTextFont Font ""
37    itk_option define -simcontrol simControl SimControl ""
38    itk_option define -simcontroloutline simControlOutline Background ""
39    itk_option define -simcontrolbackground simControlBackground Background ""
40    itk_option define -simcontrolactiveoutline simControlActiveOutline Background ""
41    itk_option define -simcontrolactivebackground simControlActiveBackground Background ""
42    itk_option define -holdwindow holdWindow HoldWindow ""
43
44    constructor {tool args} { # defined below }
45    destructor { # defined below }
46
47    public method simulate {args}
48    public method reset {}
49    public method load {file}
50    public method clear {}
51
52    protected method _plot {args}
53    protected method _reorder {comps}
54    protected method _autoLabel {xmlobj path title cntVar}
55    protected method _fixResult {}
56    protected method _fixSize {}
57    protected method _fixSimControl {}
58    protected method _simState {state args}
59
60    private variable _tool ""          ;# belongs to this tool
61    private variable _control "manual" ;# start mode
62    private variable _runs ""          ;# list of XML objects with results
63    private variable _pages 0          ;# number of pages for result sets
64    private variable _label2page       ;# maps output label => result set
65    private variable _plotlist ""      ;# items currently being plotted
66
67    private common job                 ;# array var used for blt::bgexec jobs
68}
69                                                                               
70itk::usual Analyzer {
71    keep -background -cursor foreground -font
72}
73
74# ----------------------------------------------------------------------
75# CONSTRUCTOR
76# ----------------------------------------------------------------------
77itcl::body Rappture::Analyzer::constructor {tool args} {
78    set _tool $tool
79
80    itk_option add hull.width hull.height
81    pack propagate $itk_component(hull) no
82
83    frame $itk_interior.simol -borderwidth 1 -relief flat
84    pack $itk_interior.simol -fill x
85
86    frame $itk_interior.simol.simbg -borderwidth 0
87    pack $itk_interior.simol.simbg -expand yes -fill both
88
89    itk_component add simulate {
90        button $itk_interior.simol.simbg.simulate -text "Simulate" \
91            -command [itcl::code $this simulate]
92    }
93    pack $itk_component(simulate) -side left -padx 4 -pady 4
94
95    itk_component add simstatus {
96        text $itk_interior.simol.simbg.simstatus -borderwidth 0 \
97            -highlightthickness 0 -height 1 -width 1 -wrap none \
98            -state disabled
99    } {
100        usual
101        ignore -highlightthickness
102        rename -font -textfont textFont Font
103    }
104    pack $itk_component(simstatus) -side left -expand yes -fill x
105
106    $itk_component(simstatus) tag configure popup \
107        -underline 1 -foreground blue
108
109    $itk_component(simstatus) tag bind popup \
110        <Enter> {%W configure -cursor center_ptr}
111    $itk_component(simstatus) tag bind popup \
112        <Leave> {%W configure -cursor ""}
113    $itk_component(simstatus) tag bind popup \
114        <ButtonPress> {after idle {Rappture::Tooltip::tooltip show %W}}
115
116
117    itk_component add notebook {
118        Rappture::Notebook $itk_interior.nb
119    }
120    pack $itk_interior.nb -expand yes -fill both
121
122    # ------------------------------------------------------------------
123    # ABOUT PAGE
124    # ------------------------------------------------------------------
125    set w [$itk_component(notebook) insert end about]
126
127    Rappture::Scroller $w.info -xscrollmode off -yscrollmode auto
128    pack $w.info -expand yes -fill both -padx 4 -pady 20
129    itk_component add toolinfo {
130        text $w.info.text -width 1 -height 1 -wrap word \
131            -borderwidth 0 -highlightthickness 0
132    } {
133        usual
134        ignore -borderwidth -relief
135        rename -font -textfont textFont Font
136    }
137    $w.info contents $w.info.text
138
139    # ------------------------------------------------------------------
140    # SIMULATION PAGE
141    # ------------------------------------------------------------------
142    set w [$itk_component(notebook) insert end simulate]
143    frame $w.cntls
144    pack $w.cntls -side bottom -fill x -pady 12
145    frame $w.cntls.sep -background black -height 1
146    pack $w.cntls.sep -side top -fill x
147
148    itk_component add abort {
149        button $w.cntls.abort -text "Abort" \
150            -command [itcl::code $_tool abort]
151    }
152    pack $itk_component(abort) -side left -expand yes -padx 4 -pady 4
153
154    Rappture::Scroller $w.info -xscrollmode off -yscrollmode auto
155    pack $w.info -expand yes -fill both -padx 4 -pady 4
156    itk_component add runinfo {
157        text $w.info.text -width 1 -height 1 -wrap word \
158            -borderwidth 0 -highlightthickness 0 \
159            -state disabled
160    } {
161        usual
162        ignore -borderwidth -relief
163        rename -font -textfont textFont Font
164    }
165    $w.info contents $w.info.text
166
167    # ------------------------------------------------------------------
168    # ANALYZE PAGE
169    # ------------------------------------------------------------------
170    set w [$itk_component(notebook) insert end analyze]
171
172    frame $w.top
173    pack $w.top -side top -fill x -pady 8
174    label $w.top.l -text "Result:" -font $itk_option(-font)
175    pack $w.top.l -side left
176
177    itk_component add resultselector {
178        Rappture::Combobox $w.top.sel -width 50 -editable no
179    } {
180        usual
181        rename -font -textfont textFont Font
182    }
183    pack $itk_component(resultselector) -side left -expand yes -fill x
184    bind $itk_component(resultselector) <<Value>> [itcl::code $this _fixResult]
185
186    itk_component add results {
187        Rappture::Panes $w.pane
188    }
189    pack $itk_component(results) -expand yes -fill both
190    set f [$itk_component(results) pane 0]
191
192    itk_component add resultpages {
193        Rappture::Notebook $f.nb
194    }
195    pack $itk_component(resultpages) -expand yes -fill both
196
197    set f [$itk_component(results) insert end -fraction 0.1]
198    itk_component add resultset {
199        Rappture::ResultSet $f.rset \
200            -clearcommand [itcl::code $this clear] \
201            -settingscommand [itcl::code $this _plot] \
202            -promptcommand [itcl::code $this _simState]
203    }
204    pack $itk_component(resultset) -expand yes -fill both
205    bind $itk_component(resultset) <<Control>> [itcl::code $this _fixSize]
206
207    eval itk_initialize $args
208
209    #
210    # Load up tool info on the first page.
211    #
212    $itk_component(toolinfo) tag configure title \
213        -font $itk_option(-boldtextfont)
214
215    set mesg [$tool xml get tool.title]
216    if {"" != $mesg} {
217        $itk_component(toolinfo) insert end $mesg title
218        $itk_component(toolinfo) insert end "\n\n"
219    }
220
221    set mesg [$tool xml get tool.about]
222    if {"" != $mesg} {
223        $itk_component(toolinfo) insert end $mesg
224    }
225    $itk_component(toolinfo) configure -state disabled
226    $itk_component(notebook) current about
227
228    # tool can run on "manual" (default) or "auto"
229    set cntl [$tool xml get tool.control]
230    if {"" != $cntl} {
231        set _control $cntl
232    }
233
234    # reset everything to a clean state
235    reset
236}
237
238# ----------------------------------------------------------------------
239# DESTRUCTOR
240# ----------------------------------------------------------------------
241itcl::body Rappture::Analyzer::destructor {} {
242    foreach obj $_runs {
243        itcl::delete object $obj
244    }
245    after cancel [itcl::code $this simulate]
246}
247
248# ----------------------------------------------------------------------
249# USAGE: simulate ?-ifneeded?
250# USAGE: simulate ?<path1> <value1> <path2> <value2> ...?
251#
252# Kicks off the simulator by executing the tool.command associated
253# with the tool.  If any arguments are specified, they are used to
254# set parameters for the simulation.  While the simulation is running,
255# it shows status.  When the simulation is finished, it switches
256# automatically to "analyze" mode and shows the results.
257# ----------------------------------------------------------------------
258itcl::body Rappture::Analyzer::simulate {args} {
259    if {$args == "-ifneeded"} {
260        # check to see if simulation is really needed
261        $_tool sync
262        if {[$itk_component(resultset) contains [$_tool xml object]]} {
263            # not needed -- show results and return
264            $itk_component(notebook) current analyze
265            return
266        }
267        set args ""
268    }
269
270    # simulation is needed -- go to simulation page
271    $itk_component(notebook) current simulate
272
273    _simState off
274    $itk_component(runinfo) configure -state normal
275    $itk_component(runinfo) delete 1.0 end
276    $itk_component(runinfo) insert end "Running simulation..."
277    $itk_component(runinfo) configure -state disabled
278
279    # if the hold window is set, then put up a busy cursor
280    if {$itk_option(-holdwindow) != ""} {
281        blt::busy hold $itk_option(-holdwindow)
282        raise $itk_component(hull)
283        update
284    }
285
286    # execute the job
287    foreach {status result} [eval $_tool run $args] break
288
289    # if job was aborted, then allow simulation again
290    if {$result == "ABORT"} {
291        _simState on "Aborted"
292    }
293
294    # read back the results from run.xml
295    if {$status == 0 && $result != "ABORT"} {
296        if {[regexp {=RAPPTURE-RUN=>([^\n]+)} $result match file]} {
297            set status [catch {load $file} msg]
298            if {$status != 0} {
299                set result $msg
300            }
301        } else {
302            set status 1
303            set result "Can't find result file in output:\n\n$result"
304        }
305    }
306
307    # back to normal
308    if {$itk_option(-holdwindow) != ""} {
309        blt::busy release $itk_option(-holdwindow)
310    }
311    $itk_component(abort) configure -state disabled
312
313    if {$status != 0} {
314        $itk_component(runinfo) configure -state normal
315        $itk_component(runinfo) delete 1.0 end
316        $itk_component(runinfo) insert end "Problem launching job:\n\n"
317        $itk_component(runinfo) insert end $result
318        $itk_component(runinfo) configure -state disabled
319    } else {
320        $itk_component(notebook) current analyze
321    }
322}
323
324# ----------------------------------------------------------------------
325# USAGE: reset
326#
327# Used to reset the analyzer whenever the input to a simulation has
328# changed.  Sets the mode back to "simulate", so the user has to
329# simulate again to see the output.  If the <start> option is set
330# to "auto", the simulation is invoked immediately.
331# ----------------------------------------------------------------------
332itcl::body Rappture::Analyzer::reset {} {
333    # check to see if simulation is really needed
334    $_tool sync
335    if {![$itk_component(resultset) contains [$_tool xml object]]} {
336        # if control mode is "auto", then simulate right away
337        if {[string match auto* $_control]} {
338            # auto control -- don't need button
339            pack forget $itk_interior.simol
340
341            after cancel [itcl::code $this simulate]
342            after idle [itcl::code $this simulate]
343        } else {
344            _simState on "new input parameters"
345        }
346    } else {
347        _simState off
348    }
349}
350
351# ----------------------------------------------------------------------
352# USAGE: load <file>
353#
354# Loads the data from the given <file> into the appropriate results
355# sets.  If necessary, new results sets are created to store the data.
356# ----------------------------------------------------------------------
357itcl::body Rappture::Analyzer::load {file} {
358    # try to load new results from the given file
359    set xmlobj [Rappture::library $file]
360    lappend _runs $xmlobj
361
362    # go through the analysis and find all result sets
363    set haveresults 0
364    foreach item [_reorder [$xmlobj children output]] {
365        switch -glob -- $item {
366            log* {
367                _autoLabel $xmlobj output.$item "Output Log" counters
368            }
369            curve* - field* {
370                _autoLabel $xmlobj output.$item "Plot" counters
371            }
372            table* {
373                _autoLabel $xmlobj output.$item "Energy Levels" counters
374            }
375        }
376        set label [$xmlobj get output.$item.about.group]
377        if {"" == $label} {
378            set label [$xmlobj get output.$item.about.label]
379        }
380
381        if {"" != $label} {
382            set haveresults 1
383        }
384    }
385
386    # if there are any valid results, add them to the resultset
387    if {$haveresults} {
388        set size [$itk_component(resultset) size]
389        set index [$itk_component(resultset) add $xmlobj]
390
391        # add each result to a result viewer
392        foreach item [_reorder [$xmlobj children output]] {
393            set label [$xmlobj get output.$item.about.group]
394            if {"" == $label} {
395                set label [$xmlobj get output.$item.about.label]
396            }
397
398            if {"" != $label} {
399                if {![info exists _label2page($label)]} {
400                    set name "page[incr _pages]"
401                    set page [$itk_component(resultpages) insert end $name]
402                    set _label2page($label) $page
403                    Rappture::ResultViewer $page.rviewer
404                    pack $page.rviewer -expand yes -fill both -pady 4
405
406                    $itk_component(resultselector) choices insert end \
407                        $name $label
408                }
409
410                # add/replace the latest result into this viewer
411                set page $_label2page($label)
412
413                if {![info exists reset($page)]} {
414                    $page.rviewer clear $index
415                    set reset($page) 1
416                }
417                $page.rviewer add $index $xmlobj output.$item
418            }
419        }
420    }
421
422    # if there is only one result page, take down the selector
423    set w [$itk_component(notebook) page analyze]
424    if {[$itk_component(resultselector) choices size] <= 1} {
425        pack forget $w.top
426    } else {
427        pack $w.top -before $itk_component(results) -side top -fill x
428    }
429
430    # show the first page by default
431    set first [$itk_component(resultselector) choices get -label 0]
432    if {$first != ""} {
433        set page [$itk_component(resultselector) choices get -value 0]
434        $itk_component(resultpages) current $page
435        $itk_component(resultselector) value $first
436    }
437}
438
439# ----------------------------------------------------------------------
440# USAGE: clear
441#
442# Discards all results previously loaded into the analyzer.
443# ----------------------------------------------------------------------
444itcl::body Rappture::Analyzer::clear {} {
445    foreach obj $_runs {
446        itcl::delete object $obj
447    }
448    set _runs ""
449
450    $itk_component(resultset) clear
451    $itk_component(results) fraction end 0.1
452
453    foreach label [array names _label2page] {
454        set page $_label2page($label)
455        $page.rviewer clear
456    }
457    $itk_component(resultselector) value ""
458    $itk_component(resultselector) choices delete 0 end
459    catch {unset _label2page}
460    set _plotlist ""
461
462    #
463    # HACK ALERT!!
464    # The following statement should be in place, but it causes
465    # vtk to dump core.  Leave it out until we can fix the core dump.
466    # In the mean time, we leak memory...
467    #
468    #$itk_component(resultpages) delete -all
469    #set _pages 0
470
471    _simState on
472    _fixSimControl
473    reset
474}
475
476# ----------------------------------------------------------------------
477# USAGE: _plot ?<index> <options> <index> <options>...?
478#
479# Used internally to update the plot shown in the current result
480# viewer whenever the resultset settings have changed.  Causes the
481# desired results to show up on screen.
482# ----------------------------------------------------------------------
483itcl::body Rappture::Analyzer::_plot {args} {
484    set _plotlist $args
485
486    set page [$itk_component(resultselector) value]
487    set page [$itk_component(resultselector) translate $page]
488    set f [$itk_component(resultpages) page $page]
489    $f.rviewer plot clear
490    foreach {index opts} $_plotlist {
491        $f.rviewer plot add $index $opts
492    }
493}
494
495# ----------------------------------------------------------------------
496# USAGE: _reorder <compList>
497#
498# Used internally to change the order of a series of output components
499# found in the <output> section.  Moves the <log> elements to the end
500# and returns the updated list.
501# ----------------------------------------------------------------------
502itcl::body Rappture::Analyzer::_reorder {comps} {
503    set i 0
504    set max [llength $comps]
505    while {$i < $max} {
506        set c [lindex $comps $i]
507        if {[string match log* $c]} {
508            set comps [lreplace $comps $i $i]
509            lappend comps $c
510            incr max -1
511        } else {
512            incr i
513        }
514    }
515    return $comps
516}
517
518# ----------------------------------------------------------------------
519# USAGE: _autoLabel <xmlobj> <path> <title> <cntVar>
520#
521# Used internally to check for an about.label property at the <path>
522# in <xmlobj>.  If this object doesn't have a label, then one is
523# supplied using the given <title>.  The <cntVar> is an array of
524# counters in the calling scopes for titles that have been used
525# in the past.  This is used to create titles like "Plot #2" the
526# second time it is encountered.
527#
528# The <xmlobj> is updated so that the label is inserted directly in
529# the tree.
530# ----------------------------------------------------------------------
531itcl::body Rappture::Analyzer::_autoLabel {xmlobj path title cntVar} {
532    upvar $cntVar counters
533
534    set group [$xmlobj get $path.about.group]
535    set label [$xmlobj get $path.about.label]
536    if {"" == $label} {
537        # no label -- make one up using the title specified
538        if {![info exists counters($group-$title)]} {
539            set counters($group-$title) 1
540            set label $title
541        } else {
542            set label "$title (#[incr counters($group-$title)])"
543        }
544        $xmlobj put $path.about.label $label
545    } else {
546        # handle the case of two identical labels in <output>
547        if {![info exists counters($group-$label)]} {
548            set counters($group-$label) 1
549        } else {
550            set label "$label (#[incr counters($group-$label)])"
551            $xmlobj put $path.about.label $label
552        }
553    }
554    return $label
555}
556
557# ----------------------------------------------------------------------
558# USAGE: _fixResult
559#
560# Used internally to change the result page being displayed whenever
561# the user selects a page from the results combobox.
562# ----------------------------------------------------------------------
563itcl::body Rappture::Analyzer::_fixResult {} {
564    set page [$itk_component(resultselector) value]
565    set page [$itk_component(resultselector) translate $page]
566    if {$page != ""} {
567        $itk_component(resultpages) current $page
568
569        set f [$itk_component(resultpages) page $page]
570        $f.rviewer plot clear
571        eval $f.rviewer plot add $_plotlist
572    }
573}
574
575# ----------------------------------------------------------------------
576# USAGE: _fixSize
577#
578# Used internally to change the size of the result set area whenever
579# a new control appears.  Adjusts the size available for the result
580# set up to some maximum.
581# ----------------------------------------------------------------------
582itcl::body Rappture::Analyzer::_fixSize {} {
583    set f [$itk_component(results) fraction end]
584    if {$f < 0.4} {
585        $itk_component(results) fraction end [expr {$f+0.15}]
586    }
587    _fixSimControl
588}
589
590# ----------------------------------------------------------------------
591# USAGE: _simState <boolean> ?<message>? ?<settings>?
592#
593# Used internally to change the "Simulation" button on or off.
594# If the <boolean> is on, then any <message> and <settings> are
595# displayed as well.  The <message> is a note to the user about
596# what will be simulated, and the <settings> are a list of
597# tool parameter settings of the form {path1 val1 path2 val2 ...}.
598# When these are in place, the next Simulate operation will use
599# these settings.  This helps fill in missing data values.
600# ----------------------------------------------------------------------
601itcl::body Rappture::Analyzer::_simState {state args} {
602    if {$state} {
603        $itk_interior.simol configure \
604            -background $itk_option(-simcontrolactiveoutline)
605        $itk_interior.simol.simbg configure \
606            -background $itk_option(-simcontrolactivebackground)
607        $itk_component(simulate) configure \
608            -highlightbackground $itk_option(-simcontrolactivebackground)
609        $itk_component(simstatus) configure \
610            -background $itk_option(-simcontrolactivebackground)
611
612        $itk_component(abort) configure -state disabled
613        $itk_component(simulate) configure -state normal \
614            -command [itcl::code $this simulate]
615
616        #
617        # If there's a special message, then put it up next to the button.
618        #
619        set mesg [lindex $args 0]
620        if {"" != $mesg} {
621            $itk_component(simstatus) configure -state normal
622            $itk_component(simstatus) delete 1.0 end
623            $itk_component(simstatus) insert end $mesg
624
625            #
626            # If there are any settings, then install them in the
627            # "Simulate" button.  Also, pop them up as a tooltip
628            # for the message.
629            #
630            set settings [lindex $args 1]
631            if {[llength $settings] > 0} {
632                $itk_component(simulate) configure \
633                    -command [eval itcl::code $this simulate $settings]
634
635                set details ""
636                foreach {path val} $settings {
637                    set str [$_tool xml get $path.about.label]
638                    if {"" == $str} {
639                        set str [$_tool xml element -as id $path]
640                    }
641                    append details "$str = $val\n"
642                }
643                set details [string trim $details]
644
645                Rappture::Tooltip::for $itk_component(simstatus) $details
646                $itk_component(simstatus) insert end " "
647                $itk_component(simstatus) insert end "(details...)" popup
648            }
649            $itk_component(simstatus) configure -state disabled
650        }
651    } else {
652        if {"" != $itk_option(-simcontrolbackground)} {
653            set simcbg $itk_option(-simcontrolbackground)
654        } else {
655            set simcbg $itk_option(-background)
656        }
657        $itk_interior.simol configure \
658            -background $itk_option(-simcontroloutline)
659        $itk_interior.simol.simbg configure -background $simcbg
660        $itk_component(simulate) configure -highlightbackground $simcbg
661        $itk_component(simstatus) configure -background $simcbg
662
663        $itk_component(simulate) configure -state disabled
664        $itk_component(abort) configure -state normal
665
666        $itk_component(simstatus) configure -state normal
667        $itk_component(simstatus) delete 1.0 end
668        $itk_component(simstatus) configure -state disabled
669        Rappture::Tooltip::for $itk_component(simstatus) ""
670    }
671}
672
673# ----------------------------------------------------------------------
674# USAGE: _fixSimControl
675#
676# Used internally to add or remove the simulation control at the
677# top of the analysis area.  This is controlled by the -simcontrol
678# option.
679# ----------------------------------------------------------------------
680itcl::body Rappture::Analyzer::_fixSimControl {} {
681    switch -- $itk_option(-simcontrol) {
682        on {
683            pack $itk_interior.simol -fill x -before $itk_interior.nb
684        }
685        off {
686            pack forget $itk_interior.simol
687        }
688        auto {
689            #
690            # If we have two or more radiodials, then there is a
691            # chance of encountering a combination of parameters
692            # with no data, requiring simulation.
693            #
694            if {[$itk_component(resultset) size -controls] >= 2} {
695                pack $itk_interior.simol -fill x -before $itk_interior.nb
696            } else {
697                pack forget $itk_interior.simol
698            }
699        }
700        default {
701            error "bad value \"$itk_option(-simcontrol)\": should be on, off, auto"
702        }
703    }
704}
705
706# ----------------------------------------------------------------------
707# CONFIGURATION OPTION: -simcontrol
708#
709# Controls whether or not the Simulate button is showing.  In some
710# cases, it is not needed.
711# ----------------------------------------------------------------------
712itcl::configbody Rappture::Analyzer::simcontrol {
713    _fixSimControl
714}
Note: See TracBrowser for help on using the repository browser.