Files
2026-08-03 18:42:47 +08:00

222 lines
7.2 KiB
Python

try:
import BaseHTTPServer
except ImportError:
import http.server as BaseHTTPServer
import time
import cgi
import threading
import traceback
from pymol import cmd
_server = None
def _shutdown(self_cmd=cmd):
if _server != None:
_server.socket.close()
self_cmd.quit()
# Note, this handler assumes PyMOL is running as a global singleton
def get_status(out, self_cmd=cmd):
out.write('<html>\n')
out.write('<header>\n')
out.write('<script type="text/javascript" src="pymol.js"></script>\n')
out.write('</header><body>\n')
out.write('<h3>PyMOL WebGUI Proof of Concept</h3>\n')
out.write('<table><tr>\n')
out.write('<td><form action="./status.pymol"><button type="submit">Refresh</button></form></td>\n')
out.write('<td><form target="_new" action="./ray.pymol?t=%f"><button type="submit">Ray</button></form></td>\n'%
time.time())
out.write('<td><form target="_new" action="./monitor.pymol?t=%f"><button type="submit">Monitor</button></form></td>\n'%
time.time())
out.write('<td><form action="./quit.pymol"><button type="submit">Quit</button></form></td>\n')
out.write('</tr></table>')
out.write('<a href="./status.pymol?load">load $TUT/1hpv.pdb</a>\n')
names = self_cmd.get_names('objects')
if not len(names):
out.write('<p>No objects loaded.</p>\n')
else:
out.write('<p>Loaded Objects:</p><ul>\n')
for name in names:
out.write('<li>%s</li>\n'%name)
out.write('</ul>\n')
out.write('<a href="#" onClick="updateImage()"><img src="./draw.pymol?t=%f">'%time.time()+"</img></a>")
out.write('</body></html>\n')
def get_monitor(out, self_cmd=cmd):
out.write('<html>\n')
out.write('<header>\n')
out.write('<script type="text/javascript" src="pymol.js"></script>\n')
out.write('</header><body onload="monitorOnLoad()">\n')
out.write('<img src="./draw.pymol?t=%f">'%time.time()+"</img>")
out.write('</body></html>\n')
def get_start(out, self_cmd=cmd):
window_open="javascript: window.open('./status.pymol','hello', 'location=no,toolbar=no,width=400,height=600,top=0,left=880');"
out.write('<html><body onload="'+window_open+'">\n')
out.write('<a href="./start.pymol" onclick="'+window_open+'">Launch WebGUI\n')
out.write('</body></html>')
def write_image(out, ray=0, self_cmd=cmd):
if ray:
self_cmd.ray()
# encode the file descriptor into the PNG filename
if self_cmd.png(chr(1)+str(out.fileno()),prior=-1) != 1:
# no prior image available, so wait for update / finish
self_cmd.sync()
class PymolHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass
# nuke logging feature for the time being
def do_js(self):
self.send_response(200)
self.send_header('Content-type','text/javascript')
self.end_headers()
self.wfile.write('''
function updateImage()
{
images = document.getElementsByTagName("img");
for( var i = 0; i < images.length; i++ ) {
images[i].src = "./draw.pymol?t=" + new Date().getTime();
}
return false;
}
function monitorOnLoad(event)
{
setInterval('updateImage()',1000)
}
''')
def do_pymol(self):
if "ray.pymol" in self.path: # send image
self.send_response(200)
self.send_header('Content-type', 'image/x-png')
self.send_header('Cache-control', 'no-cache')
self.send_header('Pragma', 'no-cache')
self.end_headers()
write_image(self.wfile,1)
elif "draw.pymol" in self.path:
self.send_response(200)
self.send_header('Content-type', 'image/x-png')
self.send_header('Cache-control', 'no-cache')
self.send_header('Pragma', 'no-cache')
self.end_headers()
write_image(self.wfile)
else:
if "load" in self.path: # load a structure
cmd.load("$TUT/1hpv.pdb")
cmd.rock()
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
if "status.pymol" in self.path:
get_status(self.wfile)
elif "monitor.pymol" in self.path:
get_monitor(self.wfile)
elif "quit.pymol" in self.path:
self.wfile.write('<html><body><p>Quitting...</p></body></html>')
self.wfile.flush()
_shutdown()
else: # start page
get_start(self.wfile)
self.wfile.flush()
def do_GET(self):
try:
doc = self.path.split('?')[0]
if doc.endswith('.pymol'): # PyMOL
try:
self.do_pymol()
except:
traceback.print_exc()
elif doc.endswith('.js'): # Javascript
self.do_js()
elif doc.endswith('.html'):
f = open('.'+self.path) # UNSAFE!!!
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(f.read())
f.close()
except IOError:
self.send_error(404,'File Not Found: %s' % self.path)
def do_POST(self):
global rootnode
try:
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
if ctype == 'multipart/form-data':
query=cgi.parse_multipart(self.rfile, pdict)
self.send_response(301)
self.end_headers()
upfilecontent = query.get('upfile')
print("filecontent", upfilecontent[0])
self.wfile.write('<HTML>POST OK.<BR><BR>');
self.wfile.write(upfilecontent[0]);
except :
pass
def main():
try:
global _server
_server = BaseHTTPServer.HTTPServer(('', 8080), PymolHandler)
print('started httpserver...')
_server.serve_forever()
except KeyboardInterrupt:
print('^C received, shutting down server')
_server.socket.close()
def open_browser():
import webbrowser
time.sleep(1)
webbrowser.open('http://localhost:8080/status.pymol')
# import os
# os.system('open http://localhost:8080/start.pymol')
if __name__ == '__main__':
main()
if __name__ == 'pymol':
cmd.set("image_copy_always") # copy all updates into image buffer
t = threading.Thread(target=main)
t.setDaemon(1)
t.start()
t = threading.Thread(target=open_browser)
t.setDaemon(1)
t.start()
"""
okay, what we need now is a simple safe way to pass comands and
arguments through the client brower, with escape characters, etc.
ideally, we'd like a javascript object which responds to the same messages as PyMOL
for our initial test, let's just put up some button links to control representations.
also outstanding:
- how does one send asynchronous javascript URL requests?
- we also need some way to package up images in PyMOL without passaging through the file systems
- we also need to decide what this initial user interface is going to do
"""