kaveh commited on
Commit
8bb2deb
·
1 Parent(s): 19d247f

Refactor sidebar layout for improved organization and styling. Grouped model selection, conditions, and force scale into distinct containers. Updated CSS for sidebar panels to enhance visual consistency and usability.

Browse files
S2FApp/app.py CHANGED
@@ -222,59 +222,61 @@ with st.sidebar:
222
  </div>
223
  """, unsafe_allow_html=True)
224
 
225
- model_type = st.radio(
226
- "Model type",
227
- ["single_cell", "spheroid"],
228
- format_func=lambda x: MODEL_TYPE_LABELS[x],
229
- horizontal=False,
230
- help="Single cell: substrate-aware force prediction. Spheroid: spheroid force maps.",
231
- )
 
232
 
233
- ckp_folder = get_ckp_folder(ckp_base, model_type)
234
- ckp_files = list_files_in_folder(ckp_folder, ".pth")
235
- ckp_subfolder_name = model_subfolder(model_type)
236
 
237
- if ckp_files:
238
- checkpoint = st.selectbox(
239
- "Checkpoint",
240
- ckp_files,
241
- key=f"checkpoint_{model_type}",
242
- help=f"Select a .pth file from ckp/{ckp_subfolder_name}/",
243
- )
244
- else:
245
- st.warning(f"No .pth files in ckp/{ckp_subfolder_name}/. Add checkpoints to load.")
246
- checkpoint = None
247
 
248
  substrate_config = None
249
  substrate_val = DEFAULT_SUBSTRATE
250
  use_manual = True
251
  if model_type == "single_cell":
252
  try:
253
- st.markdown('<p style="font-size: 0.95rem; font-weight: 500; margin-bottom: 0.5rem;">Conditions</p>', unsafe_allow_html=True)
254
- conditions_source = st.radio(
255
- "Conditions",
256
- ["From config", "Manually"],
257
- horizontal=True,
258
- label_visibility="collapsed",
259
- )
260
- from_config = conditions_source == "From config"
261
- if from_config:
262
- substrate_config = None
263
- substrates = list_substrates()
264
- substrate_val = st.selectbox(
265
- "Conditions (from config)",
266
- substrates,
267
- help="Select a preset from config/substrate_settings.json",
268
  label_visibility="collapsed",
269
  )
270
- use_manual = False
271
- else:
272
- manual_pixelsize = st.number_input("Pixel size (µm/px)", min_value=0.1, max_value=50.0,
273
- value=3.0769, step=0.1, format="%.4f")
274
- manual_young = st.number_input("Pascals", min_value=100.0, max_value=100000.0,
275
- value=6000.0, step=100.0, format="%.0f")
276
- substrate_config = {"pixelsize": manual_pixelsize, "young": manual_young}
277
- use_manual = True
 
 
 
 
 
 
 
 
 
 
278
  except FileNotFoundError:
279
  st.error("config/substrate_settings.json not found")
280
 
@@ -290,47 +292,48 @@ with st.sidebar:
290
  help="When on: estimate cell region from force map and use it for metrics (red contour). When off: use entire map.",
291
  )
292
 
293
- force_scale_mode = st.radio(
294
- "Force scale",
295
- ["Default", "Range"],
296
- horizontal=True,
297
- key="s2f_force_scale",
298
- help="Default: display forces on the full 0–1 scale. Range: set a sub-range; values outside are zeroed and the rest is stretched to the colormap.",
299
- )
300
- if force_scale_mode == "Default":
301
- clip_min, clip_max = 0.0, 1.0
302
- display_mode = "Default"
303
- clamp_only = True
304
- else:
305
- mn_col, mx_col = st.columns(2)
306
- with mn_col:
307
- clip_min = st.number_input(
308
- "Min",
309
- min_value=0.0,
310
- max_value=1.0,
311
- value=0.0,
312
- step=0.01,
313
- format="%.2f",
314
- key="s2f_clip_min",
315
- help="Lower bound of the display range (0–1).",
316
- )
317
- with mx_col:
318
- clip_max = st.number_input(
319
- "Max",
320
- min_value=0.0,
321
- max_value=1.0,
322
- value=1.0,
323
- step=0.01,
324
- format="%.2f",
325
- key="s2f_clip_max",
326
- help="Upper bound of the display range (0–1).",
327
- )
328
- if clip_min >= clip_max:
329
- st.warning("Min must be less than max. Using 0.00–1.00 for display.")
330
  clip_min, clip_max = 0.0, 1.0
331
- display_mode = "Range"
332
- clamp_only = False
333
- min_percentile, max_percentile = 0, 100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  cm_col_lbl, cm_col_sb = st.columns([1, 2])
336
  with cm_col_lbl:
 
222
  </div>
223
  """, unsafe_allow_html=True)
224
 
225
+ with st.container(border=False, key="s2f_grp_model"):
226
+ model_type = st.radio(
227
+ "Model type",
228
+ ["single_cell", "spheroid"],
229
+ format_func=lambda x: MODEL_TYPE_LABELS[x],
230
+ horizontal=False,
231
+ help="Single cell: substrate-aware force prediction. Spheroid: spheroid force maps.",
232
+ )
233
 
234
+ ckp_folder = get_ckp_folder(ckp_base, model_type)
235
+ ckp_files = list_files_in_folder(ckp_folder, ".pth")
236
+ ckp_subfolder_name = model_subfolder(model_type)
237
 
238
+ if ckp_files:
239
+ checkpoint = st.selectbox(
240
+ "Checkpoint",
241
+ ckp_files,
242
+ key=f"checkpoint_{model_type}",
243
+ help=f"Select a .pth file from ckp/{ckp_subfolder_name}/",
244
+ )
245
+ else:
246
+ st.warning(f"No .pth files in ckp/{ckp_subfolder_name}/. Add checkpoints to load.")
247
+ checkpoint = None
248
 
249
  substrate_config = None
250
  substrate_val = DEFAULT_SUBSTRATE
251
  use_manual = True
252
  if model_type == "single_cell":
253
  try:
254
+ with st.container(border=False, key="s2f_grp_conditions"):
255
+ st.markdown('<p style="font-size: 0.95rem; font-weight: 500; margin-bottom: 0.5rem;">Conditions</p>', unsafe_allow_html=True)
256
+ conditions_source = st.radio(
257
+ "Conditions",
258
+ ["From config", "Manually"],
259
+ horizontal=True,
 
 
 
 
 
 
 
 
 
260
  label_visibility="collapsed",
261
  )
262
+ from_config = conditions_source == "From config"
263
+ if from_config:
264
+ substrate_config = None
265
+ substrates = list_substrates()
266
+ substrate_val = st.selectbox(
267
+ "Conditions (from config)",
268
+ substrates,
269
+ help="Select a preset from config/substrate_settings.json",
270
+ label_visibility="collapsed",
271
+ )
272
+ use_manual = False
273
+ else:
274
+ manual_pixelsize = st.number_input("Pixel size (µm/px)", min_value=0.1, max_value=50.0,
275
+ value=3.0769, step=0.1, format="%.4f")
276
+ manual_young = st.number_input("Pascals", min_value=100.0, max_value=100000.0,
277
+ value=6000.0, step=100.0, format="%.0f")
278
+ substrate_config = {"pixelsize": manual_pixelsize, "young": manual_young}
279
+ use_manual = True
280
  except FileNotFoundError:
281
  st.error("config/substrate_settings.json not found")
282
 
 
292
  help="When on: estimate cell region from force map and use it for metrics (red contour). When off: use entire map.",
293
  )
294
 
295
+ with st.container(border=False, key="s2f_grp_force"):
296
+ force_scale_mode = st.radio(
297
+ "Force scale",
298
+ ["Default", "Range"],
299
+ horizontal=True,
300
+ key="s2f_force_scale",
301
+ help="Default: display forces on the full 0–1 scale. Range: set a sub-range; values outside are zeroed and the rest is stretched to the colormap.",
302
+ )
303
+ if force_scale_mode == "Default":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  clip_min, clip_max = 0.0, 1.0
305
+ display_mode = "Default"
306
+ clamp_only = True
307
+ else:
308
+ mn_col, mx_col = st.columns(2)
309
+ with mn_col:
310
+ clip_min = st.number_input(
311
+ "Min",
312
+ min_value=0.0,
313
+ max_value=1.0,
314
+ value=0.0,
315
+ step=0.01,
316
+ format="%.2f",
317
+ key="s2f_clip_min",
318
+ help="Lower bound of the display range (0–1).",
319
+ )
320
+ with mx_col:
321
+ clip_max = st.number_input(
322
+ "Max",
323
+ min_value=0.0,
324
+ max_value=1.0,
325
+ value=1.0,
326
+ step=0.01,
327
+ format="%.2f",
328
+ key="s2f_clip_max",
329
+ help="Upper bound of the display range (0–1).",
330
+ )
331
+ if clip_min >= clip_max:
332
+ st.warning("Min must be less than max. Using 0.00–1.00 for display.")
333
+ clip_min, clip_max = 0.0, 1.0
334
+ display_mode = "Range"
335
+ clamp_only = False
336
+ min_percentile, max_percentile = 0, 100
337
 
338
  cm_col_lbl, cm_col_sb = st.columns([1, 2])
339
  with cm_col_lbl:
S2FApp/static/s2f_styles.css CHANGED
@@ -6,6 +6,11 @@
6
  --s2f-primary-rgb: 13, 148, 136;
7
  /* Space for Streamlit’s fixed top toolbar (Deploy / menu). Set when header exists (see below). */
8
  --s2f-streamlit-header-offset: 0px;
 
 
 
 
 
9
  }
10
  /* Streamlit renders a fixed header; without this offset, the sidebar and first main block sit underneath it. */
11
  .stApp:has(header[data-testid="stHeader"]) {
@@ -185,9 +190,8 @@ section[data-testid="stSidebar"] [data-testid="stWidgetLabel"] p {
185
  display: flex;
186
  align-items: center;
187
  gap: 10px;
188
- padding-bottom: 0.8rem;
189
- margin-bottom: 0.5rem;
190
- border-bottom: 1px solid #e2e8f0;
191
  }
192
  .sidebar-brand .brand-icon {
193
  font-size: 1.6rem;
@@ -200,6 +204,36 @@ section[data-testid="stSidebar"] [data-testid="stWidgetLabel"] p {
200
  letter-spacing: -0.01em;
201
  }
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  /* === Metric cards === */
204
  [data-testid="stMetric"] {
205
  background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%);
 
6
  --s2f-primary-rgb: 13, 148, 136;
7
  /* Space for Streamlit’s fixed top toolbar (Deploy / menu). Set when header exists (see below). */
8
  --s2f-streamlit-header-offset: 0px;
9
+ /* Sidebar grouped blocks: faint surface, no heavy chrome */
10
+ --s2f-sidebar-panel-bg: rgba(255, 255, 255, 0.42);
11
+ --s2f-sidebar-panel-border: rgba(203, 213, 225, 0.34);
12
+ /* Match Streamlit’s sidebar horizontal padding so panels can full-bleed the track */
13
+ --s2f-sidebar-inline-inset: 1rem;
14
  }
15
  /* Streamlit renders a fixed header; without this offset, the sidebar and first main block sit underneath it. */
16
  .stApp:has(header[data-testid="stHeader"]) {
 
190
  display: flex;
191
  align-items: center;
192
  gap: 10px;
193
+ padding-bottom: 0.65rem;
194
+ margin-bottom: 0.35rem;
 
195
  }
196
  .sidebar-brand .brand-icon {
197
  font-size: 1.6rem;
 
204
  letter-spacing: -0.01em;
205
  }
206
 
207
+ /*
208
+ * Sidebar panels (model+ckp, conditions, force scale): horizontal bands — faded fill
209
+ * with top/bottom dividers; stretch edge-to-edge in the sidebar (bleed past content padding).
210
+ */
211
+ section[data-testid="stSidebar"] .st-key-s2f_grp_model,
212
+ section[data-testid="stSidebar"] .st-key-s2f_grp_conditions,
213
+ section[data-testid="stSidebar"] .st-key-s2f_grp_force {
214
+ box-sizing: border-box !important;
215
+ margin-left: calc(-1 * var(--s2f-sidebar-inline-inset)) !important;
216
+ margin-right: calc(-1 * var(--s2f-sidebar-inline-inset)) !important;
217
+ margin-bottom: 0.45rem !important;
218
+ width: calc(100% + 2 * var(--s2f-sidebar-inline-inset)) !important;
219
+ max-width: none !important;
220
+ padding: 0.58rem var(--s2f-sidebar-inline-inset) 0.62rem !important;
221
+ border-radius: 0 !important;
222
+ background: var(--s2f-sidebar-panel-bg) !important;
223
+ border: none !important;
224
+ border-top: 1px solid var(--s2f-sidebar-panel-border) !important;
225
+ border-bottom: 1px solid var(--s2f-sidebar-panel-border) !important;
226
+ box-shadow: none !important;
227
+ }
228
+ /* Inner layout flex — no inner frame */
229
+ section[data-testid="stSidebar"] .st-key-s2f_grp_model > div,
230
+ section[data-testid="stSidebar"] .st-key-s2f_grp_conditions > div,
231
+ section[data-testid="stSidebar"] .st-key-s2f_grp_force > div {
232
+ border: none !important;
233
+ box-shadow: none !important;
234
+ background: transparent !important;
235
+ }
236
+
237
  /* === Metric cards === */
238
  [data-testid="stMetric"] {
239
  background: linear-gradient(145deg, #ffffff 0%, #f8fafc 100%);
S2FApp/ui/result_display.py CHANGED
@@ -116,14 +116,7 @@ def render_batch_results(batch_results, colormap_name="Jet", display_mode="Defau
116
  )
117
  with rem_hm_cols[i % min(5, len(remaining_results))]:
118
  st.image(hm_rgb, caption=r["key_img"], use_container_width=True)
119
- render_horizontal_colorbar(
120
- colormap_name, clip_min, clip_max, is_rescale_b,
121
- caption=(
122
- "Ticks = force values in your selected [min, max] range; the strip uses the full colormap for the rescaled image (low → high)."
123
- if is_rescale_b
124
- else None
125
- ),
126
- )
127
  # Table
128
  st.dataframe(
129
  {h: [r[i] for r in rows] for i, h in enumerate(headers)},
 
116
  )
117
  with rem_hm_cols[i % min(5, len(remaining_results))]:
118
  st.image(hm_rgb, caption=r["key_img"], use_container_width=True)
119
+ render_horizontal_colorbar(colormap_name, clip_min, clip_max, is_rescale_b)
 
 
 
 
 
 
 
120
  # Table
121
  st.dataframe(
122
  {h: [r[i] for r in rows] for i, h in enumerate(headers)},