source: branches/1.4/gui/scripts/visviewer.tcl @ 5143

Last change on this file since 5143 was 5143, checked in by ldelgass, 9 years ago

Merge r5134:5135,r5137:5141 from trunk

File size: 40.5 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2
3# ----------------------------------------------------------------------
4#  VisViewer -
5#
6#  This class is the base class for the various visualization viewers
7#  that use the nanoserver render farm.
8#
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16
17itcl::class ::Rappture::VisViewer {
18    inherit itk::Widget
19
20    itk_option define -sendcommand sendCommand SendCommand ""
21    itk_option define -receivecommand receiveCommand ReceiveCommand ""
22
23    private common _servers         ;# array of visualization server lists
24    set _servers(nanovis) "localhost:2000"
25    set _servers(pymol)   "localhost:2020"
26    set _servers(vmdmds)  "localhost:2018"
27    set _servers(vtkvis)  "localhost:2010"
28
29    private common _done            ;   # Used to indicate status of send.
30    private variable _buffer        ;   # buffer for incoming/outgoing commands
31    private variable _outbuf       ;    # buffer for outgoing commands
32    private variable _blockOnWrite 0;   # Should writes to socket block?
33    private variable _initialized
34    private variable _isOpen 0
35    private variable _afterId -1
36    private variable _icon 0
37    private variable _trace 0        ;    # Protocol tracing for console
38    private variable _logging 0      ;    # Command logging to file
39    # Number of milliseconds to wait before idle timeout.  If greater than 0,
40    # automatically disconnect from the visualization server when idle timeout
41    # is reached.
42    private variable _idleTimeout 43200000; # 12 hours
43    #private variable _idleTimeout 5000;    # 5 seconds
44    #private variable _idleTimeout 0;       # No timeout
45
46    protected variable _serverType "???";# Type of server.
47    protected variable _sid ""      ;   # socket connection to server
48    protected variable _maxConnects 100
49    protected variable _buffering 0
50    protected variable _cmdSeq 0     ;    # Command sequence number
51    protected variable _dispatcher "";  # dispatcher for !events
52    protected variable _hosts ""    ;   # list of hosts for server
53    protected variable _parser ""   ;   # interpreter for incoming commands
54    protected variable _image
55    protected variable _hostname
56    protected variable _numConnectTries 0
57    protected variable _debugConsole 0
58    protected variable _reportClientInfo 1
59    # Number of milliscends to wait for server reply before displaying wait
60    # dialog.  If set to 0, dialog is never displayed.
61    protected variable _waitTimeout 0
62
63    constructor { servers args } {
64        # defined below
65    }
66    destructor {
67        # defined below
68    }
69    # Used internally only.
70    private method BuildConsole {}
71    private method DebugConsole {}
72    private method HideConsole {}
73    private method ReceiveHelper {}
74    private method SendDebugCommand {}
75    private method SendHelper {}
76    private method SendHelper.old {}
77    private method ServerDown {}
78    private method Shuffle { servers }
79    private method TraceComm { channel {data {}} }
80    private method WaitDialog { state }
81    private method Waiting { option widget }
82
83    protected method CheckConnection {}
84    protected method Color2RGB { color }
85    protected method ColorsToColormap { colors }
86    protected method Connect { servers }
87    protected method DisableWaitDialog {}
88    protected method Disconnect {}
89    protected method EnableWaitDialog { timeout }
90    protected method Euler2XYZ { theta phi psi }
91    protected method Flush {}
92    protected method GetColormapList { args }
93    protected method HandleError { args }
94    protected method HandleOk { args }
95    protected method IsConnected {}
96    protected method ReceiveBytes { nbytes }
97    protected method ReceiveEcho { channel {data ""} }
98    protected method SendBytes { bytes }
99    protected method SendCmd { string }
100    protected method SendData { bytes }
101    protected method SendEcho { channel {data ""} }
102    protected method StartBufferingCommands {}
103    protected method StartWaiting {}
104    protected method StopBufferingCommands {}
105    protected method StopWaiting {}
106    protected method ToggleConsole {}
107
108    private proc CheckNameList { namelist }  {
109        foreach host $namelist {
110            set pattern {^[a-zA-Z0-9\.]+:[0-9]}
111            if { ![regexp $pattern $host match] } {
112                error "bad visualization server address \"$host\": should be host:port,host:port,..."
113            }
114        }
115    }
116    public proc GetServerList { type } {
117        return $_servers($type)
118    }
119    public proc SetServerList { type namelist } {
120        # Convert the comma separated list into a Tcl list.  OGRE also adds
121        # a trailing comma that we want to ignore.
122        regsub -all "," $namelist " " namelist
123        CheckNameList $namelist
124        set _servers($type) $namelist
125    }
126    public proc RemoveServerFromList { type server } {
127        if { ![info exists _servers($type)] } {
128            error "unknown server type \"$type\""
129        }
130        set i [lsearch $_servers($type) $server]
131        if { $i < 0 } {
132            return
133        }
134        set _servers($type) [lreplace $_servers($type) $i $i]
135    }
136    public proc SetPymolServerList { namelist } {
137        SetServerList "pymol" $namelist
138    }
139    public proc SetNanovisServerList { namelist } {
140        SetServerList "nanovis" $namelist
141    }
142    public proc SetVtkServerList { namelist } {
143        SetServerList "vtk" $namelist
144    }
145}
146
147itk::usual Panedwindow {
148    keep -background -cursor
149}
150
151# ----------------------------------------------------------------------
152# CONSTRUCTOR
153# ----------------------------------------------------------------------
154itcl::body Rappture::VisViewer::constructor { servers args } {
155
156    Rappture::dispatcher _dispatcher
157    $_dispatcher register !serverDown
158    $_dispatcher dispatch $this !serverDown "[itcl::code $this ServerDown]; list"
159    $_dispatcher register !timeout
160    $_dispatcher dispatch $this !timeout "[itcl::code $this Disconnect]; list"
161
162    $_dispatcher register !waiting
163
164    CheckNameList $servers
165    set _buffer(in) ""
166    set _buffer(out) ""
167    #
168    # Create a parser to handle incoming requests
169    #
170    set _parser [interp create -safe]
171    foreach cmd [$_parser eval {info commands}] {
172        $_parser hide $cmd
173    }
174    # Add default handlers for "ok" acknowledgement and server errors.
175    $_parser alias ok       [itcl::code $this HandleOk]
176    $_parser alias viserror [itcl::code $this HandleError]
177
178    #
179    # Set up the widgets in the main body
180    #
181    option add hull.width hull.height
182    pack propagate $itk_component(hull) no
183
184    itk_component add main {
185        Rappture::SidebarFrame $itk_interior.main -resizeframe 1
186    }
187    pack $itk_component(main) -expand yes -fill both
188    set f [$itk_component(main) component frame]
189
190    itk_component add plotarea {
191        frame $f.plotarea -highlightthickness 0 -background black
192    } {
193        ignore -background
194    }
195    pack $itk_component(plotarea) -fill both -expand yes
196    set _image(plot) [image create photo]
197
198    global env
199    if { [info exists env(VISRECORDER)] } {
200        set _logging 1
201        if { [file exists /tmp/recording.log] } {
202            file delete /tmp/recording.log
203        }
204    }
205    if { [info exists env(VISTRACE)] } {
206        set _trace 1
207    }
208    eval itk_initialize $args
209}
210
211#
212# destructor --
213#
214itcl::body Rappture::VisViewer::destructor {} {
215    $_dispatcher cancel !timeout
216    interp delete $_parser
217    array unset _done $this
218}
219
220#
221# Shuffle --
222#
223#   Shuffle the list of server hosts.
224#
225itcl::body Rappture::VisViewer::Shuffle { hosts } {
226    set randomHosts {}
227    set ticks [clock clicks]
228    expr {srand($ticks)}
229    for { set i [llength $hosts] } { $i > 0 } { incr i -1 } {
230        set index [expr {round(rand()*$i - 0.5)}]
231        if { $index == $i } {
232            set index [expr $i - 1]
233        }
234        lappend randomHosts [lindex $hosts $index]
235        set hosts [lreplace $hosts $index $index]
236    }
237    return $randomHosts
238}
239
240#
241# ServerDown --
242#
243#    Used internally to let the user know when the connection to the
244#    visualization server has been lost.  Puts up a tip encouraging the
245#    user to press any control to reconnect.
246#
247itcl::body Rappture::VisViewer::ServerDown {} {
248    if { [info exists itk_component(plotarea)] } {
249        set x [expr {[winfo rootx $itk_component(plotarea)]+10}]
250        set y [expr {[winfo rooty $itk_component(plotarea)]+10}]
251    } else {
252        set x 0; set y 0
253    }
254    Rappture::Tooltip::cue @$x,$y "Lost connection to visualization server.  This happens sometimes when there are too many users and the system runs out of memory.\n\nTo reconnect, reset the view or press any other control.  Your picture should come right back up."
255}
256
257#
258# Connect --
259#
260#    Connect to the visualization server (e.g. nanovis, pymolproxy).
261#    Creates an event callback that is triggered when we are idle
262#    (no I/O with the server) for some specified time.
263#
264itcl::body Rappture::VisViewer::Connect { servers } {
265    blt::busy hold $itk_component(hull) -cursor watch
266
267    if { $_numConnectTries > $_maxConnects } {
268        blt::busy release $itk_component(hull)
269        set x [expr {[winfo rootx $itk_component(hull)]+10}]
270        set y [expr {[winfo rooty $itk_component(hull)]+10}]
271        Rappture::Tooltip::cue @$x,$y "Exceeded maximum number of connection attmepts to any $_serverType visualization server. Please contact support."
272        return 0;
273    }
274    foreach server [Shuffle $servers] {
275        puts stderr "connecting to $server..."
276        foreach {hostname port} [split $server ":"] break
277        if { [catch {socket $hostname $port} _sid] != 0 } {
278            set _sid ""
279            RemoveServerFromList $_serverType $server
280            continue
281        }
282        incr _numConnectTries
283        set _hostname $server
284        fconfigure $_sid -translation binary -encoding binary
285
286        # Read back the server identification string.
287        if { [gets $_sid data] <= 0 } {
288            set _sid ""
289            puts stderr "ERORR reading from server data=($data)"
290            RemoveServerFromList $_serverType $server
291            continue
292        }
293        puts stderr "Render server is $data"
294        # We're connected. Cancel any pending serverDown events and
295        # release the busy window over the hull.
296        $_dispatcher cancel !serverDown
297        if { $_idleTimeout > 0 } {
298            $_dispatcher event -after $_idleTimeout !timeout
299        }
300        blt::busy release $itk_component(hull)
301        fconfigure $_sid -buffering line
302        fileevent $_sid readable [itcl::code $this ReceiveHelper]
303        return 1
304    }
305    blt::busy release $itk_component(hull)
306    set x [expr {[winfo rootx $itk_component(hull)]+10}]
307    set y [expr {[winfo rooty $itk_component(hull)]+10}]
308    Rappture::Tooltip::cue @$x,$y "Can't connect to any $_serverType visualization server.  This may be a network problem.  Wait a few moments and try resetting the view."
309    return 0
310}
311
312#
313# Disconnect --
314#
315#    Clients use this method to disconnect from the current rendering
316#    server.  Cancel any pending idle timeout events.
317#
318itcl::body Rappture::VisViewer::Disconnect {} {
319    after cancel $_afterId
320    $_dispatcher cancel !timeout
321    catch {close $_sid}
322    set _sid ""
323    set _buffer(in) ""
324    set _outbuf ""
325    set _cmdSeq 0
326}
327
328#
329# IsConnected --
330#
331#    Indicates if we are currently connected to a server.
332#
333itcl::body Rappture::VisViewer::IsConnected {} {
334    if { $_sid == "" } {
335        return 0
336    }
337    if { [eof $_sid] } {
338        set _sid ""
339        return 0
340    }
341    return 1
342}
343
344#
345# CheckConection --
346#
347#   This routine is called whenever we're about to send/receive data on
348#   the socket connection to the visualization server.  If we're connected,
349#   then reset the timeout event.  Otherwise try to reconnect to the
350#   visualization server.
351#
352itcl::body Rappture::VisViewer::CheckConnection {} {
353    $_dispatcher cancel !timeout
354    if { $_idleTimeout > 0 } {
355        $_dispatcher event -after $_idleTimeout !timeout
356    }
357    if { [IsConnected] } {
358        return 1
359    }
360    if { $_sid != "" } {
361        fileevent $_sid writable ""
362    }
363    # If we aren't connected, assume it's because the connection to the
364    # visualization server broke. Try to open a connection and trigger a
365    # rebuild.
366    $_dispatcher cancel !serverDown
367    set x [expr {[winfo rootx $itk_component(plotarea)]+10}]
368    set y [expr {[winfo rooty $itk_component(plotarea)]+10}]
369    Rappture::Tooltip::cue @$x,$y "Connecting..."
370    set code [catch { Connect } ok]
371    if { $code == 0 && $ok} {
372        $_dispatcher event -idle !rebuild
373        Rappture::Tooltip::cue hide
374    } else {
375        Rappture::Tooltip::cue @$x,$y "Can't connect to any $_serverType visualization server.  This may be a network problem.  Wait a few moments and try resetting the view."
376        return 0
377    }
378    return 1
379}
380
381#
382# Flush --
383#
384#    Flushes the socket.
385#
386itcl::body Rappture::VisViewer::Flush {} {
387    if { [CheckConnection] } {
388        flush $_sid
389    }
390}
391
392
393#
394# SendHelper --
395#
396#   Helper routine called from a file event to send data when the
397#   connection is writable (i.e. not blocked).  Sets a magic variable
398#   _done($this) when we're done.
399#
400itcl::body Rappture::VisViewer::SendHelper {} {
401    if { ![CheckConnection] } {
402        return 0
403    }
404    puts -nonewline $_sid $_buffer(out)
405    flush $_sid
406    set _buffer(out) ""
407    set _done($this) 1;                 # Success
408}
409
410#
411# SendHelper.old --
412#
413#   Helper routine called from a file event to send data when the
414#   connection is writable (i.e. not blocked).  Sends data in chunks of 8k
415#   (or less).  Sets magic variable _done($this) to indicate that we're
416#   either finished (success) or could not send bytes to the server
417#   (failure).
418#
419itcl::body Rappture::VisViewer::SendHelper.old {} {
420    if { ![CheckConnection] } {
421        return 0
422    }
423    set bytesLeft [string length $_buffer(out)]
424    if { $bytesLeft > 0} {
425        set chunk [string range $_buffer(out) 0 8095]
426        set _buffer(out)  [string range $_buffer(out) 8096 end]
427        incr bytesLeft -8096
428        set code [catch {
429            if { $bytesLeft > 0 } {
430                puts -nonewline $_sid $chunk
431            } else {
432                puts $_sid $chunk
433            }
434        } err]
435        if { $code != 0 } {
436            puts stderr "error sending data to $_sid: $err"
437            Disconnect
438            set _done($this) 0;     # Failure
439        }
440    } else {
441        set _done($this) 1;     # Success
442    }
443}
444
445#
446# SendBytes --
447#
448#   Send a a string to the visualization server.
449#
450itcl::body Rappture::VisViewer::SendBytes { bytes } {
451    SendEcho >>line $bytes
452    if { ![CheckConnection] } {
453        return 0
454    }
455    StartWaiting
456    # Even though the data is sent in only 1 "puts", we need to verify that
457    # the server is ready first.  Wait for the socket to become writable
458    # before sending anything.
459    set _done($this) 1
460    if {$_trace && $_buffer(out) != ""} {
461        puts stderr "ERROR: re-entered SendBytes: buffer=([string range $_buffer(out) 0 70]...)"
462        puts stderr "New cmd $_cmdSeq: [string range $bytes 0 70]..."
463    }
464    set _buffer(out) $bytes
465    # There's problem when the user is interacting with the GUI at the
466    # same time we're trying to write to the server.  Don't want to
467    # block because, the GUI will look like it's dead.  We can start
468    # by putting a busy window over plot so that inadvertent things like
469    # mouse movements aren't received.
470    if {$_blockOnWrite} {
471        # Let's try this approach: allow a write to block so we don't
472        # re-enter SendBytes
473        SendHelper
474    } else {
475        # This can cause us to re-enter SendBytes during the tkwait, which
476        # is not safe because the _buffer will be clobbered
477        blt::busy hold $itk_component(main)
478        fileevent $_sid writable [itcl::code $this SendHelper]
479        tkwait variable ::Rappture::VisViewer::_done($this)
480        blt::busy release $itk_component(main)
481    }
482    set _buffer(out) ""
483    if { [IsConnected] } {
484        # The connection may have closed while we were writing to the server.
485        # This can happen if what we sent the server caused it to barf.
486        fileevent $_sid writable ""
487        flush $_sid
488    }
489    return $_done($this)
490}
491
492#
493# StartWaiting --
494#
495#    Display a waiting dialog after a timeout has passed
496#
497itcl::body Rappture::VisViewer::StartWaiting {} {
498    if { $_waitTimeout > 0 } {
499        after cancel $_afterId
500        set _afterId [after $_waitTimeout [itcl::code $this WaitDialog on]]
501    }
502}
503
504#
505# StopWaiting --
506#
507#    Take down waiting dialog
508#
509itcl::body Rappture::VisViewer::StopWaiting {} {
510    if { $_waitTimeout > 0 } {
511        WaitDialog off
512    }
513}
514
515itcl::body Rappture::VisViewer::EnableWaitDialog { value } {
516    set _waitTimeout $value
517}
518
519itcl::body Rappture::VisViewer::DisableWaitDialog {} {
520    set _waitTimeout 0
521}
522
523#
524# ReceiveBytes --
525#
526#    Read some number of bytes from the visualization server.
527#
528itcl::body Rappture::VisViewer::ReceiveBytes { size } {
529    if { ![CheckConnection] } {
530        return 0
531    }
532    set bytes [read $_sid $size]
533    ReceiveEcho <<line "<read $size bytes"
534    StopWaiting
535    return $bytes
536}
537
538#
539# ReceiveHelper --
540#
541#   Helper routine called from a file event when the connection is readable
542#   (i.e. a command response has been sent by the rendering server.  Reads
543#   the incoming command and executes it in a safe interpreter to handle the
544#   action.
545#
546#       Note: This routine currently only handles command responses from
547#         the visualization server.  It doesn't handle non-blocking
548#         reads from the visualization server.
549#
550#       nv>image -bytes 100000      yes
551#       ...following 100000 bytes...    no
552#
553#   Note: All commands from the render server are on one line.
554#         This is because the render server can send anything
555#         as an error message (restricted again to one line).
556#
557itcl::body Rappture::VisViewer::ReceiveHelper {} {
558    if { ![CheckConnection] } {
559        return 0
560    }
561    set n [gets $_sid line]
562
563    if { $n < 0 } {
564        Disconnect
565        return 0
566    }
567    set line [string trim $line]
568    if { $line == "" } {
569        return
570    }
571    if { [string compare -length 3 $line "nv>"] == 0 } {
572        ReceiveEcho <<line $line
573        if ($_trace) {
574            puts stderr "<<[string range $line 0 70]"
575        }
576        append _buffer(in) [string range $line 3 end]
577        append _buffer(in) "\n"
578        if {[info complete $_buffer(in)]} {
579            set request $_buffer(in)
580            set _buffer(in) ""
581            if { [catch {$_parser eval $request} err]  != 0 } {
582                global errorInfo
583                puts stderr "err=$err errorInfo=$errorInfo"
584            }
585        }
586    } elseif { [string compare -length 21 $line "NanoVis Server Error:"] == 0 ||
587               [string compare -length 20 $line "VtkVis Server Error:"] == 0} {
588        # this shows errors coming back from the engine
589        ReceiveEcho <<error $line
590        puts stderr "Render Server Error: $line\n"
591    } else {
592        # this shows errors coming back from the engine
593        ReceiveEcho <<error $line
594        puts stderr "Garbled message: $line\n"
595    }
596}
597
598#
599# Color2RGB --
600#
601#   Converts a color name to a list of r,g,b values needed for the engine.
602#   Each r/g/b component is scaled in the # range 0-1.
603#
604itcl::body Rappture::VisViewer::Color2RGB {color} {
605    foreach {r g b} [winfo rgb $itk_component(hull) $color] break
606    set r [expr {$r/65535.0}]
607    set g [expr {$g/65535.0}]
608    set b [expr {$b/65535.0}]
609    return [list $r $g $b]
610}
611
612#
613# Euler2XYZ --
614#
615#   Converts euler angles for the camera placement the to angles of
616#   rotation about the x/y/z axes, used by the engine.  Returns a list:
617#   {xangle, yangle, zangle}.
618#
619itcl::body Rappture::VisViewer::Euler2XYZ {theta phi psi} {
620    set xangle [expr {$theta-90.0}]
621    set yangle [expr {180.0-$phi}]
622    set zangle $psi
623    return [list $xangle $yangle $zangle]
624}
625
626#
627# SendEcho --
628#
629#     Used internally to echo sent data to clients interested in this widget.
630#     If the -sendcommand option is set, then it is invoked in the global scope
631#     with the <channel> and <data> values as arguments.  Otherwise, this does
632#     nothing.
633#
634itcl::body Rappture::VisViewer::SendEcho {channel {data ""}} {
635    if { $_logging }  {
636        set f [open "/tmp/recording.log" "a"]
637        fconfigure $f -translation binary -encoding binary
638        puts -nonewline $f $data
639        close $f
640    }
641    #puts stderr ">>($data)"
642    if {[string length $itk_option(-sendcommand)] > 0} {
643        uplevel #0 $itk_option(-sendcommand) [list $channel $data]
644    }
645}
646
647#
648# ReceiveEcho --
649#
650#     Echoes received data to clients interested in this widget.  If the
651#     -receivecommand option is set, then it is invoked in the global scope
652#     with the <channel> and <data> values as arguments.  Otherwise, this
653#     does nothing.
654#
655itcl::body Rappture::VisViewer::ReceiveEcho {channel {data ""}} {
656    #puts stderr "<<line $data"
657    if {[string length $itk_option(-receivecommand)] > 0} {
658        uplevel #0 $itk_option(-receivecommand) [list $channel $data]
659    }
660}
661
662itcl::body Rappture::VisViewer::WaitDialog { state } {
663    after cancel $_afterId
664    set _afterId -1
665    if { $state } {
666        if { [winfo exists $itk_component(plotarea).view.splash] } {
667            return
668        }
669        set inner [frame $itk_component(plotarea).view.splash]
670        $inner configure -relief raised -bd 2
671        label $inner.text1 -text "Working...\nPlease wait." \
672            -font "Arial 10"
673        label $inner.icon
674        pack $inner -expand yes -anchor c
675        blt::table $inner \
676            0,0 $inner.text1 -anchor w \
677            0,1 $inner.icon
678        Waiting start $inner.icon
679    } else {
680        if { ![winfo exists $itk_component(plotarea).view.splash] } {
681            return
682        }
683        Waiting stop $itk_component(plotarea).view.splash
684        destroy $itk_component(plotarea).view.splash
685    }
686}
687
688itcl::body Rappture::VisViewer::Waiting { option widget } {
689    switch -- $option {
690        "start" {
691            $_dispatcher dispatch $this !waiting \
692                "[itcl::code $this Waiting "next" $widget] ; list"
693            set _icon 0
694            $widget configure -image [Rappture::icon bigroller${_icon}]
695            $_dispatcher event -after 150 !waiting
696        }
697        "next" {
698            incr _icon
699            if { $_icon >= 8 } {
700                set _icon 0
701            }
702            $widget configure -image [Rappture::icon bigroller${_icon}]
703            $_dispatcher event -after 150 !waiting
704        }
705        "stop" {
706            $_dispatcher cancel !waiting
707        }
708    }
709}
710
711#
712# HideConsole --
713#
714#    Hide the debug console by withdrawing its toplevel window.
715#
716itcl::body Rappture::VisViewer::HideConsole {} {
717    set _debugConsole 0
718    DebugConsole
719}
720
721#
722# BuildConsole --
723#
724#    Create and pack the widgets that make up the debug console: a text
725#    widget to display the communication and an entry widget to type
726#    in commands to send to the render server.
727#
728itcl::body Rappture::VisViewer::BuildConsole {} {
729    toplevel .renderconsole
730    wm protocol .renderconsole WM_DELETE_WINDOW [itcl::code $this HideConsole]
731    set f .renderconsole
732    frame $f.send
733    pack $f.send -side bottom -fill x
734    label $f.send.l -text "Send:"
735    pack $f.send.l -side left
736    itk_component add command {
737        entry $f.send.e -background white
738    } {
739        ignore -background
740    }
741    pack $f.send.e -side left -expand yes -fill x
742    bind $f.send.e <Return> [itcl::code $this SendDebugCommand]
743    bind $f.send.e <KP_Enter> [itcl::code $this SendDebugCommand]
744    scrollbar $f.sb -orient vertical -command "$f.comm yview"
745    pack $f.sb -side right -fill y
746    itk_component add trace {
747        text $f.comm -wrap char -yscrollcommand "$f.sb set" -background white
748    } {
749        ignore -background
750    }
751    pack $f.comm -expand yes -fill both
752    bind $f.comm <Control-F1> [itcl::code $this ToggleConsole]
753    bind $f.comm <Enter> [list focus %W]
754    bind $f.send.e <Control-F1> [itcl::code $this ToggleConsole]
755
756    $itk_component(trace) tag configure error -foreground red \
757        -font -*-courier-medium-o-normal-*-*-120-*
758    $itk_component(trace) tag configure incoming -foreground blue
759}
760
761#
762# ToggleConsole --
763#
764#    This is used by derived classes to turn on/off debuging.  It's
765#    up the to derived class to decide how to turn on/off debugging.
766#
767itcl::body Rappture::VisViewer::ToggleConsole {} {
768    if { $_debugConsole } {
769        set _debugConsole 0
770    } else {
771        set _debugConsole 1
772    }
773    DebugConsole
774}
775
776#
777# DebugConsole --
778#
779#    Based on the value of the variable _debugConsole, turns on/off
780#    debugging. This is done by setting/unsetting a procedure that
781#    is called whenever new characters are received or sent on the
782#    socket to the render server.  Additionally, the debug console
783#    is created if necessary and hidden/shown.
784#
785itcl::body Rappture::VisViewer::DebugConsole {} {
786    if { ![winfo exists .renderconsole] } {
787        BuildConsole
788    }
789    if { $_debugConsole } {
790        $this configure -sendcommand [itcl::code $this TraceComm]
791        $this configure -receivecommand [itcl::code $this TraceComm]
792        wm deiconify .renderconsole
793    } else {
794        $this configure -sendcommand ""
795        $this configure -receivecommand ""
796        wm withdraw .renderconsole
797    }
798}
799
800# ----------------------------------------------------------------------
801# USAGE: TraceComm <channel> <data>
802#
803# Invoked automatically whenever there is communication between
804# the rendering widget and the server.  Eavesdrops on the communication
805# and posts the commands in a text viewer.
806# ----------------------------------------------------------------------
807itcl::body Rappture::VisViewer::TraceComm {channel {data ""}} {
808    $itk_component(trace) configure -state normal
809    switch -- $channel {
810        closed {
811            $itk_component(trace) insert end "--CLOSED--\n" error
812        }
813        <<line {
814            $itk_component(trace) insert end $data incoming "\n" incoming
815        }
816        >>line {
817            $itk_component(trace) insert end $data outgoing "\n" outgoing
818        }
819        error {
820            $itk_component(trace) insert end $data error "\n" error
821        }
822        default {
823            $itk_component(trace) insert end "$data\n"
824        }
825    }
826    $itk_component(trace) configure -state disabled
827    $itk_component(trace) see end
828}
829
830# ----------------------------------------------------------------------
831# USAGE: SendDebugCommand
832#
833# Invoked automatically whenever the user enters a command and
834# presses <Return>.  Sends the command along to the rendering
835# widget.
836# ----------------------------------------------------------------------
837itcl::body Rappture::VisViewer::SendDebugCommand {} {
838    incr _cmdSeq
839    set cmd [$itk_component(command) get]
840    append cmd "\n"
841    if {$_trace} {
842        puts stderr "$_cmdSeq>>[string range $cmd 0 70]"
843    }
844    SendBytes $cmd
845    $itk_component(command) delete 0 end
846}
847
848#
849# HandleOk --
850#
851#       This handles the "ok" response from the server that acknowledges
852#       the reception of a server command, but does not produce an image.
853#       It may pass an argument such as "-token 9" that could be used to
854#       determine how many commands have been processed by the server.
855#
856itcl::body Rappture::VisViewer::HandleOk { args } {
857    if { $_waitTimeout > 0 } {
858        StopWaiting
859    }
860}
861
862#
863# HandleError --
864#
865#       This handles the "viserror" response from the server that reports
866#       that a client-initiated error has occurred on the server.
867#
868itcl::body Rappture::VisViewer::HandleError { args } {
869    array set info {
870        -token "???"
871        -bytes 0
872        -type "???"
873    }
874    array set info $args
875    set bytes [ReceiveBytes $info(-bytes)]
876    if { $info(-type) == "error" } {
877        set popup $itk_component(hull).error
878        if { ![winfo exists $popup] } {
879            Rappture::Balloon $popup \
880                -title "Render Server Error"
881            set inner [$popup component inner]
882            label $inner.summary -text "" -anchor w
883
884            Rappture::Scroller $inner.scrl \
885                -xscrollmode auto -yscrollmode auto
886            text $inner.scrl.text \
887                -font "Arial 9 " -background white -relief sunken -bd 1 \
888                -height 5 -wrap word -width 60
889            $inner.scrl contents $inner.scrl.text
890            button $inner.ok -text "Dismiss" -command [list $popup deactivate] \
891                -font "Arial 9"
892            blt::table $inner \
893                0,0 $inner.scrl -fill both \
894                1,0 $inner.ok
895            $inner.scrl.text tag configure normal -font "Arial 9"
896            $inner.scrl.text tag configure italic -font "Arial 9 italic"
897            $inner.scrl.text tag configure bold -font "Arial 10 bold"
898            $inner.scrl.text tag configure code -font "Courier 10 bold"
899        } else {
900            $popup deactivate
901        }
902        update
903        set inner [$popup component inner]
904        $inner.scrl.text delete 0.0 end
905
906        $inner.scrl.text configure -state normal
907        $inner.scrl.text insert end "The following error was reported by the render server:\n\n" bold
908        $inner.scrl.text insert end $bytes code
909        $inner.scrl.text configure -state disabled
910        update
911        $popup activate $itk_component(hull) below
912    } else {
913        ReceiveEcho <<error $bytes
914        puts stderr "Render server error:\n$bytes"
915    }
916}
917
918itcl::body Rappture::VisViewer::GetColormapList { args } {
919    array set opts {
920        -includeDefault 0
921        -includeElementDefault 0
922        -includeNone 0
923    }
924    if {[llength $args] > 0} {
925        foreach opt $args {
926            set opts($opt) 1
927        }
928    }
929    set colormaps [list]
930    if {$opts(-includeDefault)} {
931        lappend colormaps "default" "default"
932    }
933    if {$opts(-includeElementDefault)} {
934        lappend colormaps "elementDefault" "elementDefault"
935    }
936    lappend colormaps \
937        "BCGYR"              "BCGYR"            \
938        "BGYOR"              "BGYOR"            \
939        "blue-to-brown"      "blue-to-brown"    \
940        "blue-to-orange"     "blue-to-orange"   \
941        "blue-to-grey"       "blue-to-grey"     \
942        "green-to-magenta"   "green-to-magenta" \
943        "greyscale"          "greyscale"        \
944        "nanohub"            "nanohub"          \
945        "rainbow"            "rainbow"          \
946        "spectral"           "spectral"         \
947        "ROYGB"              "ROYGB"            \
948        "RYGCB"              "RYGCB"            \
949        "white-to-blue"      "white-to-blue"    \
950        "brown-to-blue"      "brown-to-blue"    \
951        "grey-to-blue"       "grey-to-blue"     \
952        "orange-to-blue"     "orange-to-blue"
953    if {$opts(-includeNone)} {
954        lappend colormaps "none" "none"
955    }
956    return $colormaps
957}
958
959itcl::body Rappture::VisViewer::ColorsToColormap { colors } {
960    set cmap {}
961    switch -- $colors {
962        "grey-to-blue" {
963            set cmap {
964                0.0                      0.200 0.200 0.200
965                0.14285714285714285      0.400 0.400 0.400
966                0.2857142857142857       0.600 0.600 0.600
967                0.42857142857142855      0.900 0.900 0.900
968                0.5714285714285714       0.800 1.000 1.000
969                0.7142857142857143       0.600 1.000 1.000
970                0.8571428571428571       0.400 0.900 1.000
971                1.0                      0.000 0.600 0.800
972            }
973        }
974        "blue-to-grey" {
975            set cmap {
976                0.0                     0.000 0.600 0.800
977                0.14285714285714285     0.400 0.900 1.000
978                0.2857142857142857      0.600 1.000 1.000
979                0.42857142857142855     0.800 1.000 1.000
980                0.5714285714285714      0.900 0.900 0.900
981                0.7142857142857143      0.600 0.600 0.600
982                0.8571428571428571      0.400 0.400 0.400
983                1.0                     0.200 0.200 0.200
984            }
985        }
986        "white-to-blue" {
987            set cmap {
988                0.0                     0.900 1.000 1.000
989                0.1111111111111111      0.800 0.983 1.000
990                0.2222222222222222      0.700 0.950 1.000
991                0.3333333333333333      0.600 0.900 1.000
992                0.4444444444444444      0.500 0.833 1.000
993                0.5555555555555556      0.400 0.750 1.000
994                0.6666666666666666      0.300 0.650 1.000
995                0.7777777777777778      0.200 0.533 1.000
996                0.8888888888888888      0.100 0.400 1.000
997                1.0                     0.000 0.250 1.000
998            }
999        }
1000        "brown-to-blue" {
1001            set cmap {
1002                0.0                             0.200   0.100   0.000
1003                0.09090909090909091             0.400   0.187   0.000
1004                0.18181818181818182             0.600   0.379   0.210
1005                0.2727272727272727              0.800   0.608   0.480
1006                0.36363636363636365             0.850   0.688   0.595
1007                0.45454545454545453             0.950   0.855   0.808
1008                0.5454545454545454              0.800   0.993   1.000
1009                0.6363636363636364              0.600   0.973   1.000
1010                0.7272727272727273              0.400   0.940   1.000
1011                0.8181818181818182              0.200   0.893   1.000
1012                0.9090909090909091              0.000   0.667   0.800
1013                1.0                             0.000   0.480   0.600
1014            }
1015        }
1016        "blue-to-brown" {
1017            set cmap {
1018                0.0                             0.000   0.480   0.600
1019                0.09090909090909091             0.000   0.667   0.800
1020                0.18181818181818182             0.200   0.893   1.000
1021                0.2727272727272727              0.400   0.940   1.000
1022                0.36363636363636365             0.600   0.973   1.000
1023                0.45454545454545453             0.800   0.993   1.000
1024                0.5454545454545454              0.950   0.855   0.808
1025                0.6363636363636364              0.850   0.688   0.595
1026                0.7272727272727273              0.800   0.608   0.480
1027                0.8181818181818182              0.600   0.379   0.210
1028                0.9090909090909091              0.400   0.187   0.000
1029                1.0                             0.200   0.100   0.000
1030            }
1031        }
1032        "blue-to-orange" {
1033            set cmap {
1034                0.0                             0.000   0.167   1.000
1035                0.09090909090909091             0.100   0.400   1.000
1036                0.18181818181818182             0.200   0.600   1.000
1037                0.2727272727272727              0.400   0.800   1.000
1038                0.36363636363636365             0.600   0.933   1.000
1039                0.45454545454545453             0.800   1.000   1.000
1040                0.5454545454545454              1.000   1.000   0.800
1041                0.6363636363636364              1.000   0.933   0.600
1042                0.7272727272727273              1.000   0.800   0.400
1043                0.8181818181818182              1.000   0.600   0.200
1044                0.9090909090909091              1.000   0.400   0.100
1045                1.0                             1.000   0.167   0.000
1046            }
1047        }
1048        "orange-to-blue" {
1049            set cmap {
1050                0.0                             1.000   0.167   0.000
1051                0.09090909090909091             1.000   0.400   0.100
1052                0.18181818181818182             1.000   0.600   0.200
1053                0.2727272727272727              1.000   0.800   0.400
1054                0.36363636363636365             1.000   0.933   0.600
1055                0.45454545454545453             1.000   1.000   0.800
1056                0.5454545454545454              0.800   1.000   1.000
1057                0.6363636363636364              0.600   0.933   1.000
1058                0.7272727272727273              0.400   0.800   1.000
1059                0.8181818181818182              0.200   0.600   1.000
1060                0.9090909090909091              0.100   0.400   1.000
1061                1.0                             0.000   0.167   1.000
1062            }
1063        }
1064        "rainbow" {
1065            set clist {
1066                "#EE82EE"
1067                "#4B0082"
1068                "blue"
1069                "#008000"
1070                "yellow"
1071                "#FFA500"
1072                "red"
1073            }
1074        }
1075        "BGYOR" {
1076            set clist {
1077                "blue"
1078                "#008000"
1079                "yellow"
1080                "#FFA500"
1081                "red"
1082            }
1083        }
1084        "ROYGB" {
1085            set clist {
1086                "red"
1087                "#FFA500"
1088                "yellow"
1089                "#008000"
1090                "blue"
1091            }
1092        }
1093        "RYGCB" {
1094            set clist {
1095                "red"
1096                "yellow"
1097                "green"
1098                "cyan"
1099                "blue"
1100            }
1101        }
1102        "BCGYR" {
1103            set clist {
1104                "blue"
1105                "cyan"
1106                "green"
1107                "yellow"
1108                "red"
1109            }
1110        }
1111        "spectral" {
1112            set cmap {
1113                0.0 0.150 0.300 1.000
1114                0.1 0.250 0.630 1.000
1115                0.2 0.450 0.850 1.000
1116                0.3 0.670 0.970 1.000
1117                0.4 0.880 1.000 1.000
1118                0.5 1.000 1.000 0.750
1119                0.6 1.000 0.880 0.600
1120                0.7 1.000 0.680 0.450
1121                0.8 0.970 0.430 0.370
1122                0.9 0.850 0.150 0.196
1123                1.0 0.650 0.000 0.130
1124            }
1125        }
1126        "green-to-magenta" {
1127            set cmap {
1128                0.0 0.000 0.316 0.000
1129                0.06666666666666667 0.000 0.526 0.000
1130                0.13333333333333333 0.000 0.737 0.000
1131                0.2 0.000 0.947 0.000
1132                0.26666666666666666 0.316 1.000 0.316
1133                0.3333333333333333 0.526 1.000 0.526
1134                0.4 0.737 1.000 0.737
1135                0.4666666666666667 1.000 1.000 1.000
1136                0.5333333333333333 1.000 0.947 1.000
1137                0.6 1.000 0.737 1.000
1138                0.6666666666666666 1.000 0.526 1.000
1139                0.7333333333333333 1.000 0.316 1.000
1140                0.8 0.947 0.000 0.947
1141                0.8666666666666667 0.737 0.000 0.737
1142                0.9333333333333333 0.526 0.000 0.526
1143                1.0 0.316 0.000 0.316
1144            }
1145        }
1146        "greyscale" {
1147            set cmap {
1148                0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
1149            }
1150        }
1151        "nanohub" {
1152            set clist "white yellow green cyan blue magenta"
1153        }
1154        default {
1155            set clist [split $colors ":"]
1156        }
1157    }
1158    if {$cmap == ""} {
1159        if { [llength $clist] == 1 } {
1160            set rgb [Color2RGB $clist]
1161            append cmap "0.0 $rgb 1.0 $rgb"
1162        } else {
1163            for {set i 0} {$i < [llength $clist]} {incr i} {
1164                set x [expr {double($i)/([llength $clist]-1)}]
1165                set color [lindex $clist $i]
1166                append cmap "$x [Color2RGB $color] "
1167            }
1168        }
1169    } else {
1170        regsub -all "\[ \t\r\n\]+" [string trim $cmap] " " cmap
1171    }
1172    return $cmap
1173}
1174
1175
1176#
1177# StartBufferingCommands --
1178#
1179itcl::body Rappture::VisViewer::StartBufferingCommands { } {
1180    incr _buffering
1181    if { $_buffering == 1 } {
1182        set _outbuf ""
1183    }
1184}
1185
1186#
1187# StopBufferingCommands --
1188#
1189#       This gets called when we want to stop buffering the commands for
1190#       the server and actually send then to the server.  Note that there's
1191#       a reference count on buffering.  This is so that you can can
1192#       Start/Stop multiple times without worrying about the current state.
1193#
1194itcl::body Rappture::VisViewer::StopBufferingCommands { } {
1195    incr _buffering -1
1196    if { $_buffering == 0 } {
1197        SendBytes $_outbuf
1198        set _outbuf ""
1199    }
1200}
1201
1202#
1203# SendCmd
1204#
1205#       Send command off to the rendering server.  If we're currently
1206#       buffering, the command is queued to be sent later.
1207#
1208itcl::body Rappture::VisViewer::SendCmd {string} {
1209    incr _cmdSeq
1210    if {$_trace} {
1211        puts stderr "$_cmdSeq>>[string range $string 0 70]"
1212    }
1213    if { $_buffering } {
1214        append _outbuf $string "\n"
1215    } else {
1216        SendBytes "$string\n"
1217    }
1218}
1219
1220#
1221# SendData
1222#
1223#       Send data off to the rendering server.  If we're currently
1224#       buffering, the data is queued to be sent later.
1225#
1226itcl::body Rappture::VisViewer::SendData {bytes} {
1227    if {$_trace} {
1228        puts stderr "$_cmdSeq>>data payload"
1229    }
1230    if { $_buffering } {
1231        append _outbuf $bytes
1232    } else {
1233        SendBytes $bytes
1234    }
1235}
Note: See TracBrowser for help on using the repository browser.