source: branches/blt4/gui/scripts/analyzer.tcl @ 1923

Last change on this file since 1923 was 1923, checked in by gah, 14 years ago
File size: 50.7 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  Purdue Research Foundation
13#
14#  See the file "license.terms" for information on usage and
15#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16# ======================================================================
17package require Itk
18
19option add *Analyzer.width 3.5i widgetDefault
20option add *Analyzer.height 4i widgetDefault
21option add *Analyzer.simControl "auto" widgetDefault
22option add *Analyzer.simControlBackground "" widgetDefault
23option add *Analyzer.simControlOutline gray widgetDefault
24option add *Analyzer.simControlActiveBackground #ffffcc widgetDefault
25option add *Analyzer.simControlActiveOutline black widgetDefault
26option add *Analyzer.notebookpage "about" widgetDefault
27
28option add *Analyzer.font \
29    -*-helvetica-medium-r-normal-*-12-* widgetDefault
30option add *Analyzer.codeFont \
31    -*-courier-medium-r-normal-*-12-* widgetDefault
32option add *Analyzer.textFont \
33    -*-helvetica-medium-r-normal-*-12-* widgetDefault
34option add *Analyzer.boldTextFont \
35    -*-helvetica-bold-r-normal-*-12-* widgetDefault
36
37itk::usual Scrollset {
38}
39
40itcl::class Rappture::Analyzer {
41    inherit itk::Widget
42
43    itk_option define -codefont codeFont Font ""
44    itk_option define -textfont textFont Font ""
45    itk_option define -boldtextfont boldTextFont Font ""
46    itk_option define -simcontrol simControl SimControl ""
47    itk_option define -simcontroloutline simControlOutline Background ""
48    itk_option define -simcontrolbackground simControlBackground Background ""
49    itk_option define -simcontrolactiveoutline simControlActiveOutline Background ""
50    itk_option define -simcontrolactivebackground simControlActiveBackground Background ""
51    itk_option define -holdwindow holdWindow HoldWindow ""
52    itk_option define -notebookpage notebookPage NotebookPage ""
53
54    constructor {tool args} { # defined below }
55    destructor { # defined below }
56
57    public method simulate {args}
58    public method reset {{when -eventually}}
59    public method load {xmlobj}
60    public method clear {}
61    public method download {option args}
62
63    protected method _plot {args}
64    protected method _reorder {comps}
65    protected method _autoLabel {xmlobj path title cntVar}
66    protected method _fixResult {}
67    protected method _fixSize {}
68    protected method _fixSimControl {}
69    protected method _fixNotebook {}
70    protected method _simState {state args}
71    protected method _simOutput {message}
72    protected method _resultTooltip {}
73    protected method _isPdbTrajectory {data}
74    protected method _isLammpsTrajectory {data}
75    protected method _pdbToSequence {xmlobj path id child data}
76    protected method _lammpsToSequence {xmlobj path id child data}
77    protected method _trajToSequence {xmlobj {path ""}}
78
79    private variable _tool ""          ;# belongs to this tool
80    private variable _appName ""       ;# Name of application
81    private variable _control "manual" ;# start mode
82    private variable _runs ""          ;# list of XML objects with results
83    private variable _pages 0          ;# number of pages for result sets
84    private variable _label2page       ;# maps output label => result set
85    private variable _label2desc       ;# maps output label => description
86    private variable _lastlabel ""     ;# label of last example loaded
87    private variable _plotlist ""      ;# items currently being plotted
88
89    private common job                 ;# array var used for blt::bgexec jobs
90}
91
92itk::usual Scrollset {
93}
94itk::usual Analyzer {
95    keep -background -cursor foreground -font
96}
97
98# ----------------------------------------------------------------------
99# CONSTRUCTOR
100# ----------------------------------------------------------------------
101itcl::body Rappture::Analyzer::constructor {tool args} {
102    set _tool $tool
103
104    itk_option add hull.width hull.height
105    pack propagate $itk_component(hull) no
106
107    frame $itk_interior.simol -borderwidth 1 -relief flat
108    pack $itk_interior.simol -fill x
109
110    itk_component add simbg {
111        frame $itk_interior.simol.simbg -borderwidth 0
112    } {
113        usual
114        rename -background -simcontrolcolor simControlColor Color
115    }
116    pack $itk_component(simbg) -expand yes -fill both
117
118    set simtxt [$tool xml get tool.action.label]
119    if {"" == $simtxt} {
120        set simtxt "Simulate"
121    }
122    itk_component add simulate {
123        button $itk_component(simbg).simulate -text $simtxt \
124            -command [itcl::code $this simulate]
125    } {
126        usual
127        rename -highlightbackground -simcontrolcolor simControlColor Color
128    }
129    pack $itk_component(simulate) -side left -padx 4 -pady 4
130
131    # if there's a hub url, then add "About" and "Questions" links
132    set _appName [$_tool xml get tool.id]
133    set url [Rappture::Tool::resources -huburl]
134    if {"" != $url && "" != $_appName} {
135        itk_component add hubcntls {
136            frame $itk_component(simbg).hubcntls
137        } {
138            usual
139            rename -background -simcontrolcolor simControlColor Color
140        }
141        pack $itk_component(hubcntls) -side right -padx 4
142
143        itk_component add icon {
144            label $itk_component(hubcntls).icon -image [Rappture::icon ask] \
145                -highlightthickness 0
146        } {
147            usual
148            ignore -highlightthickness
149            rename -background -simcontrolcolor simControlColor Color
150        }
151        pack $itk_component(icon) -side left
152
153        itk_component add about {
154            button $itk_component(hubcntls).about -text "About this tool" \
155                -command [list Rappture::filexfer::webpage \
156                              "$url/tools/$_appName"]
157        } {
158            usual
159            ignore -font
160            rename -background -simcontrolcolor simControlColor Color
161            rename -highlightbackground -simcontrolcolor simControlColor Color
162        }
163        pack $itk_component(about) -side top -anchor w
164
165        itk_component add questions {
166            button $itk_component(hubcntls).questions -text Questions? \
167                -command [list Rappture::filexfer::webpage \
168                              "$url/resources/$_appName/questions"]
169        } {
170            usual
171            ignore -font
172            rename -background -simcontrolcolor simControlColor Color
173            rename -highlightbackground -simcontrolcolor simControlColor Color
174        }
175        pack $itk_component(questions) -side top -anchor w
176    }
177
178    itk_component add simstatus {
179        text $itk_component(simbg).simstatus -borderwidth 0 \
180            -highlightthickness 0 -height 1 -width 1 -wrap none \
181            -state disabled
182    } {
183        usual
184        ignore -highlightthickness
185        rename -background -simcontrolcolor simControlColor Color
186        rename -font -textfont textFont Font
187    }
188    pack $itk_component(simstatus) -side left -expand yes -fill x
189
190    $itk_component(simstatus) tag configure popup \
191        -underline 1 -foreground blue
192
193    $itk_component(simstatus) tag bind popup \
194        <Enter> {%W configure -cursor center_ptr}
195    $itk_component(simstatus) tag bind popup \
196        <Leave> {%W configure -cursor ""}
197    $itk_component(simstatus) tag bind popup \
198        <ButtonPress> {after idle {Rappture::Tooltip::tooltip show %W}}
199
200
201    itk_component add notebook {
202        Rappture::Notebook $itk_interior.nb
203    }
204    pack $itk_interior.nb -expand yes -fill both
205
206    # ------------------------------------------------------------------
207    # ABOUT PAGE
208    # ------------------------------------------------------------------
209    set w [$itk_component(notebook) insert end about]
210
211    blt::scrollset $w.info -yscrollbar $w.info.ys -window $w.info.text
212    blt::tk::scrollbar $w.info.ys
213    pack $w.info -expand yes -fill both -padx 4 -pady 20
214    itk_component add toolinfo {
215        text $w.info.text -width 1 -height 1 -wrap word \
216            -borderwidth 0 -highlightthickness 0
217    } {
218        usual
219        ignore -borderwidth -relief
220        rename -font -textfont textFont Font
221    }
222
223    # ------------------------------------------------------------------
224    # SIMULATION PAGE
225    # ------------------------------------------------------------------
226    set w [$itk_component(notebook) insert end simulate]
227    frame $w.cntls
228    pack $w.cntls -side bottom -fill x -pady 12
229    frame $w.cntls.sep -background black -height 1
230    pack $w.cntls.sep -side top -fill x
231
232    itk_component add abort {
233        button $w.cntls.abort -text "Abort" \
234            -command [itcl::code $_tool abort]
235    }
236    pack $itk_component(abort) -side left -expand yes -padx 4 -pady 4
237
238    blt::scrollset $w.info -xscrollbar $w.info.xs -yscrollbar $w.info.ys \
239        -window $w.info.text
240    blt::tk::scrollbar $w.info.xs
241    blt::tk::scrollbar $w.info.ys
242
243    pack $w.info -expand yes -fill both -padx 4 -pady 4
244    itk_component add runinfo {
245        text $w.info.text -width 1 -height 1 -wrap none \
246            -borderwidth 0 -highlightthickness 0 \
247            -state disabled
248    } {
249        usual
250        ignore -borderwidth -relief
251        rename -font -codefont codeFont Font
252    }
253    itk_component add progress {
254        Rappture::Progress $w.progress
255    }
256
257    # ------------------------------------------------------------------
258    # ANALYZE PAGE
259    # ------------------------------------------------------------------
260    set w [$itk_component(notebook) insert end analyze]
261
262    frame $w.top
263    pack $w.top -side top -fill x -pady 8
264    label $w.top.l -text "Result:" -font $itk_option(-font)
265    pack $w.top.l -side left
266
267    itk_component add resultselector {
268        Rappture::Combobox $w.top.sel -width 10 -editable no
269    } {
270        usual
271        rename -font -textfont textFont Font
272    }
273    pack $itk_component(resultselector) -side left -expand yes -fill x
274    bind $itk_component(resultselector) <<Value>> [itcl::code $this _fixResult]
275    bind $itk_component(resultselector) <Enter> \
276        [itcl::code $this download coming]
277
278    Rappture::Tooltip::for $itk_component(resultselector) \
279        "@[itcl::code $this _resultTooltip]"
280
281    $itk_component(resultselector) choices insert end \
282        --- "---"
283
284    itk_component add download {
285        button $w.top.dl -image [Rappture::icon download] -anchor e \
286            -borderwidth 1 -relief flat -overrelief raised \
287            -command [itcl::code $this download start $w.top.dl]
288    }
289    pack $itk_component(download) -side right -padx {4 0}
290    bind $itk_component(download) <Enter> \
291        [itcl::code $this download coming]
292
293    $itk_component(resultselector) choices insert end \
294        @download [Rappture::filexfer::label download]
295
296    if {[Rappture::filexfer::enabled]} {
297        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.
298
299NOTE:  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."
300    } else {
301        Rappture::Tooltip::for $itk_component(download) "Saves the current result to a file on your desktop."
302    }
303
304    itk_component add results {
305        Rappture::Panes $w.pane -sashwidth 1 -sashrelief solid -sashpadding {4 0}
306    }
307    pack $itk_component(results) -expand yes -fill both
308    set f [$itk_component(results) pane 0]
309
310    itk_component add resultpages {
311        Rappture::Notebook $f.nb
312    }
313    pack $itk_component(resultpages) -expand yes -fill both
314
315    set f [$itk_component(results) insert end -fraction 0.1]
316    itk_component add resultset {
317        Rappture::ResultSet $f.rset \
318            -clearcommand [itcl::code $this clear] \
319            -settingscommand [itcl::code $this _plot] \
320            -promptcommand [itcl::code $this _simState]
321    }
322    pack $itk_component(resultset) -expand yes -fill both
323    bind $itk_component(resultset) <<Control>> [itcl::code $this _fixSize]
324    bind $itk_component(results) <Configure> [itcl::code $this _fixSize]
325
326    eval itk_initialize $args
327
328    $itk_component(runinfo) tag configure ERROR -foreground red
329    $itk_component(runinfo) tag configure text -font $itk_option(-textfont)
330
331    #
332    # Load up tool info on the first page.
333    #
334    $itk_component(toolinfo) tag configure title \
335        -font $itk_option(-boldtextfont)
336
337    set mesg [$tool xml get tool.title]
338    if {"" != $mesg} {
339        $itk_component(toolinfo) insert end $mesg title
340        $itk_component(toolinfo) insert end "\n\n"
341    }
342
343    set mesg [$tool xml get tool.about]
344    if {"" != $mesg} {
345        $itk_component(toolinfo) insert end $mesg
346    }
347    $itk_component(toolinfo) configure -state disabled
348    $itk_component(notebook) current about
349
350    # tool can run on "manual" (default) or "auto"
351    set cntl [$tool xml get tool.control]
352    if {"" == $cntl} {
353        set cntl [$tool xml get tool.control.type]
354    }
355    if {"" != $cntl} {
356        set _control $cntl
357    }
358
359    # reset everything to a clean state
360    reset
361}
362
363# ----------------------------------------------------------------------
364# DESTRUCTOR
365# ----------------------------------------------------------------------
366itcl::body Rappture::Analyzer::destructor {} {
367    foreach obj $_runs {
368        itcl::delete object $obj
369    }
370    after cancel [itcl::code $this simulate]
371}
372
373# ----------------------------------------------------------------------
374# USAGE: simulate ?-ifneeded?
375# USAGE: simulate ?<path1> <value1> <path2> <value2> ...?
376#
377# Kicks off the simulator by executing the tool.command associated
378# with the tool.  If any arguments are specified, they are used to
379# set parameters for the simulation.  While the simulation is running,
380# it shows status.  When the simulation is finished, it switches
381# automatically to "analyze" mode and shows the results.
382# ----------------------------------------------------------------------
383itcl::body Rappture::Analyzer::simulate {args} {
384    if {$args == "-ifneeded"} {
385        # check to see if simulation is really needed
386        $_tool sync
387        if {[$itk_component(resultset) contains [$_tool xml object]]
388              && ![string equal $_control "manual-resim"]} {
389            # not needed -- show results and return
390            $itk_component(notebook) current analyze
391            return
392        }
393        set args ""
394    }
395
396    # simulation is needed -- go to simulation page
397    $itk_component(notebook) current simulate
398
399    # no progress messages yet
400    pack forget $itk_component(progress)
401    lappend args -output [itcl::code $this _simOutput]
402
403    _simState off
404    $itk_component(runinfo) configure -state normal
405    $itk_component(runinfo) delete 1.0 end
406    $itk_component(runinfo) insert end "Running simulation...\n\n" text
407    $itk_component(runinfo) configure -state disabled
408
409    # if the hold window is set, then put up a busy cursor
410    if {$itk_option(-holdwindow) != ""} {
411        blt::busy hold $itk_option(-holdwindow)
412        raise $itk_component(hull)
413    }
414
415    # execute the job
416    foreach {status result} [eval $_tool run $args] break
417
418    # if job was aborted, then allow simulation again
419    if {$result == "ABORT"} {
420        _simState on "Aborted"
421    }
422
423    # load results from run.xml into analyzer
424    if {$status == 0 && $result != "ABORT"} {
425        set status [catch {load $result} result]
426    }
427
428    # back to normal
429    if {$itk_option(-holdwindow) != ""} {
430        blt::busy release $itk_option(-holdwindow)
431    }
432    $itk_component(abort) configure -state disabled
433
434    if {$status != 0} {
435        $itk_component(runinfo) configure -state normal
436        $itk_component(runinfo) delete 1.0 end
437        $itk_component(runinfo) insert end "Problem launching job:\n\n" text
438        _simOutput $result
439        $itk_component(runinfo) configure -state disabled
440        $itk_component(runinfo) see 1.0
441
442        # Try to create a support ticket for this error.
443        # It may be a real problem.
444        if {[Rappture::bugreport::shouldReport for jobs]} {
445            set ::errorInfo "== RAPPTURE INPUT ==\n[$_tool xml xml]"
446            Rappture::bugreport::register "Problem launching job:\n$result"
447            Rappture::bugreport::send
448        }
449    } else {
450        $itk_component(notebook) current analyze
451    }
452
453    # do this last -- after _simOutput above
454    pack forget $itk_component(progress)
455}
456
457# ----------------------------------------------------------------------
458# USAGE: reset ?-eventually|-now?
459#
460# Used to reset the analyzer whenever the input to a simulation has
461# changed.  Sets the mode back to "simulate", so the user has to
462# simulate again to see the output.  If the <start> option is set
463# to "auto", the simulation is invoked immediately.
464# ----------------------------------------------------------------------
465itcl::body Rappture::Analyzer::reset {{when -eventually}} {
466    if {$when == "-eventually"} {
467        after cancel [list catch [itcl::code $this reset -now]]
468        after idle [list catch [itcl::code $this reset -now]]
469        return
470    }
471
472    # check to see if simulation is really needed
473    $_tool sync
474    if {![$itk_component(resultset) contains [$_tool xml object]]
475          || [string equal $_control "manual-resim"]} {
476        # if control mode is "auto", then simulate right away
477        if {[string match auto* $_control]} {
478            # auto control -- don't need button
479            pack forget $itk_interior.simol
480
481            after cancel [itcl::code $this simulate]
482            after idle [itcl::code $this simulate]
483        } else {
484            _simState on "new input parameters"
485        }
486    } else {
487        _simState off
488    }
489}
490
491# ----------------------------------------------------------------------
492# USAGE: load <xmlobj>
493#
494# Loads the data from the given <xmlobj> into the appropriate results
495# sets.  If necessary, new results sets are created to store the data.
496# ----------------------------------------------------------------------
497itcl::body Rappture::Analyzer::load {xmlobj} {
498    # only show the last result? then clear first
499    if {[$_tool xml get tool.analyzer] == "last"} {
500        clear
501    }
502
503    # look for all output.load children and load them first
504    # each run.xml is loaded as a previous simulation.
505    foreach item [$xmlobj children -type run output.load] {
506        set loadfile [$xmlobj get output.load.$item]
507        set loadobj [Rappture::library $loadfile]
508        load $loadobj
509    }
510
511    foreach item [$xmlobj children -type run output.include] {
512        set id [$xmlobj element -as id output.include.$item]
513        set inclfile [$xmlobj get output.include.$item]
514        set inclobj [Rappture::library $inclfile]
515        foreach c [$inclobj children output] {
516            switch -glob -- $c {
517                # We don't want to include these tags.
518                include* - time* - status* - user* {
519                    continue
520                }
521                default {
522                    set oldid [$inclobj element -as id output.$c]
523                    set oldtype [$inclobj element -as type output.$c]
524                    set newcomp "$oldtype\($id-$oldid\)"
525                    $xmlobj copy output.$newcomp from $inclobj output.$c
526                }
527            }
528        }
529    }
530
531    lappend _runs $xmlobj
532
533    # Detect molecule elements that contain trajectory data and convert
534    # to sequences.
535    _trajToSequence $xmlobj output
536
537    # Go through the analysis and find all result sets.
538    set haveresults 0
539    foreach item [_reorder [$xmlobj children output]] {
540        switch -glob -- $item {
541            log* {
542                _autoLabel $xmlobj output.$item "Output Log" counters
543            }
544            number* {
545                _autoLabel $xmlobj output.$item "Number" counters
546            }
547            integer* {
548                _autoLabel $xmlobj output.$item "Integer" counters
549            }
550            string* {
551                _autoLabel $xmlobj output.$item "String" counters
552            }
553            histogram* - curve* - field* - drawing3d* {
554                _autoLabel $xmlobj output.$item "Plot" counters
555            }
556            structure* {
557                _autoLabel $xmlobj output.$item "Structure" counters
558            }
559            table* {
560                _autoLabel $xmlobj output.$item "Energy Levels" counters
561            }
562            sequence* {
563                _autoLabel $xmlobj output.$item "Sequence" counters
564            }
565        }
566        set label [$xmlobj get output.$item.about.group]
567        if {"" == $label} {
568            set label [$xmlobj get output.$item.about.label]
569        }
570
571        set hidden [$xmlobj get output.$item.hide]
572        set hidden [expr {"" != $hidden && $hidden}]
573
574        if {"" != $label && !$hidden} {
575            set haveresults 1
576        }
577    }
578
579    # if there are any valid results, add them to the resultset
580    if {$haveresults} {
581        set index [$itk_component(resultset) add $xmlobj]
582
583        # add each result to a result viewer
584        foreach item [_reorder [$xmlobj children output]] {
585            set label [$xmlobj get output.$item.about.group]
586            if {"" == $label} {
587                set label [$xmlobj get output.$item.about.label]
588            }
589
590            set hidden [$xmlobj get output.$item.hide]
591            set hidden [expr {"" != $hidden && $hidden}]
592
593            if {"" != $label && !$hidden} {
594                if {![info exists _label2page($label)]} {
595                    set name "page[incr _pages]"
596                    set page [$itk_component(resultpages) insert end $name]
597                    set _label2page($label) $page
598                    set _label2desc($label) \
599                        [$xmlobj get output.$item.about.description]
600                    Rappture::ResultViewer $page.rviewer
601                    pack $page.rviewer -expand yes -fill both -pady 4
602
603                    set end [$itk_component(resultselector) \
604                        choices index -value ---]
605                    if {$end < 0} {
606                        set end "end"
607                    }
608                    $itk_component(resultselector) choices insert $end \
609                        $name $label
610                }
611
612                # add/replace the latest result into this viewer
613                set page $_label2page($label)
614
615                if {![info exists reset($page)]} {
616                    $page.rviewer clear $index
617                    set reset($page) 1
618                }
619                $page.rviewer add $index $xmlobj output.$item
620            }
621        }
622    }
623
624    # show the first page by default
625    set max [$itk_component(resultselector) choices size]
626    for {set i 0} {$i < $max} {incr i} {
627        set first [$itk_component(resultselector) choices get -label $i]
628        if {$first != ""} {
629            set page [$itk_component(resultselector) choices get -value $i]
630            set char [string index $page 0]
631            if {$char != "@" && $char != "-"} {
632                $itk_component(resultpages) current $page
633                $itk_component(resultselector) value $first
634                set _lastlabel $first
635                break
636            }
637        }
638    }
639}
640
641# ----------------------------------------------------------------------
642# USAGE: clear
643#
644# Discards all results previously loaded into the analyzer.
645# ----------------------------------------------------------------------
646itcl::body Rappture::Analyzer::clear {} {
647    foreach obj $_runs {
648        itcl::delete object $obj
649    }
650    set _runs ""
651
652    $itk_component(resultset) clear
653
654    # reset the size of the controls area
655    set ht [winfo height $itk_component(results)]
656    set cntlht [$itk_component(resultset) size -controlarea]
657    set frac [expr {double($cntlht)/$ht}]
658    $itk_component(results) fraction end $frac
659
660    foreach label [array names _label2page] {
661        set page $_label2page($label)
662        $page.rviewer clear
663    }
664    $itk_component(resultselector) value ""
665    $itk_component(resultselector) choices delete 0 end
666    catch {unset _label2page}
667    catch {unset _label2desc}
668    set _plotlist ""
669
670    $itk_component(resultselector) choices insert end --- "---"
671    $itk_component(resultselector) choices insert end \
672        @download [Rappture::filexfer::label download]
673    set _lastlabel ""
674
675    #
676    # HACK ALERT!!
677    # The following statement should be in place, but it causes
678    # vtk to dump core.  Leave it out until we can fix the core dump.
679    # In the mean time, we leak memory...
680    #
681    #$itk_component(resultpages) delete -all
682    #set _pages 0
683
684    _simState on
685    _fixSimControl
686    reset
687}
688
689# ----------------------------------------------------------------------
690# USAGE: download coming
691# USAGE: download controls <downloadCommand>
692# USAGE: download start ?widget?
693# USAGE: download now ?widget?
694#
695# Spools the current result so the user can download it.
696# ----------------------------------------------------------------------
697itcl::body Rappture::Analyzer::download {option args} {
698    set title [$itk_component(resultselector) value]
699    set page [$itk_component(resultselector) translate $title]
700
701    switch -- $option {
702        coming {
703            #
704            # Warn result that a download is coming, in case
705            # it needs to take a screen snap.
706            #
707            if {![regexp {^(|@download|---)$} $page]} {
708                set f [$itk_component(resultpages) page $page]
709                $f.rviewer download coming
710            }
711        }
712        controls {
713            # no controls for this download yet
714            return ""
715        }
716        start {
717            set widget $itk_component(download)
718            if {[llength $args] > 0} {
719                set widget [lindex $args 0]
720                if {[catch {winfo class $widget}]} {
721                    set widget $itk_component(download)
722                }
723            }
724            #
725            # See if this download has any controls.  If so, then
726            # post them now and let the user continue the download
727            # after selecting a file format.
728            #
729            if {$page != ""} {
730                set ext ""
731                set f [$itk_component(resultpages) page $page]
732                set arg [itcl::code $this download now $widget]
733                set popup [$f.rviewer download controls $arg]
734                if {"" != $popup} {
735                    $popup activate $widget below
736                } else {
737                    download now $widget
738                }
739            } else {
740                # this shouldn't happen
741                set file error.html
742                set data "<h1>Not Found</h1>There is no result selected."
743            }
744        }
745        now {
746            set widget $itk_component(download)
747            if {[llength $args] > 0} {
748                set widget [lindex $args 0]
749                if {[catch {winfo class $widget}]} {
750                    set widget $itk_component(download)
751                }
752            }
753            #
754            # Perform the actual download.
755            #
756            if {$page != ""} {
757                set ext ""
758                set f [$itk_component(resultpages) page $page]
759                set item [$itk_component(resultselector) value]
760                set result [$f.rviewer download now $widget $_appName $item]
761                if { $result == "" } {
762                    return;             # User cancelled the download.
763                }
764                foreach {ext data} $result break
765                if {"" == $ext} {
766                    if {"" != $widget} {
767                        Rappture::Tooltip::cue $widget \
768                            "Can't download this result."
769                    }
770                    return
771                }
772                regsub -all {[\ -\/\:-\@\{-\~]} $title {} title
773                set file "$title$ext"
774            } else {
775                # this shouldn't happen
776                set file error.html
777                set data "<h1>Not Found</h1>There is no result selected."
778            }
779
780            set mesg [Rappture::filexfer::download $data $file]
781            if {[string length $mesg] > 0} {
782                Rappture::Tooltip::cue $widget $mesg
783            }
784        }
785        default {
786            error "bad option \"$option\": should be coming, controls, now, start"
787        }
788    }
789}
790
791# ----------------------------------------------------------------------
792# USAGE: _plot ?<index> <options> <index> <options>...?
793#
794# Used internally to update the plot shown in the current result
795# viewer whenever the resultset settings have changed.  Causes the
796# desired results to show up on screen.
797# ----------------------------------------------------------------------
798itcl::body Rappture::Analyzer::_plot {args} {
799    set _plotlist $args
800
801    set page [$itk_component(resultselector) value]
802    set page [$itk_component(resultselector) translate $page]
803    if {"" != $page} {
804        set f [$itk_component(resultpages) page $page]
805        $f.rviewer plot clear
806        foreach {index opts} $_plotlist {
807            $f.rviewer plot add $index $opts
808        }
809    }
810}
811
812# ----------------------------------------------------------------------
813# USAGE: _reorder <compList>
814#
815# Used internally to change the order of a series of output components
816# found in the <output> section.  Moves the <log> elements to the end
817# and returns the updated list.
818# ----------------------------------------------------------------------
819itcl::body Rappture::Analyzer::_reorder {comps} {
820    set i 0
821    set max [llength $comps]
822    while {$i < $max} {
823        set c [lindex $comps $i]
824        if {[string match log* $c]} {
825            set comps [lreplace $comps $i $i]
826            lappend comps $c
827            incr max -1
828        } else {
829            incr i
830        }
831    }
832    return $comps
833}
834
835# ----------------------------------------------------------------------
836# USAGE: _autoLabel <xmlobj> <path> <title> <cntVar>
837#
838# Used internally to check for an about.label property at the <path>
839# in <xmlobj>.  If this object doesn't have a label, then one is
840# supplied using the given <title>.  The <cntVar> is an array of
841# counters in the calling scopes for titles that have been used
842# in the past.  This is used to create titles like "Plot #2" the
843# second time it is encountered.
844#
845# The <xmlobj> is updated so that the label is inserted directly in
846# the tree.
847# ----------------------------------------------------------------------
848itcl::body Rappture::Analyzer::_autoLabel {xmlobj path title cntVar} {
849    upvar $cntVar counters
850
851    set group [$xmlobj get $path.about.group]
852    set label [$xmlobj get $path.about.label]
853    if {"" == $label} {
854        # no label -- make one up using the title specified
855        if {![info exists counters($group-$title)]} {
856            set counters($group-$title) 1
857            set label $title
858        } else {
859            set label "$title (#[incr counters($group-$title)])"
860        }
861        $xmlobj put $path.about.label $label
862    } else {
863        # handle the case of two identical labels in <output>
864        if {![info exists counters($group-$label)]} {
865            set counters($group-$label) 1
866        } else {
867            set label "$label (#[incr counters($group-$label)])"
868            $xmlobj put $path.about.label $label
869        }
870    }
871    return $label
872}
873
874# ----------------------------------------------------------------------
875# USAGE: _fixResult
876#
877# Used internally to change the result page being displayed whenever
878# the user selects a page from the results combobox.
879# ----------------------------------------------------------------------
880itcl::body Rappture::Analyzer::_fixResult {} {
881    set name [$itk_component(resultselector) value]
882    set page ""
883    if {"" != $name} {
884        set page [$itk_component(resultselector) translate $name]
885    }
886    if {$page == "@download"} {
887        # put the combobox back to its last value
888        $itk_component(resultselector) component entry configure -state normal
889        $itk_component(resultselector) component entry delete 0 end
890        $itk_component(resultselector) component entry insert end $_lastlabel
891        $itk_component(resultselector) component entry configure -state disabled
892        # perform the actual download
893        download start $itk_component(download)
894    } elseif {$page == "---"} {
895        # put the combobox back to its last value
896        $itk_component(resultselector) component entry configure -state normal
897        $itk_component(resultselector) component entry delete 0 end
898        $itk_component(resultselector) component entry insert end $_lastlabel
899        $itk_component(resultselector) component entry configure -state disabled
900    } elseif {$page != ""} {
901        set _lastlabel $name
902        set win [winfo toplevel $itk_component(hull)]
903        blt::busy hold $win
904        $itk_component(resultpages) current $page
905
906        set f [$itk_component(resultpages) page $page]
907        $f.rviewer plot clear
908        eval $f.rviewer plot add $_plotlist
909        blt::busy release [winfo toplevel $itk_component(hull)]
910    }
911}
912
913# ----------------------------------------------------------------------
914# USAGE: _fixSize
915#
916# Used internally to change the size of the result set area whenever
917# a new control appears.  Adjusts the size available for the result
918# set up to some maximum.
919# ----------------------------------------------------------------------
920itcl::body Rappture::Analyzer::_fixSize {} {
921    set ht [winfo height $itk_component(results)]
922    if {$ht <= 1} { set ht [winfo reqheight $itk_component(results)] }
923    set cntlht [$itk_component(resultset) size -controlarea]
924    set frac [expr {double($cntlht)/$ht}]
925
926    if {$frac < 0.4} {
927        $itk_component(results) fraction end $frac
928    }
929    _fixSimControl
930}
931
932# ----------------------------------------------------------------------
933# USAGE: _simState <boolean> ?<message>? ?<settings>?
934#
935# Used internally to change the "Simulation" button on or off.
936# If the <boolean> is on, then any <message> and <settings> are
937# displayed as well.  If the <boolean> is off, then only display
938# the message. The <message> is a note to the user about
939# what will be simulated, and the <settings> are a list of
940# tool parameter settings of the form {path1 val1 path2 val2 ...}.
941# When these are in place, the next Simulate operation will use
942# these settings.  This helps fill in missing data values.
943# ----------------------------------------------------------------------
944itcl::body Rappture::Analyzer::_simState {state args} {
945    if {$state} {
946        $itk_interior.simol configure \
947            -background $itk_option(-simcontrolactiveoutline)
948        configure -simcontrolcolor $itk_option(-simcontrolactivebackground)
949
950        $itk_component(abort) configure -state disabled
951        $itk_component(simulate) configure -state normal \
952            -command [itcl::code $this simulate]
953
954        #
955        # If there's a special message, then put it up next to the button.
956        #
957        set mesg [lindex $args 0]
958        if {"" != $mesg} {
959            $itk_component(simstatus) configure -state normal
960            $itk_component(simstatus) delete 1.0 end
961            $itk_component(simstatus) insert end $mesg
962
963            #
964            # If there are any settings, then install them in the
965            # "Simulate" button.  Also, pop them up as a tooltip
966            # for the message.
967            #
968            set settings [lindex $args 1]
969            if {[llength $settings] > 0} {
970                $itk_component(simulate) configure \
971                    -command [eval itcl::code $this simulate $settings]
972
973                set details ""
974                foreach {path val} $settings {
975                    set str [$_tool xml get $path.about.label]
976                    if {"" == $str} {
9771                       set str [$_tool xml element -as id $path]
978                    }
979                    append details "$str = $val\n"
980                }
981                set details [string trim $details]
982
983                Rappture::Tooltip::for $itk_component(simstatus) $details
984                $itk_component(simstatus) insert end " "
985                $itk_component(simstatus) insert end "(details...)" popup
986            }
987            $itk_component(simstatus) configure -state disabled
988        }
989    } else {
990        if {"" != $itk_option(-simcontrolbackground)} {
991            set simcbg $itk_option(-simcontrolbackground)
992        } else {
993            set simcbg $itk_option(-background)
994        }
995        $itk_interior.simol configure \
996            -background $itk_option(-simcontroloutline)
997        configure -simcontrolcolor $simcbg
998
999        $itk_component(simulate) configure -state disabled
1000        $itk_component(abort) configure -state normal
1001
1002        $itk_component(simstatus) configure -state normal
1003        $itk_component(simstatus) delete 1.0 end
1004        set mesg [lindex $args 0]
1005        if {"" != $mesg} {
1006            $itk_component(simstatus) insert end $mesg
1007        }
1008        $itk_component(simstatus) configure -state disabled
1009    }
1010}
1011
1012# ----------------------------------------------------------------------
1013# USAGE: _simOutput <message>
1014#
1015# Invoked automatically whenever output comes in while running the
1016# tool.  Extracts any =RAPPTURE-???=> messages from the output and
1017# sends the output to the display.  For example, any
1018# =RAPPTURE-PROGRESS=> message pops up the progress meter and updates
1019# it to show the latest progress message.  This is useful for
1020# long-running tools, to let the user know how much longer the
1021# simulation will take.
1022# ----------------------------------------------------------------------
1023itcl::body Rappture::Analyzer::_simOutput {message} {
1024    #
1025    # Scan through and pick out any =RAPPTURE-PROGRESS=> messages first.
1026    #
1027    while {[regexp -indices \
1028               {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)} $message \
1029                match percent mesg]} {
1030
1031        foreach {i0 i1} $percent break
1032        set percent [string range $message $i0 $i1]
1033
1034        foreach {i0 i1} $mesg break
1035        set mesg [string range $message $i0 $i1]
1036
1037        pack $itk_component(progress) -fill x -padx 10 -pady 10
1038        $itk_component(progress) settings -percent $percent -message $mesg
1039
1040        foreach {i0 i1} $match break
1041        set message [string replace $message $i0 $i1]
1042    }
1043
1044    #
1045    # Break up the remaining lines according to =RAPPTURE-ERROR=> messages.
1046    # Show errors in a special color.
1047    #
1048    $itk_component(runinfo) configure -state normal
1049
1050    while {[regexp -indices \
1051               {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)} $message \
1052                match type mesg]} {
1053
1054        foreach {i0 i1} $match break
1055        set first [string range $message 0 [expr {$i0-1}]]
1056        if {[string length $first] > 0} {
1057            $itk_component(runinfo) insert end $first
1058            $itk_component(runinfo) insert end \n
1059        }
1060
1061        foreach {t0 t1} $type break
1062        set type [string range $message $t0 $t1]
1063        foreach {m0 m1} $mesg break
1064        set mesg [string range $message $m0 $m1]
1065        if {[string length $mesg] > 0 && $type != "RUN"} {
1066            $itk_component(runinfo) insert end $mesg $type
1067            $itk_component(runinfo) insert end \n $type
1068        }
1069
1070        set message [string range $message [expr {$i1+1}] end]
1071    }
1072
1073    if {[string length $message] > 0} {
1074        $itk_component(runinfo) insert end $message
1075        if {[$itk_component(runinfo) get end-2char] != "\n"} {
1076            $itk_component(runinfo) insert end "\n"
1077        }
1078        $itk_component(runinfo) see end
1079    }
1080    $itk_component(runinfo) configure -state disabled
1081}
1082
1083# ----------------------------------------------------------------------
1084# USAGE: _resultTooltip
1085#
1086# Used internally to build the tooltip string displayed for the
1087# result selector.  If the current page has an associated description,
1088# then it is displayed beneath the result.
1089#
1090# Returns the string for the tooltip.
1091# ----------------------------------------------------------------------
1092itcl::body Rappture::Analyzer::_resultTooltip {} {
1093    set tip ""
1094    set name [$itk_component(resultselector) value]
1095    if {[info exists _label2desc($name)] &&
1096         [string length $_label2desc($name)] > 0} {
1097        append tip "$_label2desc($name)\n\n"
1098    }
1099    if {[array size _label2page] > 1} {
1100        append tip "Use this control to display other output results."
1101    }
1102    return $tip
1103}
1104
1105# ----------------------------------------------------------------------
1106# USAGE: _fixSimControl
1107#
1108# Used internally to add or remove the simulation control at the
1109# top of the analysis area.  This is controlled by the -simcontrol
1110# option.
1111# ----------------------------------------------------------------------
1112itcl::body Rappture::Analyzer::_fixSimControl {} {
1113    switch -- $itk_option(-simcontrol) {
1114        on {
1115            pack $itk_interior.simol -fill x -before $itk_interior.nb
1116        }
1117        off {
1118            pack forget $itk_interior.simol
1119        }
1120        auto {
1121            #
1122            # If we have two or more radiodials, then there is a
1123            # chance of encountering a combination of parameters
1124            # with no data, requiring simulation.
1125            #
1126            if {[$itk_component(resultset) size -controls] >= 2} {
1127                pack $itk_interior.simol -fill x -before $itk_interior.nb
1128            } else {
1129                pack forget $itk_interior.simol
1130            }
1131        }
1132        default {
1133            error "bad value \"$itk_option(-simcontrol)\": should be on, off, auto"
1134        }
1135    }
1136}
1137
1138# ----------------------------------------------------------------------
1139# USAGE: _fixNotebook
1140#
1141# Used internally to switch the active notebook page
1142# ----------------------------------------------------------------------
1143itcl::body Rappture::Analyzer::_fixNotebook {} {
1144    switch -- $itk_option(-notebookpage) {
1145        about {
1146            $itk_component(notebook) current about
1147        }
1148        simulate {
1149            $itk_component(notebook) current simulate
1150        }
1151        analyze {
1152            $itk_component(notebook) current analyze
1153        }
1154        default {
1155            error "bad value \"$itk_option(-notebookpage)\": should be about, simulate, analyze"
1156        }
1157    }
1158}
1159
1160# ----------------------------------------------------------------------
1161# USAGE: _isPdbTrajectory <data>
1162#
1163# Used internally to determine whether pdb or lammps data represents a
1164# trajectory rather than a single frame
1165# ----------------------------------------------------------------------
1166itcl::body Rappture::Analyzer::_isPdbTrajectory {data} {
1167    if { [llength $data]  == 0 } {
1168        return 0
1169    }
1170    set nModels 0
1171    foreach line $data {
1172        if { [string match "MODEL*" $line] }  {
1173            incr nModels
1174            if { $nModels > 1 } {
1175                # Stop if more than one model found.  No need to count them
1176                # all.
1177                return 1
1178            }
1179        }
1180    }
1181    return 0
1182}
1183
1184# ----------------------------------------------------------------------
1185# USAGE: _isLammpsTrajectory <data>
1186#
1187# Used internally to determine whether pdb or lammps data represents a
1188# trajectory rather than a single frame
1189# ----------------------------------------------------------------------
1190itcl::body Rappture::Analyzer::_isLammpsTrajectory { data } {
1191    if { [llength $data]  == 0 } {
1192        return 0
1193    }
1194    set nModels 0
1195    foreach line $data {
1196        if { [regexp {^[\t ]*ITEM:[ \t]+TIMESTEP} $line] } {
1197            incr nModels
1198            if { $nModels > 1 } {
1199                # Stop if more than one model found.  No need to count them
1200                # all.
1201                return 1
1202            }
1203        }
1204    }
1205    return 0
1206}
1207
1208# ----------------------------------------------------------------------
1209# USAGE: _pdbToSequence <xmlobj> ?<path>?
1210#
1211# If the molecule element is a trajectory, delete the original
1212# and create a sequence of individual molecules.
1213# Used internally to detect any molecule output elements that contain
1214# trajectory data.  Trajectories will be converted into sequences of
1215# individual molecules.  All other elements will be unaffected. Scans
1216# the entire xml tree if a starting path is not specified.
1217# ----------------------------------------------------------------------
1218itcl::body Rappture::Analyzer::_pdbToSequence {xmlobj path id child data} {
1219
1220    set seqLabel [$xmlobj get ${child}.about.label]
1221    set descr    [$xmlobj get ${child}.about.description]
1222    set formula  [$xmlobj get ${child}.components.molecule.formula]
1223    $xmlobj remove $child
1224
1225    set sequence  $path.sequence($id)
1226    $xmlobj put ${sequence}.about.label $seqLabel
1227    $xmlobj put ${sequence}.about.description $descr
1228    $xmlobj put ${sequence}.index.label "Frame"
1229
1230    set frameNum 0
1231    set numLines [llength $data]
1232    for { set i 0 } { $i < $numLines } { incr i } {
1233        set line [lindex $data $i]
1234        set line [string trim $line]
1235        set contents {}
1236        if { [string match "MODEL*" $line] } {
1237            # Save the contents until we get an ENDMDL record.
1238            for {} { $i < $numLines } { incr i } {
1239                set line [lindex $data $i]
1240                set line [string trim $line]
1241                if { $line == "" } {
1242                    continue;           # Skip blank lines.
1243                }
1244                if { [string match "ENDMDL*" $line] } {
1245                    break;
1246                }
1247                append contents $line\n
1248            }
1249            set frame ${sequence}.element($frameNum)
1250            $xmlobj put ${frame}.index $frameNum
1251           
1252            set molecule ${frame}.structure.components.molecule
1253            $xmlobj put ${molecule}.pdb $contents
1254            $xmlobj put ${molecule}.formula $formula
1255            incr frameNum
1256        }
1257    }
1258}
1259
1260# ----------------------------------------------------------------------
1261# USAGE: _lammpsToSequence <xmlobj> ?<path>?
1262#
1263# If the molecule element is a trajectory, delete the original
1264# and create a sequence of individual molecules.
1265# Used internally to detect any molecule output elements that contain
1266# trajectory data.  Trajectories will be converted into sequences of
1267# individual molecules.  All other elements will be unaffected. Scans
1268# the entire xml tree if a starting path is not specified.
1269# ----------------------------------------------------------------------
1270itcl::body Rappture::Analyzer::_lammpsToSequence {xmlobj path id child data} {
1271
1272    set seqLabel [$xmlobj get ${child}.about.label]
1273    set descr    [$xmlobj get ${child}.about.description]
1274    set typemap  [$xmlobj get ${child}.components.molecule.lammpstypemap]
1275    $xmlobj remove $child
1276
1277    set sequence ${path}.sequence($id)
1278    $xmlobj put ${sequence}.about.label $seqLabel
1279    $xmlobj put ${sequence}.about.description $descr
1280    $xmlobj put ${sequence}.index.label "Frame"
1281
1282    set frameNum 0
1283    set frameContents ""
1284    set inModel 0
1285    foreach line $data {
1286        set line [string trim $line]
1287        if { $line == "" } {
1288            continue;                   # Skip blank lines
1289        }
1290        if {[regexp {^[\t ]*ITEM:[ \t]+ATOMS} $line] } {
1291            if { $inModel && $frameContents != "" } {
1292                set frame ${sequence}.element($frameNum)
1293                $xmlobj put ${frame}.index $frameNum
1294               
1295                set molecule ${frame}.structure.components.molecule
1296                $xmlobj put ${molecule}.lammps $frameContents
1297                $xmlobj put ${molecule}.lammpstypemap $typemap
1298               
1299                incr frameNum
1300                set frameContents ""
1301            }
1302            set inModel 1
1303        } elseif { [scan $line "%d %d %f %f %f" a b c d e] == 5 } {
1304            if { !$inModel } {
1305                puts stderr "found \"$line\" without previous \"ITEM: ATOMS\""
1306                set inModel 1
1307            }
1308            append frameContents $line\n
1309        }
1310    }
1311    if { $frameContents != "" } {
1312        set frame ${sequence}.element($frameNum)
1313        $xmlobj put ${frame}.index $frameNum
1314       
1315        set molecule ${frame}.structure.components.molecule
1316        $xmlobj put ${molecule}.lammps $frameContents
1317        $xmlobj put ${molecule}.lammpstypemap $typemap
1318    }
1319}
1320
1321# ----------------------------------------------------------------------
1322# USAGE: _trajToSequence <xmlobj> ?<path>?
1323#
1324#       Check for PDB and LAMMPS trajectories in molecule data and rewrite
1325#       the individual models as a sequence of molecules.  Used internally
1326#       to detect any molecule output elements that contain trajectory data.
1327#       Trajectories will be converted into sequences of individual molecules.
1328#       All other elements will be unaffected. Scans the entire xml tree if a
1329#       starting path is not specified. 
1330#
1331# ----------------------------------------------------------------------
1332itcl::body Rappture::Analyzer::_trajToSequence {xmlobj {path ""}} {
1333    # Remove leading dot from path, if present.
1334    if { [string index $path 0] == "." } {
1335        set path [string range $path 1 end]
1336    }
1337    # Otherwise check each child.
1338    foreach child [$xmlobj children $path] {
1339        set current ${path}.${child}
1340        if { [string match "structure*" $child] } {
1341            set isTraj [$xmlobj get ${current}.components.molecule.trajectory]
1342            if { $isTraj == "" || !$isTraj } {
1343                continue;               # Not a trajectory.
1344            }
1345            # Look for trajectory if molecule element found.  Check both pdb
1346            # data and lammps data.
1347            set type [$xmlobj element -as type $current]
1348            set id   [$xmlobj element -as id $current]
1349            set pdbdata    [$xmlobj get ${current}.components.molecule.pdb]
1350            set lammpsdata [$xmlobj get ${current}.components.molecule.lammps]
1351            if { $pdbdata != "" && $lammpsdata != "" } {
1352                puts stderr \
1353                    "found both <pdb> and <lammps> elements: picking pdb"
1354            }
1355            set pdbdata [split $pdbdata \n]
1356            set lammpsdata [split $lammpsdata \n]
1357            if { [_isPdbTrajectory $pdbdata] } {
1358                _pdbToSequence $xmlobj $path $id $current $pdbdata
1359            } elseif { [_isLammpsTrajectory $lammpsdata] } {
1360                _lammpsToSequence $xmlobj $path $id $current $lammpsdata
1361            }
1362            continue
1363        }
1364        if 0 {
1365        # Recurse over all child nodes.
1366        _trajToSequence $xmlobj $current
1367        }
1368    }
1369}
1370
1371# ----------------------------------------------------------------------
1372# CONFIGURATION OPTION: -simcontrol
1373#
1374# Controls whether or not the Simulate button is showing.  In some
1375# cases, it is not needed.
1376# ----------------------------------------------------------------------
1377itcl::configbody Rappture::Analyzer::simcontrol {
1378    _fixSimControl
1379}
1380
1381# ----------------------------------------------------------------------
1382# CONFIGURATION OPTION: -notebookpage
1383#
1384# Controls which page of the analyzer notebook is shown. It is
1385# particularly needed when using rerun, when you don't want to
1386# "simulate -ifneeded" because an actual simulation might be
1387# kicked off due to differences between tool.xml and run.xml
1388# ----------------------------------------------------------------------
1389itcl::configbody Rappture::Analyzer::notebookpage {
1390    _fixNotebook
1391}
Note: See TracBrowser for help on using the repository browser.