Search code examples
pythonpython-3.xcomcomtypescheminformatics

How to create a .cdx file from InChI with ChemDraw/Python?


I would like to create a ChemDraw .cdx file from an InChI with Python. This answer gives a solution for cdx --> InChI.

The minimal example below cdx_to_inchi works fine, but I could not figure out how I can get inchi_to_cdx to work.

import comtypes.client as w32

def cdx_to_inchi(cdx):
    ChemDraw = w32.CreateObject("ChemDraw.Application")
    ChemDraw.Visible = False
    Compound = ChemDraw.Documents.Open(cdx)            # opens existing file
    inchi = Compound.Objects.Data("chemical/x-inchi")
    print(inchi)
    ChemDraw.Quit()

def inchi_to_cdx(inchi):
    ChemDraw = w32.CreateObject("ChemDraw.Application")
    ChemDraw.Visible = False

    Compound = ChemDraw.Documents.Open("Emtpy.cdx")   # opens existing file
    # ChemDraw.Documents.New("NewFile.cdx")           # How to create a new file?

    # Compound.Objects.SetData(inchi)                 # How to paste InChi data?
    # ChemDraw.Documents.Save()                       # How to save?
    # ChemDraw.Documents.SaveAs("MyMolecule.cdx")     # How to save under different name?
    ChemDraw.Quit()

cdx = r'C:\Data\Test.cdx'
inchi = '1S/C6H6/c1-2-4-6-5-3-1/h1-6H'

cdx_to_inchi(cdx)
inchi_to_cdx(inchi)

Solution

  • The following is at least a somehow working solution. It basically "mimics" a human user with keypresses. It is adapted from here. The drawbacks are:

    1. it seems to be Windows specific
    2. ChemDraw has to be the active application, i.e. will not work in the background
    3. additional delays slow down the whole procedure. a few 10'000 molecules would take about a day (well, better than by hand ;-)

    These questions remain:

    1. How to make it platform independent?
    2. How to let it work in the background?
    3. How to avoid additional delays or how short can the delays be made that it still works reliably?

    Code:

    ### create a ChemDraw .cdx file from an InChI string
    import win32com.client as win32
    import win32clipboard
    import time
    
    def sleep():
        time.sleep(0.5)   # wait for 0.5 seconds that script is not too fast for ChemDraw
    
    def sleep_short():
        time.sleep(0.25)  # wait for 0.25 seconds between key presses
    
    # initialize windows shell (so we can send keyboard commands)
    shell = win32.Dispatch("WScript.Shell")
    
    def hit_keys(keys): # function to wait, then send a keypress signal
        sleep_short()
        shell.SendKeys(keys,1)
    
    # initialize ChemDraw
    chemdraw = win32.gencache.EnsureDispatch('ChemDraw.Application') # connect to ChemDraw
    chemdraw.Visible = True  # window needs to be visible otherwise keypresses will not work
    sleep()
    
    doc = chemdraw.Documents.Add()    # create a new document
    doc.Activate()
    sleep()
    
    # ChemDraw must be the active application, so it can receive keyboard commands
    shell.AppActivate("ChemDraw Prime") # name of the ChemDraw window bar
    sleep()
    
    inchi = 'InChI=1S/C6H6/c1-2-4-6-5-3-1/h1-6H'
    # set clipboard data
    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardText(inchi)
    win32clipboard.CloseClipboard()
    sleep()     # not sure whether a delay is needed here
    
    hit_keys("%e") # edit
    hit_keys("s")  # paste special
    hit_keys("i")  # pase as inchi
    sleep()
    
    ffname = r'C:\Users\Test\Scripts\MyMolecule.cdx'  # full filename
    doc.SaveAs(ffname)   # save the file
    doc.Close()
    chemdraw.Quit()    # close ChemDraw
    ### end of code