wangleiofficial commited on
Commit
364e78b
·
verified ·
1 Parent(s): 3a16f3d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -128
app.py CHANGED
@@ -12,9 +12,9 @@ from PIL import Image
12
  from transformers import AutoTokenizer, AutoModel
13
 
14
  # ==========================
15
- # 0. 环境初始化
16
  # ==========================
17
- plt.switch_backend('Agg') # 关键:防止服务器绘图报错
18
 
19
  os.environ["HF_HOME"] = "/tmp/hf_cache"
20
  os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf_cache"
@@ -64,7 +64,7 @@ class ProtDualBranchEnhancedClassifier(nn.Module):
64
  return self.classifier_head(z_fused_gated)
65
 
66
  # ==========================
67
- # 2. 加载模型
68
  # ==========================
69
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
70
  PLM_MODEL_NAME = "facebook/esm2_t30_150M_UR50D"
@@ -90,28 +90,16 @@ classifier.eval()
90
  print("✅ Ready.")
91
 
92
  # ==========================
93
- # 3. UniProt 风格绘图引擎 (Schematic Vector Style)
94
  # ==========================
95
  def draw_uniprot_style_cell(target_class):
96
- """
97
- 绘制类似 UniProt/SwissBioPics 的扁平化矢量风格细胞图。
98
- 特点:清晰的黑色描边,柔和的填充色,双层膜结构。
99
- """
100
  target = target_class.lower() if target_class else ""
101
 
102
- # === UniProt/SwissBioPics 配色板 ===
103
- # 柔和、低饱和度背景,高饱和度高亮
104
  c = {
105
- 'stroke': '#263238', # 深灰/黑色描边 (清晰感来源)
106
- 'cyto_bg': '#FFF9C4', # 极淡的黄色 (胞质)
107
- 'peri_bg': '#E1F5FE', # 极淡的蓝色 (周质)
108
- 'ext_bg': '#ffffff', # 纯白背景
109
- 'mem_line': '#546E7A', # 膜的线条颜色
110
- 'cw_pattern': '#90A4AE', # 细胞壁颜色
111
- 'dna': '#B0BEC5', # DNA 灰色
112
- 'ribo': '#CFD8DC', # 核糖体 浅灰
113
- 'active': '#D32F2F', # UniProt 经典红 (高亮)
114
- 'active_fill': '#FFCDD2' # 红色填充
115
  }
116
 
117
  # 状态判断
@@ -122,127 +110,107 @@ def draw_uniprot_style_cell(target_class):
122
  is_im = "plasma membrane" in target or "inner membrane" in target
123
  is_cyto = "cytoplasm" in target or "cytosol" in target
124
 
125
- # 创建画布 (宽高比 16:9)
126
  fig, ax = plt.subplots(figsize=(8, 4.5), dpi=150)
127
  ax.set_xlim(0, 10)
128
  ax.set_ylim(0, 6)
129
  ax.axis('off')
130
 
131
- # === 绘图辅助函数 ===
132
  def draw_membrane_layer(x, y, w, h, z, is_active):
133
- """绘制双层膜结构 (两条平行线)"""
134
  color = c['active'] if is_active else c['stroke']
135
  lw = 2.5 if is_active else 1.2
136
-
137
- # 外层线
138
  p1 = patches.FancyBboxPatch((x, y), w-h, h, boxstyle=f"round,pad=0,rounding_size={h/2}",
139
  fc="none", ec=color, lw=lw, zorder=z)
140
  ax.add_patch(p1)
141
- # 内层线 (稍微小一圈,模拟双层膜厚度)
142
  gap = 0.15
143
  p2 = patches.FancyBboxPatch((x+gap, y+gap), w-h-2*gap, h-2*gap, boxstyle=f"round,pad=0,rounding_size={(h-2*gap)/2}",
144
  fc="none", ec=color, lw=lw, zorder=z)
145
  ax.add_patch(p2)
146
 
147
- # === 1. 背景层 (Backgrounds) ===
148
-
149
- # 外膜区域背景 (Periplasm starts here)
150
- # 整个胶囊的底色设为周质颜色 (Light Blue)
151
  base_capsule = patches.FancyBboxPatch((1, 1), 8, 4, boxstyle="round,pad=0,rounding_size=2",
152
  fc=c['peri_bg'], ec="none", zorder=1)
153
  ax.add_patch(base_capsule)
154
-
155
- # 内膜区域背景 (Cytoplasm starts here)
156
- # 内部胶囊底色设为胞质颜色 (Light Yellow)
157
  cyto_capsule = patches.FancyBboxPatch((1.6, 1.6), 6.8, 2.8, boxstyle="round,pad=0,rounding_size=1.4",
158
  fc=c['cyto_bg'], ec="none", zorder=2)
159
  ax.add_patch(cyto_capsule)
160
 
161
- # === 2. 结构层 (Structures with Strokes) ===
162
-
163
- # A. Outer Membrane (外膜) - 双层线
164
  draw_membrane_layer(1, 1, 8+2, 4, z=5, is_active=is_om)
165
 
166
- # B. Cell Wall (细胞壁) - 网格纹理层
167
- # 在周质层中间画一层
168
  cw_color = c['active'] if is_cw else c['cw_pattern']
169
  cw_lw = 2 if is_cw else 0.5
170
- cw_alpha = 1.0 if is_cw else 0.4
171
  cw_patch = patches.FancyBboxPatch((1.3, 1.3), 7.4, 3.4, boxstyle="round,pad=0,rounding_size=1.7",
172
  fc="none", ec=cw_color, lw=cw_lw, linestyle=(0, (5, 5)), zorder=4)
173
  ax.add_patch(cw_patch)
174
 
175
- # 如果是周质高亮,填充浅红色
176
  if is_peri:
177
  peri_hl = patches.FancyBboxPatch((1.1, 1.1), 7.8, 3.8, boxstyle="round,pad=0,rounding_size=1.9",
178
  fc=c['active_fill'], ec="none", alpha=0.5, zorder=1.5)
179
  ax.add_patch(peri_hl)
180
 
181
- # C. Inner Membrane (内膜) - 双层线
182
  draw_membrane_layer(1.6, 1.6, 6.8, 2.8, z=6, is_active=is_im)
183
 
184
- # D. Cytoplasm (胞质) - 如果高亮则填充红
185
  if is_cyto:
186
  cyto_hl = patches.FancyBboxPatch((1.7, 1.7), 6.6, 2.6, boxstyle="round,pad=0,rounding_size=1.3",
187
  fc=c['active_fill'], ec="none", zorder=2.1)
188
  ax.add_patch(cyto_hl)
189
 
190
- # === 3. 内容细节 (Details) ===
191
-
192
- # DNA (拟核) - 清晰的线条,不再是模糊的
193
  t = np.linspace(0, 15, 300)
194
  x_dna = 5 + 2.0 * np.cos(t) * np.sin(t*0.5)
195
  y_dna = 3 + 0.6 * np.sin(t)
196
  ax.plot(x_dna, y_dna, color=c['dna'], lw=2, zorder=3, solid_capstyle='round')
197
 
198
- # Ribosomes (核糖体) - 规则的小圆圈,而不是随机噪点
199
- # 使用规律的分布让画面更整洁
200
  rng = np.random.default_rng(42)
201
  for _ in range(30):
202
  rx, ry = rng.uniform(2.5, 7.5), rng.uniform(2.2, 3.8)
203
- # 避开 DNA 区域 (简单判断)
204
  if not (4 < rx < 6 and 2.5 < ry < 3.5):
205
  circle = patches.Circle((rx, ry), radius=0.06, fc=c['ribo'], ec="none", zorder=3)
206
  ax.add_patch(circle)
207
 
208
- # Secreted Proteins (胞外)
209
  if is_secreted:
210
- # 画几个 UniProt 风格的小球+箭头
211
  ax.text(5, 5.5, "Secreted", ha='center', va='bottom', color=c['active'], fontweight='bold', fontsize=11)
212
  for i in range(3):
213
  sx = 4 + i
214
- sy = 5.2
215
- # 箭头
216
  ax.arrow(sx, 4.8, 0, 0.4, head_width=0.15, head_length=0.15, fc=c['active'], ec=c['active'], width=0.04, zorder=10)
217
- # 蛋白球
218
  circle = patches.Circle((sx, 4.7), radius=0.15, fc=c['active'], ec=c['stroke'], zorder=10)
219
  ax.add_patch(circle)
220
 
221
- # === 4. 标注 (Annotations) ===
222
- # 使用简单的线条指引
223
  labels = [
224
- ("Outer Membrane", 1, 5, is_om),
225
- ("Periplasm", 1, 4.2, is_peri),
226
- ("Cell Wall", 1, 3.6, is_cw),
227
- ("Inner Membrane", 1, 3.0, is_im),
228
- ("Cytoplasm", 1, 2.0, is_cyto)
 
229
  ]
230
 
231
- for txt, x_anchor, y_anchor, active in labels:
232
  if active:
233
- # 激活状态:红色加粗字体 + 指示线
234
  ax.text(0.2, y_anchor, txt, ha='left', va='center', color=c['active'], fontweight='bold', fontsize=10)
235
- # 画一条线连到细胞对应位置
236
- con_x = 1.0 # 胶囊左边缘附近
237
- ax.plot([0.8, con_x], [y_anchor, 3], color=c['active'], lw=1, ls='dotted', alpha=0.5)
238
  else:
239
  # 非激活状态:灰色小字
240
  ax.text(0.2, y_anchor, txt, ha='left', va='center', color='#90A4AE', fontsize=8)
241
 
242
- # 标题
243
  ax.text(5, 0.2, f"Localization: {target_class}", ha='center', va='bottom', fontsize=11, fontweight='bold', color=c['stroke'])
244
 
245
- # 保存
246
  buf = BytesIO()
247
  plt.savefig(buf, format='png', bbox_inches='tight', transparent=True, dpi=150)
248
  buf.seek(0)
@@ -251,7 +219,7 @@ def draw_uniprot_style_cell(target_class):
251
  return img
252
 
253
  # ==========================
254
- # 4. 预测逻辑
255
  # ==========================
256
  def predict(sequence_input):
257
  if not sequence_input or sequence_input.isspace():
@@ -274,46 +242,16 @@ def predict(sequence_input):
274
  return confidences, cell_diagram
275
 
276
  # ==========================
277
- # 5. UI 界面 (Paper-White Style)
278
  # ==========================
279
- # 极简学术风 CSS:白底、黑字、细线
280
  paper_css = """
281
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap');
282
  body { font-family: 'Roboto', sans-serif !important; background-color: #ffffff; color: #1a1a1a; }
283
-
284
- /* 标题区:纯白背景,底部细线 */
285
- .header-box {
286
- background: #ffffff;
287
- padding: 2rem 0;
288
- border-bottom: 1px solid #e5e7eb;
289
- margin-bottom: 2rem;
290
- text-align: left;
291
- }
292
  .header-title { font-size: 2.5rem; font-weight: 700; color: #000000; letter-spacing: -1px; line-height: 1.1; }
293
  .header-subtitle { font-size: 1.2rem; color: #52525b; font-weight: 300; margin-top: 10px; }
294
-
295
- /* 标签徽章:简约线条风 */
296
- .badge {
297
- display: inline-block;
298
- padding: 4px 10px;
299
- font-size: 0.8rem;
300
- font-weight: 500;
301
- color: #0f172a;
302
- background: #f1f5f9;
303
- border-radius: 4px;
304
- border: 1px solid #e2e8f0;
305
- margin-right: 8px;
306
- }
307
-
308
- /* 内容卡片:无阴影,仅边框,类似论文中的 Figure */
309
- .content-box {
310
- background: #ffffff;
311
- border: 1px solid #e5e7eb;
312
- border-radius: 8px;
313
- padding: 1.5rem;
314
- }
315
-
316
- /* 按钮:学术蓝 */
317
  button.primary { background: #2563eb !important; color: white !important; font-weight: 500; }
318
  """
319
 
@@ -328,51 +266,29 @@ theme = gr.themes.Base(
328
  )
329
 
330
  with gr.Blocks(theme=theme, css=paper_css, title="LocPred-Prok") as app:
331
-
332
- # --- Header (Paper Style) ---
333
  with gr.Column(elem_classes="header-box"):
334
  gr.HTML("""
335
  <div class="header-title">LocPred-Prok</div>
336
- <div class="header-subtitle">
337
- Accurate prokaryotic subcellular localization using dual-branch protein language models
338
- </div>
339
- <div style="margin-top: 15px;">
340
- <span class="badge">Article</span>
341
- <span class="badge">ESM-2 Enhanced</span>
342
- <span class="badge">Gram-negative</span>
343
- </div>
344
  """)
345
 
346
  with gr.Tabs():
347
  with gr.TabItem("Prediction Interface"):
348
  with gr.Row():
349
- # Input
350
  with gr.Column(scale=4, elem_classes="content-box"):
351
  gr.Markdown("### 1. Sequence Input")
352
  gr.Markdown("<small style='color:gray'>Enter protein sequence in FASTA format.</small>")
353
  sequence_input = gr.Textbox(lines=10, show_label=False, placeholder=">Sequence...")
354
-
355
  with gr.Row():
356
  clear_btn = gr.ClearButton(sequence_input, value="Clear")
357
  submit_btn = gr.Button("Analyze", variant="primary")
358
-
359
  gr.Markdown("#### Example Data")
360
- gr.Examples(
361
- examples=[
362
- [">Outer Membrane Protein A (OmpA)\nAPKNTWYTGAKLGWSQYHDTGFINNNGPTHENQLGAGAFGGYQVNPYVGFEMGYDWLGRMPYKGSVENGAYKAQGVQLTAKLGYPITDDLDIYTRLGGMVWRADTKSNVYGKNHDTGVSPVFAGGVEYAITPEIATRLEYQWTNNIGDAHTIGTRPDNGMLSLGVSYRFGQGEAAPVVAPAPAPAPEVQTKHFTLKSDVLFNFNKATLKPEGQAALDQLYSQLSNLDPKDGSVVVLGYTDRIGSDAYNQGLSERRAQSVVDYLISKGIPADKISARGMGESNPVTGNTCDNVKQRAALIDCLAPDRRVEIEVKGIKDVVTQPQA"],
363
- [">Cytoplasmic Protein (Ribo)\nARYLGPKLKLSRREGTDLFLKSGVRAIDTKCKIEQAPGQHGARKPRLSDYGVQLREKQKVRRIYGVLERQFRNYYKEAARLKGNTGENLLALLEGRLDNVVYRMGFG"]
364
- ],
365
- inputs=sequence_input, label=None
366
- )
367
-
368
- # Output
369
  with gr.Column(scale=6, elem_classes="content-box"):
370
  gr.Markdown("### 2. Localization Analysis")
371
-
372
- # 矢量风格图
373
  output_image = gr.Image(label="Schematic Visualization", show_label=False, show_download_button=True, interactive=False, type="pil", height=350)
374
-
375
- # 概率分布
376
  gr.Markdown("#### Probability Scores")
377
  output_label = gr.Label(num_top_classes=NUM_CLASSES, show_label=False)
378
 
 
12
  from transformers import AutoTokenizer, AutoModel
13
 
14
  # ==========================
15
+ # 0. 环境初始化 (保持不变)
16
  # ==========================
17
+ plt.switch_backend('Agg')
18
 
19
  os.environ["HF_HOME"] = "/tmp/hf_cache"
20
  os.environ["TRANSFORMERS_CACHE"] = "/tmp/hf_cache"
 
64
  return self.classifier_head(z_fused_gated)
65
 
66
  # ==========================
67
+ # 2. 加载模型 (保持不变)
68
  # ==========================
69
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
70
  PLM_MODEL_NAME = "facebook/esm2_t30_150M_UR50D"
 
90
  print("✅ Ready.")
91
 
92
  # ==========================
93
+ # 3. UniProt 风格绘图引擎 (修复高亮逻辑)
94
  # ==========================
95
  def draw_uniprot_style_cell(target_class):
 
 
 
 
96
  target = target_class.lower() if target_class else ""
97
 
98
+ # 配色板
 
99
  c = {
100
+ 'stroke': '#263238', 'cyto_bg': '#FFF9C4', 'peri_bg': '#E1F5FE',
101
+ 'mem_line': '#546E7A', 'cw_pattern': '#90A4AE', 'dna': '#B0BEC5',
102
+ 'ribo': '#CFD8DC', 'active': '#D32F2F', 'active_fill': '#FFCDD2'
 
 
 
 
 
 
 
103
  }
104
 
105
  # 状态判断
 
110
  is_im = "plasma membrane" in target or "inner membrane" in target
111
  is_cyto = "cytoplasm" in target or "cytosol" in target
112
 
 
113
  fig, ax = plt.subplots(figsize=(8, 4.5), dpi=150)
114
  ax.set_xlim(0, 10)
115
  ax.set_ylim(0, 6)
116
  ax.axis('off')
117
 
 
118
  def draw_membrane_layer(x, y, w, h, z, is_active):
119
+ # 修复:正确使用 is_active 判断颜色
120
  color = c['active'] if is_active else c['stroke']
121
  lw = 2.5 if is_active else 1.2
 
 
122
  p1 = patches.FancyBboxPatch((x, y), w-h, h, boxstyle=f"round,pad=0,rounding_size={h/2}",
123
  fc="none", ec=color, lw=lw, zorder=z)
124
  ax.add_patch(p1)
 
125
  gap = 0.15
126
  p2 = patches.FancyBboxPatch((x+gap, y+gap), w-h-2*gap, h-2*gap, boxstyle=f"round,pad=0,rounding_size={(h-2*gap)/2}",
127
  fc="none", ec=color, lw=lw, zorder=z)
128
  ax.add_patch(p2)
129
 
130
+ # === 1. 背景层 ===
131
+ # Periplasm Background
 
 
132
  base_capsule = patches.FancyBboxPatch((1, 1), 8, 4, boxstyle="round,pad=0,rounding_size=2",
133
  fc=c['peri_bg'], ec="none", zorder=1)
134
  ax.add_patch(base_capsule)
135
+
136
+ # Cytoplasm Background
 
137
  cyto_capsule = patches.FancyBboxPatch((1.6, 1.6), 6.8, 2.8, boxstyle="round,pad=0,rounding_size=1.4",
138
  fc=c['cyto_bg'], ec="none", zorder=2)
139
  ax.add_patch(cyto_capsule)
140
 
141
+ # === 2. 结构层 (修复高亮) ===
142
+
143
+ # A. Outer Membrane (外膜) - 最外层
144
  draw_membrane_layer(1, 1, 8+2, 4, z=5, is_active=is_om)
145
 
146
+ # B. Cell Wall (细胞壁)
 
147
  cw_color = c['active'] if is_cw else c['cw_pattern']
148
  cw_lw = 2 if is_cw else 0.5
 
149
  cw_patch = patches.FancyBboxPatch((1.3, 1.3), 7.4, 3.4, boxstyle="round,pad=0,rounding_size=1.7",
150
  fc="none", ec=cw_color, lw=cw_lw, linestyle=(0, (5, 5)), zorder=4)
151
  ax.add_patch(cw_patch)
152
 
153
+ # Periplasm Highlight Fill
154
  if is_peri:
155
  peri_hl = patches.FancyBboxPatch((1.1, 1.1), 7.8, 3.8, boxstyle="round,pad=0,rounding_size=1.9",
156
  fc=c['active_fill'], ec="none", alpha=0.5, zorder=1.5)
157
  ax.add_patch(peri_hl)
158
 
159
+ # C. Inner Membrane (内膜)
160
  draw_membrane_layer(1.6, 1.6, 6.8, 2.8, z=6, is_active=is_im)
161
 
162
+ # D. Cytoplasm Highlight Fill (修复:只在胞质激活时填充)
163
  if is_cyto:
164
  cyto_hl = patches.FancyBboxPatch((1.7, 1.7), 6.6, 2.6, boxstyle="round,pad=0,rounding_size=1.3",
165
  fc=c['active_fill'], ec="none", zorder=2.1)
166
  ax.add_patch(cyto_hl)
167
 
168
+ # === 3. 内容细节 ===
169
+ # DNA
 
170
  t = np.linspace(0, 15, 300)
171
  x_dna = 5 + 2.0 * np.cos(t) * np.sin(t*0.5)
172
  y_dna = 3 + 0.6 * np.sin(t)
173
  ax.plot(x_dna, y_dna, color=c['dna'], lw=2, zorder=3, solid_capstyle='round')
174
 
175
+ # Ribosomes
 
176
  rng = np.random.default_rng(42)
177
  for _ in range(30):
178
  rx, ry = rng.uniform(2.5, 7.5), rng.uniform(2.2, 3.8)
 
179
  if not (4 < rx < 6 and 2.5 < ry < 3.5):
180
  circle = patches.Circle((rx, ry), radius=0.06, fc=c['ribo'], ec="none", zorder=3)
181
  ax.add_patch(circle)
182
 
183
+ # Secreted Proteins
184
  if is_secreted:
 
185
  ax.text(5, 5.5, "Secreted", ha='center', va='bottom', color=c['active'], fontweight='bold', fontsize=11)
186
  for i in range(3):
187
  sx = 4 + i
 
 
188
  ax.arrow(sx, 4.8, 0, 0.4, head_width=0.15, head_length=0.15, fc=c['active'], ec=c['active'], width=0.04, zorder=10)
 
189
  circle = patches.Circle((sx, 4.7), radius=0.15, fc=c['active'], ec=c['stroke'], zorder=10)
190
  ax.add_patch(circle)
191
 
192
+ # === 4. 标注 (修复指示线) ===
 
193
  labels = [
194
+ # (Text, y_pos, x_connect, is_active)
195
+ ("Outer Membrane", 5, 1.0, is_om),
196
+ ("Periplasm", 4.2, 1.2, is_peri),
197
+ ("Cell Wall", 3.6, 1.3, is_cw),
198
+ ("Inner Membrane", 3.0, 1.6, is_im),
199
+ ("Cytoplasm", 2.0, 2.0, is_cyto)
200
  ]
201
 
202
+ for txt, y_anchor, x_connect, active in labels:
203
  if active:
204
+ # 激活状态:红色加粗 + 指示线连到正确位置
205
  ax.text(0.2, y_anchor, txt, ha='left', va='center', color=c['active'], fontweight='bold', fontsize=10)
206
+ # 修复:指示线连接到对应的 x_connect 位置
207
+ ax.plot([0.8, x_connect], [y_anchor, y_anchor], color=c['active'], lw=1.5, ls='-', alpha=0.8)
 
208
  else:
209
  # 非激活状态:灰色小字
210
  ax.text(0.2, y_anchor, txt, ha='left', va='center', color='#90A4AE', fontsize=8)
211
 
 
212
  ax.text(5, 0.2, f"Localization: {target_class}", ha='center', va='bottom', fontsize=11, fontweight='bold', color=c['stroke'])
213
 
 
214
  buf = BytesIO()
215
  plt.savefig(buf, format='png', bbox_inches='tight', transparent=True, dpi=150)
216
  buf.seek(0)
 
219
  return img
220
 
221
  # ==========================
222
+ # 4. 预测逻辑 (保持不变)
223
  # ==========================
224
  def predict(sequence_input):
225
  if not sequence_input or sequence_input.isspace():
 
242
  return confidences, cell_diagram
243
 
244
  # ==========================
245
+ # 5. UI 界面 (Paper-White Style, 保持不变)
246
  # ==========================
 
247
  paper_css = """
248
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap');
249
  body { font-family: 'Roboto', sans-serif !important; background-color: #ffffff; color: #1a1a1a; }
250
+ .header-box { background: #ffffff; padding: 2rem 0; border-bottom: 1px solid #e5e7eb; margin-bottom: 2rem; text-align: left; }
 
 
 
 
 
 
 
 
251
  .header-title { font-size: 2.5rem; font-weight: 700; color: #000000; letter-spacing: -1px; line-height: 1.1; }
252
  .header-subtitle { font-size: 1.2rem; color: #52525b; font-weight: 300; margin-top: 10px; }
253
+ .badge { display: inline-block; padding: 4px 10px; font-size: 0.8rem; font-weight: 500; color: #0f172a; background: #f1f5f9; border-radius: 4px; border: 1px solid #e2e8f0; margin-right: 8px; }
254
+ .content-box { background: #ffffff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 1.5rem; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  button.primary { background: #2563eb !important; color: white !important; font-weight: 500; }
256
  """
257
 
 
266
  )
267
 
268
  with gr.Blocks(theme=theme, css=paper_css, title="LocPred-Prok") as app:
 
 
269
  with gr.Column(elem_classes="header-box"):
270
  gr.HTML("""
271
  <div class="header-title">LocPred-Prok</div>
272
+ <div class="header-subtitle">Accurate prokaryotic subcellular localization using dual-branch protein language models</div>
273
+ <div style="margin-top: 15px;"><span class="badge">Article</span><span class="badge">ESM-2 Enhanced</span><span class="badge">Gram-negative</span></div>
 
 
 
 
 
 
274
  """)
275
 
276
  with gr.Tabs():
277
  with gr.TabItem("Prediction Interface"):
278
  with gr.Row():
 
279
  with gr.Column(scale=4, elem_classes="content-box"):
280
  gr.Markdown("### 1. Sequence Input")
281
  gr.Markdown("<small style='color:gray'>Enter protein sequence in FASTA format.</small>")
282
  sequence_input = gr.Textbox(lines=10, show_label=False, placeholder=">Sequence...")
 
283
  with gr.Row():
284
  clear_btn = gr.ClearButton(sequence_input, value="Clear")
285
  submit_btn = gr.Button("Analyze", variant="primary")
 
286
  gr.Markdown("#### Example Data")
287
+ gr.Examples(examples=[[">Outer Membrane Protein A (OmpA)\nAPKNTWYTGAKLGWSQYHDTGFINNNGPTHENQLGAGAFGGYQVNPYVGFEMGYDWLGRMPYKGSVENGAYKAQGVQLTAKLGYPITDDLDIYTRLGGMVWRADTKSNVYGKNHDTGVSPVFAGGVEYAITPEIATRLEYQWTNNIGDAHTIGTRPDNGMLSLGVSYRFGQGEAAPVVAPAPAPAPEVQTKHFTLKSDVLFNFNKATLKPEGQAALDQLYSQLSNLDPKDGSVVVLGYTDRIGSDAYNQGLSERRAQSVVDYLISKGIPADKISARGMGESNPVTGNTCDNVKQRAALIDCLAPDRRVEIEVKGIKDVVTQPQA"], [">Cytoplasmic Protein (Ribo)\nARYLGPKLKLSRREGTDLFLKSGVRAIDTKCKIEQAPGQHGARKPRLSDYGVQLREKQKVRRIYGVLERQFRNYYKEAARLKGNTGENLLALLEGRLDNVVYRMGFG"]], inputs=sequence_input, label=None)
288
+
 
 
 
 
 
 
 
289
  with gr.Column(scale=6, elem_classes="content-box"):
290
  gr.Markdown("### 2. Localization Analysis")
 
 
291
  output_image = gr.Image(label="Schematic Visualization", show_label=False, show_download_button=True, interactive=False, type="pil", height=350)
 
 
292
  gr.Markdown("#### Probability Scores")
293
  output_label = gr.Label(num_top_classes=NUM_CLASSES, show_label=False)
294