mirror of
https://github.com/schrodinger/pymol-open-source.git
synced 2026-06-03 19:54:24 +08:00
866 lines
24 KiB
Python
866 lines
24 KiB
Python
#A* -------------------------------------------------------------------
|
|
#B* This file contains source code for the PyMOL computer program
|
|
#C* Copyright (c) Schrodinger, LLC.
|
|
#D* -------------------------------------------------------------------
|
|
#E* It is unlawful to modify or remove this copyright notice.
|
|
#F* -------------------------------------------------------------------
|
|
#G* Please see the accompanying LICENSE file for further information.
|
|
#H* -------------------------------------------------------------------
|
|
#I* Additional authors of this source file include:
|
|
#-*
|
|
#-*
|
|
#-*
|
|
#Z* -------------------------------------------------------------------
|
|
|
|
from pymol.shortcut import Shortcut
|
|
from pymol.parser import __file__ as _parser_filename
|
|
|
|
if True:
|
|
|
|
import sys
|
|
if True:
|
|
import _thread as thread
|
|
import urllib.request as urllib2
|
|
from io import FileIO as file
|
|
|
|
import inspect
|
|
import glob
|
|
import shlex
|
|
import tokenize
|
|
import builtins
|
|
from io import BytesIO
|
|
from enum import Enum
|
|
from functools import wraps
|
|
from pathlib import Path
|
|
from textwrap import dedent
|
|
from typing import Tuple, Iterable, get_args, Optional, Union, Any, NewType, List, get_origin
|
|
|
|
import re
|
|
import os
|
|
import time
|
|
import threading
|
|
import traceback
|
|
from . import colorprinting
|
|
from . import parsing
|
|
cmd = sys.modules["pymol.cmd"]
|
|
import pymol
|
|
|
|
from .cmd import _cmd, QuietException, \
|
|
fb_module, fb_mask, is_list, \
|
|
DEFAULT_ERROR, DEFAULT_SUCCESS, is_ok, is_error, is_string
|
|
|
|
def resume(filename, _self=cmd):
|
|
'''
|
|
|
|
DESCRIPTION
|
|
|
|
"resume" executes a log file and opens it for recording of
|
|
additional commands.
|
|
|
|
USAGE
|
|
|
|
resume filename
|
|
|
|
SEE ALSO
|
|
|
|
log, log_close
|
|
|
|
'''
|
|
r = DEFAULT_ERROR
|
|
if os.path.exists(filename):
|
|
if(re.search(r"\.py$|\.PY$|\.pym$|.PYM$",filename)):
|
|
r = _self.do("run %s"%filename)
|
|
else:
|
|
r = _self.do("@%s"%filename)
|
|
if is_ok(r):
|
|
r = _self.do("log_open %s,a"%filename)
|
|
if _self._raising(r,_self): raise pymol.CmdException
|
|
return r
|
|
|
|
|
|
class QueueFile:
|
|
|
|
def __init__(self,queue):
|
|
self.queue = queue
|
|
|
|
def write(self,command):
|
|
self.queue.put(command)
|
|
|
|
def flush(self):
|
|
pass
|
|
|
|
def close(self):
|
|
del self.queue
|
|
|
|
re_fetch = re.compile(r'(\bfetch\s+\w[^;\r\n\'"]+)')
|
|
|
|
class LogFile(file):
|
|
def _append_async0(self, m):
|
|
s = m.group()
|
|
if 'async' in s:
|
|
return s
|
|
return s.rstrip(',') + ', async=0'
|
|
def write(self, s):
|
|
s = re_fetch.sub(self._append_async0, s)
|
|
file.write(self, s.encode())
|
|
|
|
def log_open(filename='log.pml', mode='w', _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"log_open" opens a log file for writing.
|
|
|
|
USAGE
|
|
|
|
log_open [ filename [, mode ]]
|
|
|
|
ARGUMENTS
|
|
|
|
filename = str: file to write to (.pml or .py) {default: log.pml}
|
|
|
|
mode = w/a: "w" to open an empty log file, "a" to append {default: w}
|
|
|
|
SEE ALSO
|
|
|
|
log, log_close
|
|
|
|
'''
|
|
pymol=_self._pymol
|
|
if not is_string(filename): # we're logging to Queue, not a file.
|
|
pymol._log_file = QueueFile(filename)
|
|
_self.set("logging",1,quiet=1)
|
|
else:
|
|
try:
|
|
try:
|
|
if hasattr(pymol,"_log_file"):
|
|
if pymol._log_file is not None:
|
|
pymol._log_file.close()
|
|
del pymol._log_file
|
|
except:
|
|
pass
|
|
pymol._log_file = LogFile(filename,mode)
|
|
if _self._feedback(fb_module.cmd,fb_mask.details): # redundant
|
|
if mode!='a':
|
|
print(" Cmd: logging to '%s'."%filename)
|
|
else:
|
|
print(" Cmd: appending to '%s'."%filename)
|
|
if mode=='a':
|
|
pymol._log_file.write("\n") # always start on a new line
|
|
if(re.search(r"\.py$|\.PY$|\.pym$|\.PYM$",filename)):
|
|
_self.set("logging",2,quiet=1)
|
|
else:
|
|
_self.set("logging",1,quiet=1)
|
|
except:
|
|
print("Error: unable to open log file '%s'"%filename)
|
|
pymol._log_file = None
|
|
_self.set("logging",0,quiet=1)
|
|
traceback.print_exc()
|
|
raise QuietException
|
|
|
|
def log(text, alt_text=None, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"log" writes a command to the log file (if one is open).
|
|
|
|
`text` and/or `alt_text` must include the terminating line feed.
|
|
|
|
ARGUMENTS
|
|
|
|
text = str: PyMOL command (optional if alt_text is given)
|
|
alt_text = str: Python expression (optional)
|
|
|
|
SEE ALSO
|
|
|
|
log_open, log_close
|
|
|
|
'''
|
|
# See also equivalent C impelemtation: PLog
|
|
|
|
log_file = getattr(_self._pymol, "_log_file", None)
|
|
|
|
if log_file is None:
|
|
return
|
|
|
|
mode = _self.get_setting_int("logging")
|
|
|
|
if mode == 1:
|
|
# .pml
|
|
if not text and alt_text:
|
|
text = '/' + alt_text
|
|
elif mode == 2:
|
|
# .py
|
|
if alt_text:
|
|
text = alt_text
|
|
elif text.startswith('/'):
|
|
text = text[1:]
|
|
else:
|
|
text = "cmd.do(" + repr(text.strip()) + ")\n"
|
|
else:
|
|
return
|
|
|
|
if text:
|
|
log_file.write(text)
|
|
log_file.flush()
|
|
|
|
def log_close(_self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"log_close" closes the current log file (if one is open).
|
|
|
|
USAGE
|
|
|
|
log_close
|
|
|
|
SEE ALSO
|
|
|
|
log, log_open
|
|
|
|
'''
|
|
pymol=_self._pymol
|
|
cmd=_self
|
|
if hasattr(pymol,"_log_file"):
|
|
if pymol._log_file is not None:
|
|
pymol._log_file.close()
|
|
del pymol._log_file
|
|
_self.set("logging",0,quiet=1)
|
|
if _self._feedback(fb_module.cmd,fb_mask.details): # redundant
|
|
print(" Cmd: log closed.")
|
|
|
|
def cls(_self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"cls" clears the output buffer.
|
|
|
|
USAGE
|
|
|
|
cls
|
|
'''
|
|
r = DEFAULT_ERROR
|
|
try:
|
|
_self.lock(_self)
|
|
r = _cmd.cls(_self._COb)
|
|
finally:
|
|
_self.unlock(r,_self)
|
|
if _self._raising(r,_self): raise pymol.CmdException
|
|
return r
|
|
|
|
def _load_splash_image(filename, url, _self=cmd):
|
|
import tempfile
|
|
import struct
|
|
|
|
tmp_filename = ""
|
|
contents = None
|
|
|
|
if url:
|
|
try:
|
|
handle = urllib2.urlopen(url)
|
|
contents = handle.read()
|
|
handle.close()
|
|
|
|
# png magic number
|
|
if contents[:4] != b'\x89\x50\x4e\x47':
|
|
raise IOError
|
|
|
|
shape = struct.unpack('>II', contents[16:24])
|
|
|
|
tmp_filename = tempfile.mktemp('.png')
|
|
with open(tmp_filename, 'wb') as handle:
|
|
handle.write(contents)
|
|
|
|
filename = tmp_filename
|
|
except IOError:
|
|
pass
|
|
|
|
if os.path.exists(filename) and not _self.get_names():
|
|
# hide text splash
|
|
print()
|
|
|
|
# fit window to image
|
|
try:
|
|
if not contents:
|
|
contents = open(filename, 'rb').read(24)
|
|
shape = struct.unpack('>II', contents[16:24])
|
|
scale = _self.get_setting_int('display_scale_factor')
|
|
_self.viewport(shape[0] * scale, shape[1] * scale)
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
# load image
|
|
_self.load_png(filename, 0, quiet=1)
|
|
|
|
if tmp_filename:
|
|
os.unlink(tmp_filename)
|
|
|
|
def splash(mode=0, _self=cmd):
|
|
cmd=_self
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"splash" shows the splash screen information.
|
|
|
|
USAGE
|
|
|
|
splash
|
|
'''
|
|
r = DEFAULT_ERROR
|
|
mode = int(mode)
|
|
if mode == 2: # query
|
|
try:
|
|
_self.lock(_self)
|
|
r = _cmd.splash(_self._COb,1)
|
|
finally:
|
|
_self.unlock(0,_self)
|
|
elif mode == 1: # just show PNG
|
|
show_splash = 1
|
|
try:
|
|
_self.lock(_self)
|
|
show_splash = _cmd.splash(_self._COb,1)
|
|
finally:
|
|
_self.unlock(0,_self)
|
|
r = DEFAULT_SUCCESS
|
|
png_url = ""
|
|
if show_splash==1: # generic / open-source
|
|
png_path = _self.exp_path("$PYMOL_DATA/pymol/splash.png")
|
|
elif show_splash==2: # evaluation builds
|
|
png_path = _self.exp_path("$PYMOL_DATA/pymol/epymol.png")
|
|
elif show_splash==3: # edu builds
|
|
png_path = _self.exp_path("$PYMOL_DATA/pymol/splash_edu.png")
|
|
png_url = "http://pymol.org/splash/splash_edu_2.png"
|
|
else: # incentive builds
|
|
png_path = _self.exp_path("$PYMOL_DATA/pymol/ipymol.png")
|
|
|
|
t = threading.Thread(target=_load_splash_image, args=(png_path, png_url, _self))
|
|
t.setDaemon(1)
|
|
t.start()
|
|
else:
|
|
if _self.get_setting_int("internal_feedback") > 0:
|
|
_self.set("text","1",quiet=1)
|
|
print()
|
|
try:
|
|
_self.lock(_self)
|
|
r = _cmd.splash(_self._COb,0)
|
|
finally:
|
|
_self.unlock(r,_self)
|
|
if _self._raising(r,_self): raise pymol.CmdException
|
|
return r
|
|
|
|
reinit_code = {
|
|
'everything' : 0,
|
|
'settings' : 1,
|
|
'store_defaults' : 2,
|
|
'original_settings' : 3,
|
|
'purge_defaults' : 4,
|
|
}
|
|
|
|
reinit_sc = Shortcut(reinit_code.keys())
|
|
|
|
def reinitialize(what='everything', object='', _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"reinitialize" reinitializes the program by deleting all objects
|
|
and restoring the default program settings.
|
|
|
|
USAGE
|
|
|
|
reinitialize
|
|
|
|
'''
|
|
r = DEFAULT_ERROR
|
|
what = reinit_code[reinit_sc.auto_err(str(what),'option')]
|
|
try:
|
|
_self.lock(_self)
|
|
r = _cmd.reinitialize(_self._COb,int(what),str(object))
|
|
finally:
|
|
_self.unlock(r,_self)
|
|
if _self._raising(r,_self): raise pymol.CmdException
|
|
return r
|
|
|
|
def sync(timeout=1.0,poll=0.05,_self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"sync" is an API-only function which waits until all current
|
|
commmands have been executed before returning. A timeout
|
|
can be used to insure that this command eventually returns.
|
|
|
|
PYMOL API
|
|
|
|
cmd.sync(float timeout=1.0,float poll=0.05)
|
|
|
|
SEE ALSO
|
|
|
|
frame
|
|
'''
|
|
for t in async_threads:
|
|
t.join()
|
|
|
|
now = time.time()
|
|
timeout = float(timeout)
|
|
poll = float(poll)
|
|
# first, make sure there aren't any commands waiting...
|
|
if _cmd.wait_queue(_self._COb): # commands waiting to be executed?
|
|
while 1:
|
|
if not _cmd.wait_queue(_self._COb):
|
|
break
|
|
e = threading.Event() # using this for portable delay
|
|
e.wait(poll)
|
|
del e
|
|
if (timeout>=0.0) and ((time.time()-now)>timeout):
|
|
colorprinting.warning('cmd.sync() timed out (wait_queue)')
|
|
break
|
|
if _cmd.wait_deferred(_self._COb):
|
|
# deferred tasks waiting for a display event?
|
|
if _self.is_gui_thread():
|
|
_self.refresh()
|
|
else:
|
|
while 1:
|
|
if not _cmd.wait_queue(_self._COb):
|
|
break
|
|
e = threading.Event() # using this for portable delay
|
|
e.wait(poll)
|
|
del e
|
|
if (timeout>=0.0) and ((time.time()-now)>timeout):
|
|
colorprinting.warning('cmd.sync() timed out (wait_deferred)')
|
|
break
|
|
# then make sure we can grab the API
|
|
while 1:
|
|
if _self.lock_attempt(_self):
|
|
_self.unlock(_self)
|
|
break
|
|
e = threading.Event() # using this for portable delay
|
|
e.wait(poll)
|
|
del e
|
|
if (timeout>=0.0) and ((time.time()-now)>timeout):
|
|
colorprinting.warning('cmd.sync() timed out (lock_attempt)')
|
|
break
|
|
|
|
def do(commands,log=1,echo=1,flush=0,_self=cmd):
|
|
# WARNING: don't call this routine if you already have the API lock
|
|
# use cmd._do instead
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"do" makes it possible for python programs to issue simple PyMOL
|
|
commands as if they were entered on the command line.
|
|
|
|
PYMOL API
|
|
|
|
cmd.do( commands )
|
|
|
|
USAGE (PYTHON)
|
|
|
|
from pymol import cmd
|
|
cmd.do("load file.pdb")
|
|
'''
|
|
log = int(log)
|
|
cmmd_list = commands if is_list(commands) else [commands]
|
|
cmmd_list = [a for cmmd in cmmd_list for a in cmmd.splitlines() if a]
|
|
n_cmmd = len(cmmd_list)
|
|
if n_cmmd>1: # if processing a list of commands, defer updates
|
|
defer = _self.get_setting_int("defer_updates")
|
|
_self.set('defer_updates',1)
|
|
if flush or (_self.is_gui_thread() and _self.lock_api_allow_flush):
|
|
for a in cmmd_list:
|
|
with _self.lockcm:
|
|
_cmd.do(_self._COb,a,log,echo)
|
|
else:
|
|
with _self.lockcm:
|
|
for a in cmmd_list:
|
|
_cmd.do(_self._COb,a,log,echo)
|
|
if n_cmmd>1:
|
|
_self.set('defer_updates',defer)
|
|
|
|
def quit(code=0, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"quit" terminates the program.
|
|
|
|
USAGE
|
|
|
|
quit [code]
|
|
|
|
ARGUMENTS
|
|
|
|
code = int: exit the application with status "code" {default: 0}
|
|
|
|
PYMOL API
|
|
|
|
cmd.quit(int code)
|
|
'''
|
|
code = int(code)
|
|
if _self.is_gui_thread():
|
|
_self._quit(code, _self)
|
|
else:
|
|
try:
|
|
_self.lock(_self)
|
|
_cmd.do(_self._COb,"_ time.sleep(0.100);cmd._quit(%d)" % (code),0,0)
|
|
# allow time for a graceful exit from the calling thread
|
|
try:
|
|
thread.exit()
|
|
except SystemExit:
|
|
pass
|
|
finally:
|
|
_self.unlock(-1,_self=_self)
|
|
return None
|
|
|
|
def delete(name, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"delete" removes objects and named selections
|
|
|
|
USAGE
|
|
|
|
delete name
|
|
|
|
ARGUMENTS
|
|
|
|
name = name(s) of object(s) or selection(s), supports wildcards (*)
|
|
|
|
EXAMPLES
|
|
|
|
delete measure* # delete all objects which names start with "measure"
|
|
delete all # delete all objects and selections
|
|
|
|
PYMOL API
|
|
|
|
cmd.delete (string name = object-or-selection-name )
|
|
|
|
SEE ALSO
|
|
|
|
remove
|
|
delete_states
|
|
'''
|
|
r = DEFAULT_ERROR
|
|
try:
|
|
_self.lock(_self)
|
|
r = _cmd.delete(_self._COb,str(name))
|
|
finally:
|
|
_self.unlock(r,_self)
|
|
if _self._raising(r,_self): raise pymol.CmdException
|
|
return r
|
|
|
|
def delete_states(name: str, states: str, *, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
USAGE
|
|
|
|
delete_states name, states
|
|
|
|
ARGUMENTS
|
|
|
|
name = name(s) of object(s), supports wildcards (*)
|
|
|
|
states = string: space separated list of state numbers or ranges
|
|
|
|
EXAMPLES
|
|
|
|
delete_state 1nmr, 1-5 # delete states 1 to 5 from 1nmr
|
|
delete_state *, 1-3 10-40 # deletes states 1 to 3 and 10 to 40 from all applicable objects
|
|
|
|
PYMOL API
|
|
|
|
cmd.delete_states(string name = object-name, string states = states string)
|
|
|
|
SEE ALSO
|
|
|
|
delete
|
|
'''
|
|
|
|
def get_state_list(states_str):
|
|
output=[]
|
|
input_str = re.sub(r"\s"," ", states_str)
|
|
input_str = input_str.replace("-"," -")
|
|
input_str = input_str.replace("- ","-")
|
|
input_str = input_str.strip().split()
|
|
|
|
last = -1
|
|
for x in input_str:
|
|
if not x.isdigit():
|
|
if x.startswith("-"):
|
|
direction = 1
|
|
current = last
|
|
last = int(x[1:]) - 1
|
|
if last < current:
|
|
direction = -1
|
|
while current != last:
|
|
current += direction
|
|
output.append(str(current))
|
|
else:
|
|
val = int(x) - 1
|
|
output.append(str(val))
|
|
last = val
|
|
return output
|
|
|
|
with _self.lockcm:
|
|
output = get_state_list(states)
|
|
states_list = sorted(set(map(int, output)))
|
|
return _cmd.delete_states(_self._COb, name, states_list)
|
|
|
|
def _into_types(type, value):
|
|
if repr(type) == 'typing.Any':
|
|
return value
|
|
elif type is bool:
|
|
if isinstance(value, bool):
|
|
return value
|
|
if value.lower() in ["yes", "1", "true", "on", "y"]:
|
|
return True
|
|
elif value.lower() in ["no", "0", "false", "off", "n"]:
|
|
return False
|
|
else:
|
|
raise pymol.CmdException(f"Invalid boolean value: {value}")
|
|
|
|
elif isinstance(type, builtins.type):
|
|
return type(value)
|
|
|
|
if origin := get_origin(type):
|
|
if not repr(origin).startswith('typing.') and issubclass(origin, tuple):
|
|
args = get_args(type)
|
|
new_values = []
|
|
for i, new_value in enumerate(shlex.split(value)):
|
|
new_values.append(_into_types(args[i], new_value))
|
|
return tuple(new_values)
|
|
|
|
elif origin == Union:
|
|
args = get_args(type)
|
|
found = False
|
|
for i, arg in enumerate(args):
|
|
try:
|
|
found = True
|
|
return _into_types(arg, value)
|
|
except:
|
|
found = False
|
|
if not found:
|
|
raise pymol.CmdException(f"Union was not able to cast {value}")
|
|
|
|
elif issubclass(list, origin):
|
|
args = get_args(type)
|
|
if len(args) > 0:
|
|
f = args[0]
|
|
else:
|
|
f = lambda x: x
|
|
return [f(i) for i in shlex.split(value)]
|
|
|
|
elif issubclass(type, Enum):
|
|
if value in type:
|
|
return type(value)
|
|
else:
|
|
raise pymol.CmdException(f"Invalid value for enum {type.__name__}: {value}")
|
|
|
|
elif isinstance(type, str):
|
|
return str(value)
|
|
|
|
raise pymol.CmdException(f"Unsupported argument type annotation {type}")
|
|
|
|
|
|
def new_command(name, function=None, _self=cmd):
|
|
|
|
if function is None:
|
|
name, function = name.__name__, name
|
|
|
|
# docstring text, if present, should be dedented
|
|
if function.__doc__ is not None:
|
|
function.__doc__ = dedent(function.__doc__)
|
|
|
|
# Analysing arguments
|
|
spec = inspect.getfullargspec(function)
|
|
kwargs_ = {}
|
|
args_ = spec.args[:]
|
|
defaults = list(spec.defaults or [])
|
|
|
|
args2_ = args_[:]
|
|
while args_ and defaults:
|
|
kwargs_[args_.pop(-1)] = defaults.pop(-1)
|
|
|
|
funcs = {}
|
|
for idx, (var, func) in enumerate(spec.annotations.items()):
|
|
funcs[var] = func
|
|
|
|
# Inner function that will be callable every time the command is executed
|
|
@wraps(function)
|
|
def inner(*args, **kwargs):
|
|
caller = traceback.extract_stack(limit=2)[0].filename
|
|
# It was called from command line or pml script, so parse arguments
|
|
if caller == _parser_filename:
|
|
kwargs = {**kwargs, **dict(zip(args2_, args))}
|
|
kwargs.pop("_self", None)
|
|
new_kwargs = {}
|
|
for var, type in funcs.items():
|
|
if var in kwargs:
|
|
value = kwargs[var]
|
|
new_kwargs[var] = _into_types(type, value)
|
|
final_kwargs = {}
|
|
for k, v in kwargs_.items():
|
|
final_kwargs[k] = v
|
|
for k, v in new_kwargs.items():
|
|
if k not in final_kwargs:
|
|
final_kwargs[k] = v
|
|
return function(**final_kwargs)
|
|
|
|
# It was called from Python, so pass the arguments as is
|
|
else:
|
|
return function(*args, **kwargs)
|
|
|
|
_self.keyword[name] = [inner, 0,0,',',parsing.STRICT]
|
|
|
|
# Accessor to the original function so bypass the stack extraction.
|
|
# The purpose is optimization (loops, for instance).
|
|
inner.func = inner.__wrapped__
|
|
return inner
|
|
|
|
def extend(name, function=None, _self=cmd):
|
|
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"extend" is an API-only function which binds a new external
|
|
function as a command into the PyMOL scripting language.
|
|
|
|
PYMOL API
|
|
|
|
cmd.extend(string name,function function)
|
|
|
|
PYTHON EXAMPLE
|
|
|
|
def foo(moo=2): print moo
|
|
cmd.extend('foo',foo)
|
|
|
|
The following would now work within PyMOL:
|
|
|
|
PyMOL>foo
|
|
2
|
|
PyMOL>foo 3
|
|
3
|
|
PyMOL>foo moo=5
|
|
5
|
|
PyMOL>foo ?
|
|
Usage: foo [ moo ]
|
|
|
|
NOTES
|
|
|
|
For security reasons, new PyMOL commands created using "extend" are
|
|
not saved or restored in sessions.
|
|
|
|
SEE ALSO
|
|
|
|
alias, api
|
|
'''
|
|
if function is None:
|
|
name, function = name.__name__, name
|
|
_self.keyword[name] = [function, 0,0,',',parsing.STRICT]
|
|
_self.kwhash.append(name)
|
|
_self.help_sc.append(name)
|
|
return function
|
|
|
|
# for aliasing compound commands to a single keyword
|
|
|
|
def extendaa(*arg, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
API-only function to decorate a function as a PyMOL command with
|
|
argument auto-completion.
|
|
|
|
EXAMPLE
|
|
|
|
@cmd.extendaa(cmd.auto_arg[0]['zoom'])
|
|
def zoom_organic(selection='*'):
|
|
cmd.zoom('organic & (%s)' % selection)
|
|
'''
|
|
auto_arg = _self.auto_arg
|
|
def wrapper(func):
|
|
name = func.__name__
|
|
_self.extend(name, func)
|
|
for (i, aa) in enumerate(arg):
|
|
if i == len(auto_arg):
|
|
auto_arg.append({})
|
|
if aa is not None:
|
|
auto_arg[i][name] = aa
|
|
return func
|
|
return wrapper
|
|
|
|
def alias(name, command, _self=cmd):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
"alias" binds routinely-used command inputs to a new command
|
|
keyword.
|
|
|
|
USAGE
|
|
|
|
alias name, command
|
|
|
|
ARGUMENTS
|
|
|
|
name = string: new keyword
|
|
|
|
command = string: literal input with commands separated by semicolons.
|
|
|
|
EXAMPLE
|
|
|
|
alias my_scene, hide; show ribbon, polymer; show sticks, organic; show nonbonded, solvent
|
|
|
|
my_scene
|
|
|
|
NOTES
|
|
|
|
For security reasons, aliased commands are not saved or restored
|
|
in sessions.
|
|
|
|
SEE ALSO
|
|
|
|
cmd.extend, api
|
|
'''
|
|
_self.keyword[name] = [eval("lambda :do('''%s ''')"%command.replace("'''","")),
|
|
0,0,',',parsing.STRICT]
|
|
_self.kwhash.append(name)
|
|
|
|
async_threads = []
|
|
|
|
def async_(func, *args, _self=cmd, **kwargs):
|
|
'''
|
|
DESCRIPTION
|
|
|
|
Run function threaded and show "please wait..." message.
|
|
'''
|
|
from .wizard.message import Message
|
|
|
|
wiz = Message(['please wait ...'], dismiss=0, _self=_self)
|
|
|
|
try:
|
|
_self.set_wizard(wiz)
|
|
except:
|
|
wiz = None
|
|
|
|
if isinstance(func, str):
|
|
func = _self.keyword[func][0]
|
|
|
|
def wrapper():
|
|
async_threads.append(t)
|
|
try:
|
|
func(*args, **kwargs)
|
|
except (pymol.CmdException, cmd.QuietException) as e:
|
|
if e.args:
|
|
print(e)
|
|
finally:
|
|
if wiz is not None:
|
|
try:
|
|
_self.set_wizard_stack([w
|
|
for w in _self.get_wizard_stack() if w != wiz])
|
|
except:
|
|
_self.do('_ wizard')
|
|
else:
|
|
_self.refresh_wizard()
|
|
|
|
async_threads.remove(t)
|
|
|
|
t = threading.Thread(target=wrapper)
|
|
t.setDaemon(1)
|
|
t.start()
|