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

476 lines
18 KiB
Python

from pymol.wizard import Wizard
from pymol import cmd
import pymol
import traceback
sele_prefix = "_mw"
sele_prefix_len = len(sele_prefix)
indi_sele = "_indicate_mw"
obj_prefix = "measure"
class Measurement(Wizard):
modes = [
'pairs',
'rings',
'angle',
'dihed',
'polar',
'heavy',
'neigh',
'hbond',
]
mode_name = {
'polar':'Polar Neighbors',
'heavy':'Heavy Neighbors',
'neigh':'Neighbors',
'pairs':'Distances',
'angle':'Angles',
'dihed':'Dihedrals',
'hbond':'Polar Contacts',
'rings':'Distances to Rings',
}
neighbor_modes = [
'same',
'other',
'enabled',
'all',
'in_object',
'in_selection'
]
object_modes = [
'merge',
'overwr',
'append',
]
object_mode_name = {
'merge':'Merge With Previous',
'overwr':'Replace Previous',
'append':'Create New Object',
}
def __init__(self,_self=cmd):
Wizard.__init__(self,_self)
self.pick_state = [0, 0, 0, 0]
self.cmd.unpick()
self.cutoff = self.cmd.get_setting_float("neighbor_cutoff")
self.heavy_neighbor_cutoff = self.cmd.get_setting_float("heavy_neighbor_cutoff")
self.polar_neighbor_cutoff = self.cmd.get_setting_float("polar_neighbor_cutoff")
self.hbond_cutoff = self.cmd.get_setting_float("h_bond_cutoff_center")
self.status = 0 # 0 no atoms selections, 1 atom selected, 2 atoms selected, 3 atoms selected
self.error = None
self.object_name = None
# mode selection subsystem
self.mode = self.session.get('default_mode','pairs')
self.neighbor_target = ""
# TODO:
# make this a function, and call it when we call refresh wizard
# to update the object/selection list
smm = []
smm.append([ 2, 'Measurement Mode', '' ])
for a in self.modes:
if a in ("neigh", "polar", "heavy"):
smm.append([ 1, self.mode_name[a], self.neighbor_submenu(a)])
else:
smm.append([ 1, self.mode_name[a], 'cmd.get_wizard().set_mode("'+a+'")'])
self.menu['mode']=smm
# overwrite mode selection subsystem
self.object_mode = self.session.get('default_object_mode','append')
smm = []
smm.append([ 2, 'New Measurements?', '' ])
for a in self.object_modes:
smm.append([ 1, self.object_mode_name[a], 'cmd.get_wizard().set_object_mode("'+a+'")'])
self.menu['object_mode']=smm
# initially select atoms, but now users can change this
self.selection_mode = self.cmd.get_setting_int("mouse_selection_mode")
self.cmd.set("mouse_selection_mode",0) # set selection mode to atomic
self.cmd.deselect() # disable the active selection (if any)
self.mouse_mode = 0
def get_event_mask(self):
"""
Sets what this Wizard listens for. event_mask_dirty is too coarse an event,
so we should consider something more fine grain for mouse mode updates
"""
return Wizard.event_mask_pick + Wizard.event_mask_select + Wizard.event_mask_dirty
def neighbor_objects(self,a):
"""
for neighbor selecting, populate the menu with these names
"""
list = self.cmd.get_names("public_objects",1)[0:25] # keep this practical
list = [x for x in list if self.cmd.get_type(x)=="object:molecule"]
result = [[ 2, 'Object: ', '']]
for b in list:
result.append( [ 1, b, 'cmd.get_wizard().set_neighbor_target("%s","%s")' % (a,b)])
return result
def neighbor_selections(self,a):
"""
get list of public selections for populating the menu
"""
list = self.cmd.get_names("public_selections",1)[0:25] # keep this practical
list = [x for x in list if self.cmd.get_type(x)=="selection"]
result = [[ 2, 'Selections: ', '']]
for b in list:
result.append( [ 1, b, 'cmd.get_wizard().set_neighbor_target("%s","%s")' % (a,b)])
return result
def neighbor_submenu(self,a,_self=cmd):
return [ [2, self.mode_name[a]+": ", ''],
[1, "in all objects", 'cmd.get_wizard().set_neighbor_target("'+a+'","all")'],
[1, "in object", self.neighbor_objects(a) ], # while this is a submenu this is also a command, so [1, ...]
[1, "in selection", self.neighbor_selections(a) ], # not [2, ...]
[1, "in other objects", 'cmd.get_wizard().set_neighbor_target("'+a+'","other")'],
[1, "in same object", 'cmd.get_wizard().set_neighbor_target("'+a+'", "same")'],
]
def _validate_instance(self):
Wizard._validate_instance(self)
if not hasattr(self,'meas_count'):
self.meas_count = self.session.get('meas_count',0)
def get_name(self,untaken=1,increment=1):
"""
get a name for the next measurement object
"""
self._validate_instance()
if increment or self.meas_count<1:
self.meas_count = self.meas_count + 1
obj_name = obj_prefix+"%02d"%self.meas_count
if untaken:
name_dict = {}
for tmp_name in cmd.get_names("all"):
name_dict[tmp_name] = None
while obj_name in name_dict:
self.meas_count = self.meas_count + 1
obj_name = obj_prefix+"%02d"%self.meas_count
return obj_name
# generic set routines
def set_neighbor_target(self,mode,target):
"""
sets the neighbor target in the menu
"""
self.set_mode(mode)
self.neighbor_target=target
self.status = 0
self.clear_input()
self.cmd.refresh_wizard()
def set_mode(self,mode):
"""
sets what we're measuring, distance, angle, dihedral, etc.
"""
if mode in self.modes:
self.mode = mode
# if setting mode, we're restarting the selection process
self.status = 0
self.clear_input()
if self.mode=='hbond':
self.cmd.set("mouse_selection_mode", 5)
self.cmd.refresh_wizard()
def set_object_mode(self,mode):
if mode in self.object_modes:
self.object_mode = mode
self.status = 0
self.cmd.refresh_wizard()
def get_panel(self):
return [
[ 1, 'Measurement',''],
[ 3, self.mode_name[self.mode],'mode'],
[ 3, self.object_mode_name[self.object_mode],'object_mode'],
[ 2, 'Delete Last Object' , 'cmd.get_wizard().delete_last()'],
[ 2, 'Delete All Measurements' , 'cmd.get_wizard().delete_all()'],
[ 2, 'Done','cmd.set_wizard()'],
]
def cleanup(self):
"""
restore user session how we found it
"""
self.session['default_mode'] = self.mode
self.session['default_object_mode'] = self.object_mode
self.clear_input()
self.cmd.set("mouse_selection_mode",self.selection_mode) # restore selection mode
def clear_input(self):
"""
delete our user selections for this wizard
"""
self.cmd.delete(sele_prefix+"*")
self.cmd.delete(indi_sele)
self.cmd.delete("pk1")
self.status = 0
def get_selection_name(self):
"""
Return a textual description of the mouse mode
"""
if self.cmd.get("mouse_selection_mode", quiet=1)=="0":
return ("atom","")
elif self.cmd.get("mouse_selection_mode", quiet=1)=="1":
return ("residue"," br. ")
elif self.cmd.get("mouse_selection_mode", quiet=1)=="2":
return ("chain", " bc. ")
elif self.cmd.get("mouse_selection_mode", quiet=1)=="3":
return ("segment", " bs. ")
elif self.cmd.get("mouse_selection_mode", quiet=1)=="4":
return ("object", " bo. ")
elif self.cmd.get("mouse_selection_mode", quiet=1)=="5":
return ("molecule", " bm. ")
elif self.cmd.get("mouse_selection_mode", quiet=1)=="6":
return ("C-alpha", " bca. ")
def get_prompt(self):
(what, code) = self.get_selection_name()
self.prompt = None
if self.mode in ['pairs', 'angle', 'dihed', 'hbond', 'rings' ]:
if self.status==0:
self.prompt = [ 'Please click on the first %s...' % what]
elif self.status==1:
self.prompt = [ 'Please click on the second %s...' % what ]
elif self.status==2:
self.prompt = [ 'Please click on the third %s...' % what]
elif self.status==3:
self.prompt = [ 'Please click on the fourth %s...' % what]
elif self.mode in [ 'polar', 'neigh', 'heavy', 'surf' ]:
(what, code) = self.get_selection_name()
letterN=""
if what[0] in ('a', 'e', 'i', 'o', 'u'):
letterN = "n"
else:
letterN = ""
self.prompt = [ 'Please click a%s %s...' % (letterN, what)]
if self.error is not None:
self.prompt.append(self.error)
return self.prompt
def delete_last(self):
"""
Corresponds to the "Delete Last Object" menu button
"""
self._validate_instance()
if self.status==0:
if self.meas_count>0:
name = self.get_name(0,0)
self.cmd.delete(name)
self.meas_count = self.meas_count - 1
self.status=0
self.error = None
self.clear_input()
self.cmd.refresh_wizard()
def delete_all(self):
"""
Corresponds to the "Delete All Measurements" menu button
"""
self.meas_count = 0
self.cmd.delete(obj_prefix+"*")
self.status=0
self.error = None
self.clear_input()
self.cmd.refresh_wizard()
def do_pick_state(self, state):
self.pick_state[self.status] = state
def do_select(self,name): # map selects into picks
self.cmd.unpick()
try:
self.cmd.select("pk1", name + " and not " + sele_prefix + "*") # note, using new object name wildcards
self.cmd.delete(name)
self.do_pick(0)
except pymol.CmdException:
if self.status:
sele_name = sele_prefix + str(self.status-1)
self.cmd.select(indi_sele, sele_name)
self.cmd.enable(indi_sele)
def do_pick(self,bondFlag):
# update pk1 based on current mouse mode
(what,code) = self.get_selection_name()
self.cmd.select("pk1", code + "(pk1)")
if bondFlag:
self.error = "Error: please select an atom, not a bond."
print(self.error)
else:
reset = 1
sele_name = sele_prefix + str(self.status)
state1, state2, state3, state4 = self.pick_state
if self.mode == 'pairs':
if self.status==0:
self.cmd.select(sele_name,"(pk1)")
self.cmd.select(indi_sele, sele_name)
self.cmd.enable(indi_sele)
self.status = 1
self.error = None
elif self.status==1:
obj_name = self.get_name((self.object_mode=='append'),
(self.object_mode=='append'))
if self.object_mode=='merge':
reset = 0
if state1 == state2 and self.cmd.get_selection_state('?pk1') != 0:
state1 = state2 = -3
self.cmd.dist(obj_name,"(v. and " + sele_prefix+"0)","(v. and (pk1))",reset=reset,
state1=state1, state2=state2)
self.cmd.enable(obj_name)
self.clear_input()
self.status = 0
self.cmd.unpick()
elif self.mode == 'rings':
if self.status==0:
self.cmd.select(sele_name,"(?pk1 | byring ?pk1)")
self.cmd.select(indi_sele, sele_name)
self.cmd.enable(indi_sele)
self.status = 1
self.error = None
elif self.status==1:
obj_name = self.get_name((self.object_mode=='append'),
(self.object_mode=='append'))
if self.object_mode=='merge':
reset = 0
if state1 == state2 and self.cmd.get_selection_state('?pk1') != 0:
state1 = state2 = -3
self.cmd.distance(obj_name,
"?" + sele_prefix + "0",
"(?pk1 | byring ?pk1)",
mode=4, reset=reset,
state1=state1, state2=state2)
self.cmd.enable(obj_name)
self.clear_input()
self.status = 0
self.cmd.unpick()
elif self.mode == 'angle':
if self.status<2:
self.cmd.select(sele_name,"(pk1)")
self.cmd.unpick()
self.cmd.select(indi_sele, sele_name)
self.cmd.enable(indi_sele)
self.status = self.status + 1
self.error = None
else:
obj_name = self.get_name((self.object_mode=='append'),
(self.object_mode=='append'))
if self.object_mode=='merge':
reset = 0
if state1 == state2 == state3 and self.cmd.get_selection_state('?pk1') != 0:
state1 = state2 = state3 = -3
self.cmd.angle(obj_name, "(v. and " + sele_prefix+"0)", "(v. and " + sele_prefix+"1)",
"(v. and (pk1))", reset=reset,
state1=state1, state2=state2, state3=state3)
self.cmd.enable(obj_name)
self.clear_input()
self.status = 0
self.cmd.unpick()
elif self.mode == 'dihed':
if self.status<3:
self.cmd.select(sele_name,"(pk1)")
self.cmd.unpick()
self.cmd.select(indi_sele, sele_name)
self.cmd.enable(indi_sele)
self.status = self.status + 1
self.error = None
else:
obj_name = self.get_name((self.object_mode=='append'),
(self.object_mode=='append'))
if self.object_mode=='merge':
reset = 0
self.cmd.dihedral(obj_name, "(v. and " + sele_prefix+"0)", "(v. and " + sele_prefix+"1)",
"(v. and " + sele_prefix+"2)", "(v. and (pk1))", reset=reset)
self.cmd.enable(obj_name)
self.clear_input()
self.status = 0
self.cmd.unpick()
if self.mode == 'hbond':
if self.status==0:
self.cmd.select(sele_name,"(pk1)")
self.cmd.select(indi_sele, sele_name)
self.cmd.enable(indi_sele)
self.status = 1
self.error = None
elif self.status==1:
obj_name = self.get_name((self.object_mode=='append'),
(self.object_mode=='append'))
if self.object_mode=='merge':
reset = 0
self.cmd.dist(obj_name,"(v. and " + sele_prefix+"0)","(v. and (pk1))",mode=2,cutoff=self.hbond_cutoff,reset=reset)
self.cmd.enable(obj_name)
self.clear_input()
self.status = 0
self.cmd.unpick()
elif self.mode in ['neigh','polar','heavy']:
reset = 1
obj_name = self.get_name((self.object_mode=='append'),
(self.object_mode=='append'))
if self.object_mode=='merge':
reset = 0
cnt = 0
sel_mod = ""
if self.mode in ('neigh', 'polar', 'heavy'):
if self.neighbor_target=="same":
sel_mod = "bm. pk1"
elif self.neighbor_target=="other":
sel_mod = "(not bm. pk1)"
elif self.neighbor_target=="enabled":
sel_mod = "(enabled)"
elif self.neighbor_target=="all":
sel_mod = "all"
else:
sel_mod = "(%s)" % self.neighbor_target
cutoffType=0
if self.mode == 'neigh':
cnt = self.cmd.select(sele_prefix,
"(v. and (pk1 a; %f) and (not (nbr. pk1)) and (not (nbr. (nbr. pk1))) and (not (nbr. (nbr. (nbr. pk1)))) and (%s))"
%(self.cutoff, sel_mod))
cutoffType=self.cutoff
elif self.mode == 'polar':
cnt = self.cmd.select(sele_prefix,
"(v. and (pk1 a; %f) and (e. n,o) and (not (nbr. pk1)) and (not (nbr. (nbr. pk1))) and (not (nbr. (nbr. (nbr. pk1)))) and (%s))"
%(self.polar_neighbor_cutoff, sel_mod))
cutoffType = self.polar_neighbor_cutoff
elif self.mode == 'heavy':
cutoffType = self.heavy_neighbor_cutoff
cnt = self.cmd.select(sele_prefix,
"(v. and (pk1 a; %f) and (not h.) and (not (nbr. pk1)) and (not (nbr. (nbr. pk1))) and (not (nbr. (nbr. (nbr. pk1)))) and (%s))"
%(self.heavy_neighbor_cutoff, sel_mod))
if cnt:
self.cmd.dist(obj_name,"(pk1)",sele_prefix,cutoff=cutoffType,reset=reset)
else:
print(" Wizard: No neighbors found.")
self.clear_input()
self.cmd.unpick()
self.cmd.enable(obj_name)
self.cmd.refresh_wizard()
def do_dirty(self):
if self.mouse_mode != self.cmd.get("mouse_selection_mode"):
self.mouse_mode = self.cmd.get("mouse_selection_mode")
self.cmd.refresh_wizard()