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

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