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

Last change on this file since 2035 was 1940, checked in by braffert, 14 years ago

To more closely match pdb conventions, use "model" instead of "frame" as the index label when converting trajectory to sequence. Model numbers should begin at 1. Also changed variable names to match.

File size: 50.6 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 model
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 model
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 "Model"
1222
1223    set modelNum 1
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 model ${sequence}.element($modelNum)
1243            $xmlobj put ${model}.index $modelNum
1244           
1245            set molecule ${model}.structure.components.molecule
1246            $xmlobj put ${molecule}.pdb $contents
1247            $xmlobj put ${molecule}.formula $formula
1248            incr modelNum
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 "Model"
1274
1275    set modelNum 1
1276    set modelContents ""
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 && $modelContents != "" } {
1285                set model ${sequence}.element($modelNum)
1286                $xmlobj put ${model}.index $modelNum
1287               
1288                set molecule ${model}.structure.components.molecule
1289                $xmlobj put ${molecule}.lammps $modelContents
1290                $xmlobj put ${molecule}.lammpstypemap $typemap
1291               
1292                incr modelNum
1293                set modelContents ""
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 modelContents $line\n
1302        }
1303    }
1304    if { $modelContents != "" } {
1305        set model ${sequence}.element($modelNum)
1306        $xmlobj put ${model}.index $modelNum
1307       
1308        set molecule ${model}.structure.components.molecule
1309        $xmlobj put ${molecule}.lammps $modelContents
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.