File: syncmol.py

package info (click to toggle)
pymol 2.5.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 42,288 kB
  • sloc: cpp: 476,472; python: 76,538; ansic: 29,510; javascript: 6,792; sh: 47; makefile: 24
file content (211 lines) | stat: -rw-r--r-- 6,572 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# simple proof-of-concept exercise in PyMOL synchronization

# NOT FOR PRODUCTION USE! HIGHLY INSECURE!

# You need to launch two PyMOL simultaneously as follows...

# MASTER/SLAVE (on same machine):

# pymol syncmol.py -- recv 8000
# pymol syncmol.py -- send 8000

# MUTUAL SYNCHRONIZATION (on same machine):

# pymol syncmol.py -- recv 8001 send 8002
# pymol syncmol.py -- send 8001 recv 8002

# MUTUAL SYNCHRONIZATION (over a lan):

# on host1: pymol syncmol.py -- recv 8000 send host2:8000
# on host2: pymol syncmol.py -- recv 8000 send host1:8000

import threading
import socket
import socket # For gethostbyaddr()
import sys
import traceback
import copy
import os
import time

try:
    import cPickle
    import SocketServer
    import Queue
except ImportError:
    import pickle as cPickle
    import socketserver as SocketServer
    import queue as Queue

    
class PyMOLWriter: # this class transmits

    def __init__(self, pymol, host='localhost', port=8000):
        host = str(host)
        port = int(port)
        self.host = host
        self.port = port
        self.sock = None
        self.cmd = pymol.cmd
        self.fifo = Queue.Queue(0)
        cmd = self.cmd

        print(" syncmol: writing to %s:%d"%(host,port))
        pymol.cmd.log_open(self.fifo)
        
        last_view = None
        last_frame = 0
        while 1:
            time.sleep(0.1) # update 10x a second
            view = cmd.get_view(output=4)
            try:
                cmd.lock()
                if hasattr(cmd,"_last_view"):
                    last_view = cmd._last_view
                    del cmd._last_view
            finally:
                cmd.unlock(-1)
            deferred = 0
            if not self.fifo.empty():
                if view != last_view:
                    self._remote_call("do",("_ cmd.set('defer_updates')",),{'log':0})
                    deferred = 1
                do_list = []
                while not self.fifo.empty():
                    do_list.append(self.fifo.get())
                self._remote_call("do",(do_list,),{'log':0})
            if view != last_view:
                self._remote_call("remote_set_view",(view,))
                last_view = view
                if deferred:
                    self._remote_call("do",("_ cmd.unset('defer_updates')",),{'log':0})
            if not cmd.get_movie_playing():
                frame = int(cmd.get("frame"))
                if last_frame != frame:
                    self._remote_call("frame",(frame,))
                    last_frame = frame
            else:
                last_frame = None
            
                
    def _remote_call(self,meth,args=(),kwds={}):
        result = None
        while self.sock == None:
            try:
                self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                self.sock.connect((self.host,self.port))
                self.send = self.sock.makefile('w')
                self.recv = self.sock.makefile('r')
            except:
                self.sock=None
                print("retrying...")
                time.sleep(1)
        cPickle.dump(meth,self.send,1) # binary by default
        cPickle.dump(args,self.send,1)
        cPickle.dump(kwds,self.send,1)
        self.send.flush()
        result = cPickle.load(self.recv)
        return result

def remote_set_view(view,_self=cmd):
    try:
        _self.lock()
        _self._last_view = view
        _self.set_view(view)
    finally:
        _self.unlock(-1)
    
class PyMOLReader: # this class receives

    def __init__(self,pymol,port):

        sys.setcheckinterval(0)
        
        server_address = ('', port)

        ddbs = _PyMOLReader(server_address, _PyMOLRequestHandler)  

        # bind pymol instance to the reader

        pymol.cmd.remote_set_view = remote_set_view
        ddbs.cmd = pymol.cmd

        print(" syncmol: reading from port %d"%(port))
        # now serve requests forever
        ddbs.keep_alive = 1
        while ddbs.keep_alive:
            ddbs.handle_request()
        
class _PyMOLReader(SocketServer.ThreadingTCPServer):

     def server_bind(self):
          """Override server_bind to store the server name."""
          SocketServer.ThreadingTCPServer.server_bind(self)
          host, port = self.socket.getsockname()
          if not host or host == '0.0.0.0':
                host = socket.gethostname()
          try:
              hostname, hostnames, hostaddrs = socket.gethostbyaddr(host)
              if '.' not in hostname:
                    for host in hostnames:
                         if '.' in host:
                              hostname = host
                              break
          except:
              hostname = 'localhost'
          self.server_name = hostname
          self.server_port = port

class _PyMOLRequestHandler(SocketServer.StreamRequestHandler):

     def handle(self):
         while self.server.keep_alive:
             # get method name from client

             try:
                 method = cPickle.load(self.rfile)
             except (EOFError, socket.error):
                 break

             if method == 'shutdown':
                 self.server.keep_alive = 0
                 
             # get arguments from client
             args = cPickle.load(self.rfile)
             kw = cPickle.load(self.rfile)

             # get cmd method pointer
             meth_obj = getattr(self.server.cmd,method)

             # call method and return result
             cPickle.dump(meth_obj(*args, **kw),self.wfile,1) # binary by default
             self.wfile.flush()
             
if __name__=='pymol':
    import os
    import pymol
    sys.argv.reverse()
    sys.argv.pop()
    while len(sys.argv):
        tok = sys.argv.pop()
        if tok == 'recv':
            port = int(sys.argv.pop())
            _stdin_reader_thread = threading.Thread(target=PyMOLReader,
                                                    args=(pymol,port))
            _stdin_reader_thread.setDaemon(1)
            _stdin_reader_thread.start()
        elif tok == 'send':
            addr = sys.argv.pop().split(':')
            if len(addr)==1:
                host = 'localhost'
                port = int(addr[0])
            else:
                host = addr[0]
                port = addr[1]
            _stdin_reader_thread = threading.Thread(target=PyMOLWriter,
                                                    args=(pymol,host,port))
            _stdin_reader_thread.setDaemon(1)
            _stdin_reader_thread.start()