mirror of
https://github.com/rdkit/rdkit.git
synced 2026-06-04 21:54:27 +08:00
421 lines
11 KiB
Python
Executable File
421 lines
11 KiB
Python
Executable File
# $Id$
|
|
#
|
|
# Copyright (C) 2002-2006 Greg Landrum and Rational Discovery LLC
|
|
#
|
|
# @@ All Rights Reserved @@
|
|
#
|
|
""" tools for interacting with chemdraw
|
|
|
|
"""
|
|
from win32com.client import gencache,Dispatch,constants
|
|
import pythoncom
|
|
import string,tempfile,os,time
|
|
import win32com.client.gencache
|
|
try:
|
|
cdxModule = win32com.client.gencache.EnsureModule("{5F646AAB-3B56-48D2-904C-A68D7989C251}", 0, 7, 0)
|
|
except:
|
|
cdxModule = None
|
|
_cdxVersion=0
|
|
raise ImportError,"ChemDraw version (at least version 7) not found."
|
|
else:
|
|
_cdxVersion=7
|
|
from win32com.client import Dispatch
|
|
import win32gui
|
|
import re
|
|
|
|
cdApp = None
|
|
theDoc = None
|
|
theObjs = None
|
|
selectItem = None
|
|
cleanItem = None
|
|
centerItem = None
|
|
|
|
def StartChemDraw(visible=True,openDoc=False,showDoc=False):
|
|
""" launches chemdraw """
|
|
global cdApp,theDoc,theObjs,selectItem,cleanItem,centerItem
|
|
if cdApp is not None:
|
|
# if called more than once, do a restart
|
|
holder = None
|
|
selectItem = None
|
|
cleanItem = None
|
|
centerItem = None
|
|
theObjs = None
|
|
theDoc = None
|
|
cdApp = None
|
|
|
|
cdApp = Dispatch('ChemDraw.Application')
|
|
if openDoc:
|
|
theDoc = cdApp.Documents.Add()
|
|
theObjs = theDoc.Objects
|
|
else:
|
|
theDoc = None
|
|
selectItem = cdApp.MenuBars(1).Menus(2).MenuItems(8)
|
|
cleanItem = cdApp.MenuBars(1).Menus(5).MenuItems(6)
|
|
if _cdxVersion == 6:
|
|
centerItem = cdApp.MenuBars(1).Menus(4).MenuItems(1)
|
|
else:
|
|
centerItem = cdApp.MenuBars(1).Menus(4).MenuItems(7)
|
|
if visible:
|
|
cdApp.Visible=1
|
|
if theDoc and showDoc:
|
|
theDoc.Activate()
|
|
|
|
def ReactivateChemDraw(openDoc=True,showDoc=True):
|
|
global cdApp,theDoc,theObjs
|
|
cdApp.Visible=1
|
|
if openDoc:
|
|
theDoc = cdApp.Documents.Add()
|
|
if theDoc and showDoc:
|
|
theDoc.Activate()
|
|
theObjs = theDoc.Objects
|
|
|
|
# ------------------------------------------------------------------
|
|
# interactions with Chemdraw
|
|
# ------------------------------------------------------------------
|
|
def CDXConvert(inData,inFormat,outFormat):
|
|
"""converts the data passed in from one format to another
|
|
|
|
inFormat should be one of the following:
|
|
chemical/x-cdx chemical/cdx
|
|
chemical/x-daylight-smiles chemical/daylight-smiles
|
|
chemical/x-mdl-isis chemical/mdl-isis
|
|
chemical/x-mdl-molfile chemical/mdl-molfile
|
|
chemical/x-mdl-rxn chemical/mdl-rxn
|
|
chemical/x-mdl-tgf chemical/mdl-tgf
|
|
chemical/x-questel-F1
|
|
chemical/x-questel-F1-query
|
|
|
|
outFormat should be one of the preceding or:
|
|
image/x-png image/png
|
|
image/x-wmf image/wmf
|
|
image/tiff
|
|
application/postscript
|
|
image/gif
|
|
"""
|
|
global theObjs,theDoc
|
|
if cdApp is None:
|
|
StartChemDraw()
|
|
if theObjs is None:
|
|
if theDoc is None:
|
|
theDoc = cdApp.Documents.Add()
|
|
theObjs = theDoc.Objects
|
|
theObjs.SetData(inFormat,inData,pythoncom.Missing)
|
|
outD = theObjs.GetData(outFormat)
|
|
theObjs.Clear()
|
|
return outD
|
|
|
|
|
|
|
|
def CDXClean(inData,inFormat,outFormat):
|
|
"""calls the CDXLib Clean function on the data passed in.
|
|
|
|
CDXLib_Clean attempts to clean (prettify) the data before
|
|
doing an output conversion. It can be thought of as CDXConvert++.
|
|
|
|
CDXClean supports the same input and output specifiers as CDXConvert
|
|
(see above)
|
|
|
|
"""
|
|
global cdApp,theDoc,theObjs,selectItem,cleanItem
|
|
if cdApp is None:
|
|
StartChemDraw()
|
|
if theObjs is None:
|
|
if theDoc is None:
|
|
theDoc = cdApp.Documents.Add()
|
|
theObjs = theDoc.Objects
|
|
theObjs.SetData(inFormat,inData,pythoncom.Missing)
|
|
theObjs.Select()
|
|
cleanItem.Execute()
|
|
outD = theObjs.GetData(outFormat)
|
|
theObjs.Clear()
|
|
return outD
|
|
|
|
def CDXDisplay(inData,inFormat='chemical/cdx',clear=1):
|
|
""" displays the data in Chemdraw """
|
|
global cdApp,theDoc,theObjs,selectItem,cleanItem,centerItem
|
|
if cdApp is None:
|
|
StartChemDraw()
|
|
try:
|
|
theDoc.Activate()
|
|
except:
|
|
ReactivateChemDraw()
|
|
theObjs = theDoc.Objects
|
|
if clear:
|
|
theObjs.Clear()
|
|
theObjs.SetData(inFormat,inData,pythoncom.Missing)
|
|
return
|
|
|
|
def CDXGrab(outFormat='chemical/x-mdl-molfile'):
|
|
""" returns the contents of the active chemdraw document
|
|
|
|
"""
|
|
global cdApp,theDoc
|
|
if cdApp is None:
|
|
res = ""
|
|
else:
|
|
cdApp.Visible=1
|
|
if not cdApp.ActiveDocument:
|
|
ReactivateChemDraw()
|
|
try:
|
|
res = cdApp.ActiveDocument.Objects.GetData(outFormat)
|
|
except:
|
|
res = ""
|
|
return res
|
|
|
|
def CloseChemdraw():
|
|
""" shuts down chemdraw
|
|
|
|
"""
|
|
global cdApp
|
|
try:
|
|
cdApp.Quit()
|
|
except:
|
|
pass
|
|
Exit()
|
|
|
|
def Exit():
|
|
""" destroys our link to Chemdraw
|
|
|
|
"""
|
|
global cdApp
|
|
cdApp = None
|
|
|
|
def SaveChemDrawDoc(fileName='save.cdx'):
|
|
"""force chemdraw to save the active document
|
|
|
|
NOTE: the extension of the filename will determine the format
|
|
used to save the file.
|
|
"""
|
|
d = cdApp.ActiveDocument
|
|
d.SaveAs(fileName)
|
|
|
|
def CloseChemDrawDoc():
|
|
"""force chemdraw to save the active document
|
|
|
|
NOTE: the extension of the filename will determine the format
|
|
used to save the file.
|
|
"""
|
|
d = cdApp.ActiveDocument
|
|
d.Close()
|
|
|
|
def RaiseWindowNamed(nameRe):
|
|
# start by getting a list of all the windows:
|
|
cb = lambda x,y: y.append(x)
|
|
wins = []
|
|
win32gui.EnumWindows(cb,wins)
|
|
|
|
# now check to see if any match our regexp:
|
|
tgtWin = -1
|
|
for win in wins:
|
|
txt = win32gui.GetWindowText(win)
|
|
if nameRe.match(txt):
|
|
tgtWin=win
|
|
break
|
|
|
|
if tgtWin>=0:
|
|
win32gui.ShowWindow(tgtWin,1)
|
|
win32gui.BringWindowToTop(tgtWin)
|
|
|
|
def RaiseChemDraw():
|
|
e = re.compile('^ChemDraw')
|
|
RaiseWindowNamed(e)
|
|
|
|
|
|
try:
|
|
from PIL import Image
|
|
import cStringIO
|
|
def SmilesToPilImage(smilesStr):
|
|
"""takes a SMILES string and returns a PIL image using chemdraw
|
|
|
|
"""
|
|
return MolToPilImage(smilesStr,inFormat='chemical/daylight-smiles',outFormat='image/gif')
|
|
def MolToPilImage(dataStr,inFormat='chemical/daylight-smiles',outFormat='image/gif'):
|
|
"""takes a molecule string and returns a PIL image using chemdraw
|
|
|
|
"""
|
|
# do the conversion...
|
|
res = CDXConvert(dataStr,inFormat,outFormat)
|
|
dataFile = cStringIO.StringIO(str(res))
|
|
img = Image.open(dataFile).convert('RGB')
|
|
return img
|
|
except ImportError:
|
|
def SmilesToPilImage(smilesStr):
|
|
print 'You need to have PIL installed to use this functionality'
|
|
return None
|
|
def MolToPilImage(dataStr,inFormat='chemical/daylight-smiles',outFormat='image/gif'):
|
|
print 'You need to have PIL installed to use this functionality'
|
|
return None
|
|
|
|
|
|
# ------------------------------------------------------------------
|
|
# interactions with Chem3D
|
|
# ------------------------------------------------------------------
|
|
c3dApp = None
|
|
def StartChem3D(visible=0):
|
|
""" launches Chem3D """
|
|
global c3dApp
|
|
c3dApp = Dispatch('Chem3D.Application')
|
|
if not c3dApp.Visible:
|
|
c3dApp.Visible = visible
|
|
|
|
def CloseChem3D():
|
|
""" shuts down Chem3D """
|
|
global c3dApp
|
|
c3dApp.Quit()
|
|
c3dApp = None
|
|
|
|
availChem3DProps = ('DipoleMoment','BendEnergy','Non14VDWEnergy','StericEnergy',
|
|
'StretchBendEnergy','StretchEnergy','TorsionEnergy','VDW14Energy')
|
|
def Add3DCoordsToMol(data,format,props={}):
|
|
""" adds 3D coordinates to the data passed in using Chem3D
|
|
|
|
**Arguments**
|
|
|
|
- data: the molecular data
|
|
|
|
- format: the format of _data_. Should be something accepted by
|
|
_CDXConvert_
|
|
|
|
- props: (optional) a dictionary used to return calculated properties
|
|
|
|
"""
|
|
global c3dApp
|
|
if c3dApp is None:
|
|
StartChem3D()
|
|
if format != 'chemical/mdl-molfile':
|
|
molData = CDXClean(data,format,'chemical/mdl-molfile')
|
|
else:
|
|
molData = data
|
|
molFName = tempfile.mktemp('.mol')
|
|
open(molFName,'wb+').write(molData)
|
|
doc = c3dApp.Documents.Open(molFName)
|
|
|
|
if not doc:
|
|
print 'cannot open molecule'
|
|
raise ValueError,'No Molecule'
|
|
|
|
# set up the MM2 job
|
|
job = Dispatch('Chem3D.MM2Job')
|
|
job.Type=1
|
|
job.DisplayEveryIteration=0
|
|
job.RecordEveryIteration=0
|
|
|
|
# start the calculation...
|
|
doc.MM2Compute(job)
|
|
# and wait for it to finish
|
|
while doc.ComputeStatus in [0x434f4d50,0x50454e44]:
|
|
pass
|
|
#outFName = tempfile.mktemp('.mol')
|
|
# this is horrible, but apparently Chem3D gets pissy with tempfiles:
|
|
outFName = os.getcwd()+'/to3d.mol'
|
|
doc.SaveAs(outFName)
|
|
|
|
# generate the properties
|
|
for prop in availChem3DProps:
|
|
props[prop] = eval('doc.%s'%prop)
|
|
|
|
doc.Close(0)
|
|
|
|
os.unlink(molFName)
|
|
c3dData = open(outFName,'r').read()
|
|
gone = 0
|
|
while not gone:
|
|
try:
|
|
os.unlink(outFName)
|
|
except:
|
|
time.sleep(.5)
|
|
else:
|
|
gone = 1
|
|
return c3dData
|
|
|
|
def OptimizeSDFile(inFileName,outFileName,problemFileName='problems.sdf',
|
|
restartEvery=20):
|
|
""" optimizes the structure of every molecule in the input SD file
|
|
|
|
**Arguments**
|
|
|
|
- inFileName: name of the input SD file
|
|
|
|
- outFileName: name of the output SD file
|
|
|
|
- problemFileName: (optional) name of the SD file used to store molecules which
|
|
fail during the optimization process
|
|
|
|
- restartEvery: (optional) Chem3D will be shut down and restarted
|
|
every _restartEvery_ molecules to try and keep core leaks under control
|
|
|
|
"""
|
|
inFile = open(inFileName,'r')
|
|
outFile = open(outFileName,'w+')
|
|
problemFile = None
|
|
props = {}
|
|
lines = []
|
|
nextLine = inFile.readline()
|
|
skip = 0
|
|
nDone = 0
|
|
t1 = time.time()
|
|
while nextLine != '':
|
|
if nextLine.find('M END') != -1:
|
|
lines.append(nextLine)
|
|
molBlock = string.join(lines,'')
|
|
|
|
try:
|
|
newMolBlock = Add3DCoordsToMol(molBlock,'chemical/mdl-molfile',props=props)
|
|
except:
|
|
badBlock = molBlock
|
|
skip = 1
|
|
lines = []
|
|
else:
|
|
skip = 0
|
|
lines = [newMolBlock]
|
|
elif nextLine.find('$$$$') != -1:
|
|
t2 = time.time()
|
|
nDone += 1
|
|
print 'finished molecule %d in %f seconds'%(nDone,time.time()-t1)
|
|
t1 = time.time()
|
|
if nDone%restartEvery == 0:
|
|
CloseChem3D()
|
|
StartChem3D()
|
|
outFile.close()
|
|
outFile = open(outFileName,'a')
|
|
if not skip:
|
|
for prop in props.keys():
|
|
lines.append('> <%s>\n%f\n\n'%(prop,props[prop]))
|
|
lines.append(nextLine)
|
|
outFile.write(string.join(lines,''))
|
|
lines = []
|
|
else:
|
|
skip = 0
|
|
lines.append(nextLine)
|
|
if problemFile is None:
|
|
problemFile = open(problemFileName,'w+')
|
|
problemFile.write(badBlock)
|
|
problemFile.write(string.join(lines,''))
|
|
lines = []
|
|
else:
|
|
lines.append(nextLine)
|
|
nextLine = inFile.readline()
|
|
outFile.close()
|
|
if problemFile is not None:
|
|
problemFile.close()
|
|
|
|
if __name__=='__main__':
|
|
inStr = 'CCC(C=O)CCC'
|
|
img = SmilesToPilImage(inStr)
|
|
img.save('foo.jpg')
|
|
convStr = CDXClean(inStr,'chemical/x-daylight-smiles','chemical/x-daylight-smiles')
|
|
print 'in:',inStr
|
|
print 'out:',convStr
|
|
convStr = CDXConvert(inStr,'chemical/x-daylight-smiles','chemical/x-mdl-molfile')
|
|
print 'in:',inStr
|
|
print 'out:',convStr
|
|
convStr2 = CDXClean(convStr,'chemical/x-mdl-molfile','chemical/x-mdl-molfile')
|
|
print 'out2:',convStr2
|
|
|
|
inStr = 'COc1ccc(c2onc(c2C(=O)NCCc3ccc(F)cc3)c4ccc(F)cc4)c(OC)c1'
|
|
convStr = CDXConvert(inStr,'chemical/x-daylight-smiles','chemical/x-mdl-molfile')
|
|
out = open('test.mol','w+')
|
|
out.write(convStr)
|
|
out.close()
|
|
|