|
|
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", |
|
|
"CC(=O)OC1=CC=CC=C1C(=O)O", |
|
|
"CCN(CC)CC", |
|
|
"c1ccccc1O", |
|
|
"CC(C)CC(=O)O", |
|
|
"CN1C=NC2=C1N=CN2", |
|
|
"O=C(O)C1=CC=CC=C1", |
|
|
"C1CCCCC1", |
|
|
"CC(=O)N1CCCCC1", |
|
|
] |
|
|
|
|
|
|
|
|
def _extract_smiles(smiles_input): |
|
|
""" |
|
|
Normalize the Gradio input into a list of non-empty SMILES strings. |
|
|
""" |
|
|
if smiles_input is None: |
|
|
return [] |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
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() |
|
|
|