File size: 6,233 Bytes
c98108b
9c759ad
 
c98108b
b169fb2
 
 
 
 
c98108b
b169fb2
 
 
 
c98108b
b169fb2
 
 
 
 
 
 
c98108b
b169fb2
 
 
c98108b
b169fb2
c98108b
 
 
 
 
 
 
 
 
 
9c759ad
c98108b
9c759ad
c98108b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b169fb2
 
c98108b
 
 
 
 
 
 
 
 
 
9c759ad
 
 
c98108b
 
 
6fb723e
c98108b
 
6fb723e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c759ad
 
 
 
 
 
 
 
 
 
 
 
c98108b
9c759ad
 
 
c98108b
9c759ad
6fb723e
 
c98108b
 
 
6fb723e
b169fb2
 
 
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import gradio as gr
import pandas as pd
import time

# --- 模拟后端功能 (来自第二个版本) ---
def smiles_to_structure(smiles):
    """Dummy function"""
    time.sleep(1)
    return f"Structure Generated from: {smiles}"

def fragment_molecule(smiles):
    """Dummy function"""
    time.sleep(2)
    return "Fragment1", "Fragment2", "1-2"

def generate_analogs(main_cls, minor_cls, number, delta_value):
    """Dummy function"""
    time.sleep(3)
    return [
        {"SMILE": "c1cccc1", "MolWt": 100, "TPSA": 20, "SLogP": 1, "SA": 30, "QED": 0.8},
        {"SMILE": "c1ccccc1", "MolWt": 105, "TPSA": 25, "SLogP": 1.2, "SA": 32, "QED": 0.9},
    ]

def update_output_table(data):
    df = pd.DataFrame(data)
    return df

# --- Gradio 界面 ---
KETCHER_HTML = r'''
<iframe id="ifKetcher" src="/ketcher/index.html" width="100%" height="600px" style="border: 1px solid #ccc;"></iframe>

<script>
console.log("[Front-end] Ketcher-Gradio integration script loaded.");

let ketcher = null;
let lastSmiles = '';

function findSmilesInput() {
    const inputContainer = document.getElementById('combined_smiles_input');
    if (!inputContainer) {
        console.warn("[Front-end] combined_smiles_input element not found.");
        return null;
    }
    const input = inputContainer.querySelector('input[type="text"]');
    return input;
}

function updateGradioInput(smiles) {
    const input = findSmilesInput();
    if (input && input.value !== smiles) {
        input.value = smiles;
        input.dispatchEvent(new Event('input', { bubbles: true }));
        console.log("[Front-end] Updated Gradio input with SMILES:", smiles);
    }
}

async function handleKetcherChange() {
    console.log("[Front-end] handleKetcherChange called, retrieving SMILES...");
    try {
        const smiles = await ketcher.getSmiles({ arom: false });
        console.log("[Front-end] SMILES retrieved from Ketcher:", smiles);
        if (smiles !== lastSmiles) {
            lastSmiles = smiles;
            updateGradioInput(smiles);
        }
    } catch (err) {
        console.error("[Front-end] Error getting SMILES from Ketcher:", err);
    }
}

function initKetcher() {
    console.log("[Front-end] initKetcher started.");
    const iframe = document.getElementById('ifKetcher');
    if (!iframe) {
        console.error("[Front-end] iframe not found.");
        setTimeout(initKetcher, 500);
        return;
    }

    const ketcherWindow = iframe.contentWindow;
    if (!ketcherWindow || !ketcherWindow.ketcher) {
        console.log("[Front-end] ketcher not yet available in iframe, retrying...");
        setTimeout(initKetcher, 500);
        return;
    }

    ketcher = ketcherWindow.ketcher;
    console.log("[Front-end] Ketcher instance acquired:", ketcher);

    ketcher.setMolecule('C').then(() => {
        console.log("[Front-end] Initial molecule set to 'C'.");
    });

    const editor = ketcher.editor;
    if (editor && typeof editor.subscribe === 'function') {
        console.log("[Front-end] Using editor.subscribe('change', ...)");
        editor.subscribe('change', handleKetcherChange);
    } else {
        console.error("[Front-end] No suitable event binding found.");
    }
}

document.getElementById('ifKetcher').addEventListener('load', () => {
    console.log("[Front-end] iframe loaded. Initializing Ketcher in 1s...");
    setTimeout(initKetcher, 1000);
});
</script>
'''

def create_combined_interface():
    with gr.Blocks() as demo:
        gr.Markdown("# Fragment Optimization Tools with Ketcher")

        with gr.Row():
            with gr.Column(scale=2):
                gr.HTML(KETCHER_HTML)  # 嵌入 Ketcher,仅加载一次

            with gr.Column(scale=1):
                gr.Markdown("### Input SMILES (From Ketcher)")
                combined_smiles_input = gr.Textbox(
                    label="",
                    value="C",
                    placeholder="SMILES from Ketcher will appear here",
                    elem_id="combined_smiles_input"
                )
                get_ketcher_smiles_btn = gr.Button("Get SMILES from Ketcher")
                fragment_btn = gr.Button("Fragmentize Molecule")

                constant_frag_input = gr.Textbox(label="Constant Fragment", placeholder="SMILES of constant fragment")
                variable_frag_input = gr.Textbox(label="Variable Fragment", placeholder="SMILES of variable fragment")
                attach_order_input = gr.Textbox(label="Attachment Order", placeholder="Attachment Order of SMILES")

                main_cls_dropdown = gr.Dropdown(label="Main Cls", choices=["None", "Cl", "Br"])
                minor_cls_dropdown = gr.Dropdown(label="Minor Cls", choices=["None", "Cl", "Br"])
                number_input = gr.Number(label="Number", value=5, step=1)
                delta_value_slider = gr.Slider(minimum=0, maximum=10, step=1, label="Delta Value", interactive=True)
                generate_analogs_btn = gr.Button("Generate")

        output_table = gr.Dataframe(headers=["SMILE", "MolWt", "TPSA", "SLogP", "SA", "QED"])

        # --- 事件处理 ---
        get_ketcher_smiles_btn.click(
            fn=None,
            inputs=None,
            outputs=combined_smiles_input,
            js="async () => { const iframe = document.getElementById('ifKetcher'); if(iframe && iframe.contentWindow && iframe.contentWindow.ketcher) { const smiles = await iframe.contentWindow.ketcher.getSmiles(); return smiles; } else { console.error('Ketcher not ready'); return ''; } }"
        )

        fragment_btn.click(fragment_molecule, inputs=combined_smiles_input, outputs=[constant_frag_input, variable_frag_input, attach_order_input])

        def update_table_with_analogs(main_cls, minor_cls, number, delta_value):
            analogs_data = generate_analogs(main_cls, minor_cls, number, delta_value)
            return update_output_table(analogs_data)

        generate_analogs_btn.click(update_table_with_analogs,
                                   inputs=[main_cls_dropdown, minor_cls_dropdown, number_input, delta_value_slider],
                                   outputs=output_table)

    return demo


# 创建 Gradio 界面并启动
demo = create_combined_interface()
demo.launch()