File size: 4,283 Bytes
43e7ae4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
import gradio as gr
from rdkit import Chem
from rdkit.Chem import Draw
from rdkit.Chem.Draw import rdMolDraw2D
list_smiles = [
"C[C@H](N)C(=O)O", # - Alanine
"CC(=O)OC1=CC=CC=C1C(=O)O", # - Aspirin
"CCN(CC)CC", # - Triethylamine
"c1ccccc1O", # - Phenol
"CC(C)CC(=O)O", # - Valeric acid
"CN1C=NC2=C1N=CN2", # - Adenine
"O=C(O)C1=CC=CC=C1", # - Benzoic acid
"C1CCCCC1", # - Cyclohexane
"CC(=O)N1CCCCC1", # - (Unnamed)
]
def _extract_smiles(smiles_input):
"""
Normalize the Gradio input into a list of non-empty SMILES strings.
"""
if smiles_input is None:
return []
# Handle text areas or other string-based inputs
if isinstance(smiles_input, str):
return [line.strip() for line in smiles_input.splitlines() if line.strip()]
smiles_strings = []
for row in smiles_input:
if isinstance(row, str):
candidate = row.strip()
if candidate:
smiles_strings.append(candidate)
continue
if not row:
continue
first_cell = row[0]
if isinstance(first_cell, str):
candidate = first_cell.strip()
if candidate:
smiles_strings.append(candidate)
return smiles_strings
def render_images(smiles_input):
"""
Converts SMILES strings into a list of PIL Images for gallery display.
Invalid SMILES entries are skipped.
"""
smiles_strings = _extract_smiles(smiles_input)
gallery_items = []
for smiles in smiles_strings:
mol = Chem.MolFromSmiles(smiles)
if mol is None:
continue
image = Draw.MolToImage(mol, size=(300, 300))
gallery_items.append((image, smiles))
return gallery_items
def render_svgs(smiles_input):
"""
Converts SMILES strings into SVG representations of the molecules.
Returns HTML containing all SVGs and error notices for invalid inputs.
"""
smiles_strings = _extract_smiles(smiles_input)
svg_fragments = []
invalid_smiles = []
for smiles in smiles_strings:
mol = Chem.MolFromSmiles(smiles)
if mol is None:
invalid_smiles.append(smiles)
continue
drawer = rdMolDraw2D.MolDraw2DSVG(300, 300)
drawer.DrawMolecule(mol)
drawer.FinishDrawing()
svg = drawer.GetDrawingText().replace("svg:", "")
svg_fragments.append(
f"<figure style='display:inline-block;margin:0 16px 16px 0;'>"
f"<figcaption style='text-align:center;font-family:monospace;margin-bottom:8px;'>{smiles}</figcaption>"
f"{svg}"
"</figure>"
)
html_parts = []
if svg_fragments:
html_parts.append("<div>" + "".join(svg_fragments) + "</div>")
if invalid_smiles:
invalid_list_items = "".join(f"<li>{smiles}</li>" for smiles in invalid_smiles)
html_parts.append(
"<div style='color:#b00020;margin-top:12px;'>"
"<strong>Invalid SMILES strings:</strong>"
f"<ul style='margin:4px 0 0 16px;'>{invalid_list_items}</ul>"
"</div>"
)
return "".join(html_parts)
# --- Gradio Interface ---
with gr.Blocks() as demo:
gr.Markdown("## Molecule Renderer")
gr.Markdown("Enter one or more SMILES strings to visualize the molecules.")
with gr.Row():
smiles_input = gr.DataFrame(
value=[[smiles] for smiles in list_smiles],
headers=["SMILES"],
label="SMILES Strings",
row_count=(len(list_smiles), "dynamic"),
col_count=1,
type="array",
wrap=True,
)
render_image_button = gr.Button("Render Image")
render_svg_button = gr.Button("Render SVG")
gr.Markdown("#### Image")
image_output = gr.Gallery(label="Molecule Structures")
gr.Markdown("#### SVG")
svg_output = gr.HTML(label="SVG Output")
# Separate actions for independent rendering
render_image_button.click(
fn=render_images,
inputs=smiles_input,
outputs=image_output,
)
render_svg_button.click(
fn=render_svgs,
inputs=smiles_input,
outputs=svg_output,
)
if __name__ == "__main__":
demo.launch()
|