The cheminformatics RDKit tool features the RDKit Cookbook with example code for a range of applications. One of them is to Highlight the Differences between 2 Molecules, here by using fMCS to map similarities, then marking those residues which are not shared. This example works well, the code is here
I need the change the highlight color, but cannot get it to work. Based on the code link above, I've tried so far:
from rdkit import Chem
from rdkit.Chem import Draw
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit.Chem.Draw import IPythonConsole
from rdkit.Chem import rdFMCS
from rdkit.Chem.Draw import rdDepictor
rdDepictor.SetPreferCoordGen(True)
IPythonConsole.drawOptions.minFontSize=20
IPythonConsole.drawOptions.setHighlightColour=(0,1,0)
from collections import defaultdict
This had no effect at all, pic was drawn with default highlight.
d2g = Draw.MolsToGridImage([mol1, mol2])
opts = d2g.drawOptions()
opts.setHighlightColour = (0,1,0)
d2g([mol1, mol2],highlightAtomLists=[target_atm1, target_atm2])
This throws the error
AttributeError: 'Image' object has no attribute 'drawOptions'
Then I got it to work with this one:
from rdkit import Chem
from rdkit.Chem import Draw
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit.Chem.Draw import IPythonConsole
from rdkit.Chem import rdFMCS
from rdkit.Chem.Draw import rdDepictor
rdDepictor.SetPreferCoordGen(True)
IPythonConsole.drawOptions.minFontSize=20
from collections import defaultdict
mol1 = Chem.MolFromSmiles('FC1=CC=C2C(=C1)C=NN2')
mol2 = Chem.MolFromSmiles('CCC1=C2NN=CC2=CC(Cl)=C1')
from PIL import Image
from io import BytesIO
def show_mol(d2d,mol,legend='',highlightAtoms=[]):
d2d.DrawMolecule(mol,legend=legend, highlightAtoms=highlightAtoms)
d2d.FinishDrawing()
bio = BytesIO(d2d.GetDrawingText())
return Image.open(bio)
def show_images(imgs,buffer=5):
height = 0
width = 0
for img in imgs:
height = max(height,img.height)
width += img.width
width += buffer*(len(imgs)-1)
res = Image.new("RGBA",(width,height))
x = 0
for img in imgs:
res.paste(img,(x,0))
x += img.width + buffer
return res
mcs = rdFMCS.FindMCS([mol1,mol2])
mcs_mol = Chem.MolFromSmarts(mcs.smartsString)
match1 = mol1.GetSubstructMatch(mcs_mol)
target_atm1 = []
for atom in mol1.GetAtoms():
if atom.GetIdx() not in match1:
target_atm1.append(atom.GetIdx())
match2 = mol2.GetSubstructMatch(mcs_mol)
target_atm2 = []
for atom in mol2.GetAtoms():
if atom.GetIdx() not in match2:
target_atm2.append(atom.GetIdx())
img1 = []
d2dx = Draw.MolDraw2DCairo(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((1,0.84,0,.5))
img1.append(show_mol(d2dx,mol1,highlightAtoms=target_atm1))
d2dx = Draw.MolDraw2DCairo(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((1,0.84,0,.5))
img1.append(show_mol(d2dx,mol2, highlightAtoms=target_atm2))
pic = show_images(img1)
pic.save('hallo.png')
pic
Problems are, 1) I need an svg output and, 2) if the molecules have different size or alternate SMILE config is used, only MolsToGrid places them properly adjacent within same subpanel boundaries. So I need to use MolsToGrid for scientific publication-quality images.
I am a complete python n00b using jupyter lab on a windows 11 PC.
Python 3.8.18 (default, Sep 11 2023, 13:39:12) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
I was not able to figure out how to set HighlightAtomColors
in MolToGridImage
I resorted to:
import rdkit
print('\n-------------------------------')
print('\n rdkit Version : ', rdkit.__version__)
print('\n-------------------------------')
from rdkit import Chem
from rdkit.Chem import Draw
from rdkit.Chem import rdFMCS
from rdkit.Chem.Draw import rdDepictor
rdDepictor.SetPreferCoordGen(True)
# rdDepictor.SetPreferCoordGen(False) ## molecules in final pics are not mirrored
from io import BytesIO, StringIO
from IPython.display import SVG, display
smi1 = 'FC1=CC=C2C(=C1)C=NN2'
smi2 = 'CCC1=C2NN=CC2=CC(Cl)=C1'
print(smi1)
print(smi2)
mol1 = Chem.MolFromSmiles(smi1)
mol2 = Chem.MolFromSmiles(smi2)
smi_mol1 = Chem.MolToSmiles(mol1)
smi_mol2 = Chem.MolToSmiles(mol2)
print(smi_mol1)
print(smi_mol2)
def show_mol(d2d,mol,legend='',highlightAtoms=[]):
d2d.DrawMolecule(mol,legend=legend, highlightAtoms=highlightAtoms)
d2d.FinishDrawing()
svg = d2d.GetDrawingText()
img = BytesIO(svg.encode())
# print(img.read()) # prints SVG file content (tex file in XML markup language)
# display(SVG(img) # should show the image inside a jupyter notebook, check to be sure it does
# img.seek(0)
return img
mcs = rdFMCS.FindMCS([mol1,mol2])
mcs_mol = Chem.MolFromSmarts(mcs.smartsString)
match1 = mol1.GetSubstructMatch(mcs_mol)
target_atm1 = []
for atom in mol1.GetAtoms():
if atom.GetIdx() not in match1:
target_atm1.append(atom.GetIdx())
match2 = mol2.GetSubstructMatch(mcs_mol)
target_atm2 = []
for atom in mol2.GetAtoms():
if atom.GetIdx() not in match2:
target_atm2.append(atom.GetIdx())
img1 = []
d2dx = Draw.MolDraw2DSVG(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((1,0.84,0,.5))
doptsx.bgColor=None
img1.append(show_mol(d2dx,mol1,highlightAtoms=target_atm1))
d2dx = Draw.MolDraw2DSVG(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((0,0.84,0,.5))
doptsx.bgColor=None
img1.append(show_mol(d2dx,mol2, highlightAtoms=target_atm2))
#### https://stackoverflow.com/questions/61800296/python-merging-2-svg-elelments-into-one-svg-element
import io
from svglib.svglib import svg2rlg
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.renderSVG import SVGCanvas, draw
background_content="""<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="700" height="300">
</svg>"""
svg_1_content = img1[0]
print('svg_1_content : ', svg_1_content , type(svg_1_content))
svg_2_content = img1[1]
svg_1_element = svg2rlg(svg_1_content)
print('svg_1_element : ' , svg_1_element, svg_1_element.width , svg_1_element.height)
svg_2_element = svg2rlg(svg_2_content)
print('svg_2_element : ' , svg_2_element, svg_2_element.width , svg_2_element.height)
width = 350
height = 300
svg_element = svg2rlg(io.BytesIO(background_content.encode()))
print('svg_element :', svg_element, svg_element.width , svg_element.height)
d = Drawing(width, height) # setting the width and height
print('d :' , d , d.width)
print('svg_1_element : ' , svg_1_element, svg_1_element.width , svg_1_element.height)
print('svg_2_element : ' , svg_2_element, svg_2_element.width , svg_2_element.height)
d.add(svg_1_element)
d2 = Drawing(width, height)
d2.add(svg_2_element)
d2.translate(350,0)
s = StringIO()
c = SVGCanvas((2*width, height))
draw(d,c)
draw(d2,c)
c.save(s)
# print(s.getvalue())
with open('output.svg' , 'w') as handler:
s.seek(0)
handler.write(s.read())
# for i in dir(d):
# print(i)
# print(help(Drawing))
# print(help(SVGCanvas))
Need more libraries: svglib
, reportlab
.
Output *** it is a SVG picture, here it is an uploaded .png copy:
About the problem of the different image scales, maybe could be fixed with following DRAWING options:
d2dx = Draw.MolDraw2DSVG(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((0,0.84,0,.5))
doptsx.bgColor=None
doptsx.maxFontSize=20 # <-----
doptsx.minFontSize=20 # <-----
doptsx.fixedBondLength=40 # <-----
In code:
import rdkit
print('\n-------------------------------')
print('\n rdkit Version : ', rdkit.__version__)
print('\n-------------------------------')
from io import BytesIO, StringIO
from rdkit import Chem
from rdkit.Chem import Draw
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit.Chem.Draw import IPythonConsole
from rdkit.Chem import rdFMCS
from rdkit.Chem.Draw import rdDepictor
rdDepictor.SetPreferCoordGen(True)
IPythonConsole.drawOptions.minFontSize=20
from collections import defaultdict
mol1 = Chem.MolFromSmiles('C12=CC(O)=CC=C1C1=CC=C(O)C=C1C(=O)O2')
mol2 = Chem.MolFromSmiles('C12=CC(O)=CC=C1C(=O)OC1C=C(O)C=CC=12') #mol2_1
# mol2 = Chem.MolFromSmiles('C1=CC2=C(C=C1O)C3=C(C=C(C=C3)O)OC2=O') #mol2_2
def show_mol(d2d,mol,legend='',highlightAtoms=[]):
d2d.DrawMolecule(mol,legend=legend, highlightAtoms=highlightAtoms)
d2d.FinishDrawing()
svg = d2d.GetDrawingText()
img = BytesIO(svg.encode())
return img
mcs = rdFMCS.FindMCS([mol1,mol2])
mcs_mol = Chem.MolFromSmarts(mcs.smartsString)
match1 = mol1.GetSubstructMatch(mcs_mol)
target_atm1 = []
for atom in mol1.GetAtoms():
if atom.GetIdx() not in match1:
target_atm1.append(atom.GetIdx())
match2 = mol2.GetSubstructMatch(mcs_mol)
target_atm2 = []
for atom in mol2.GetAtoms():
if atom.GetIdx() not in match2:
target_atm2.append(atom.GetIdx())
img1 = []
d2dx = Draw.MolDraw2DSVG(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((1,0.84,0,.5))
doptsx.bgColor=None
doptsx.maxFontSize=20
doptsx.minFontSize=20
doptsx.fixedBondLength=40
img1.append(show_mol(d2dx,(mol1),highlightAtoms=target_atm1))
d2dx = Draw.MolDraw2DSVG(350,300)
doptsx = d2dx.drawOptions()
doptsx.setHighlightColour((0,0.84,0,.5))
doptsx.bgColor=None
doptsx.maxFontSize=20
doptsx.minFontSize=20
doptsx.fixedBondLength=40
img1.append(show_mol(d2dx,mol2, highlightAtoms=target_atm2))
#### https://stackoverflow.com/questions/61800296/python-merging-2-svg-elelments-into-one-svg-element
import io
from svglib.svglib import svg2rlg
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.renderSVG import SVGCanvas, draw
background_content="""<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="700" height="300">
</svg>"""
svg_1_content = img1[0]
print('svg_1_content : ', svg_1_content , type(svg_1_content))
svg_2_content = img1[1]
svg_1_element = svg2rlg(svg_1_content)
print('svg_1_element : ' , svg_1_element, svg_1_element.width , svg_1_element.height)
svg_2_element = svg2rlg(svg_2_content)
print('svg_2_element : ' , svg_2_element, svg_2_element.width , svg_2_element.height)
width = 350
height = 300
svg_element = svg2rlg(io.BytesIO(background_content.encode()))
print('svg_element :', svg_element, svg_element.width , svg_element.height)
d = Drawing(width, height) # setting the width and height
print('d :' , d , d.width)
print('svg_1_element : ' , svg_1_element, svg_1_element.width , svg_1_element.height)
print('svg_2_element : ' , svg_2_element, svg_2_element.width , svg_2_element.height)
d.add(svg_1_element)
d2 = Drawing(width, height)
d2.add(svg_2_element)
d2.translate(350,0)
s = StringIO()
c = SVGCanvas((2*width, height))
draw(d,c, )
draw(d2,c, )
c.save(s)
# print(s.getvalue())
with open('output_mol1_mol2_1.svg' , 'w') as handler:
s.seek(0)
handler.write(s.read())
Output with options above set:
With options (#<----) commented out:
Orientation problem remains, see output using mol2_1 (in code above):
Using:
mol1 = Chem.MolFromSmiles('C12=CC(O)=CC=C1C1=CC=C(O)C=C1C(=O)O2')
mol2 = Chem.MolFromSmiles('C1=CC2=C(C=C1O)C3=C(C=C(C=C3)O)OC2=O') #mol2_2
smi_mol1 = Chem.MolToSmiles(mol1, canonical=False)
smi_mol2 = Chem.MolToSmiles(mol2, canonical=False)
smi_mol1 = Chem.MolToSmiles(mol1, canonical=True)
smi_mol2 = Chem.MolToSmiles(mol2, canonical=True)
print(smi_mol1)
print(smi_mol2)
mol1 = Chem.MolFromSmiles(smi_mol1)
mol2 = Chem.MolFromSmiles(smi_mol2)
At the beginning of my code I get as output SMILES:
O=c1oc2cc(O)ccc2c2ccc(O)cc12
O=c1oc2cc(O)ccc2c2cc(O)ccc12
That feed to my script give picture: