source: trunk/gui/scripts/visviewer.tcl @ 5145

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

Fix for automatic ticket #274586. Also, don't change cursor for busy window
while writing to socket since this was causing the cursor to flicker even for
short sends. The waiting dialog (if enabled) will still appear after a timeout
if a send causes a long round trip. The timeout allows short waits to occur
while sending without showing a wait cursor/dialog.

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