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

Last change on this file since 2782 was 2782, checked in by gah, 12 years ago

add disablestyle element to hide/greout element when disabled.

File size: 51.0 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 "\n\n== RAPPTURE INPUT ==\n[$_tool xml xml]"
439            Rappture::bugreport::register "Problem launching job:\n$result"
440            Rappture::bugreport::attachment [$_tool xml xml]
441            Rappture::bugreport::send
442        }
443    } else {
444        $itk_component(notebook) current analyze
445    }
446
447    # do this last -- after _simOutput above
448    pack forget $itk_component(progress)
449}
450
451# ----------------------------------------------------------------------
452# USAGE: reset ?-eventually|-now?
453#
454# Used to reset the analyzer whenever the input to a simulation has
455# changed.  Sets the mode back to "simulate", so the user has to
456# simulate again to see the output.  If the <start> option is set
457# to "auto", the simulation is invoked immediately.
458# ----------------------------------------------------------------------
459itcl::body Rappture::Analyzer::reset {{when -eventually}} {
460    if {$when == "-eventually"} {
461        after cancel [list catch [itcl::code $this reset -now]]
462        after idle [list catch [itcl::code $this reset -now]]
463        return
464    }
465
466    # check to see if simulation is really needed
467    $_tool sync
468    if {![$itk_component(resultset) contains [$_tool xml object]]
469          || [string equal $_control "manual-resim"]} {
470        # if control mode is "auto", then simulate right away
471        if {[string match auto* $_control]} {
472            # auto control -- don't need button
473            pack forget $itk_interior.simol
474
475            after cancel [itcl::code $this simulate]
476            after idle [itcl::code $this simulate]
477        } else {
478            _simState on "new input parameters"
479        }
480    } else {
481        _simState off
482    }
483}
484
485# ----------------------------------------------------------------------
486# USAGE: load <xmlobj>
487#
488# Loads the data from the given <xmlobj> into the appropriate results
489# sets.  If necessary, new results sets are created to store the data.
490# ----------------------------------------------------------------------
491itcl::body Rappture::Analyzer::load {xmlobj} {
492    # only show the last result? then clear first
493    if {[$_tool xml get tool.analyzer] == "last"} {
494        clear
495    }
496
497    # look for all output.load children and load them first
498    # each run.xml is loaded as a previous simulation.
499    foreach item [$xmlobj children -type run output.load] {
500        set loadfile [$xmlobj get output.load.$item]
501        set loadobj [Rappture::library $loadfile]
502        load $loadobj
503    }
504
505    foreach item [$xmlobj children -type run output.include] {
506        set id [$xmlobj element -as id output.include.$item]
507        set inclfile [$xmlobj get output.include.$item]
508        set inclobj [Rappture::library $inclfile]
509        foreach c [$inclobj children output] {
510            switch -glob -- $c {
511                # We don't want to include these tags.
512                include* - time* - status* - user* {
513                    continue
514                }
515                default {
516                    set oldid [$inclobj element -as id output.$c]
517                    set oldtype [$inclobj element -as type output.$c]
518                    set newcomp "$oldtype\($id-$oldid\)"
519                    $xmlobj copy output.$newcomp from $inclobj output.$c
520                }
521            }
522        }
523    }
524
525    lappend _runs $xmlobj
526
527    # Detect molecule elements that contain trajectory data and convert
528    # to sequences.
529    _trajToSequence $xmlobj output
530
531    # Go through the analysis and find all result sets.
532    set haveresults 0
533    foreach item [_reorder [$xmlobj children output]] {
534        switch -glob -- $item {
535            log* {
536                _autoLabel $xmlobj output.$item "Output Log" counters
537            }
538            number* {
539                _autoLabel $xmlobj output.$item "Number" counters
540            }
541            integer* {
542                _autoLabel $xmlobj output.$item "Integer" counters
543            }
544            string* {
545                _autoLabel $xmlobj output.$item "String" counters
546            }
547            histogram* - curve* - field* {
548                _autoLabel $xmlobj output.$item "Plot" counters
549            }
550            drawing* {
551                _autoLabel $xmlobj output.$item "Drawing" counters
552            }
553            structure* {
554                _autoLabel $xmlobj output.$item "Structure" counters
555            }
556            table* {
557                _autoLabel $xmlobj output.$item "Energy Levels" counters
558            }
559            sequence* {
560                _autoLabel $xmlobj output.$item "Sequence" counters
561            }
562            default {
563                if 0 {
564                    puts stderr "unknown output $item"
565                }
566            }
567        }
568        set label [$xmlobj get output.$item.about.group]
569        if {"" == $label} {
570            set label [$xmlobj get output.$item.about.label]
571        }
572
573        set hidden [$xmlobj get output.$item.hide]
574        set hidden [expr {"" != $hidden && $hidden}]
575
576        if {"" != $label && !$hidden} {
577            set haveresults 1
578        }
579    }
580    # if there are any valid results, add them to the resultset
581    if {$haveresults} {
582        set index [$itk_component(resultset) add $xmlobj]
583
584        # add each result to a result viewer
585        foreach item [_reorder [$xmlobj children output]] {
586            set label [$xmlobj get output.$item.about.group]
587            if {"" == $label} {
588                set label [$xmlobj get output.$item.about.label]
589            }
590            set hidden [$xmlobj get output.$item.hide]
591            if { $hidden == "" } {
592                set hidden 0
593            }
594            if {"" != $label && !$hidden} {
595                if {![info exists _label2page($label)]} {
596                    set name "page[incr _pages]"
597                    set page [$itk_component(resultpages) insert end $name]
598                    set _label2page($label) $page
599                    set _label2desc($label) \
600                        [$xmlobj get output.$item.about.description]
601                    Rappture::ResultViewer $page.rviewer
602                    pack $page.rviewer -expand yes -fill both -pady 4
603
604                    set end [$itk_component(resultselector) \
605                        choices index -value ---]
606                    if {$end < 0} {
607                        set end "end"
608                    }
609                    $itk_component(resultselector) choices insert $end \
610                        $name $label
611                }
612
613                # add/replace the latest result into this viewer
614                set page $_label2page($label)
615
616                if {![info exists reset($page)]} {
617                    $page.rviewer clear $index
618                    set reset($page) 1
619                }
620                $page.rviewer add $index $xmlobj output.$item
621            }
622        }
623    }
624
625    # show the first page by default
626    set max [$itk_component(resultselector) choices size]
627    for {set i 0} {$i < $max} {incr i} {
628        set first [$itk_component(resultselector) choices get -label $i]
629        if {$first != ""} {
630            set page [$itk_component(resultselector) choices get -value $i]
631            set char [string index $page 0]
632            if {$char != "@" && $char != "-"} {
633                $itk_component(resultpages) current $page
634                $itk_component(resultselector) value $first
635                set _lastlabel $first
636                break
637            }
638        }
639    }
640}
641
642# ----------------------------------------------------------------------
643# USAGE: clear
644#
645# Discards all results previously loaded into the analyzer.
646# ----------------------------------------------------------------------
647itcl::body Rappture::Analyzer::clear {} {
648    $itk_component(resultset) addtotree
649
650    foreach obj $_runs {
651        itcl::delete object $obj
652    }
653    set _runs ""
654
655    $itk_component(resultset) clear
656
657    # reset the size of the controls area
658    set ht [winfo height $itk_component(results)]
659    set cntlht [$itk_component(resultset) size -controlarea]
660    set frac [expr {double($cntlht)/$ht}]
661    $itk_component(results) fraction end $frac
662
663    foreach label [array names _label2page] {
664        set page $_label2page($label)
665        destroy $page.rviewer
666        #$page.rviewer clear
667    }
668    $itk_component(resultselector) value ""
669    $itk_component(resultselector) choices delete 0 end
670    catch {unset _label2page}
671    catch {unset _label2desc}
672    set _plotlist ""
673
674    $itk_component(resultselector) choices insert end --- "---"
675    $itk_component(resultselector) choices insert end \
676        @download [Rappture::filexfer::label download]
677    set _lastlabel ""
678
679    #
680    # HACK ALERT!!
681    # The following statement should be in place, but it causes
682    # vtk to dump core.  Leave it out until we can fix the core dump.
683    # In the mean time, we leak memory...
684    #
685    #$itk_component(resultpages) delete -all
686    #set _pages 0
687
688    _simState on
689    _fixSimControl
690    reset
691}
692
693# ----------------------------------------------------------------------
694# USAGE: download coming
695# USAGE: download controls <downloadCommand>
696# USAGE: download start ?widget?
697# USAGE: download now ?widget?
698#
699# Spools the current result so the user can download it.
700# ----------------------------------------------------------------------
701itcl::body Rappture::Analyzer::download {option args} {
702    set title [$itk_component(resultselector) value]
703    set page [$itk_component(resultselector) translate $title]
704
705    switch -- $option {
706        coming {
707            #
708            # Warn result that a download is coming, in case
709            # it needs to take a screen snap.
710            #
711            if {![regexp {^(|@download|---)$} $page]} {
712                set f [$itk_component(resultpages) page $page]
713                $f.rviewer download coming
714            }
715        }
716        controls {
717            # no controls for this download yet
718            return ""
719        }
720        start {
721            set widget $itk_component(download)
722            if {[llength $args] > 0} {
723                set widget [lindex $args 0]
724                if {[catch {winfo class $widget}]} {
725                    set widget $itk_component(download)
726                }
727            }
728            #
729            # See if this download has any controls.  If so, then
730            # post them now and let the user continue the download
731            # after selecting a file format.
732            #
733            if {$page != ""} {
734                set ext ""
735                set f [$itk_component(resultpages) page $page]
736                set arg [itcl::code $this download now $widget]
737                set popup [$f.rviewer download controls $arg]
738                if {"" != $popup} {
739                    $popup activate $widget below
740                } else {
741                    download now $widget
742                }
743            } else {
744                # this shouldn't happen
745                set file error.html
746                set data "<h1>Not Found</h1>There is no result selected."
747            }
748        }
749        now {
750            set widget $itk_component(download)
751            if {[llength $args] > 0} {
752                set widget [lindex $args 0]
753                if {[catch {winfo class $widget}]} {
754                    set widget $itk_component(download)
755                }
756            }
757            #
758            # Perform the actual download.
759            #
760            if {$page != ""} {
761                set ext ""
762                set f [$itk_component(resultpages) page $page]
763                set item [$itk_component(resultselector) value]
764                set result [$f.rviewer download now $widget $_appName $item]
765                if { $result == "" } {
766                    return;                # User cancelled the download.
767                }
768                foreach {ext data} $result break
769                if {"" == $ext} {
770                    if {"" != $widget} {
771                        Rappture::Tooltip::cue $widget \
772                            "Can't download this result."
773                    }
774                    return
775                }
776                regsub -all {[\ -\/\:-\@\{-\~]} $title {} title
777                set file "$title$ext"
778            } else {
779                # this shouldn't happen
780                set file error.html
781                set data "<h1>Not Found</h1>There is no result selected."
782            }
783
784            set mesg [Rappture::filexfer::download $data $file]
785            if {[string length $mesg] > 0} {
786                Rappture::Tooltip::cue $widget $mesg
787            }
788        }
789        default {
790            error "bad option \"$option\": should be coming, controls, now, start"
791        }
792    }
793}
794
795# ----------------------------------------------------------------------
796# USAGE: _plot ?<index> <options> <index> <options>...?
797#
798# Used internally to update the plot shown in the current result
799# viewer whenever the resultset settings have changed.  Causes the
800# desired results to show up on screen.
801# ----------------------------------------------------------------------
802itcl::body Rappture::Analyzer::_plot {args} {
803    set _plotlist $args
804
805    set page [$itk_component(resultselector) value]
806    set page [$itk_component(resultselector) translate $page]
807    if {"" != $page} {
808        set f [$itk_component(resultpages) page $page]
809        $f.rviewer plot clear
810        foreach {index opts} $_plotlist {
811            $f.rviewer plot add $index $opts
812        }
813    }
814}
815
816# ----------------------------------------------------------------------
817# USAGE: _reorder <compList>
818#
819# Used internally to change the order of a series of output components
820# found in the <output> section.  Moves the <log> elements to the end
821# and returns the updated list.
822# ----------------------------------------------------------------------
823itcl::body Rappture::Analyzer::_reorder {comps} {
824    set i 0
825    set max [llength $comps]
826    while {$i < $max} {
827        set c [lindex $comps $i]
828        if {[string match log* $c]} {
829            set comps [lreplace $comps $i $i]
830            lappend comps $c
831            incr max -1
832        } else {
833            incr i
834        }
835    }
836    return $comps
837}
838
839# ----------------------------------------------------------------------
840# USAGE: _autoLabel <xmlobj> <path> <title> <cntVar>
841#
842# Used internally to check for an about.label property at the <path>
843# in <xmlobj>.  If this object doesn't have a label, then one is
844# supplied using the given <title>.  The <cntVar> is an array of
845# counters in the calling scopes for titles that have been used
846# in the past.  This is used to create titles like "Plot #2" the
847# second time it is encountered.
848#
849# The <xmlobj> is updated so that the label is inserted directly in
850# the tree.
851# ----------------------------------------------------------------------
852itcl::body Rappture::Analyzer::_autoLabel {xmlobj path title cntVar} {
853    upvar $cntVar counters
854
855    set group [$xmlobj get $path.about.group]
856    set label [$xmlobj get $path.about.label]
857    if {"" == $label} {
858        # no label -- make one up using the title specified
859        if {![info exists counters($group-$title)]} {
860            set counters($group-$title) 1
861            set label $title
862        } else {
863            set label "$title (#[incr counters($group-$title)])"
864        }
865        $xmlobj put $path.about.label $label
866    } else {
867        # handle the case of two identical labels in <output>
868        if {![info exists counters($group-$label)]} {
869            set counters($group-$label) 1
870        } else {
871            set label "$label (#[incr counters($group-$label)])"
872            $xmlobj put $path.about.label $label
873        }
874    }
875    return $label
876}
877
878# ----------------------------------------------------------------------
879# USAGE: _fixResult
880#
881# Used internally to change the result page being displayed whenever
882# the user selects a page from the results combobox.
883# ----------------------------------------------------------------------
884itcl::body Rappture::Analyzer::_fixResult {} {
885    set name [$itk_component(resultselector) value]
886    set page ""
887    if {"" != $name} {
888        set page [$itk_component(resultselector) translate $name]
889    }
890    if {$page == "@download"} {
891        # put the combobox back to its last value
892        $itk_component(resultselector) component entry configure -state normal
893        $itk_component(resultselector) component entry delete 0 end
894        $itk_component(resultselector) component entry insert end $_lastlabel
895        $itk_component(resultselector) component entry configure -state disabled
896        # perform the actual download
897        download start $itk_component(download)
898    } elseif {$page == "---"} {
899        # put the combobox back to its last value
900        $itk_component(resultselector) component entry configure -state normal
901        $itk_component(resultselector) component entry delete 0 end
902        $itk_component(resultselector) component entry insert end $_lastlabel
903        $itk_component(resultselector) component entry configure -state disabled
904    } elseif {$page != ""} {
905        set _lastlabel $name
906        set win [winfo toplevel $itk_component(hull)]
907        blt::busy hold $win
908        $itk_component(resultpages) current $page
909
910        set f [$itk_component(resultpages) page $page]
911        $f.rviewer plot clear
912        eval $f.rviewer plot add $_plotlist
913        blt::busy release [winfo toplevel $itk_component(hull)]
914    }
915}
916
917# ----------------------------------------------------------------------
918# USAGE: _fixSize
919#
920# Used internally to change the size of the result set area whenever
921# a new control appears.  Adjusts the size available for the result
922# set up to some maximum.
923# ----------------------------------------------------------------------
924itcl::body Rappture::Analyzer::_fixSize {} {
925    set ht [winfo height $itk_component(results)]
926    if {$ht <= 1} { set ht [winfo reqheight $itk_component(results)] }
927    set cntlht [$itk_component(resultset) size -controlarea]
928    set frac [expr {double($cntlht)/$ht}]
929
930    if {$frac < 0.4} {
931        $itk_component(results) fraction end $frac
932    }
933    _fixSimControl
934}
935
936# ----------------------------------------------------------------------
937# USAGE: _simState <boolean> ?<message>? ?<settings>?
938#
939# Used internally to change the "Simulation" button on or off.
940# If the <boolean> is on, then any <message> and <settings> are
941# displayed as well.  If the <boolean> is off, then only display
942# the message. The <message> is a note to the user about
943# what will be simulated, and the <settings> are a list of
944# tool parameter settings of the form {path1 val1 path2 val2 ...}.
945# When these are in place, the next Simulate operation will use
946# these settings.  This helps fill in missing data values.
947# ----------------------------------------------------------------------
948itcl::body Rappture::Analyzer::_simState {state args} {
949    if {$state} {
950        $itk_interior.simol configure \
951            -background $itk_option(-simcontrolactiveoutline)
952        configure -simcontrolcolor $itk_option(-simcontrolactivebackground)
953
954        $itk_component(abort) configure -state disabled
955        $itk_component(simulate) configure -state normal \
956            -command [itcl::code $this simulate]
957
958        #
959        # If there's a special message, then put it up next to the button.
960        #
961        set mesg [lindex $args 0]
962        if {"" != $mesg} {
963            $itk_component(simstatus) configure -state normal
964            $itk_component(simstatus) delete 1.0 end
965            $itk_component(simstatus) insert end $mesg
966
967            #
968            # If there are any settings, then install them in the
969            # "Simulate" button.  Also, pop them up as a tooltip
970            # for the message.
971            #
972            set settings [lindex $args 1]
973            if {[llength $settings] > 0} {
974                $itk_component(simulate) configure \
975                    -command [eval itcl::code $this simulate $settings]
976
977                set details ""
978                foreach {path val} $settings {
979                    set str [$_tool xml get $path.about.label]
980                    if {"" == $str} {
981                        set str [$_tool xml element -as id $path]
982                    }
983                    append details "$str = $val\n"
984                }
985                set details [string trim $details]
986
987                Rappture::Tooltip::for $itk_component(simstatus) $details
988                $itk_component(simstatus) insert end " "
989                $itk_component(simstatus) insert end "(details...)" popup
990            }
991            $itk_component(simstatus) configure -state disabled
992        }
993    } else {
994        if {"" != $itk_option(-simcontrolbackground)} {
995            set simcbg $itk_option(-simcontrolbackground)
996        } else {
997            set simcbg $itk_option(-background)
998        }
999        $itk_interior.simol configure \
1000            -background $itk_option(-simcontroloutline)
1001        configure -simcontrolcolor $simcbg
1002
1003        $itk_component(simulate) configure -state disabled
1004        $itk_component(abort) configure -state normal
1005
1006        $itk_component(simstatus) configure -state normal
1007        $itk_component(simstatus) delete 1.0 end
1008        set mesg [lindex $args 0]
1009        if {"" != $mesg} {
1010            $itk_component(simstatus) insert end $mesg
1011        }
1012        $itk_component(simstatus) configure -state disabled
1013    }
1014}
1015
1016# ----------------------------------------------------------------------
1017# USAGE: _simOutput <message>
1018#
1019# Invoked automatically whenever output comes in while running the
1020# tool.  Extracts any =RAPPTURE-???=> messages from the output and
1021# sends the output to the display.  For example, any
1022# =RAPPTURE-PROGRESS=> message pops up the progress meter and updates
1023# it to show the latest progress message.  This is useful for
1024# long-running tools, to let the user know how much longer the
1025# simulation will take.
1026# ----------------------------------------------------------------------
1027itcl::body Rappture::Analyzer::_simOutput {message} {
1028    #
1029    # Scan through and pick out any =RAPPTURE-PROGRESS=> messages first.
1030    #
1031    while {[regexp -indices \
1032               {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)} $message \
1033                match percent mesg]} {
1034
1035        foreach {i0 i1} $percent break
1036        set percent [string range $message $i0 $i1]
1037
1038        foreach {i0 i1} $mesg break
1039        set mesg [string range $message $i0 $i1]
1040
1041        pack $itk_component(progress) -fill x -padx 10 -pady 10
1042        $itk_component(progress) settings -percent $percent -message $mesg
1043
1044        foreach {i0 i1} $match break
1045        set message [string replace $message $i0 $i1]
1046    }
1047
1048    #
1049    # Break up the remaining lines according to =RAPPTURE-ERROR=> messages.
1050    # Show errors in a special color.
1051    #
1052    $itk_component(runinfo) configure -state normal
1053
1054    while {[regexp -indices \
1055               {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)} $message \
1056                match type mesg]} {
1057
1058        foreach {i0 i1} $match break
1059        set first [string range $message 0 [expr {$i0-1}]]
1060        if {[string length $first] > 0} {
1061            $itk_component(runinfo) insert end $first
1062            $itk_component(runinfo) insert end \n
1063        }
1064
1065        foreach {t0 t1} $type break
1066        set type [string range $message $t0 $t1]
1067        foreach {m0 m1} $mesg break
1068        set mesg [string range $message $m0 $m1]
1069        if {[string length $mesg] > 0 && $type != "RUN"} {
1070            $itk_component(runinfo) insert end $mesg $type
1071            $itk_component(runinfo) insert end \n $type
1072        }
1073
1074        set message [string range $message [expr {$i1+1}] end]
1075    }
1076
1077    if {[string length $message] > 0} {
1078        $itk_component(runinfo) insert end $message
1079        if {[$itk_component(runinfo) get end-2char] != "\n"} {
1080            $itk_component(runinfo) insert end "\n"
1081        }
1082        $itk_component(runinfo) see end
1083    }
1084    $itk_component(runinfo) configure -state disabled
1085}
1086
1087# ----------------------------------------------------------------------
1088# USAGE: _resultTooltip
1089#
1090# Used internally to build the tooltip string displayed for the
1091# result selector.  If the current page has an associated description,
1092# then it is displayed beneath the result.
1093#
1094# Returns the string for the tooltip.
1095# ----------------------------------------------------------------------
1096itcl::body Rappture::Analyzer::_resultTooltip {} {
1097    set tip ""
1098    set name [$itk_component(resultselector) value]
1099    if {[info exists _label2desc($name)] &&
1100         [string length $_label2desc($name)] > 0} {
1101        append tip "$_label2desc($name)\n\n"
1102    }
1103    if {[array size _label2page] > 1} {
1104        append tip "Use this control to display other output results."
1105    }
1106    return $tip
1107}
1108
1109# ----------------------------------------------------------------------
1110# USAGE: _fixSimControl
1111#
1112# Used internally to add or remove the simulation control at the
1113# top of the analysis area.  This is controlled by the -simcontrol
1114# option.
1115# ----------------------------------------------------------------------
1116itcl::body Rappture::Analyzer::_fixSimControl {} {
1117    switch -- $itk_option(-simcontrol) {
1118        on {
1119            pack $itk_interior.simol -fill x -before $itk_interior.nb
1120        }
1121        off {
1122            pack forget $itk_interior.simol
1123        }
1124        auto {
1125            #
1126            # If we have two or more radiodials, then there is a
1127            # chance of encountering a combination of parameters
1128            # with no data, requiring simulation.
1129            #
1130            if {[$itk_component(resultset) size -controls] >= 2} {
1131                pack $itk_interior.simol -fill x -before $itk_interior.nb
1132            } else {
1133                pack forget $itk_interior.simol
1134            }
1135        }
1136        default {
1137            error "bad value \"$itk_option(-simcontrol)\": should be on, off, auto"
1138        }
1139    }
1140}
1141
1142# ----------------------------------------------------------------------
1143# USAGE: _fixNotebook
1144#
1145# Used internally to switch the active notebook page
1146# ----------------------------------------------------------------------
1147itcl::body Rappture::Analyzer::_fixNotebook {} {
1148    switch -- $itk_option(-notebookpage) {
1149        about {
1150            $itk_component(notebook) current about
1151        }
1152        simulate {
1153            $itk_component(notebook) current simulate
1154        }
1155        analyze {
1156            $itk_component(notebook) current analyze
1157        }
1158        default {
1159            error "bad value \"$itk_option(-notebookpage)\": should be about, simulate, analyze"
1160        }
1161    }
1162}
1163
1164# ----------------------------------------------------------------------
1165# USAGE: _isPdbTrajectory <data>
1166#
1167# Used internally to determine whether pdb or lammps data represents a
1168# trajectory rather than a single frame
1169# ----------------------------------------------------------------------
1170itcl::body Rappture::Analyzer::_isPdbTrajectory {data} {
1171    if { [llength $data]  == 0 } {
1172        return 0
1173    }
1174    set nModels 0
1175    foreach line $data {
1176        if { [string match "MODEL*" $line] }  {
1177            incr nModels
1178            if { $nModels > 1 } {
1179                # Stop if more than one model found.  No need to count them
1180                # all.
1181                return 1
1182            }
1183        }
1184    }
1185    return 0
1186}
1187
1188# ----------------------------------------------------------------------
1189# USAGE: _isLammpsTrajectory <data>
1190#
1191# Used internally to determine whether pdb or lammps data represents a
1192# trajectory rather than a single frame
1193# ----------------------------------------------------------------------
1194itcl::body Rappture::Analyzer::_isLammpsTrajectory { data } {
1195    if { [llength $data]  == 0 } {
1196        return 0
1197    }
1198    set nModels 0
1199    foreach line $data {
1200        if { [regexp {^[\t ]*ITEM:[ \t]+TIMESTEP} $line] } {
1201            incr nModels
1202            if { $nModels > 1 } {
1203                # Stop if more than one model found.  No need to count them
1204                # all.
1205                return 1
1206            }
1207        }
1208    }
1209    return 0
1210}
1211
1212# ----------------------------------------------------------------------
1213# USAGE: _pdbToSequence <xmlobj> ?<path>?
1214#
1215# If the molecule element is a trajectory, delete the original
1216# and create a sequence of individual molecules.
1217# Used internally to detect any molecule output elements that contain
1218# trajectory data.  Trajectories will be converted into sequences of
1219# individual molecules.  All other elements will be unaffected. Scans
1220# the entire xml tree if a starting path is not specified.
1221# ----------------------------------------------------------------------
1222itcl::body Rappture::Analyzer::_pdbToSequence {xmlobj path id child data} {
1223
1224    set seqLabel [$xmlobj get ${child}.about.label]
1225    set descr    [$xmlobj get ${child}.about.description]
1226    set formula  [$xmlobj get ${child}.components.molecule.formula]
1227    $xmlobj remove $child
1228
1229    set sequence  $path.sequence($id)
1230    $xmlobj put ${sequence}.about.label $seqLabel
1231    $xmlobj put ${sequence}.about.description $descr
1232    $xmlobj put ${sequence}.index.label "Frame"
1233
1234    set frameNum 0
1235    set numLines [llength $data]
1236    for { set i 0 } { $i < $numLines } { incr i } {
1237        set line [lindex $data $i]
1238        set line [string trim $line]
1239        set contents {}
1240        if { [string match "MODEL*" $line] } {
1241            # Save the contents until we get an ENDMDL record.
1242            for {} { $i < $numLines } { incr i } {
1243                set line [lindex $data $i]
1244                set line [string trim $line]
1245                if { $line == "" } {
1246                    continue;           # Skip blank lines.
1247                }
1248                if { [string match "ENDMDL*" $line] } {
1249                    break;
1250                }
1251                append contents $line\n
1252            }
1253            set frame ${sequence}.element($frameNum)
1254            $xmlobj put ${frame}.index $frameNum
1255           
1256            set molecule ${frame}.structure.components.molecule
1257            $xmlobj put ${molecule}.pdb $contents
1258            $xmlobj put ${molecule}.formula $formula
1259            incr frameNum
1260        }
1261    }
1262}
1263
1264# ----------------------------------------------------------------------
1265# USAGE: _lammpsToSequence <xmlobj> ?<path>?
1266#
1267# If the molecule element is a trajectory, delete the original
1268# and create a sequence of individual molecules.
1269# Used internally to detect any molecule output elements that contain
1270# trajectory data.  Trajectories will be converted into sequences of
1271# individual molecules.  All other elements will be unaffected. Scans
1272# the entire xml tree if a starting path is not specified.
1273# ----------------------------------------------------------------------
1274itcl::body Rappture::Analyzer::_lammpsToSequence {xmlobj path id child data} {
1275
1276    set seqLabel [$xmlobj get ${child}.about.label]
1277    set descr    [$xmlobj get ${child}.about.description]
1278    set typemap  [$xmlobj get ${child}.components.molecule.lammpstypemap]
1279    $xmlobj remove $child
1280
1281    set sequence ${path}.sequence($id)
1282    $xmlobj put ${sequence}.about.label $seqLabel
1283    $xmlobj put ${sequence}.about.description $descr
1284    $xmlobj put ${sequence}.index.label "Frame"
1285
1286    set frameNum 0
1287    set frameContents ""
1288    set inModel 0
1289    foreach line $data {
1290        set line [string trim $line]
1291        if { $line == "" } {
1292            continue;                   # Skip blank lines
1293        }
1294        if {[regexp {^[\t ]*ITEM:[ \t]+ATOMS} $line] } {
1295            if { $inModel && $frameContents != "" } {
1296                set frame ${sequence}.element($frameNum)
1297                $xmlobj put ${frame}.index $frameNum
1298               
1299                set molecule ${frame}.structure.components.molecule
1300                $xmlobj put ${molecule}.lammps $frameContents
1301                $xmlobj put ${molecule}.lammpstypemap $typemap
1302               
1303                incr frameNum
1304                set frameContents ""
1305            }
1306            set inModel 1
1307        } elseif { [scan $line "%d %d %f %f %f" a b c d e] == 5 } {
1308            if { !$inModel } {
1309                puts stderr "found \"$line\" without previous \"ITEM: ATOMS\""
1310                set inModel 1
1311            }
1312            append frameContents $line\n
1313        }
1314    }
1315    if { $frameContents != "" } {
1316        set frame ${sequence}.element($frameNum)
1317        $xmlobj put ${frame}.index $frameNum
1318       
1319        set molecule ${frame}.structure.components.molecule
1320        $xmlobj put ${molecule}.lammps $frameContents
1321        $xmlobj put ${molecule}.lammpstypemap $typemap
1322    }
1323}
1324
1325# ----------------------------------------------------------------------
1326# USAGE: _trajToSequence <xmlobj> ?<path>?
1327#
1328#       Check for PDB and LAMMPS trajectories in molecule data and rewrite
1329#       the individual models as a sequence of molecules.  Used internally
1330#       to detect any molecule output elements that contain trajectory data.
1331#       Trajectories will be converted into sequences of individual molecules.
1332#       All other elements will be unaffected. Scans the entire xml tree if a
1333#       starting path is not specified. 
1334#
1335# ----------------------------------------------------------------------
1336itcl::body Rappture::Analyzer::_trajToSequence {xmlobj {path ""}} {
1337    # Remove leading dot from path, if present.
1338    if { [string index $path 0] == "." } {
1339        set path [string range $path 1 end]
1340    }
1341    # Otherwise check each child.
1342    foreach child [$xmlobj children $path] {
1343        set current ${path}.${child}
1344        if { [string match "structure*" $child] } {
1345            set isTraj [$xmlobj get ${current}.components.molecule.trajectory]
1346            if { $isTraj == "" || !$isTraj } {
1347                continue;               # Not a trajectory.
1348            }
1349            # Look for trajectory if molecule element found.  Check both pdb
1350            # data and lammps data.
1351            set type [$xmlobj element -as type $current]
1352            set id   [$xmlobj element -as id $current]
1353            set pdbdata    [$xmlobj get ${current}.components.molecule.pdb]
1354            set lammpsdata [$xmlobj get ${current}.components.molecule.lammps]
1355            if { $pdbdata != "" && $lammpsdata != "" } {
1356                puts stderr \
1357                    "found both <pdb> and <lammps> elements: picking pdb"
1358            }
1359            set pdbdata [split $pdbdata \n]
1360            set lammpsdata [split $lammpsdata \n]
1361            if { [_isPdbTrajectory $pdbdata] } {
1362                _pdbToSequence $xmlobj $path $id $current $pdbdata
1363            } elseif { [_isLammpsTrajectory $lammpsdata] } {
1364                _lammpsToSequence $xmlobj $path $id $current $lammpsdata
1365            }
1366            continue
1367        }
1368        if 0 {
1369        # Recurse over all child nodes.
1370        _trajToSequence $xmlobj $current
1371        }
1372    }
1373}
1374
1375# ----------------------------------------------------------------------
1376# CONFIGURATION OPTION: -simcontrol
1377#
1378# Controls whether or not the Simulate button is showing.  In some
1379# cases, it is not needed.
1380# ----------------------------------------------------------------------
1381itcl::configbody Rappture::Analyzer::simcontrol {
1382    _fixSimControl
1383}
1384
1385# ----------------------------------------------------------------------
1386# CONFIGURATION OPTION: -notebookpage
1387#
1388# Controls which page of the analyzer notebook is shown. It is
1389# particularly needed when using rerun, when you don't want to
1390# "simulate -ifneeded" because an actual simulation might be
1391# kicked off due to differences between tool.xml and run.xml
1392# ----------------------------------------------------------------------
1393itcl::configbody Rappture::Analyzer::notebookpage {
1394    _fixNotebook
1395}
Note: See TracBrowser for help on using the repository browser.