source: branches/1.5/gui/scripts/analyzer.tcl @ 6118

Last change on this file since 6118 was 6118, checked in by gah, 9 years ago

only download viewable elements in xyresult plot

File size: 73.9 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: analyzer - output area for Rappture
4#
5#  This widget acts as the output side of a Rappture application.
6#  When the input has changed, it displays a Simulate button that
7#  launches the simulation.  When a simulation is running, this
8#  area shows status.  When it is finished, the results appear
9#  in place of the button, according to the specs in the <analyze>
10#  XML data.
11# ======================================================================
12#  AUTHOR:  Michael McLennan, Purdue University
13#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
14#
15#  See the file "license.terms" for information on usage and
16#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17# ======================================================================
18package require Itk
19
20option add *Analyzer.width 3.5i widgetDefault
21option add *Analyzer.height 4i widgetDefault
22option add *Analyzer.simControl "auto" widgetDefault
23option add *Analyzer.simControlBackground "" widgetDefault
24option add *Analyzer.simControlOutline gray widgetDefault
25option add *Analyzer.simControlActiveBackground #ffffcc widgetDefault
26option add *Analyzer.simControlActiveOutline black widgetDefault
27option add *Analyzer.notebookpage "about" widgetDefault
28
29option add *Analyzer.font \
30    -*-helvetica-medium-r-normal-*-12-* widgetDefault
31option add *Analyzer.codeFont \
32    -*-courier-medium-r-normal-*-12-* widgetDefault
33option add *Analyzer.textFont \
34    -*-helvetica-medium-r-normal-*-12-* widgetDefault
35option add *Analyzer.boldTextFont \
36    -*-helvetica-bold-r-normal-*-12-* widgetDefault
37
38itcl::class Rappture::Analyzer {
39    inherit itk::Widget
40
41    itk_option define -codefont codeFont Font ""
42    itk_option define -textfont textFont Font ""
43    itk_option define -boldtextfont boldTextFont Font ""
44    itk_option define -simcontrol simControl SimControl ""
45    itk_option define -simcontroloutline simControlOutline Background ""
46    itk_option define -simcontrolbackground simControlBackground Background ""
47    itk_option define -simcontrolactiveoutline simControlActiveOutline Background ""
48    itk_option define -simcontrolactivebackground simControlActiveBackground Background ""
49    itk_option define -holdwindow holdWindow HoldWindow ""
50    itk_option define -notebookpage notebookPage NotebookPage ""
51
52    private variable _done 0
53    private variable _tree ""
54    private variable _saved
55    private variable _name ""
56   
57    constructor {tool args} {
58        # defined below
59    }
60    destructor {
61        # defined below
62    }
63
64    public method simulate {args}
65    public method reset {{when -eventually}}
66    public method load {xmlobj}
67    public method reload { fileName }
68    public method clear {{xmlobj "all"}}
69    public method download {option args}
70    public method buildMenu { w url appName }
71
72    protected method _plot {args}
73    protected method _reorder {comps}
74    protected method _autoLabel {xmlobj path title cntVar}
75    protected method _fixResult {}
76    protected method _fixResultSet {args}
77    protected method _fixSize {}
78    protected method _fixSimControl {}
79    protected method _fixNotebook {}
80    protected method _simState {state args}
81    protected method _simOutput {message}
82    protected method _resultTooltip {}
83    protected method _isPdbTrajectory {data}
84    protected method _isLammpsTrajectory {data}
85    protected method _pdbToSequence {xmlobj path id child data}
86    protected method _lammpsToSequence {xmlobj path id child data}
87    protected method _trajToSequence {xmlobj {path ""}}
88    protected method _pop_uq_dialog {win}
89    protected method _setWaitVariable {state}
90    protected method _adjust_level {win}
91
92    private variable _tool ""          ;# belongs to this tool
93    private variable _appName ""       ;# Name of application
94    private variable _control "manual" ;# start mode
95    private variable _resultset ""     ;# ResultSet object with all results
96    private variable _pages 0          ;# number of pages for result sets
97    private variable _label2page       ;# maps output label => result set
98    private variable _label2desc       ;# maps output label => description
99    private variable _label2item       ;# maps output label => output.xxx item
100    private variable _lastlabel ""     ;# label of last example loaded
101    private variable _plotlist ""      ;# items currently being plotted
102    private variable _lastPlot
103    private common job                 ;# array var used for blt::bgexec jobs
104    private variable _uq_active 0      ;# a UQ variables has been used
105    private variable _wait_uq 0
106
107    private method BuildSimulationTable { w }
108    private method Cancel {}
109    private method CheckSimsetDetails {}
110    private method EditSimset {}
111    private method FindSimsetsForApp { appName }
112    private method GetSimset { name }
113    private method LoadSimulations { runfiles }
114    private method Ok {}
115    private method PostMenu { menu }
116    private method ReadSimsetFile { fileName }
117    private method SaveSimulations {}
118    private method SelectSimsetForDeletion {}
119    private method SelectSimsetForLoading {}
120    private method SelectSimsetNameAndSave {}
121    private method SelectSimsetToPublish {}
122    private method WriteSimsetFile { appName fileName }
123}
124
125itk::usual Analyzer {
126    keep -background -cursor foreground -font
127}
128
129# ----------------------------------------------------------------------
130# CONSTRUCTOR
131# ----------------------------------------------------------------------
132itcl::body Rappture::Analyzer::constructor {tool args} {
133    set _tool $tool
134    set _tree [blt::tree create]
135
136    # use this to store all simulation results
137    set _resultset [Rappture::ResultSet ::\#auto]
138    $_resultset notify add $this [itcl::code $this _fixResultSet]
139
140    # widget settings...
141    itk_option add hull.width hull.height
142    pack propagate $itk_component(hull) no
143
144    frame $itk_interior.simol -borderwidth 1 -relief flat
145    pack $itk_interior.simol -fill x
146
147    itk_component add simbg {
148        frame $itk_interior.simol.simbg -borderwidth 0
149    } {
150        usual
151        rename -background -simcontrolcolor simControlColor Color
152    }
153    pack $itk_component(simbg) -expand yes -fill both
154
155    set simtxt [string trim [$tool xml get tool.action.label]]
156    if {"" == $simtxt} {
157        set simtxt "Simulate"
158    }
159    itk_component add simulate {
160        button $itk_component(simbg).simulate -text $simtxt \
161            -command [itcl::code $this simulate]
162    } {
163        usual
164        rename -highlightbackground -simcontrolcolor simControlColor Color
165    }
166    pack $itk_component(simulate) -side left -padx 4 -pady 4
167
168    # BE CAREFUL: Shift focus when we click on "Simulate".
169    #   This shifts focus away from input widgets and causes them to
170    #   finalize and log any pending changes.  A better way would be
171    #   to have the "sync" operation finalize/sync, but this works
172    #   for now.
173    bind $itk_component(simulate) <ButtonPress> {focus %W}
174
175    # if there's a hub url, then add "About" and "Questions" links
176    global rapptureInfo
177    if { $rapptureInfo(appName) == "" } {
178        set _appName [$_tool xml get tool.id]
179    } else {
180        set _appName $rapptureInfo(appName)
181    }
182    set url [Rappture::Tool::resources -huburl]
183
184    itk_component add hubcntls {
185        frame $itk_component(simbg).hubcntls
186    } {
187        usual
188        rename -background -simcontrolcolor simControlColor Color
189    }
190    pack $itk_component(hubcntls) -side right -padx 4
191   
192    itk_component add help {
193        menubutton $itk_component(hubcntls).help \
194            -image [Rappture::icon hamburger_menu] \
195            -highlightthickness 0 \
196            -menu $itk_component(hubcntls).help.menu
197    } {
198        usual
199        ignore -highlightthickness
200        rename -background -simcontrolcolor simControlColor Color
201    }
202    pack $itk_component(help) -side left
203    buildMenu $itk_component(help).menu $url $_appName
204   
205    if 0 {
206        # To be eliminated with menu changes.
207    itk_component add about {
208        button $itk_component(hubcntls).about \
209            -text "About this tool" \
210            -command [list Rappture::filexfer::webpage \
211                          "$url/tools/$_appName"]
212    } {
213        usual
214        ignore -font
215        rename -background -simcontrolcolor simControlColor Color
216        rename -highlightbackground -simcontrolcolor simControlColor Color
217    }
218    pack $itk_component(about) -side top -anchor w
219   
220    itk_component add questions {
221        button $itk_component(hubcntls).questions -text Questions? \
222            -command [list Rappture::filexfer::webpage \
223                          "$url/resources/$_appName/questions"]
224    } {
225        usual
226        ignore -font
227        rename -background -simcontrolcolor simControlColor Color
228        rename -highlightbackground -simcontrolcolor simControlColor Color
229    }
230    pack $itk_component(questions) -side top -anchor w
231    if { $url == "" || $_appName == "" } {
232        $itk_component(questions) configure -state disabled
233        $itk_component(about) configure -state disabled
234    } else {
235        $itk_component(questions) configure -state normal
236        $itk_component(about) configure -state normal
237    }       
238    }
239    itk_component add simstatus {
240        text $itk_component(simbg).simstatus -borderwidth 0 \
241            -highlightthickness 0 -height 1 -width 1 -wrap none \
242            -state disabled
243    } {
244        usual
245        ignore -highlightthickness
246        rename -background -simcontrolcolor simControlColor Color
247        rename -font -textfont textFont Font
248    }
249    pack $itk_component(simstatus) -side left -expand yes -fill x
250
251    $itk_component(simstatus) tag configure popup \
252        -underline 1 -foreground blue
253
254    $itk_component(simstatus) tag bind popup \
255        <Enter> {%W configure -cursor center_ptr}
256    $itk_component(simstatus) tag bind popup \
257        <Leave> {%W configure -cursor ""}
258    $itk_component(simstatus) tag bind popup \
259        <ButtonPress> {after idle {Rappture::Tooltip::tooltip show %W}}
260
261
262    itk_component add notebook {
263        Rappture::Notebook $itk_interior.nb
264    }
265    pack $itk_interior.nb -expand yes -fill both
266
267    # ------------------------------------------------------------------
268    # ABOUT PAGE
269    # ------------------------------------------------------------------
270    set w [$itk_component(notebook) insert end about]
271
272    Rappture::Scroller $w.info -xscrollmode off -yscrollmode auto
273    pack $w.info -expand yes -fill both -padx 4 -pady 20
274    itk_component add toolinfo {
275        text $w.info.text -width 1 -height 1 -wrap word \
276            -borderwidth 0 -highlightthickness 0
277    } {
278        usual
279        ignore -borderwidth -relief
280        rename -font -textfont textFont Font
281    }
282    $w.info contents $w.info.text
283
284    # ------------------------------------------------------------------
285    # SIMULATION PAGE
286    # ------------------------------------------------------------------
287    set w [$itk_component(notebook) insert end simulate]
288    frame $w.cntls
289    pack $w.cntls -side bottom -fill x -pady 12
290    frame $w.cntls.sep -background black -height 1
291    pack $w.cntls.sep -side top -fill x
292
293    itk_component add abort {
294        button $w.cntls.abort -text "Abort" \
295            -command [itcl::code $_tool abort]
296    }
297    pack $itk_component(abort) -side left -expand yes -padx 4 -pady 4
298
299    Rappture::Scroller $w.info -xscrollmode auto -yscrollmode auto
300    pack $w.info -expand yes -fill both -padx 4 -pady 4
301    itk_component add runinfo {
302        text $w.info.text -width 1 -height 1 -wrap none \
303            -borderwidth 0 -highlightthickness 0 \
304            -state disabled
305    } {
306        usual
307        ignore -borderwidth -relief
308        rename -font -codefont codeFont Font
309    }
310    $w.info contents $w.info.text
311
312    itk_component add progress {
313        Rappture::Progress $w.progress
314    }
315
316    # ------------------------------------------------------------------
317    # ANALYZE PAGE
318    # ------------------------------------------------------------------
319    set w [$itk_component(notebook) insert end analyze]
320
321    frame $w.top
322    pack $w.top -side top -fill x -pady 8
323    label $w.top.l -text "Result:" -font $itk_option(-font)
324    pack $w.top.l -side left
325
326    itk_component add viewselector {
327        Rappture::Combobox $w.top.sel -width 10 -editable no
328    } {
329        usual
330        rename -font -textfont textFont Font
331    }
332    pack $itk_component(viewselector) -side left -expand yes -fill x
333    bind $itk_component(viewselector) <<Value>> [itcl::code $this _fixResult]
334    bind $itk_component(viewselector) <Enter> \
335        [itcl::code $this download coming]
336
337    Rappture::Tooltip::for $itk_component(viewselector) \
338        "@[itcl::code $this _resultTooltip]"
339
340    $itk_component(viewselector) choices insert end \
341        --- "---"
342
343    itk_component add download {
344        button $w.top.dl -image [Rappture::icon download] -anchor e \
345            -borderwidth 1 -relief flat -overrelief raised \
346            -command [itcl::code $this download start $w.top.dl]
347    }
348    pack $itk_component(download) -side right -padx {4 0}
349    bind $itk_component(download) <Enter> \
350        [itcl::code $this download coming]
351
352    $itk_component(viewselector) choices insert end \
353        @download [Rappture::filexfer::label download]
354
355    if {[Rappture::filexfer::enabled]} {
356        Rappture::Tooltip::for $itk_component(download) "Downloads the current result to a new web browser window on your desktop.  From there, you can easily print or save results.
357
358NOTE:  Your web browser must allow pop-ups from this site.  If your output does not appear, look for a 'pop-up blocked' message and enable pop-ups."
359    } else {
360        Rappture::Tooltip::for $itk_component(download) "Saves the current result to a file on your desktop."
361    }
362
363    itk_component add results {
364        Rappture::Panes $w.pane \
365            -sashwidth 2 -sashrelief solid -sashpadding {2 0}
366    } {
367        usual
368        ignore -sashwidth -sashrelief -sashpadding
369    }
370    pack $itk_component(results) -expand yes -fill both
371    set f [$itk_component(results) pane 0]
372
373    itk_component add resultpages {
374        Rappture::Notebook $f.nb
375    }
376    pack $itk_component(resultpages) -expand yes -fill both
377    set f [$itk_component(results) insert end -fraction 0.1]
378    itk_component add resultselector {
379        Rappture::ResultSelector $f.rsel -resultset $_resultset \
380            -settingscommand [itcl::code $this _plot]
381    }
382    pack $itk_component(resultselector) -expand yes -fill both
383    bind $itk_component(resultselector) <<Layout>> [itcl::code $this _fixSize]
384    bind $itk_component(results) <Configure> [itcl::code $this _fixSize]
385
386    eval itk_initialize $args
387
388    $itk_component(runinfo) tag configure ERROR -foreground red
389    $itk_component(runinfo) tag configure text -font $itk_option(-textfont)
390
391    #
392    # Load up tool info on the first page.
393    #
394    $itk_component(toolinfo) tag configure title \
395        -font $itk_option(-boldtextfont)
396
397    set mesg [string trim [$tool xml get tool.title]]
398    if {"" != $mesg} {
399        $itk_component(toolinfo) insert end $mesg title
400        $itk_component(toolinfo) insert end "\n\n"
401    }
402
403    set mesg [string trim [$tool xml get tool.about]]
404    if {"" != $mesg} {
405        $itk_component(toolinfo) insert end $mesg
406    }
407    $itk_component(toolinfo) configure -state disabled
408    $itk_component(notebook) current about
409
410    # tool can run on "manual" (default) or "auto"
411    set cntl [string trim [$tool xml get tool.control]]
412    if {"" == $cntl} {
413        set cntl [string trim [$tool xml get tool.control.type]]
414    }
415    if {"" != $cntl} {
416        set _control $cntl
417    }
418
419    # reset everything to a clean state
420    reset
421}
422
423# ----------------------------------------------------------------------
424# DESTRUCTOR
425# ----------------------------------------------------------------------
426itcl::body Rappture::Analyzer::destructor {} {
427    after cancel [itcl::code $this simulate]
428    $_resultset notify remove $this
429    itcl::delete object $_resultset
430}
431
432# ----------------------------------------------------------------------
433# USAGE: simulate ?-ifneeded?
434# USAGE: simulate ?<path1> <value1> <path2> <value2> ...?
435#
436# Kicks off the simulator by executing the tool.command associated
437# with the tool.  If any arguments are specified, they are used to
438# set parameters for the simulation.  While the simulation is running,
439# it shows status.  When the simulation is finished, it switches
440# automatically to "analyze" mode and shows the results.
441# ----------------------------------------------------------------------
442itcl::body Rappture::Analyzer::simulate {args} {
443    #puts "simulate args='$args'"
444
445    set uq [$_tool get_uq -uq_type smolyak -uq_args 2]
446
447    # pop up UQ window
448    if {[$uq num_runs] > 1} {
449        set _uq_active 1
450        set status [$uq run_dialog $itk_component(simulate)]
451        if {$status == 0} {
452            # cancelled
453            return
454        }
455        lappend args -uq_type [$uq type]
456        lappend args -uq_args [$uq args]
457        # Need to put these UQ values into the driver file
458        # so the call to resultset::contains will be correct.
459        set _xml [$_tool xml object]
460        $_xml put uq.type.current [$uq type]
461        $_xml put uq.args.current [$uq args]
462        $_xml put uq.args.about.label "level"
463        $_xml put uq.args.about.description "Polynomial Degree of Smolyak GPC method."
464    }
465    #puts "simulate args=$args"
466
467    if {[lindex $args 0] == "-ifneeded"} {
468        # check to see if simulation is really needed
469        $_tool sync
470        if {[$_resultset contains [$_tool xml object]]
471              && ![string equal $_control "manual-resim"]} {
472            # not needed -- show results and return
473            $itk_component(notebook) current analyze
474            return
475        }
476        set args [lreplace $args 0 0]
477    }
478
479    # simulation is needed -- go to simulation page
480    $itk_component(notebook) current simulate
481
482    # no progress messages yet
483    pack forget $itk_component(progress)
484    lappend args -output [itcl::code $this _simOutput]
485
486    _simState off
487    $itk_component(runinfo) configure -state normal
488    $itk_component(runinfo) delete 1.0 end
489    $itk_component(runinfo) insert end "Running simulation...\n\n" text
490    $itk_component(runinfo) configure -state disabled
491
492    # if the hold window is set, then put up a busy cursor
493    if {$itk_option(-holdwindow) != ""} {
494        blt::busy hold $itk_option(-holdwindow)
495        raise $itk_component(hull)
496    }
497
498    # execute the job
499    #puts "$_tool run $args"
500
501    foreach {status result} [eval $_tool run $args] break
502
503    # if job was aborted, then allow simulation again
504    if {$result == "ABORT"} {
505        _simState on "Aborted"
506    }
507
508    # load results from run.xml into analyzer
509    if {$status == 0 && $result != "ABORT"} {
510        set status [catch {load $result} result]
511    }
512
513    # back to normal
514    if {$itk_option(-holdwindow) != ""} {
515        blt::busy release $itk_option(-holdwindow)
516    }
517    $itk_component(abort) configure -state disabled
518
519    if {$status != 0} {
520        $itk_component(runinfo) configure -state normal
521        # Don't erase program error messages.
522        # $itk_component(runinfo) delete 1.0 end
523        $itk_component(runinfo) insert end "\n\nProblem launching job:\n\n" text
524        _simOutput $result
525        $itk_component(runinfo) configure -state disabled
526        $itk_component(runinfo) see 1.0
527
528        # Try to create a support ticket for this error.
529        # It may be a real problem.
530        if {[Rappture::bugreport::shouldReport for jobs]} {
531            set ::errorInfo "\n\n== RAPPTURE INPUT ==\n[$_tool xml xml]"
532            Rappture::bugreport::register "Problem launching job:\n$result"
533            Rappture::bugreport::attachment [$_tool xml xml]
534            Rappture::bugreport::send
535        }
536    } else {
537        $itk_component(notebook) current analyze
538    }
539
540    # do this last -- after _simOutput above
541    pack forget $itk_component(progress)
542}
543
544
545# ----------------------------------------------------------------------
546# USAGE: reset ?-eventually|-now?
547#
548# Used to reset the analyzer whenever the input to a simulation has
549# changed.  Sets the mode back to "simulate", so the user has to
550# simulate again to see the output.  If the <start> option is set
551# to "auto", the simulation is invoked immediately.
552# ----------------------------------------------------------------------
553itcl::body Rappture::Analyzer::reset {{when -eventually}} {
554    if {$when == "-eventually"} {
555        after cancel [list catch [itcl::code $this reset -now]]
556        after idle [list catch [itcl::code $this reset -now]]
557        return
558    }
559
560    # check to see if simulation is really needed
561    $_tool sync
562    if {![$_resultset contains [$_tool xml object]]
563          || [string equal $_control "manual-resim"]} {
564        # if control mode is "auto", then simulate right away
565        if {[string match auto* $_control]} {
566            # auto control -- don't need button
567            pack forget $itk_interior.simol
568
569            after cancel [itcl::code $this simulate]
570            after idle [itcl::code $this simulate]
571        } else {
572            _simState on "new input parameters"
573        }
574    } else {
575        _simState off
576    }
577}
578
579# ----------------------------------------------------------------------
580# USAGE: load <xmlobj>
581#
582# Loads the data from the given <xmlobj> into the appropriate results
583# sets.  If necessary, new results sets are created to store the data.
584# ----------------------------------------------------------------------
585itcl::body Rappture::Analyzer::load {xmlobj} {
586    # only show the last result? then clear first
587    if {[string trim [$_tool xml get tool.analyzer]] == "last"} {
588        clear
589    }
590    #puts "Analyzer::load"
591    $_resultset add $xmlobj
592
593    # NOTE: Adding will trigger a !change event on the ResultSet
594    # object, which will trigger calls to _fixResultSet to add
595    # the results to display.
596}
597
598# ----------------------------------------------------------------------
599# USAGE: clear ?<xmlobj>?
600#
601# Discards one or more results previously loaded into the analyzer.
602# If an <xmlobj> is specified, then that one result is cleared.
603# Otherwise, all results are cleared.
604# ----------------------------------------------------------------------
605itcl::body Rappture::Analyzer::clear {{xmlobj "all"}} {
606    if {$xmlobj eq "" || $xmlobj eq "all"} {
607        $_resultset clear
608    } else {
609        $_resultset clear $xmlobj
610    }
611
612    # NOTE: Clearing will trigger a !change event on the ResultSet
613    # object, which will trigger calls to _fixResultSet to clean up
614    # the results being displayed.
615}
616
617# ----------------------------------------------------------------------
618# USAGE: download coming
619# USAGE: download controls <downloadCommand>
620# USAGE: download start ?widget?
621# USAGE: download now ?widget?
622#
623# Spools the current result so the user can download it.
624# ----------------------------------------------------------------------
625itcl::body Rappture::Analyzer::download {option args} {
626    set title [$itk_component(viewselector) value]
627    set page [$itk_component(viewselector) translate $title]
628
629    switch -- $option {
630        coming {
631            #
632            # Warn result that a download is coming, in case
633            # it needs to take a screen snap.
634            #
635            if {![regexp {^(|@download|---)$} $page]} {
636                set f [$itk_component(resultpages) page $page]
637                $f.rviewer download coming
638            }
639        }
640        controls {
641            # no controls for this download yet
642            return ""
643        }
644        start {
645            set widget $itk_component(download)
646            if {[llength $args] > 0} {
647                set widget [lindex $args 0]
648                if {[catch {winfo class $widget}]} {
649                    set widget $itk_component(download)
650                }
651            }
652            #
653            # See if this download has any controls.  If so, then
654            # post them now and let the user continue the download
655            # after selecting a file format.
656            #
657            if {$page != ""} {
658                set ext ""
659                set f [$itk_component(resultpages) page $page]
660                set arg [itcl::code $this download now $widget]
661                set popup [$f.rviewer download controls $arg]
662                if {"" != $popup} {
663                    $popup activate $widget below
664                } else {
665                    download now $widget
666                }
667            } else {
668                # this shouldn't happen
669                set file error.html
670                set data "<h1>Not Found</h1>There is no result selected."
671            }
672        }
673        now {
674            set widget $itk_component(download)
675            if {[llength $args] > 0} {
676                set widget [lindex $args 0]
677                if {[catch {winfo class $widget}]} {
678                    set widget $itk_component(download)
679                }
680            }
681            #
682            # Perform the actual download.
683            #
684            if {$page != ""} {
685                set ext ""
686                set f [$itk_component(resultpages) page $page]
687                set item [$itk_component(viewselector) value]
688                set result [$f.rviewer download now $widget $_appName $item]
689                if { $result == "" } {
690                    return;                # User cancelled the download.
691                }
692                foreach {ext data} $result break
693                if {"" == $ext} {
694                    if {"" != $widget} {
695                        Rappture::Tooltip::cue $widget \
696                            "Can't download this result."
697                    }
698                    return
699                }
700                regsub -all {[\ -\/\:-\@\{-\~]} $title {} title
701                set file "$title$ext"
702            } else {
703                # this shouldn't happen
704                set file error.html
705                set data "<h1>Not Found</h1>There is no result selected."
706            }
707
708            Rappture::Logger::log download [$itk_component(viewselector) value]
709            set mesg [Rappture::filexfer::download $data $file]
710            if {[string length $mesg] > 0} {
711                Rappture::Tooltip::cue $widget $mesg
712            }
713        }
714        default {
715            error "bad option \"$option\": should be coming, controls, now, start"
716        }
717    }
718}
719
720# ----------------------------------------------------------------------
721# USAGE: _plot ?<index> <options> <index> <options>...?
722#
723# Used internally to update the plot shown in the current result
724# viewer whenever the resultselector settings have changed.  Causes the
725# desired results to show up on screen.
726# ----------------------------------------------------------------------
727itcl::body Rappture::Analyzer::_plot {args} {
728    #puts "analyzer::_plot"
729    set _plotlist $args
730
731    set page [$itk_component(viewselector) value]
732    set page [$itk_component(viewselector) translate $page]
733    if {"" != $page} {
734        set f [$itk_component(resultpages) page $page]
735        $f.rviewer plot clear
736        foreach {index opts} $_plotlist {
737            $f.rviewer plot add $index $opts
738        }
739    }
740}
741
742# ----------------------------------------------------------------------
743# USAGE: _reorder <compList>
744#
745# Used internally to change the order of a series of output components
746# found in the <output> section.  Moves the <log> elements to the end
747# and returns the updated list.
748# ----------------------------------------------------------------------
749itcl::body Rappture::Analyzer::_reorder {comps} {
750    set i 0
751    set max [llength $comps]
752    while {$i < $max} {
753        set c [lindex $comps $i]
754        if {[string match log* $c]} {
755            set comps [lreplace $comps $i $i]
756            lappend comps $c
757            incr max -1
758        } else {
759            incr i
760        }
761    }
762    return $comps
763}
764
765# ----------------------------------------------------------------------
766# USAGE: _autoLabel <xmlobj> <path> <title> <cntVar>
767#
768# Used internally to check for an about.label property at the <path>
769# in <xmlobj>.  If this object doesn't have a label, then one is
770# supplied using the given <title>.  The <cntVar> is an array of
771# counters in the calling scopes for titles that have been used
772# in the past.  This is used to create titles like "Plot #2" the
773# second time it is encountered.
774#
775# The <xmlobj> is updated so that the label is inserted directly in
776# the tree.
777# ----------------------------------------------------------------------
778itcl::body Rappture::Analyzer::_autoLabel {xmlobj path title cntVar} {
779    upvar $cntVar counters
780
781    set group [$xmlobj get $path.about.group]
782    set label [$xmlobj get $path.about.label]
783    if {"" == $label} {
784        # no label -- make one up using the title specified
785        if {![info exists counters($group-$title)]} {
786            set counters($group-$title) 1
787            set label $title
788        } else {
789            set label "$title (#[incr counters($group-$title)])"
790        }
791        $xmlobj put $path.about.label $label
792    } else {
793        # handle the case of two identical labels in <output>
794        if {![info exists counters($group-$label)]} {
795            set counters($group-$label) 1
796        } else {
797            set label "$label (#[incr counters($group-$label)])"
798            $xmlobj put $path.about.label $label
799        }
800    }
801    return $label
802}
803
804# ----------------------------------------------------------------------
805# USAGE: _fixResult
806#
807# Used internally to change the result page being displayed whenever
808# the user selects a page from the results combobox.
809# ----------------------------------------------------------------------
810itcl::body Rappture::Analyzer::_fixResult {} {
811    set name [$itk_component(viewselector) value]
812    set page ""
813    if {"" != $name} {
814        set page [$itk_component(viewselector) translate $name]
815    }
816    if {$page == "@download"} {
817        # put the combobox back to its last value
818        $itk_component(viewselector) component entry configure -state normal
819        $itk_component(viewselector) component entry delete 0 end
820        $itk_component(viewselector) component entry insert end $_lastlabel
821        $itk_component(viewselector) component entry configure -state disabled
822        # perform the actual download
823        download start $itk_component(download)
824    } elseif {$page == "---"} {
825        # put the combobox back to its last value
826        $itk_component(viewselector) component entry configure -state normal
827        $itk_component(viewselector) component entry delete 0 end
828        $itk_component(viewselector) component entry insert end $_lastlabel
829        $itk_component(viewselector) component entry configure -state disabled
830    } elseif {$page != ""} {
831        set _lastlabel $name
832        $itk_component(resultpages) current $page
833        set f [$itk_component(resultpages) page $page]
834        # We don't want to replot if we're using an existing viewer with the
835        # the same list of objects to plot.  So track the viewer and the list.
836        if { ![info exists _lastPlot($f)] || $_plotlist != $_lastPlot($f) } {
837            set _lastPlot($f) $_plotlist
838            set win [winfo toplevel $itk_component(hull)]
839            blt::busy hold $win
840            #puts "rviewer = $f.rviewer"
841            #puts "_plotlist = $_plotlist"
842            $f.rviewer plot clear
843            eval $f.rviewer plot add $_plotlist
844            blt::busy release $win
845        }
846        Rappture::Logger::log output $_label2item($name)
847        Rappture::Tooltip::for $itk_component(viewselector) \
848            "@[itcl::code $this _resultTooltip]" -log $_label2item($name)
849    }
850}
851
852# ----------------------------------------------------------------------
853# USAGE: _fixResultSet ?<eventData>...?
854#
855# Used internally to react to changes within the ResultSet.  When a
856# result is added, a new result viewer is created for the object.
857# When all results are cleared, the viewers are deleted.
858# ----------------------------------------------------------------------
859itcl::body Rappture::Analyzer::_fixResultSet {args} {
860    #puts "Analyzer::_fixResultSet $args"
861    array set eventData $args
862    switch -- $eventData(op) {
863        add {
864            set xmlobj $eventData(what)
865
866            # Detect molecule elements that contain trajectory data
867            # and convert to sequences.
868            _trajToSequence $xmlobj output
869
870            # Go through the analysis and find all result sets.
871            set haveresults 0
872            foreach item [_reorder [$xmlobj children output]] {
873                if {[$xmlobj get output.$item.about.uqtype] == ""} {
874                    switch -glob -- $item {
875                        log* {
876                            _autoLabel $xmlobj output.$item "Output Log" counters
877                        }
878                        number* {
879                            _autoLabel $xmlobj output.$item "Number" counters
880                        }
881                        integer* {
882                            _autoLabel $xmlobj output.$item "Integer" counters
883                        }
884                        mesh* {
885                            _autoLabel $xmlobj output.$item "Mesh" counters
886                        }
887                        string* {
888                            _autoLabel $xmlobj output.$item "String" counters
889                        }
890                        histogram* - curve* - field* {
891                            _autoLabel $xmlobj output.$item "Plot" counters
892                        }
893                        drawing* {
894                            _autoLabel $xmlobj output.$item "Drawing" counters
895                        }
896                        structure* {
897                            _autoLabel $xmlobj output.$item "Structure" counters
898                        }
899                        table* {
900                            _autoLabel $xmlobj output.$item "Energy Levels" counters
901                        }
902                        sequence* {
903                            _autoLabel $xmlobj output.$item "Sequence" counters
904                        }
905                    }
906                }
907                set label [$xmlobj get output.$item.about.group]
908                if {"" == $label} {
909                    set label [$xmlobj get output.$item.about.label]
910                }
911
912                set hidden [$xmlobj get output.$item.hide]
913                set hidden [expr {"" != $hidden && $hidden}]
914
915                if {"" != $label && !$hidden} {
916                    set haveresults 1
917                }
918            }
919
920            # if there are any valid results, add them to the resultset
921            if {$haveresults} {
922                set index [$_resultset get simnum $xmlobj]
923
924                # add each result to a result viewer
925                foreach item [_reorder [$xmlobj children output]] {
926                    set label [$xmlobj get output.$item.about.group]
927                    if {"" == $label} {
928                        set label [$xmlobj get output.$item.about.label]
929                    }
930                    set hidden [$xmlobj get output.$item.hide]
931                    if {$hidden == ""} {
932                        set hidden 0
933                    }
934                    if {"" != $label && !$hidden} {
935                        set uq_part [$xmlobj get output.$item.about.uqtype]
936
937                        #puts "label=$label uq_part=$uq_part"
938
939                        if {![info exists _label2page($label)]} {
940                            #puts "Adding label: '$label'"
941                            set name "page[incr _pages]"
942                            #puts "Inserting $name into resultpages"
943                            set page [$itk_component(resultpages) \
944                                insert end $name]
945                            set _label2page($label) $page
946                            set _label2item($label) output.$item
947                            set _label2desc($label) \
948                                [$xmlobj get output.$item.about.description]
949                            Rappture::ResultViewer $page.rviewer
950                            pack $page.rviewer -expand yes -fill both -pady 4
951
952                            set end [$itk_component(viewselector) \
953                                choices index -value ---]
954                            if {$end < 0} {
955                                set end "end"
956                            }
957                            $itk_component(viewselector) choices insert $end \
958                                $name $label
959                        }
960
961                        # add/replace the latest result into this viewer
962                        set page $_label2page($label)
963
964                        if {![info exists reset($page)]} {
965                            $page.rviewer clear $index
966                            set reset($page) 1
967                        }
968                        $page.rviewer add $index $xmlobj output.$item $label $uq_part
969                    }
970                }
971            }
972
973            # show the first page by default
974            set max [$itk_component(viewselector) choices size]
975            for {set i 0} {$i < $max} {incr i} {
976                set first [$itk_component(viewselector) choices get -label $i]
977                if {$first != ""} {
978                    set page [$itk_component(viewselector) choices get -value $i]
979                    set char [string index $page 0]
980                    if {$char != "@" && $char != "-"} {
981                        $itk_component(resultpages) current $page
982                        $itk_component(viewselector) value $first
983                        set _lastlabel $first
984                        break
985                    }
986                }
987            }
988        }
989        clear {
990            if {$eventData(what) ne "all"} {
991                # delete this result from all viewers
992                array set params $eventData(what)
993                foreach label [array names _label2page] {
994                    set page $_label2page($label)
995                    $page.rviewer clear $params(simnum)
996                }
997            }
998
999            if {[$_resultset size] == 0} {
1000                # reset the size of the controls area
1001                set ht [winfo height $itk_component(results)]
1002                set cntlht [$itk_component(resultselector) size -controlarea]
1003                set frac [expr {double($cntlht)/$ht}]
1004                $itk_component(results) fraction end $frac
1005
1006                foreach label [array names _label2page] {
1007                    set page $_label2page($label)
1008                    destroy $page.rviewer
1009                }
1010                $itk_component(resultpages) delete -all
1011                set _pages 0
1012
1013                $itk_component(viewselector) value ""
1014                $itk_component(viewselector) choices delete 0 end
1015                catch {unset _label2page}
1016                catch {unset _label2item}
1017                catch {unset _label2desc}
1018                set _plotlist ""
1019
1020                $itk_component(viewselector) choices insert end --- "---"
1021                $itk_component(viewselector) choices insert end \
1022                    @download [Rappture::filexfer::label download]
1023                set _lastlabel ""
1024            }
1025
1026            # fix Simulate button state
1027            reset
1028        }
1029        default {
1030            error "don't know how to handle op \"$eventData(op)\""
1031        }
1032    }
1033}
1034
1035# ----------------------------------------------------------------------
1036# USAGE: _fixSize
1037#
1038# Used internally to change the size of the result set area whenever
1039# a new control appears.  Adjusts the size available for the result
1040# set up to some maximum.
1041# ----------------------------------------------------------------------
1042itcl::body Rappture::Analyzer::_fixSize {} {
1043    set ht [winfo height $itk_component(results)]
1044    if {$ht <= 1} { set ht [winfo reqheight $itk_component(results)] }
1045    set cntlht [$itk_component(resultselector) size -controlarea]
1046    set frac [expr {double($cntlht)/$ht}]
1047
1048    if {$frac < 0.4} {
1049        $itk_component(results) fraction end $frac
1050    }
1051    _fixSimControl
1052}
1053
1054# ----------------------------------------------------------------------
1055# USAGE: _simState <boolean> ?<message>? ?<settings>?
1056#
1057# Used internally to change the "Simulation" button on or off.
1058# If the <boolean> is on, then any <message> and <settings> are
1059# displayed as well.  If the <boolean> is off, then only display
1060# the message. The <message> is a note to the user about
1061# what will be simulated, and the <settings> are a list of
1062# tool parameter settings of the form {path1 val1 path2 val2 ...}.
1063# When these are in place, the next Simulate operation will use
1064# these settings.  This helps fill in missing data values.
1065# ----------------------------------------------------------------------
1066itcl::body Rappture::Analyzer::_simState {state args} {
1067    if {$state} {
1068        $itk_interior.simol configure \
1069            -background $itk_option(-simcontrolactiveoutline)
1070        configure -simcontrolcolor $itk_option(-simcontrolactivebackground)
1071
1072        $itk_component(abort) configure -state disabled
1073        $itk_component(simulate) configure -state normal \
1074            -command [itcl::code $this simulate]
1075
1076        #
1077        # If there's a special message, then put it up next to the button.
1078        #
1079        set mesg [lindex $args 0]
1080        if {"" != $mesg} {
1081            $itk_component(simstatus) configure -state normal
1082            $itk_component(simstatus) delete 1.0 end
1083            $itk_component(simstatus) insert end $mesg
1084
1085            #
1086            # If there are any settings, then install them in the
1087            # "Simulate" button.  Also, pop them up as a tooltip
1088            # for the message.
1089            #
1090            set settings [lindex $args 1]
1091            if {[llength $settings] > 0} {
1092                $itk_component(simulate) configure \
1093                    -command [eval itcl::code $this simulate $settings]
1094
1095                set details ""
1096                foreach {path val} $settings {
1097                    set str [string trim [$_tool xml get $path.about.label]]
1098                    if {"" == $str} {
1099                        set str [$_tool xml element -as id $path]
1100                    }
1101                    append details "$str = $val\n"
1102                }
1103                set details [string trim $details]
1104
1105                Rappture::Tooltip::for $itk_component(simstatus) $details
1106                $itk_component(simstatus) insert end " "
1107                $itk_component(simstatus) insert end "(details...)" popup
1108            }
1109            $itk_component(simstatus) configure -state disabled
1110        }
1111    } else {
1112        if {"" != $itk_option(-simcontrolbackground)} {
1113            set simcbg $itk_option(-simcontrolbackground)
1114        } else {
1115            set simcbg $itk_option(-background)
1116        }
1117        $itk_interior.simol configure \
1118            -background $itk_option(-simcontroloutline)
1119        configure -simcontrolcolor $simcbg
1120
1121        if {$_uq_active == 0} {
1122            $itk_component(simulate) configure -state disabled
1123        }
1124        $itk_component(abort) configure -state normal
1125
1126        $itk_component(simstatus) configure -state normal
1127        $itk_component(simstatus) delete 1.0 end
1128        set mesg [lindex $args 0]
1129        if {"" != $mesg} {
1130            $itk_component(simstatus) insert end $mesg
1131        }
1132        $itk_component(simstatus) configure -state disabled
1133    }
1134}
1135
1136# ----------------------------------------------------------------------
1137# USAGE: _simOutput <message>
1138#
1139# Invoked automatically whenever output comes in while running the
1140# tool.  Extracts any =RAPPTURE-???=> messages from the output and
1141# sends the output to the display.  For example, any
1142# =RAPPTURE-PROGRESS=> message pops up the progress meter and updates
1143# it to show the latest progress message.  This is useful for
1144# long-running tools, to let the user know how much longer the
1145# simulation will take.
1146# ----------------------------------------------------------------------
1147itcl::body Rappture::Analyzer::_simOutput {message} {
1148    #
1149    # Scan through and pick out any =RAPPTURE-PROGRESS=> messages first.
1150    #
1151
1152    while {[regexp -indices \
1153               {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)} $message \
1154                match percent mesg]} {
1155
1156        foreach {i0 i1} $percent break
1157        set percent [string range $message $i0 $i1]
1158
1159        foreach {i0 i1} $mesg break
1160        set mesg [string range $message $i0 $i1]
1161
1162        pack $itk_component(progress) -fill x -padx 10 -pady 10
1163        $itk_component(progress) settings -percent $percent -message $mesg
1164
1165        foreach {i0 i1} $match break
1166        set message [string replace $message $i0 $i1]
1167    }
1168
1169    #
1170    # Now handle SUBMIT-PROGRESS
1171    #
1172    while {[regexp -indices {=SUBMIT-PROGRESS=> aborted=([0-9]+) finished=([0-9]+) failed=([0-9]+) executing=([0-9]+)\
1173        waiting=([0-9]+) setting_up=([0-9]+) setup=([0-9]+) %done=([0-9.]+) timestamp=([0-9.]+)(\n|$)} $message \
1174        match aborted finished failed executing waiting setting_up setup percent ts mesg]} {
1175
1176        set mesg ""
1177        foreach {i0 i1} $percent break
1178        set percent [string range $message $i0 $i1]
1179        foreach {i0 i1} $failed break
1180        set failed [string range $message $i0 $i1]
1181        foreach {i0 i1} $match break
1182        set message [string replace $message $i0 $i1]
1183
1184        if {$failed != 0} {set mesg "$failed jobs failed!"}
1185        if {$percent >= 100} { set mesg "Jobs finished.  Analyzing results..."}
1186
1187        pack $itk_component(progress) -fill x -padx 10 -pady 10
1188        $itk_component(progress) settings -percent $percent -message $mesg
1189    }
1190
1191    #
1192    # Break up the remaining lines according to =RAPPTURE-ERROR=> messages.
1193    # Show errors in a special color.
1194    #
1195    $itk_component(runinfo) configure -state normal
1196
1197    while {[regexp -indices \
1198               {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)} $message \
1199                match type mesg]} {
1200
1201        foreach {i0 i1} $match break
1202        set first [string range $message 0 [expr {$i0-1}]]
1203        if {[string length $first] > 0} {
1204            $itk_component(runinfo) insert end $first
1205            $itk_component(runinfo) insert end \n
1206        }
1207
1208        foreach {t0 t1} $type break
1209        set type [string range $message $t0 $t1]
1210        foreach {m0 m1} $mesg break
1211        set mesg [string range $message $m0 $m1]
1212        if {[string length $mesg] > 0 && $type != "RUN"} {
1213            $itk_component(runinfo) insert end $mesg $type
1214            $itk_component(runinfo) insert end \n $type
1215        }
1216
1217        set message [string range $message [expr {$i1+1}] end]
1218    }
1219
1220    if {[string length $message] > 0} {
1221        $itk_component(runinfo) insert end $message
1222        if {[$itk_component(runinfo) get end-2char] != "\n"} {
1223            $itk_component(runinfo) insert end "\n"
1224        }
1225        $itk_component(runinfo) see end
1226    }
1227    $itk_component(runinfo) configure -state disabled
1228}
1229
1230# ----------------------------------------------------------------------
1231# USAGE: _resultTooltip
1232#
1233# Used internally to build the tooltip string displayed for the
1234# result selector.  If the current page has an associated description,
1235# then it is displayed beneath the result.
1236#
1237# Returns the string for the tooltip.
1238# ----------------------------------------------------------------------
1239itcl::body Rappture::Analyzer::_resultTooltip {} {
1240    set tip ""
1241    set name [$itk_component(viewselector) value]
1242    if {[info exists _label2desc($name)] &&
1243         [string length $_label2desc($name)] > 0} {
1244        append tip "$_label2desc($name)\n\n"
1245    }
1246    if {[array size _label2page] > 1} {
1247        append tip "Use this control to display other output results."
1248    }
1249    return $tip
1250}
1251
1252# ----------------------------------------------------------------------
1253# USAGE: _fixSimControl
1254#
1255# Used internally to add or remove the simulation control at the
1256# top of the analysis area.  This is controlled by the -simcontrol
1257# option.
1258# ----------------------------------------------------------------------
1259itcl::body Rappture::Analyzer::_fixSimControl {} {
1260    switch -- $itk_option(-simcontrol) {
1261        on {
1262            pack $itk_interior.simol -fill x -before $itk_interior.nb
1263        }
1264        off {
1265            pack forget $itk_interior.simol
1266        }
1267        auto {
1268            #
1269            # If we have two or more radiodials, then there is a
1270            # chance of encountering a combination of parameters
1271            # with no data, requiring simulation.
1272            #
1273            if {[$itk_component(resultselector) size -controls] >= 2} {
1274                pack $itk_interior.simol -fill x -before $itk_interior.nb
1275            } else {
1276                pack forget $itk_interior.simol
1277            }
1278        }
1279        default {
1280            error "bad value \"$itk_option(-simcontrol)\": should be on, off, auto"
1281        }
1282    }
1283}
1284
1285# ----------------------------------------------------------------------
1286# USAGE: _fixNotebook
1287#
1288# Used internally to switch the active notebook page
1289# ----------------------------------------------------------------------
1290itcl::body Rappture::Analyzer::_fixNotebook {} {
1291    switch -- $itk_option(-notebookpage) {
1292        about {
1293            $itk_component(notebook) current about
1294        }
1295        simulate {
1296            $itk_component(notebook) current simulate
1297        }
1298        analyze {
1299            $itk_component(notebook) current analyze
1300        }
1301        default {
1302            error "bad value \"$itk_option(-notebookpage)\": should be about, simulate, analyze"
1303        }
1304    }
1305}
1306
1307# ----------------------------------------------------------------------
1308# USAGE: _isPdbTrajectory <data>
1309#
1310# Used internally to determine whether pdb or lammps data represents a
1311# trajectory rather than a single frame
1312# ----------------------------------------------------------------------
1313itcl::body Rappture::Analyzer::_isPdbTrajectory {data} {
1314    if { [llength $data] == 0 } {
1315        return 0
1316    }
1317    set nModels 0
1318    foreach line $data {
1319        if { [string match "MODEL*" $line] }  {
1320            incr nModels
1321            if { $nModels > 1 } {
1322                # Stop if more than one model found.  No need to count them
1323                # all.
1324                return 1
1325            }
1326        }
1327    }
1328    return 0
1329}
1330
1331# ----------------------------------------------------------------------
1332# USAGE: _isLammpsTrajectory <data>
1333#
1334# Used internally to determine whether pdb or lammps data represents a
1335# trajectory rather than a single frame
1336# ----------------------------------------------------------------------
1337itcl::body Rappture::Analyzer::_isLammpsTrajectory { data } {
1338    if { [llength $data] == 0 } {
1339        return 0
1340    }
1341    set nModels 0
1342    foreach line $data {
1343        if { [regexp {^[\t ]*ITEM:[ \t]+TIMESTEP} $line] } {
1344            incr nModels
1345            if { $nModels > 1 } {
1346                # Stop if more than one model found.  No need to count them
1347                # all.
1348                return 1
1349            }
1350        }
1351    }
1352    return 0
1353}
1354
1355# ----------------------------------------------------------------------
1356# USAGE: _pdbToSequence <xmlobj> ?<path>?
1357#
1358# If the molecule element is a trajectory, delete the original
1359# and create a sequence of individual molecules.
1360# Used internally to detect any molecule output elements that contain
1361# trajectory data.  Trajectories will be converted into sequences of
1362# individual molecules.  All other elements will be unaffected. Scans
1363# the entire xml tree if a starting path is not specified.
1364# ----------------------------------------------------------------------
1365itcl::body Rappture::Analyzer::_pdbToSequence {xmlobj path id child data} {
1366
1367    set seqLabel [$xmlobj get ${child}.about.label]
1368    set descr    [$xmlobj get ${child}.about.description]
1369    set formula  [$xmlobj get ${child}.components.molecule.formula]
1370    $xmlobj remove $child
1371
1372    set sequence  $path.sequence($id)
1373    $xmlobj put ${sequence}.about.label $seqLabel
1374    $xmlobj put ${sequence}.about.description $descr
1375    $xmlobj put ${sequence}.index.label "Frame"
1376
1377    set frameNum 0
1378    set numLines [llength $data]
1379    for { set i 0 } { $i < $numLines } { incr i } {
1380        set line [lindex $data $i]
1381        set line [string trim $line]
1382        set contents {}
1383        if { [string match "MODEL*" $line] } {
1384            # Save the contents until we get an ENDMDL record.
1385            for {} { $i < $numLines } { incr i } {
1386                set line [lindex $data $i]
1387                set line [string trim $line]
1388                if { $line == "" } {
1389                    continue;           # Skip blank lines.
1390                }
1391                if { [string match "ENDMDL*" $line] } {
1392                    break;
1393                }
1394                append contents $line\n
1395            }
1396            set frame ${sequence}.element($frameNum)
1397            $xmlobj put ${frame}.index $frameNum
1398
1399            set molecule ${frame}.structure.components.molecule
1400            $xmlobj put ${molecule}.pdb $contents
1401            $xmlobj put ${molecule}.formula $formula
1402            incr frameNum
1403        }
1404    }
1405}
1406
1407# ----------------------------------------------------------------------
1408# USAGE: _lammpsToSequence <xmlobj> ?<path>?
1409#
1410# If the molecule element is a trajectory, delete the original
1411# and create a sequence of individual molecules.
1412# Used internally to detect any molecule output elements that contain
1413# trajectory data.  Trajectories will be converted into sequences of
1414# individual molecules.  All other elements will be unaffected. Scans
1415# the entire xml tree if a starting path is not specified.
1416# ----------------------------------------------------------------------
1417itcl::body Rappture::Analyzer::_lammpsToSequence {xmlobj path id child data} {
1418
1419    set seqLabel [$xmlobj get ${child}.about.label]
1420    set descr    [$xmlobj get ${child}.about.description]
1421    set typemap  [$xmlobj get ${child}.components.molecule.lammpstypemap]
1422    $xmlobj remove $child
1423
1424    set sequence ${path}.sequence($id)
1425    $xmlobj put ${sequence}.about.label $seqLabel
1426    $xmlobj put ${sequence}.about.description $descr
1427    $xmlobj put ${sequence}.index.label "Frame"
1428
1429    set frameNum 0
1430    set frameContents ""
1431    set inModel 0
1432    foreach line $data {
1433        set line [string trim $line]
1434        if { $line == "" } {
1435            continue;                   # Skip blank lines
1436        }
1437        if {[regexp {^[\t ]*ITEM:[ \t]+ATOMS} $line] } {
1438            if { $inModel && $frameContents != "" } {
1439                set frame ${sequence}.element($frameNum)
1440                $xmlobj put ${frame}.index $frameNum
1441
1442                set molecule ${frame}.structure.components.molecule
1443                $xmlobj put ${molecule}.lammps $frameContents
1444                $xmlobj put ${molecule}.lammpstypemap $typemap
1445
1446                incr frameNum
1447                set frameContents ""
1448            }
1449            set inModel 1
1450        } elseif { [scan $line "%d %d %f %f %f" a b c d e] == 5 } {
1451            if { !$inModel } {
1452                puts stderr "found \"$line\" without previous \"ITEM: ATOMS\""
1453                set inModel 1
1454            }
1455            append frameContents $line\n
1456        }
1457    }
1458    if { $frameContents != "" } {
1459        set frame ${sequence}.element($frameNum)
1460        $xmlobj put ${frame}.index $frameNum
1461
1462        set molecule ${frame}.structure.components.molecule
1463        $xmlobj put ${molecule}.lammps $frameContents
1464        $xmlobj put ${molecule}.lammpstypemap $typemap
1465    }
1466}
1467
1468# ----------------------------------------------------------------------
1469# USAGE: _trajToSequence <xmlobj> ?<path>?
1470#
1471#       Check for PDB and LAMMPS trajectories in molecule data and rewrite
1472#       the individual models as a sequence of molecules.  Used internally
1473#       to detect any molecule output elements that contain trajectory data.
1474#       Trajectories will be converted into sequences of individual molecules.
1475#       All other elements will be unaffected. Scans the entire xml tree if a
1476#       starting path is not specified.
1477#
1478# ----------------------------------------------------------------------
1479itcl::body Rappture::Analyzer::_trajToSequence {xmlobj {path ""}} {
1480    # Remove leading dot from path, if present.
1481    if { [string index $path 0] == "." } {
1482        set path [string range $path 1 end]
1483    }
1484    # Otherwise check each child.
1485    foreach child [$xmlobj children $path] {
1486        set current ${path}.${child}
1487        if { [string match "structure*" $child] } {
1488            set isTraj [$xmlobj get ${current}.components.molecule.trajectory]
1489            if { $isTraj == "" || !$isTraj } {
1490                continue;               # Not a trajectory.
1491            }
1492            # Look for trajectory if molecule element found.  Check both pdb
1493            # data and lammps data.
1494            set type [$xmlobj element -as type $current]
1495            set id   [$xmlobj element -as id $current]
1496            set pdbdata    [$xmlobj get ${current}.components.molecule.pdb]
1497            set lammpsdata [$xmlobj get ${current}.components.molecule.lammps]
1498            if { $pdbdata != "" && $lammpsdata != "" } {
1499                puts stderr \
1500                    "found both <pdb> and <lammps> elements: picking pdb"
1501            }
1502            set pdbdata [split $pdbdata \n]
1503            set lammpsdata [split $lammpsdata \n]
1504            if { [_isPdbTrajectory $pdbdata] } {
1505                _pdbToSequence $xmlobj $path $id $current $pdbdata
1506            } elseif { [_isLammpsTrajectory $lammpsdata] } {
1507                _lammpsToSequence $xmlobj $path $id $current $lammpsdata
1508            }
1509            continue
1510        }
1511        if 0 {
1512        # Recurse over all child nodes.
1513        _trajToSequence $xmlobj $current
1514        }
1515    }
1516}
1517
1518# ----------------------------------------------------------------------
1519# CONFIGURATION OPTION: -simcontrol
1520#
1521# Controls whether or not the Simulate button is showing.  In some
1522# cases, it is not needed.
1523# ----------------------------------------------------------------------
1524itcl::configbody Rappture::Analyzer::simcontrol {
1525    _fixSimControl
1526}
1527
1528# ----------------------------------------------------------------------
1529# CONFIGURATION OPTION: -notebookpage
1530#
1531# Controls which page of the analyzer notebook is shown. It is
1532# particularly needed when using rerun, when you don't want to
1533# "simulate -ifneeded" because an actual simulation might be
1534# kicked off due to differences between tool.xml and run.xml
1535# ----------------------------------------------------------------------
1536itcl::configbody Rappture::Analyzer::notebookpage {
1537    _fixNotebook
1538}
1539
1540itcl::body Rappture::Analyzer::PostMenu { m } {
1541    if { [FindSimsetsForApp $_appName] == 0 } {
1542        $m entryconfigure "Load Simulations" -state disable
1543        $m entryconfigure "Publish Simulations" -state disable
1544        $m entryconfigure "Delete Simulations" -state disabled
1545    } else {
1546        $m entryconfigure "Load Simulations" -state normal
1547        $m entryconfigure "Publish Simulations" -state normal
1548        $m entryconfigure "Delete Simulations" -state normal
1549    }
1550    if { [$_resultset size] == 0 } {
1551        $m entryconfigure "Save Simulations" -state disabled
1552    } else {
1553        $m entryconfigure "Save Simulations" -state normal
1554    }
1555}
1556       
1557#
1558# Build Help menu
1559#
1560itcl::body Rappture::Analyzer::buildMenu { m url appName } {
1561    menu $m \
1562        -tearoff 0 \
1563        -postcommand [itcl::code $this PostMenu $m]
1564    if { $appName == "" || $url == "" } {
1565        set state disabled
1566    } else {
1567        set state normal
1568    }
1569    set webcmd Rappture::filexfer::webpage
1570    set group "app-$_appName"
1571    $m add command -label "About this tool" \
1572        -command [list $webcmd "$url/tools/$appName"] \
1573        -state $state
1574    $m add command -label "Questions?" \
1575        -command [list $webcmd "$url/resources/$_appName/questions"] \
1576        -state $state
1577    $m add command -label "Tickets" -state $state \
1578        -command [list $webcmd "$url/feedback/report_problems?group=$group"]
1579    $m add command -label "Wish List" -state disabled
1580    $m add separator
1581    $m add command -label "Save Simulations" \
1582        -command [itcl::code $this SelectSimsetNameAndSave] \
1583        -state $state
1584    $m add command -label "Load Simulations" \
1585        -command [itcl::code $this SelectSimsetForLoading] \
1586        -state $state
1587    $m add command -label "Delete Simulations" \
1588        -command [itcl::code $this SelectSimsetForDeletion] \
1589        -state $state
1590    $m add command -label "Publish Simulations" \
1591        -command [itcl::code $this SelectSimsetToPublish] \
1592        -state $state
1593    return $m
1594}
1595
1596itcl::body Rappture::Analyzer::BuildSimulationTable { top } {
1597    # All weirdness is due to the Resultset object.
1598
1599    # First create nodes for each simulation.  The node label is the
1600    # simulation number (such as #1, #2, etc). 
1601    eval $_tree delete 0
1602    set labels "selected simnum"
1603    foreach {name index} [$_resultset diff values simnum] {
1604        set node [$_tree insert 0 -label $name]
1605        $_tree set $node "simnum" $name
1606        set index2name($index) $name
1607       
1608    }
1609    # Next fill in the xmlobj associated with each simulation.  We assume
1610    # that the order is the the same as the simulation number.
1611    set index 0
1612    foreach {xmlobj dummy} [$_resultset diff values xmlobj]  {
1613        set node [$_tree findchild 0 $index2name($index)]
1614        set runfile [$xmlobj get output.filename]
1615        set version [$xmlobj get output.version]
1616        $_tree set $node \
1617            "selected" 1 "xmlobj" $xmlobj "runfile" $runfile "version" $version
1618        incr index
1619    }
1620    # Finally for each different input, for each simulation add the
1621    # label and value for the specific input.
1622    foreach name [$_resultset diff names] {
1623        # Ignore non-input names.
1624        if { $name == "xmlobj" || $name == "simnum" } {
1625            continue
1626        }
1627        foreach node [$_tree children 0] {
1628            set xmlobj [$_tree get $node xmlobj]
1629            set label [$xmlobj get $name.about.label]
1630            set value [$xmlobj get $name.current]
1631            if { [$_tree exists $node $label] } {
1632                puts stderr "This can't be: $label already exists in $node"
1633            }
1634            if { [lsearch $labels $label] < 0 } {
1635                lappend labels $label
1636            }
1637            $_tree set $node $label $value
1638        }
1639    }
1640    frame $top
1641    set tv $top.tv
1642    blt::treeview $top.tv -tree $_tree \
1643        -height 1i \
1644        -xscrollcommand [list $top.xs set] \
1645        -yscrollcommand [list $top.ys set] \
1646        -font "Arial 10"
1647    scrollbar $top.xs -orient horizontal -command "$tv xview"
1648    scrollbar $top.ys -orient vertical -command "$tv yview"
1649       
1650    eval $tv column insert end $labels
1651    $tv style checkbox checkbox -showvalue no
1652    $tv column configure simnum -title "Simulation"
1653    $tv column configure selected -title "Save" -style checkbox \
1654        -width .5i -edit yes
1655    $tv column configure treeView -hide yes
1656    blt::table $top \
1657        0,0 $top.tv -fill both \
1658        0,1 $top.ys -fill y \
1659        1,0 $top.xs -fill x
1660    blt::table configure $top r* c* -resize none
1661    blt::table configure $top r0 c0 -resize both
1662}
1663#
1664# Get the results and display them in a table.
1665#
1666# Use the table to omit specific results
1667# Use file chooser to save to specific
1668itcl::body Rappture::Analyzer::SelectSimsetNameAndSave {} {
1669    if { [EditSimset] } {
1670        set appDir [file normalize ~/data/saved/$_appName]
1671        file mkdir $appDir
1672        set fileName [tk_getSaveFile \
1673                          -initialdir $appDir \
1674                          -filetypes {{ "Simulations Save File" ".sav" }} \
1675                          -title "Save Simulations To File"]
1676        if { $fileName != "" } {
1677            WriteSimsetFile $_appName $fileName
1678        }
1679    }
1680}
1681
1682#
1683# Get the results and display them in a table.
1684#
1685# Use the table to omit specific results
1686# Use file chooser to save to specific
1687itcl::body Rappture::Analyzer::SelectSimsetToPublish {} {
1688    if { [GetSimset "publish"] } {
1689        global env
1690        set shareDir [file join /data/tools/shared $_appName $env(USER)]
1691        if { [catch {
1692            file mkdir $shareDir
1693        } errs] != 0 } {
1694            puts stderr errs=$errs
1695            return
1696        }
1697        set fileName [tk_getSaveFile \
1698                          -initialdir $shareDir \
1699                          -filetypes {{ "Simulations Set File" ".sav" }} \
1700                          -title "Save Simulations To File"]
1701        if { $fileName != "" } {
1702            WriteSimsetFile $_appName $fileName
1703        }
1704    }
1705}
1706
1707#
1708# Checks editor widgets to see if the name and description have been
1709# set.  If this is so it enables the save button in the editor.
1710#
1711itcl::body Rappture::Analyzer::CheckSimsetDetails {} {
1712    set popup .savesimset
1713    if { ![winfo exists $popup] } {
1714        return
1715    }
1716    set inner [$popup component inner]
1717    set name [string trim [$inner.name get]]
1718    set desc [string trim [$inner.desc get 0.0 end]]
1719    $inner.controls.save configure -state disabled
1720    if { $name == "" || $desc == "" } {
1721        return
1722    }
1723    $inner.controls.save configure -state normal
1724}
1725
1726#
1727# Gathers all the information from the simset editor into the _saved array
1728#
1729itcl::body Rappture::Analyzer::SaveSimulations {} {
1730    set popup .savesimset
1731    if { ![winfo exists $popup] } {
1732        return
1733    }
1734    set inner [$popup component inner]
1735    array unset _saved
1736    set name [string trim [$inner.name get]]
1737    set desc [string trim [$inner.desc get 0.0 end]]
1738    set _saved(Application) $_appName
1739    set _saved(Date) [clock format [clock seconds]]
1740    set _saved(Name) $name
1741    set _saved(Description) $desc
1742    set files {}
1743    foreach node [$_tree children root] {
1744        set fileName [$_tree get $node runfile]
1745        lappend files $fileName
1746    }
1747    set _saved(Files) $files
1748    set _done 1
1749}
1750
1751itcl::body Rappture::Analyzer::Cancel {} {
1752    set _done 0
1753}
1754
1755itcl::body Rappture::Analyzer::Ok {} {
1756    set popup .selectsimset
1757    if { ![winfo exists $popup] } {
1758        return
1759    }
1760    set inner [$popup component inner]
1761    set tv $inner.tv
1762    if { ![$tv selection present] } {
1763        return
1764    }
1765    set node [$tv curselection]
1766    array unset _saved
1767    array set _saved [$_tree get $node]
1768    set root [file root $_saved(FileName)]
1769    set files {}
1770    foreach file $_saved(Files) {
1771        if { [file pathtype $file] == "relative" } {
1772            set file [file join $root $file]
1773        }
1774        lappend files $file
1775    }
1776    set _saved(Files) $files
1777    set _done 1
1778}
1779
1780#
1781# WriteSimsetFile --
1782#
1783#       Write the .sav file and the runfiles in the ~/data/saved/$_appName
1784#       directory.  The subdirectory where the runfiles are written is
1785#       the root of the .save file name.
1786#
1787itcl::body Rappture::Analyzer::WriteSimsetFile { appName fileName } {
1788    # The runfiles directory is the root of the filename.
1789    # For example, if the simset file is /path/to/myName.sav,
1790    # the runfile directory is /path/to/myName
1791    set root [file root $fileName]
1792    if { [file exists $root] } {
1793        file delete -force $root
1794    }
1795    if { [catch {
1796        file mkdir $root
1797        set f [open $fileName "w"]
1798        puts $f [list "Name" $_saved(Name)]
1799        puts $f [list "Description" $_saved(Description)]
1800        puts $f [list "Date" $_saved(Date)]
1801        global env
1802        puts $f [list "Creator" $env(USER)]
1803        puts $f [list "Application" $_saved(Application)]
1804        set runfiles ""
1805        foreach file $_saved(Files) {
1806            set tail [file tail $file]
1807            set dest [file join $root $tail]
1808            file copy -force $file $dest
1809            lappend runfiles $tail
1810        }
1811        puts $f [list "Files" $runfiles]
1812        close $f
1813    } errs] != 0 } {
1814        puts stderr errs=$errs
1815    }
1816}
1817
1818itcl::body Rappture::Analyzer::EditSimset {} {
1819    set popup .savesimset
1820    if { [winfo exists $popup] } {
1821        destroy $popup
1822    }
1823    # Create a popup for the print dialog
1824    Rappture::Balloon $popup -title "Save set of simulations..."
1825    set inner [$popup component inner]
1826   
1827    label $inner.name_l -text "Simulation Set Name"
1828    entry $inner.name -background white
1829    label $inner.desc_l -text "Simulation Set Description" -height 5
1830    text $inner.desc  -background white
1831    label $inner.select_l -text "Selected Simulations"
1832    BuildSimulationTable $inner.select
1833    frame $inner.controls
1834    bind $inner.desc <KeyPress> [itcl::code $this CheckSimsetDetails]
1835    bind $inner.name <KeyPress> [itcl::code $this CheckSimsetDetails]
1836    bind $inner.desc <Motion> [itcl::code $this CheckSimsetDetails]
1837    bind $inner.name <Motion> [itcl::code $this CheckSimsetDetails]
1838    button $inner.controls.cancel -text "Cancel" \
1839        -command [itcl::code $this Cancel]
1840    button $inner.controls.save -text "Save" \
1841        -command [itcl::code $this SaveSimulations]
1842    blt::table $inner.controls \
1843        0,0 $inner.controls.cancel \
1844        0,1 $inner.controls.save
1845   
1846    blt::table $inner \
1847        0,0 $inner.name_l -anchor ne \
1848        0,1 $inner.name -fill x \
1849        1,0 $inner.desc_l -anchor ne \
1850        1,1 $inner.desc -fill both \
1851        2,0 $inner.select_l -anchor ne \
1852        2,1 $inner.select -fill both \
1853        3,1 $inner.controls  -fill x
1854    blt::table configure $inner r1 -height 1i
1855    blt::table configure $inner r2 -pad 20
1856    $popup configure \
1857        -deactivatecommand [itcl::code $this Cancel]
1858
1859    set inner [$popup component inner]
1860    if {[$_resultset size] > 1} {
1861        blt::table $inner \
1862            2,0 $inner.select_l -anchor ne \
1863            2,1 $inner.select -fill both
1864    } else {
1865        blt::table forget $inner.select $inner.select_l
1866    }
1867    CheckSimsetDetails
1868    update
1869    # Activate the popup and call for the output.
1870    $popup activate $itk_component(help) below
1871    Cancel
1872    tkwait variable [itcl::scope _done]
1873    set doSave $_done
1874    $popup deactivate
1875    return $doSave
1876}
1877
1878
1879itcl::body Rappture::Analyzer::ReadSimsetFile { fileName } {
1880    if { [catch {
1881        set f [open $fileName "r"]
1882        set contents [read $f]
1883        close $f
1884        array set _saved $contents
1885    } errs] != 0 } {
1886        return 0
1887    }
1888    if { ![info exists _saved(Files)] } {
1889        return 0
1890    }
1891    set root [file root $fileName]
1892    foreach file $_saved(Files) {
1893        if { [file pathtype $file] == "relative" } {
1894            set file [file join $root $file]
1895        }
1896        if { ![file readable $file] } {
1897            puts stderr "runfile $file isn't readable"
1898            return 0
1899        }
1900    }
1901    set _saved(FileName) $fileName
1902    return 1
1903}
1904
1905#
1906# Run files can be either explicitly named with their absolute or
1907# relative path.  If the path is relative it is assumed the parent
1908# directory is relative to the simset file.
1909#
1910#   /my/path/to/myfile.sav
1911#   /my/path/to/myfile/runfiles...
1912#
1913itcl::body Rappture::Analyzer::LoadSimulations { files } {
1914    set loadobjs {}
1915    set root [file root $_saved(FileName)]
1916    foreach runfile $files {
1917        if { [file pathtype $runfile] == "relative" } {
1918            set runfile [file join $root $runfile]
1919        }
1920        if { ![file exists $runfile] } {
1921            puts stderr "can't find run: \"$runfile\""
1922            continue
1923        }
1924        set status [catch {Rappture::library $runfile} result]
1925        lappend loadobjs $result
1926    }
1927    clear
1928    foreach runobj $loadobjs {
1929        load $runobj
1930    }
1931    configure -notebookpage analyze
1932    global win
1933    $win.pager current analyzer
1934   if 0 {
1935    # Load the inputs for the very last run
1936    global tool
1937    $tool load $runobj
1938}
1939}
1940
1941#
1942# Delete selected simulation set.
1943#
1944itcl::body Rappture::Analyzer::SelectSimsetForDeletion {} {
1945    if { [FindSimsetsForApp $_appName] == 0 } {
1946        return
1947    }
1948    if { [GetSimset "delete"] } {
1949        set root [file root $_saved(FileName)]
1950        file delete -force $root
1951        file delete -force $_saved(FileName)
1952    }
1953}
1954
1955#
1956# Find all .sav files for an application and load them into the tree.
1957#
1958itcl::body Rappture::Analyzer::FindSimsetsForApp { appName } {
1959    $_tree delete 0
1960    foreach fileName [glob -nocomplain ~/data/saved/$appName/*.sav] {
1961        if { [ReadSimsetFile $fileName] } {
1962            $_tree insert 0 -label $_saved(Name) -data [array get _saved]
1963        }
1964    }
1965    return [$_tree degree 0]
1966}
1967
1968#
1969# Create dialog to select a simset from the currently available simsets.
1970#
1971itcl::body Rappture::Analyzer::GetSimset { name } {
1972    set popup .selectsimset
1973    if { [winfo exists $popup] } {
1974        destroy $popup
1975    }
1976    # Create a popup for the print dialog
1977    set title [string tolower $name]
1978    Rappture::Balloon $popup -title "Select set of simulations to $title..."
1979    set inner [$popup component inner]
1980   
1981    set tv $inner.tv
1982    blt::treeview $inner.tv -tree $_tree \
1983        -height 1i \
1984        -width 5i \
1985        -xscrollcommand [list $inner.xs set] \
1986        -yscrollcommand [list $inner.ys set] \
1987        -font "Arial 10"
1988    scrollbar $inner.xs -orient horizontal -command "$tv xview"
1989    scrollbar $inner.ys -orient vertical -command "$tv yview"
1990    frame $inner.controls
1991       
1992    $tv column insert end "Name" "Description" "Date" "Creator"
1993    $tv column configure treeView -hide yes
1994    blt::table $inner \
1995        0,0 $inner.tv -fill both \
1996        0,1 $inner.ys -fill y \
1997        1,0 $inner.xs -fill x
1998    blt::table configure $inner r* c* -resize none
1999    blt::table configure $inner r0 c0 -resize both
2000
2001    button $inner.controls.cancel -text "Cancel" \
2002        -command [itcl::code $this Cancel]
2003    set title [string totitle $name]
2004    button $inner.controls.save -text $title \
2005        -command [itcl::code $this Ok]
2006    blt::table $inner.controls \
2007        0,0 $inner.controls.cancel \
2008        0,1 $inner.controls.save
2009   
2010    blt::table $inner \
2011        0,0 $inner.tv -fill both \
2012        0,1 $inner.ys -fill y \
2013        1,0 $inner.xs -fill x \
2014        3,0 $inner.controls  -fill x -cspan 2
2015    blt::table configure $inner r* c* -resize none
2016    blt::table configure $inner r0 c0 -resize both
2017    blt::table configure $inner r2 -height 0.1i
2018   
2019    $popup configure \
2020        -deactivatecommand [itcl::code $this Cancel]
2021
2022    $tv selection set [$_tree firstchild 0]
2023    set inner [$popup component inner]
2024    update
2025    # Activate the popup and call for the output.
2026    $popup activate $itk_component(help) below
2027    Cancel
2028    tkwait variable [itcl::scope _done]
2029    set result $_done
2030    $popup deactivate
2031    return $result
2032}
2033
2034itcl::body Rappture::Analyzer::SelectSimsetForLoading {} {
2035    if { [FindSimsetsForApp $_appName] == 0 } {
2036        return
2037    }
2038    if { [GetSimset "load"] } {
2039        LoadSimulations $_saved(Files)
2040    }
2041}
2042
2043itcl::body Rappture::Analyzer::reload { fileName } {
2044    if { [catch {
2045        ReadSimsetFile $fileName
2046        if { [llength $_saved(Files)] > 0 } {
2047            #StartSplashScreen
2048            LoadSimulations $_saved(Files)
2049            #HideSplashScreen
2050        }
2051    } errs] != 0 } {
2052        puts stderr "can't load \"$fileName\": errs=$errs"
2053    }
2054}
2055
Note: See TracBrowser for help on using the repository browser.