mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-05 20:14:24 +08:00
212 lines
6.4 KiB
Python
212 lines
6.4 KiB
Python
# 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()
|
|
|
|
|
|
|