spacr 0.1.50__py3-none-any.whl → 0.1.55__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
spacr/gui_utils.py CHANGED
@@ -1,28 +1,22 @@
1
- import os, spacr, inspect, traceback, io, sys, ast, ctypes, matplotlib, re, csv, requests, ast
1
+ import os, spacr, traceback, io, sys, ast, ctypes, matplotlib, re, csv, requests, ast
2
2
  import matplotlib.pyplot as plt
3
3
  matplotlib.use('Agg')
4
- import numpy as np
5
4
  import tkinter as tk
6
- from tkinter import ttk, messagebox
5
+ from tkinter import ttk
7
6
  import tkinter.font as tkFont
8
7
  from tkinter import filedialog
9
8
  from tkinter import Checkbutton
10
9
  from tkinter import font as tkFont
11
- from torchvision import models
12
-
13
10
  from multiprocessing import Process, Value, Queue, Manager, set_start_method
14
- import multiprocessing as mp
15
-
16
11
  from tkinter import ttk, scrolledtext
17
12
  from matplotlib.figure import Figure
18
13
  from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
19
14
  import time
20
15
  import requests
21
- from requests.exceptions import HTTPError, Timeout
22
16
  from huggingface_hub import list_repo_files, hf_hub_download
23
17
 
24
18
  from .logger import log_function_call
25
- from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks
19
+ from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks, get_analyze_reads_default_settings, set_default_umap_image_settings
26
20
 
27
21
  try:
28
22
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -42,42 +36,15 @@ fig_queue = None
42
36
 
43
37
  thread_control = {"run_thread": None, "stop_requested": False}
44
38
 
45
- def set_dark_style(style):
46
- font_style = tkFont.Font(family="Helvetica", size=10)
47
- style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style) # Entry
48
- style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style) # Combobox
49
- style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
50
- style.map('Custom.TButton',
51
- background=[('active', 'teal'), ('!active', 'black')],
52
- foreground=[('active', 'white'), ('!active', 'white')],
53
- bordercolor=[('active', 'white'), ('!active', 'white')])
54
- style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style) # Custom Label
55
- style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style) # Checkbutton
56
- style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
57
- style.configure('TLabel', background='black', foreground='#ffffff', font=font_style) # Label
58
- style.configure('TFrame', background='black') # Frame
59
- style.configure('TPanedwindow', background='black') # PanedWindow
60
- style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0]) # Notebook
61
- style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
62
- style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')])
63
- style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style) # Button (regular)
64
- style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
65
- style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black') # Scrollbar
66
- style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black') # Scrollbar
67
-
68
- # Define custom LabelFrame style
69
- style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
70
- style.configure('Custom.TLabelFrame.Label', background='black', foreground='white') # Style for the Label inside LabelFrame
71
- style.configure('Custom.TLabelFrame.Label', font=('Helvetica', 10, 'bold'))
72
-
73
- def set_default_font(root, font_name="Helvetica", size=12):
74
- default_font = (font_name, size)
75
- root.option_add("*Font", default_font)
76
- root.option_add("*TButton.Font", default_font)
77
- root.option_add("*TLabel.Font", default_font)
78
- root.option_add("*TEntry.Font", default_font)
39
+ class spacrCheckbutton(ttk.Checkbutton):
40
+ def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
41
+ super().__init__(parent, *args, **kwargs)
42
+ self.text = text
43
+ self.variable = variable if variable else tk.BooleanVar()
44
+ self.command = command
45
+ self.configure(text=self.text, variable=self.variable, command=self.command, style='Spacr.TCheckbutton')
79
46
 
80
- class ScrollableFrame(ttk.Frame):
47
+ class spacrFrame(ttk.Frame):
81
48
  def __init__(self, container, width=None, *args, bg='black', **kwargs):
82
49
  super().__init__(container, *args, **kwargs)
83
50
  self.configure(style='TFrame')
@@ -105,40 +72,64 @@ class ScrollableFrame(ttk.Frame):
105
72
  for child in self.scrollable_frame.winfo_children():
106
73
  child.configure(bg='black')
107
74
 
108
- class StdoutRedirector:
109
- def __init__(self, text_widget):
110
- self.text_widget = text_widget
111
-
112
- def write(self, string):
113
- try:
114
- if self.text_widget.winfo_exists():
115
- self.text_widget.insert(tk.END, string)
116
- self.text_widget.see(tk.END)
117
- except tk.TclError:
118
- pass # Handle or log the error as needed
75
+ class spacrLabel(tk.Frame):
76
+ def __init__(self, parent, text="", font=None, style=None, align="right", **kwargs):
77
+ label_kwargs = {k: v for k, v in kwargs.items() if k in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
78
+ for key in label_kwargs.keys():
79
+ kwargs.pop(key)
80
+ super().__init__(parent, **kwargs)
81
+ self.text = text
82
+ self.kwargs = label_kwargs
83
+ self.align = align
84
+ screen_height = self.winfo_screenheight()
85
+ label_height = screen_height // 50
86
+ label_width = label_height * 10
87
+ self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=self.kwargs.get("background", "black"))
88
+ self.canvas.grid(row=0, column=0, sticky="ew")
89
+
90
+ self.font_style = font if font else tkFont.Font(family=self.kwargs.get("font_family", "Helvetica"), size=self.kwargs.get("font_size", 12), weight=tkFont.NORMAL)
91
+ self.style = style
92
+
93
+ if self.align == "center":
94
+ anchor_value = tk.CENTER
95
+ text_anchor = 'center'
96
+ else: # default to right alignment
97
+ anchor_value = tk.E
98
+ text_anchor = 'e'
99
+
100
+ if self.style:
101
+ ttk_style = ttk.Style()
102
+ ttk_style.configure(self.style, **label_kwargs)
103
+ self.label_text = ttk.Label(self.canvas, text=self.text, style=self.style, anchor=text_anchor, justify=text_anchor)
104
+ self.label_text.pack(fill=tk.BOTH, expand=True)
105
+ else:
106
+ self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
107
+ label_height // 2, text=self.text, fill=self.kwargs.get("foreground", "white"),
108
+ font=self.font_style, anchor=anchor_value, justify=tk.RIGHT)
119
109
 
120
- def flush(self):
121
- pass
110
+ def set_text(self, text):
111
+ if self.style:
112
+ self.label_text.config(text=text)
113
+ else:
114
+ self.canvas.itemconfig(self.label_text, text=text)
122
115
 
123
- class CustomButton(tk.Frame):
116
+ class spacrButton(tk.Frame):
124
117
  def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
125
118
  super().__init__(parent, *args, **kwargs)
126
119
  self.text = text
127
120
  self.command = command
128
-
129
- # Detect screen height and calculate button dimensions
130
121
  screen_height = self.winfo_screenheight()
131
122
  button_height = screen_height // 50
132
123
  button_width = button_height * 3
133
124
 
134
- self.canvas = tk.Canvas(self, width=button_width, height=button_height, highlightthickness=0, bg="black")
125
+ # Increase the canvas size to accommodate the button and the rim
126
+ self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg="black")
135
127
  self.canvas.grid(row=0, column=0)
136
128
 
137
- self.button_bg = self.create_rounded_rectangle(0, 0, button_width, button_height, radius=20, fill="#800080")
129
+ self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
138
130
 
139
- # Use the passed font or default to Helvetica if not provided
140
131
  self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
141
- self.button_text = self.canvas.create_text(button_width // 2, button_height // 2, text=self.text, fill="white", font=self.font_style)
132
+ self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill="white", font=self.font_style)
142
133
 
143
134
  self.bind("<Enter>", self.on_enter)
144
135
  self.bind("<Leave>", self.on_leave)
@@ -148,62 +139,56 @@ class CustomButton(tk.Frame):
148
139
  self.canvas.bind("<Button-1>", self.on_click)
149
140
 
150
141
  def on_enter(self, event=None):
151
- self.canvas.itemconfig(self.button_bg, fill="#993399")
142
+ self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
152
143
 
153
144
  def on_leave(self, event=None):
154
- self.canvas.itemconfig(self.button_bg, fill="#800080")
145
+ self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
155
146
 
156
147
  def on_click(self, event=None):
157
148
  if self.command:
158
149
  self.command()
159
150
 
160
151
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
161
- points = [x1 + radius, y1,
162
- x1 + radius, y1,
163
- x2 - radius, y1,
164
- x2 - radius, y1,
165
- x2, y1,
166
- x2, y1 + radius,
167
- x2, y1 + radius,
168
- x2, y2 - radius,
169
- x2, y2 - radius,
170
- x2, y2,
171
- x2 - radius, y2,
172
- x2 - radius, y2,
173
- x1 + radius, y2,
174
- x1 + radius, y2,
175
- x1, y2,
176
- x1, y2 - radius,
177
- x1, y2 - radius,
178
- x1, y1 + radius,
179
- x1, y1 + radius,
180
- x1, y1]
181
-
152
+ points = [
153
+ x1 + radius, y1,
154
+ x1 + radius, y1,
155
+ x2 - radius, y1,
156
+ x2 - radius, y1,
157
+ x2, y1,
158
+ x2, y1 + radius,
159
+ x2, y1 + radius,
160
+ x2, y2 - radius,
161
+ x2, y2 - radius,
162
+ x2, y2,
163
+ x2 - radius, y2,
164
+ x2 - radius, y2,
165
+ x1 + radius, y2,
166
+ x1 + radius, y2,
167
+ x1, y2,
168
+ x1, y2 - radius,
169
+ x1, y2 - radius,
170
+ x1, y1 + radius,
171
+ x1, y1 + radius,
172
+ x1, y1
173
+ ]
182
174
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
183
175
 
184
- class ToggleSwitch(ttk.Frame):
176
+
177
+ class spacrSwitch(ttk.Frame):
185
178
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
186
179
  super().__init__(parent, *args, **kwargs)
187
180
  self.text = text
188
181
  self.variable = variable if variable else tk.BooleanVar()
189
182
  self.command = command
190
-
191
183
  self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
192
184
  self.canvas.grid(row=0, column=1, padx=(10, 0))
193
-
194
- # Background rounded rectangle with smaller dimensions and no outline
195
185
  self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
196
-
197
- # Switch ball with no outline
198
186
  self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
199
-
200
- self.label = ttk.Label(self, text=self.text, background="black", foreground="white")
187
+ self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
201
188
  self.label.grid(row=0, column=0, padx=(0, 10))
202
-
203
189
  self.bind("<Button-1>", self.toggle)
204
190
  self.canvas.bind("<Button-1>", self.toggle)
205
191
  self.label.bind("<Button-1>", self.toggle)
206
-
207
192
  self.update_switch()
208
193
 
209
194
  def toggle(self, event=None):
@@ -269,7 +254,7 @@ class ToggleSwitch(ttk.Frame):
269
254
 
270
255
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
271
256
 
272
- class ToolTip:
257
+ class spacrToolTip:
273
258
  def __init__(self, widget, text):
274
259
  self.widget = widget
275
260
  self.text = text
@@ -283,7 +268,8 @@ class ToolTip:
283
268
  self.tooltip_window = tk.Toplevel(self.widget)
284
269
  self.tooltip_window.wm_overrideredirect(True)
285
270
  self.tooltip_window.wm_geometry(f"+{x}+{y}")
286
- label = tk.Label(self.tooltip_window, text=self.text, background="yellow", relief='solid', borderwidth=1)
271
+ self.tooltip_window.configure(bg='black')
272
+ label = tk.Label(self.tooltip_window, text=self.text, background="#333333", foreground="white", relief='flat', borderwidth=0)
287
273
  label.grid(row=0, column=0, padx=5, pady=5)
288
274
 
289
275
  def hide_tooltip(self, event):
@@ -291,6 +277,114 @@ class ToolTip:
291
277
  self.tooltip_window.destroy()
292
278
  self.tooltip_window = None
293
279
 
280
+ def initiate_abort():
281
+ global thread_control
282
+ if thread_control.get("stop_requested") is not None:
283
+ thread_control["stop_requested"].value = 1
284
+
285
+ if thread_control.get("run_thread") is not None:
286
+ thread_control["run_thread"].join(timeout=5)
287
+ if thread_control["run_thread"].is_alive():
288
+ thread_control["run_thread"].terminate()
289
+ thread_control["run_thread"] = None
290
+
291
+ def start_process(q, fig_queue, settings_type='mask'):
292
+ global thread_control, vars_dict
293
+ from .settings import check_settings
294
+
295
+ settings = check_settings(vars_dict)
296
+ if thread_control.get("run_thread") is not None:
297
+ initiate_abort()
298
+ stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
299
+ thread_control["stop_requested"] = stop_requested
300
+ if settings_type == 'mask':
301
+ thread_control["run_thread"] = Process(target=run_mask_gui, args=(settings, q, fig_queue, stop_requested))
302
+ elif settings_type == 'measure':
303
+ thread_control["run_thread"] = Process(target=run_measure_gui, args=(settings, q, fig_queue, stop_requested))
304
+ elif settings_type == 'classify':
305
+ thread_control["run_thread"] = Process(target=run_classify_gui, args=(settings, q, fig_queue, stop_requested))
306
+ elif settings_type == 'sequencing':
307
+ thread_control["run_thread"] = Process(target=run_sequencing_gui, args=(settings, q, fig_queue, stop_requested))
308
+ elif settings_type == 'umap':
309
+ thread_control["run_thread"] = Process(target=run_umap_gui, args=(settings, q, fig_queue, stop_requested))
310
+ thread_control["run_thread"].start()
311
+
312
+ def import_settings(settings_type='mask'):
313
+ global vars_dict, scrollable_frame
314
+ from .settings import generate_fields
315
+ csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
316
+ csv_settings = read_settings_from_csv(csv_file_path)
317
+ if settings_type == 'mask':
318
+ settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
319
+ elif settings_type == 'measure':
320
+ settings = get_measure_crop_settings(settings={})
321
+ elif settings_type == 'classify':
322
+ settings = set_default_train_test_model(settings={})
323
+ elif settings_type == 'sequencing':
324
+ settings = get_analyze_reads_default_settings(settings={})
325
+ elif settings_type == 'umap':
326
+ settings = set_default_umap_image_settings(settings={})
327
+ else:
328
+ raise ValueError(f"Invalid settings type: {settings_type}")
329
+
330
+ variables = convert_settings_dict_for_gui(settings)
331
+ new_settings = update_settings_from_csv(variables, csv_settings)
332
+ vars_dict = generate_fields(new_settings, scrollable_frame)
333
+
334
+ def set_dark_style(style):
335
+ font_style = tkFont.Font(family="Helvetica", size=24)
336
+
337
+ # Entry
338
+ style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
339
+
340
+ # Combobox
341
+ style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
342
+ style.map('TCombobox', fieldbackground=[('readonly', 'black')], foreground=[('readonly', '#ffffff')])
343
+
344
+ # Custom Button
345
+ style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
346
+ style.map('Custom.TButton', background=[('active', 'teal'), ('!active', 'black')], foreground=[('active', 'white'), ('!active', 'white')], bordercolor=[('active', 'white'), ('!active', 'white')])
347
+
348
+ # Custom Label
349
+ style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style)
350
+
351
+ # Checkbutton
352
+ style.configure('Spacr.TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font="15")
353
+ style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
354
+
355
+ # General Label
356
+ style.configure('TLabel', background='black', foreground='#ffffff', font=font_style)
357
+
358
+ # Frame
359
+ style.configure('TFrame', background='black')
360
+
361
+ # PanedWindow
362
+ style.configure('TPanedwindow', background='black')
363
+
364
+ # Notebook
365
+ style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0])
366
+ style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
367
+ style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
368
+
369
+ # Button (regular)
370
+ style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style)
371
+ style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
372
+
373
+ # Scrollbar
374
+ style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black')
375
+ style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black')
376
+
377
+ # LabelFrame
378
+ style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
379
+ style.configure('Custom.TLabelFrame.Label', background='black', foreground='white', font=('Helvetica', 10, 'bold'))
380
+
381
+ def set_default_font(root, font_name="Helvetica", size=12):
382
+ default_font = (font_name, size)
383
+ root.option_add("*Font", default_font)
384
+ root.option_add("*TButton.Font", default_font)
385
+ root.option_add("*TLabel.Font", default_font)
386
+ root.option_add("*TEntry.Font", default_font)
387
+
294
388
  def create_menu_bar(root):
295
389
  from .app_annotate import initiate_annotation_app_root
296
390
  from .app_make_masks import initiate_mask_app_root
@@ -300,7 +394,9 @@ def create_menu_bar(root):
300
394
  "Measure": 'measure',
301
395
  "Annotate": initiate_annotation_app_root,
302
396
  "Make Masks": initiate_mask_app_root,
303
- "Classify": 'classify'
397
+ "Classify": 'classify',
398
+ "Sequencing": 'sequencing',
399
+ "Umap": 'umap'
304
400
  }
305
401
 
306
402
  def load_app_wrapper(app_name, app_func):
@@ -345,6 +441,10 @@ def proceed_with_app(root, app_name, app_func):
345
441
  initiate_root(root.content_frame, 'measure')
346
442
  elif app_name == "Classify":
347
443
  initiate_root(root.content_frame, 'classify')
444
+ elif app_name == "Sequencing":
445
+ initiate_root(root.content_frame, 'sequencing')
446
+ elif app_name == "Umap":
447
+ initiate_root(root.content_frame, 'umap')
348
448
  elif app_name == "Annotate":
349
449
  gui_annotate()
350
450
  elif app_name == "Make Masks":
@@ -397,464 +497,39 @@ def parse_list(value):
397
497
  except (ValueError, SyntaxError):
398
498
  raise ValueError("Invalid format for list")
399
499
 
500
+ def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
501
+ label_column = 0
502
+ widget_column = 1
400
503
 
401
- def check_settings(vars_dict):
402
- global q
403
- settings = {}
404
- # Define the expected types for each key, including None where applicable
405
- expected_types = {
406
- "src": str,
407
- "metadata_type": str,
408
- "custom_regex": (str, type(None)),
409
- "experiment": str,
410
- "channels": list,
411
- "magnification": int,
412
- "nucleus_channel": (int, type(None)),
413
- "nucleus_background": int,
414
- "nucleus_Signal_to_noise": float,
415
- "nucleus_CP_prob": float,
416
- "nucleus_FT": float,
417
- "cell_channel": (int, type(None)),
418
- "cell_background": (int, float),
419
- "cell_Signal_to_noise": (int, float),
420
- "cell_CP_prob": (int, float),
421
- "cell_FT": (int, float),
422
- "pathogen_channel": (int, type(None)),
423
- "pathogen_background": (int, float),
424
- "pathogen_Signal_to_noise": (int, float),
425
- "pathogen_CP_prob": (int, float),
426
- "pathogen_FT": (int, float),
427
- "preprocess": bool,
428
- "masks": bool,
429
- "examples_to_plot": int,
430
- "randomize": bool,
431
- "batch_size": int,
432
- "timelapse": bool,
433
- "timelapse_displacement": int,
434
- "timelapse_memory": int,
435
- "timelapse_frame_limits": list, # This can be a list of lists
436
- "timelapse_remove_transient": bool,
437
- "timelapse_mode": str,
438
- "timelapse_objects": list,
439
- "fps": int,
440
- "remove_background": bool,
441
- "lower_percentile": (int, float),
442
- "merge_pathogens": bool,
443
- "normalize_plots": bool,
444
- "all_to_mip": bool,
445
- "pick_slice": bool,
446
- "skip_mode": str,
447
- "save": bool,
448
- "plot": bool,
449
- "workers": int,
450
- "verbose": bool,
451
- "input_folder": str,
452
- "cell_mask_dim": int,
453
- "cell_min_size": int,
454
- "cytoplasm_min_size": int,
455
- "nucleus_mask_dim": int,
456
- "nucleus_min_size": int,
457
- "pathogen_mask_dim": int,
458
- "pathogen_min_size": int,
459
- "save_png": bool,
460
- "crop_mode": list,
461
- "use_bounding_box": bool,
462
- "png_size": list, # This can be a list of lists
463
- "normalize": bool,
464
- "png_dims": list,
465
- "normalize_by": str,
466
- "save_measurements": bool,
467
- "representative_images": bool,
468
- "plot_filtration": bool,
469
- "include_uninfected": bool,
470
- "dialate_pngs": bool,
471
- "dialate_png_ratios": list,
472
- "max_workers": int,
473
- "cells": list,
474
- "cell_loc": list,
475
- "pathogens": list,
476
- "pathogen_loc": (list, list), # This can be a list of lists
477
- "treatments": list,
478
- "treatment_loc": (list, list), # This can be a list of lists
479
- "channel_of_interest": int,
480
- "compartments": list,
481
- "measurement": str,
482
- "nr_imgs": int,
483
- "um_per_pixel": (int, float),
484
- # Additional settings based on provided defaults
485
- "include_noninfected": bool,
486
- "include_multiinfected": bool,
487
- "include_multinucleated": bool,
488
- "filter_min_max": (list, type(None)),
489
- "channel_dims": list,
490
- "backgrounds": list,
491
- "outline_thickness": int,
492
- "outline_color": str,
493
- "overlay_chans": list,
494
- "overlay": bool,
495
- "normalization_percentiles": list,
496
- "print_object_number": bool,
497
- "nr": int,
498
- "figuresize": int,
499
- "cmap": str,
500
- "test_mode": bool,
501
- "test_images": int,
502
- "remove_background_cell": bool,
503
- "remove_background_nucleus": bool,
504
- "remove_background_pathogen": bool,
505
- "pathogen_model": (str, type(None)),
506
- "filter": bool,
507
- "upscale": bool,
508
- "upscale_factor": float,
509
- "adjust_cells": bool,
510
- "row_limit": int,
511
- "tables": list,
512
- "visualize": str,
513
- "image_nr": int,
514
- "dot_size": int,
515
- "n_neighbors": int,
516
- "min_dist": float,
517
- "metric": str,
518
- "eps": float,
519
- "min_samples": int,
520
- "filter_by": str,
521
- "img_zoom": float,
522
- "plot_by_cluster": bool,
523
- "plot_cluster_grids": bool,
524
- "remove_cluster_noise": bool,
525
- "remove_highly_correlated": bool,
526
- "log_data": bool,
527
- "black_background": bool,
528
- "remove_image_canvas": bool,
529
- "plot_outlines": bool,
530
- "plot_points": bool,
531
- "smooth_lines": bool,
532
- "clustering": str,
533
- "exclude": (str, type(None)),
534
- "col_to_compare": str,
535
- "pos": str,
536
- "neg": str,
537
- "embedding_by_controls": bool,
538
- "plot_images": bool,
539
- "reduction_method": str,
540
- "save_figure": bool,
541
- "color_by": (str, type(None)),
542
- "analyze_clusters": bool,
543
- "resnet_features": bool,
544
- "test_nr": int,
545
- "radial_dist": bool,
546
- "calculate_correlation": bool,
547
- "manders_thresholds": list,
548
- "homogeneity": bool,
549
- "homogeneity_distances": list,
550
- "save_arrays": bool,
551
- "cytoplasm": bool,
552
- "merge_edge_pathogen_cells": bool,
553
- "cells_per_well": int,
554
- "pathogen_size_range": list,
555
- "nucleus_size_range": list,
556
- "cell_size_range": list,
557
- "pathogen_intensity_range": list,
558
- "nucleus_intensity_range": list,
559
- "cell_intensity_range": list,
560
- "target_intensity_min": int,
561
- "model_type": str,
562
- "heatmap_feature": str,
563
- "grouping": str,
564
- "min_max": str,
565
- "minimum_cell_count": int,
566
- "n_estimators": int,
567
- "test_size": float,
568
- "location_column": str,
569
- "positive_control": str,
570
- "negative_control": str,
571
- "n_repeats": int,
572
- "top_features": int,
573
- "remove_low_variance_features": bool,
574
- "n_jobs": int,
575
- "classes": list,
576
- "schedule": str,
577
- "loss_type": str,
578
- "image_size": int,
579
- "epochs": int,
580
- "val_split": float,
581
- "train_mode": str,
582
- "learning_rate": float,
583
- "weight_decay": float,
584
- "dropout_rate": float,
585
- "init_weights": bool,
586
- "amsgrad": bool,
587
- "use_checkpoint": bool,
588
- "gradient_accumulation": bool,
589
- "gradient_accumulation_steps": int,
590
- "intermedeate_save": bool,
591
- "pin_memory": bool,
592
- "num_workers": int,
593
- "augment": bool,
594
- "target": str,
595
- "cell_types": list,
596
- "cell_plate_metadata": (list, type(None)),
597
- "pathogen_types": list,
598
- "pathogen_plate_metadata": (list, list), # This can be a list of lists
599
- "treatment_plate_metadata": (list, list), # This can be a list of lists
600
- "metadata_types": list,
601
- "cell_chann_dim": int,
602
- "nucleus_chann_dim": int,
603
- "pathogen_chann_dim": int,
604
- "plot_nr": int,
605
- "plot_control": bool,
606
- "remove_background": bool,
607
- "target": str,
608
- "upstream": str,
609
- "downstream": str,
610
- "barecode_length_1": int,
611
- "barecode_length_2": int,
612
- "chunk_size": int,
613
- "grna": str,
614
- "barcodes": str,
615
- "plate_dict": dict,
616
- "pc": str,
617
- "pc_loc": str,
618
- "nc": str,
619
- "nc_loc": str,
620
- "dependent_variable": str,
621
- "transform": (str, type(None)),
622
- "agg_type": str,
623
- "min_cell_count": int,
624
- "regression_type": str,
625
- "remove_row_column_effect": bool,
626
- "alpha": float,
627
- "fraction_threshold": float,
628
- "class_1_threshold": (float, type(None)),
629
- "batch_size": int,
630
- "CP_prob": float,
631
- "flow_threshold": float,
632
- "percentiles": (list, type(None)),
633
- "circular": bool,
634
- "invert": bool,
635
- "diameter": int,
636
- "grayscale": bool,
637
- "resize": bool,
638
- "target_height": (int, type(None)),
639
- "target_width": (int, type(None)),
640
- "rescale": bool,
641
- "resample": bool,
642
- "model_name": str,
643
- "Signal_to_noise": int,
644
- "learning_rate": float,
645
- "weight_decay": float,
646
- "batch_size": int,
647
- "n_epochs": int,
648
- "from_scratch": bool,
649
- "width_height": list,
650
- "resize": bool,
651
- "gene_weights_csv": str,
652
- "fraction_threshold": float,
653
- }
654
-
655
- for key, (label, widget, var) in vars_dict.items():
656
- if key not in expected_types:
657
- if key not in ["General","Nucleus","Cell","Pathogen","Timelapse","Plot","Object Image","Annotate Data","Measurements","Advanced","Miscellaneous","Test"]:
658
-
659
- q.put(f"Key {key} not found in expected types.")
660
- continue
661
-
662
- value = var.get()
663
- expected_type = expected_types.get(key, str)
664
-
665
- try:
666
- if key in ["png_size", "pathogen_plate_metadata", "treatment_plate_metadata"]:
667
- parsed_value = ast.literal_eval(value) if value else None
668
- if isinstance(parsed_value, list):
669
- if all(isinstance(i, list) for i in parsed_value) or all(not isinstance(i, list) for i in parsed_value):
670
- settings[key] = parsed_value
671
- else:
672
- raise ValueError("Invalid format: Mixed list and list of lists")
673
- else:
674
- raise ValueError("Invalid format for list or list of lists")
675
- elif expected_type == list:
676
- settings[key] = parse_list(value) if value else None
677
- elif expected_type == bool:
678
- settings[key] = value if isinstance(value, bool) else value.lower() in ['true', '1', 't', 'y', 'yes']
679
- elif expected_type == (int, type(None)):
680
- settings[key] = int(value) if value else None
681
- elif expected_type == (float, type(None)):
682
- settings[key] = float(value) if value else None
683
- elif expected_type == (int, float):
684
- settings[key] = float(value) if '.' in value else int(value)
685
- elif expected_type == (str, type(None)):
686
- settings[key] = str(value) if value else None
687
- elif isinstance(expected_type, tuple):
688
- for typ in expected_type:
689
- try:
690
- settings[key] = typ(value) if value else None
691
- break
692
- except (ValueError, TypeError):
693
- continue
694
- else:
695
- raise ValueError
696
- else:
697
- settings[key] = expected_type(value) if value else None
698
- except (ValueError, SyntaxError):
699
- expected_type_name = ' or '.join([t.__name__ for t in expected_type]) if isinstance(expected_type, tuple) else expected_type.__name__
700
- q.put(f"Error: Invalid format for {key}. Expected type: {expected_type_name}.")
701
- return
504
+ # Configure the column widths
505
+ frame.grid_columnconfigure(label_column, weight=0) # Allow the label column to expand
506
+ frame.grid_columnconfigure(widget_column, weight=1) # Allow the widget column to expand
702
507
 
703
- return settings
508
+ # Right-align the label text and the label itself
509
+ label = spacrLabel(frame, text=label_text, background="black", foreground="white", anchor='e', justify='right')
510
+ label.grid(column=label_column, row=row, sticky=tk.E, padx=(5, 2), pady=5) # Align label to the right
704
511
 
705
- def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
706
- label = ttk.Label(frame, text=label_text, style='Custom.TLabel') # Apply Custom.TLabel style for labels
707
- label.grid(column=0, row=row, sticky=tk.W, padx=5, pady=5)
708
-
709
512
  if var_type == 'entry':
710
513
  var = tk.StringVar(value=default_value) # Set default value
711
514
  entry = ttk.Entry(frame, textvariable=var, style='TEntry') # Apply TEntry style for entries
712
- entry.grid(column=1, row=row, sticky=tk.EW, padx=5)
515
+ entry.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
713
516
  return (label, entry, var) # Return both the label and the entry, and the variable
714
517
  elif var_type == 'check':
715
518
  var = tk.BooleanVar(value=default_value) # Set default value (True/False)
716
- check = Checkbutton(frame, text="", variable=var)
717
- check.grid(column=1, row=row, sticky=tk.W, padx=5)
519
+ check = spacrCheckbutton(frame, text="", variable=var, style='TCheckbutton')
520
+ check.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
718
521
  return (label, check, var) # Return both the label and the checkbutton, and the variable
719
522
  elif var_type == 'combo':
720
523
  var = tk.StringVar(value=default_value) # Set default value
721
524
  combo = ttk.Combobox(frame, textvariable=var, values=options, style='TCombobox') # Apply TCombobox style
722
- combo.grid(column=1, row=row, sticky=tk.EW, padx=5)
525
+ combo.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
723
526
  if default_value:
724
527
  combo.set(default_value)
725
528
  return (label, combo, var) # Return both the label and the combobox, and the variable
726
529
  else:
727
530
  var = None # Placeholder in case of an undefined var_type
728
531
  return (label, None, var)
729
-
730
- def generate_fields(variables, scrollable_frame):
731
- row = 1
732
- vars_dict = {}
733
- tooltips = {
734
- "src": "Path to the folder containing the images.",
735
- "metadata_type": "Type of metadata to expect in the images. This will determine how the images are processed. If 'custom' is selected, you can provide a custom regex pattern to extract metadata from the image names.",
736
- "custom_regex": "Custom regex pattern to extract metadata from the image names. This will only be used if 'custom' is selected for 'metadata_type'.",
737
- "experiment": "Name of the experiment. This will be used to name the output files.",
738
- "channels": "List of channels to use for the analysis. The first channel is 0, the second is 1, and so on. For example, [0,1,2] will use channels 0, 1, and 2.",
739
- "magnification": "At what magnification the images were taken. This will be used to determine the size of the objects in the images.",
740
- "nucleus_channel": "The channel to use for the nucleus. If None, the nucleus will not be segmented.",
741
- "nucleus_background": "The background intensity for the nucleus channel. This will be used to remove background noise.",
742
- "nucleus_Signal_to_noise": "The signal-to-noise ratio for the nucleus channel. This will be used to determine the range of intensities to normalize images to for nucleus segmentation.",
743
- "nucleus_CP_prob": "The cellpose probability threshold for the nucleus channel. This will be used to segment the nucleus.",
744
- "nucleus_FT": "The flow threshold for nucleus objects. This will be used in nuclues segmentation.",
745
- "cell_channel": "The channel to use for the cell. If None, the cell will not be segmented.",
746
- "cell_background": "The background intensity for the cell channel. This will be used to remove background noise.",
747
- "cell_Signal_to_noise": "The signal-to-noise ratio for the cell channel. This will be used to determine the range of intensities to normalize images to for cell segmentation.",
748
- "cell_CP_prob": "The cellpose probability threshold for the cell channel. This will be used in cell segmentation.",
749
- "cell_FT": "The flow threshold for cell objects. This will be used to segment the cells.",
750
- "pathogen_channel": "The channel to use for the pathogen. If None, the pathogen will not be segmented.",
751
- "pathogen_background": "The background intensity for the pathogen channel. This will be used to remove background noise.",
752
- "pathogen_Signal_to_noise": "The signal-to-noise ratio for the pathogen channel. This will be used to determine the range of intensities to normalize images to for pathogen segmentation.",
753
- "pathogen_CP_prob": "The cellpose probability threshold for the pathogen channel. This will be used to segment the pathogen.",
754
- "pathogen_FT": "The flow threshold for pathogen objects. This will be used in pathogen segmentation.",
755
- "preprocess": "Whether to preprocess the images before segmentation. This includes background removal and normalization. Set to False only if this step has already been done.",
756
- "masks": "Whether to generate masks for the segmented objects. If True, masks will be generated for the nucleus, cell, and pathogen.",
757
- "examples_to_plot": "The number of images to plot for each segmented object. This will be used to visually inspect the segmentation results and normalization.",
758
- "randomize": "Whether to randomize the order of the images before processing. Recommended to avoid bias in the segmentation.",
759
- "batch_size": "The batch size to use for processing the images. This will determine how many images are processed at once. Images are normalized and segmented in batches. Lower if application runs out of RAM or VRAM.",
760
- "timelapse": "Whether to process the images as a timelapse.",
761
- "timelapse_displacement": "The displacement between frames in the timelapse. This will be used to align the frames before processing.",
762
- "timelapse_memory": "The number of frames to in tandem objects must be present in to be considered the same object in the timelapse.",
763
- "timelapse_frame_limits": "The frame limits to use for the timelapse. This will determine which frames are processed. For example, [5,20] will process frames 5 to 20.",
764
- "timelapse_remove_transient": "Whether to remove transient objects in the timelapse. Transient objects are present in fewer than all frames.",
765
- "timelapse_mode": "The mode to use for processing the timelapse. 'trackpy' uses the trackpy library for tracking objects, while 'btrack' uses the btrack library.",
766
- "timelapse_objects": "The objects to track in the timelapse (cell, nucleus or pathogen). This will determine which objects are tracked over time. If None, all objects will be tracked.",
767
- "fps": "Frames per second of the automatically generated timelapse movies.",
768
- "remove_background": "Whether to remove background noise from the images. This will help improve the quality of the segmentation.",
769
- "lower_percentile": "The lower quantile to use for normalizing the images. This will be used to determine the range of intensities to normalize images to.",
770
- "merge_pathogens": "Whether to merge pathogen objects that share more than 75% of their perimeter.",
771
- "normalize_plots": "Whether to normalize the plots.",
772
- "all_to_mip": "Whether to convert all images to maximum intensity projections before processing.",
773
- "pick_slice": "Whether to pick a single slice from the z-stack images. If False, the maximum intensity projection will be used.",
774
- "skip_mode": "The mode to use for skipping images. This will determine how to handle images that cannot be processed.",
775
- "save": "Whether to save the results to disk.",
776
- "merge_edge_pathogen_cells": "Whether to merge cells that share pathogen objects.",
777
- "plot": "Whether to plot the results.",
778
- "workers": "The number of workers to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
779
- "verbose": "Whether to print verbose output during processing.",
780
- "input_folder": "Path to the folder containing the images.",
781
- "cell_mask_dim": "The dimension of the array the cell mask is saved in.",
782
- "cell_min_size": "The minimum size of cell objects in pixels^2.",
783
- "cytoplasm": "Whether to segment the cytoplasm (Cell - Nucleus + Pathogen).",
784
- "cytoplasm_min_size": "The minimum size of cytoplasm objects in pixels^2.",
785
- "nucleus_mask_dim": "The dimension of the array the nucleus mask is saved in.",
786
- "nucleus_min_size": "The minimum size of nucleus objects in pixels^2.",
787
- "pathogen_mask_dim": "The dimension of the array the pathogen mask is saved in.",
788
- "pathogen_min_size": "The minimum size of pathogen objects in pixels^2.",
789
- "save_png": "Whether to save the segmented objects as PNG images.",
790
- "crop_mode": "The mode to use for cropping the images. This will determine which objects are cropped from the images (cell, nucleus, pathogen, cytoplasm).",
791
- "use_bounding_box": "Whether to use the bounding box of the objects for cropping. If False, only the object itself will be cropped.",
792
- "png_size": "The size of the PNG images to save. This will determine the size of the saved images.",
793
- "normalize": "The percentiles to use for normalizing the images. This will be used to determine the range of intensities to normalize images to. If None, no normalization is done.",
794
- "png_dims": "The dimensions of the PNG images to save. This will determine the dimensions of the saved images. Maximum of 3 dimensions e.g. [1,2,3].",
795
- "normalize_by": "Whether to normalize the images by field of view (fov) or by PNG image (png).",
796
- "save_measurements": "Whether to save the measurements to disk.",
797
- "representative_images": "Whether to save representative images of the segmented objects (Not working yet).",
798
- "plot_filtration": "Whether to plot the filtration steps.",
799
- "include_uninfected": "Whether to include uninfected cells in the analysis.",
800
- "dialate_pngs": "Whether to dilate the PNG images before saving.",
801
- "dialate_png_ratios": "The ratios to use for dilating the PNG images. This will determine the amount of dilation applied to the images before cropping.",
802
- "max_workers": "The number of workers to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
803
- "cells": "The cell types to include in the analysis.",
804
- "cell_loc": "The locations of the cell types in the images.",
805
- "pathogens": "The pathogen types to include in the analysis.",
806
- "pathogen_loc": "The locations of the pathogen types in the images.",
807
- "treatments": "The treatments to include in the analysis.",
808
- "treatment_loc": "The locations of the treatments in the images.",
809
- "channel_of_interest": "The channel of interest to use for the analysis.",
810
- "compartments": "The compartments to measure in the images.",
811
- "measurement": "The measurement to use for the analysis.",
812
- "nr_imgs": "The number of images to plot.",
813
- "um_per_pixel": "The micrometers per pixel for the images."
814
- }
815
-
816
- for key, (var_type, options, default_value) in variables.items():
817
- label, widget, var = create_input_field(scrollable_frame.scrollable_frame, key, row, var_type, options, default_value)
818
- vars_dict[key] = (label, widget, var) # Store the label, widget, and variable
819
-
820
- # Add tooltip to the label if it exists in the tooltips dictionary
821
- if key in tooltips:
822
- ToolTip(label, tooltips[key])
823
- row += 1
824
- return vars_dict
825
-
826
- class TextRedirector(object):
827
- def __init__(self, widget, queue):
828
- self.widget = widget
829
- self.queue = queue
830
532
 
831
- def write(self, str):
832
- self.queue.put(str)
833
-
834
- def flush(self):
835
- pass
836
-
837
- def create_dark_mode(root, style, console_output):
838
- dark_bg = 'black'
839
- light_text = 'white'
840
- dark_text = 'black'
841
- input_bg = '#555555' # Slightly lighter background for input fields
842
-
843
- # Configure ttkcompartments('TFrame', background=dark_bg)
844
- style.configure('TLabel', background=dark_bg, foreground=light_text)
845
- style.configure('TEntry', fieldbackground=input_bg, foreground=dark_text, background=dark_bg)
846
- style.configure('TButton', background=dark_bg, foreground=dark_text)
847
- style.map('TButton', background=[('active', dark_bg)], foreground=[('active', dark_text)])
848
- style.configure('Dark.TCheckbutton', background=dark_bg, foreground=dark_text)
849
- style.map('Dark.TCheckbutton', background=[('active', dark_bg)], foreground=[('active', dark_text)])
850
- style.configure('TCombobox', fieldbackground=input_bg, foreground=dark_text, background=dark_bg, selectbackground=input_bg, selectforeground=dark_text)
851
- style.map('TCombobox', fieldbackground=[('readonly', input_bg)], selectbackground=[('readonly', input_bg)], foreground=[('readonly', dark_text)])
852
-
853
- if console_output != None:
854
- console_output.config(bg=dark_bg, fg=light_text, insertbackground=light_text) #, font=("Helvetica", 12)
855
- root.configure(bg=dark_bg)
856
-
857
- ##@log_function_call
858
533
  def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label):
859
534
  try:
860
535
  ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
@@ -910,6 +585,14 @@ def clear_canvas(canvas):
910
585
 
911
586
  # Redraw the now empty canvas without changing its size
912
587
  canvas.draw_idle()
588
+
589
+ def my_show():
590
+ """
591
+ Replacement for plt.show() that queues figures instead of displaying them.
592
+ """
593
+ fig = plt.gcf()
594
+ fig_queue.put(fig)
595
+ plt.close(fig)
913
596
 
914
597
  def measure_crop_wrapper(settings, q, fig_queue):
915
598
  """
@@ -921,14 +604,6 @@ def measure_crop_wrapper(settings, q, fig_queue):
921
604
  - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
922
605
  """
923
606
 
924
- def my_show():
925
- """
926
- Replacement for plt.show() that queues figures instead of displaying them.
927
- """
928
- fig = plt.gcf()
929
- fig_queue.put(fig) # Queue the figure for GUI display
930
- plt.close(fig) # Prevent the figure from being shown by plt.show()
931
-
932
607
  # Temporarily override plt.show
933
608
  original_show = plt.show
934
609
  plt.show = my_show
@@ -938,12 +613,11 @@ def measure_crop_wrapper(settings, q, fig_queue):
938
613
  spacr.measure.measure_crop(settings=settings)
939
614
  except Exception as e:
940
615
  errorMessage = f"Error during processing: {e}"
941
- q.put(errorMessage) # Send the error message to the GUI via the queue
616
+ q.put(errorMessage)
942
617
  traceback.print_exc()
943
618
  finally:
944
- plt.show = original_show # Restore the original plt.show function
619
+ plt.show = original_show
945
620
 
946
- #@log_function_call
947
621
  def preprocess_generate_masks_wrapper(settings, q, fig_queue):
948
622
  """
949
623
  Wraps the measure_crop function to integrate with GUI processes.
@@ -954,14 +628,6 @@ def preprocess_generate_masks_wrapper(settings, q, fig_queue):
954
628
  - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
955
629
  """
956
630
 
957
- def my_show():
958
- """
959
- Replacement for plt.show() that queues figures instead of displaying them.
960
- """
961
- fig = plt.gcf()
962
- fig_queue.put(fig) # Queue the figure for GUI display
963
- plt.close(fig) # Prevent the figure from being shown by plt.show()
964
-
965
631
  # Temporarily override plt.show
966
632
  original_show = plt.show
967
633
  plt.show = my_show
@@ -970,10 +636,40 @@ def preprocess_generate_masks_wrapper(settings, q, fig_queue):
970
636
  spacr.core.preprocess_generate_masks(src=settings['src'], settings=settings)
971
637
  except Exception as e:
972
638
  errorMessage = f"Error during processing: {e}"
973
- q.put(errorMessage) # Send the error message to the GUI via the queue
639
+ q.put(errorMessage)
974
640
  traceback.print_exc()
975
641
  finally:
976
- plt.show = original_show # Restore the original plt.show function
642
+ plt.show = original_show
643
+
644
+ def sequencing_wrapper(settings, q, fig_queue):
645
+
646
+ # Temporarily override plt.show
647
+ original_show = plt.show
648
+ plt.show = my_show
649
+
650
+ try:
651
+ spacr.sequencing.analyze_reads(settings=settings)
652
+ except Exception as e:
653
+ errorMessage = f"Error during processing: {e}"
654
+ q.put(errorMessage)
655
+ traceback.print_exc()
656
+ finally:
657
+ plt.show = original_show
658
+
659
+ def umap_wrapper(settings, q, fig_queue):
660
+
661
+ # Temporarily override plt.show
662
+ original_show = plt.show
663
+ plt.show = my_show
664
+
665
+ try:
666
+ spacr.core.generate_image_umap(settings=settings)
667
+ except Exception as e:
668
+ errorMessage = f"Error during processing: {e}"
669
+ q.put(errorMessage)
670
+ traceback.print_exc()
671
+ finally:
672
+ plt.show = original_show
977
673
 
978
674
  def train_test_model_wrapper(settings, q, fig_queue):
979
675
  """
@@ -1077,9 +773,29 @@ def convert_settings_dict_for_gui(settings):
1077
773
  variables[key] = ('entry', None, str(value))
1078
774
  return variables
1079
775
 
776
+ def setup_frame(parent_frame):
777
+ style = ttk.Style(parent_frame)
778
+ set_dark_style(style)
779
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
780
+ parent_frame.configure(bg='black')
781
+ parent_frame.grid_rowconfigure(0, weight=1)
782
+ parent_frame.grid_columnconfigure(0, weight=1)
783
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, bg='black')
784
+ vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
785
+ horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, bg='black')
786
+ vertical_container.add(horizontal_container, stretch="always")
787
+ horizontal_container.grid_columnconfigure(0, weight=1)
788
+ horizontal_container.grid_columnconfigure(1, weight=1)
789
+ settings_frame = tk.Frame(horizontal_container, bg='black')
790
+ settings_frame.grid_rowconfigure(0, weight=0)
791
+ settings_frame.grid_rowconfigure(1, weight=1)
792
+ settings_frame.grid_columnconfigure(0, weight=1)
793
+ horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
794
+ return parent_frame, vertical_container, horizontal_container
795
+
1080
796
  def setup_settings_panel(vertical_container, settings_type='mask', frame_height=500, frame_width=1000):
1081
797
  global vars_dict, scrollable_frame
1082
- from .settings import set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model
798
+ from .settings import set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model, get_analyze_reads_default_settings, set_default_umap_image_settings, generate_fields
1083
799
 
1084
800
  print("Setting up settings panel")
1085
801
 
@@ -1088,11 +804,11 @@ def setup_settings_panel(vertical_container, settings_type='mask', frame_height=
1088
804
  vertical_container.add(settings_frame, stretch="always")
1089
805
 
1090
806
  # Add settings label
1091
- settings_label = ttk.Label(settings_frame, text="Settings", style="Custom.TLabel", background="black", foreground="white")
807
+ settings_label = spacrLabel(settings_frame, text="Settings", background="black", foreground="white", anchor='center', justify='center', align="center")
1092
808
  settings_label.grid(row=0, column=0, pady=10, padx=10)
1093
809
 
1094
- # Create a ScrollableFrame inside the settings_frame
1095
- scrollable_frame = ScrollableFrame(settings_frame, bg='black', width=frame_width)
810
+ # Create a spacrFrame inside the settings_frame
811
+ scrollable_frame = spacrFrame(settings_frame, bg='black', width=frame_width)
1096
812
  scrollable_frame.grid(row=1, column=0, sticky="nsew")
1097
813
 
1098
814
  # Configure the weights for resizing
@@ -1106,17 +822,19 @@ def setup_settings_panel(vertical_container, settings_type='mask', frame_height=
1106
822
  settings = get_measure_crop_settings(settings={})
1107
823
  elif settings_type == 'classify':
1108
824
  settings = set_default_train_test_model(settings={})
825
+ elif settings_type == 'sequencing':
826
+ settings = get_analyze_reads_default_settings(settings={})
827
+ elif settings_type == 'umap':
828
+ settings = set_default_umap_image_settings(settings={})
1109
829
  else:
1110
830
  raise ValueError(f"Invalid settings type: {settings_type}")
1111
831
 
1112
832
  # Generate fields for settings
1113
833
  variables = convert_settings_dict_for_gui(settings)
1114
834
  vars_dict = generate_fields(variables, scrollable_frame)
1115
-
1116
835
  print("Settings panel setup complete")
1117
836
  return scrollable_frame, vars_dict
1118
837
 
1119
-
1120
838
  def setup_plot_section(vertical_container):
1121
839
  global canvas, canvas_widget
1122
840
  plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
@@ -1133,6 +851,35 @@ def setup_plot_section(vertical_container):
1133
851
  canvas.figure = figure
1134
852
  return canvas, canvas_widget
1135
853
 
854
+ def setup_console(vertical_container):
855
+ global console_output
856
+ print("Setting up console frame")
857
+ console_frame = tk.Frame(vertical_container, bg='black')
858
+ vertical_container.add(console_frame, stretch="always")
859
+ console_label = spacrLabel(console_frame, text="Console", background="black", foreground="white", anchor='center', justify='center', align="center")
860
+ console_label.grid(row=0, column=0, pady=10, padx=10)
861
+ console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
862
+ console_output.grid(row=1, column=0, sticky="nsew")
863
+ console_frame.grid_rowconfigure(1, weight=1)
864
+ console_frame.grid_columnconfigure(0, weight=1)
865
+ print("Console setup complete")
866
+ return console_output
867
+
868
+ def setup_progress_frame(vertical_container):
869
+ global progress_output
870
+ progress_frame = tk.Frame(vertical_container, bg='black')
871
+ vertical_container.add(progress_frame, stretch="always")
872
+ label_frame = tk.Frame(progress_frame, bg='black')
873
+ label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
874
+ progress_label = spacrLabel(label_frame, text="Processing: 0%", background="black", foreground="white", font=('Helvetica', 12), anchor='w', justify='left', align="left")
875
+ progress_label.grid(row=0, column=0, sticky="w")
876
+ progress_output = scrolledtext.ScrolledText(progress_frame, height=10, bg='black', fg='white', insertbackground='white')
877
+ progress_output.grid(row=1, column=0, sticky="nsew")
878
+ progress_frame.grid_rowconfigure(1, weight=1)
879
+ progress_frame.grid_columnconfigure(0, weight=1)
880
+ print("Progress frame setup complete")
881
+ return progress_output
882
+
1136
883
  def download_hug_dataset():
1137
884
  global vars_dict, q
1138
885
  repo_id = "einarolafsson/toxo_mito"
@@ -1206,8 +953,8 @@ def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
1206
953
 
1207
954
  raise Exception("Failed to download dataset after multiple attempts.")
1208
955
 
1209
- def setup_button_section(horizontal_container, settings_type='mask', btn_row=1, settings_row=5, run=True, abort=True, download=True, import_btn=True, progress=True):
1210
- global button_frame, run_button, abort_button, download_dataset_button, import_button, progress_label, q, fig_queue, vars_dict
956
+ def setup_button_section(horizontal_container, settings_type='mask', settings_row=5, run=True, abort=True, download=True, import_btn=True):
957
+ global button_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict
1211
958
 
1212
959
  button_frame = tk.Frame(horizontal_container, bg='black')
1213
960
  horizontal_container.add(button_frame, stretch="always", sticky="nsew")
@@ -1215,54 +962,38 @@ def setup_button_section(horizontal_container, settings_type='mask', btn_row=1,
1215
962
  button_frame.grid_rowconfigure(1, weight=1)
1216
963
  button_frame.grid_columnconfigure(0, weight=1)
1217
964
 
1218
- categories_label = ttk.Label(button_frame, text="Categories", style="Custom.TLabel", background="black", foreground="white")
965
+ categories_label = spacrLabel(button_frame, text="Categories", background="black", foreground="white", font=('Helvetica', 12), anchor='center', justify='center', align="center") # Increase font size
1219
966
  categories_label.grid(row=0, column=0, pady=10, padx=10)
1220
967
 
1221
- button_scrollable_frame = ScrollableFrame(button_frame, bg='black')
968
+ button_scrollable_frame = spacrFrame(button_frame, bg='black')
1222
969
  button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
1223
970
 
1224
- button_scrollable_frame.scrollable_frame.grid_columnconfigure(0, weight=1, minsize=100)
1225
- button_scrollable_frame.scrollable_frame.grid_columnconfigure(1, weight=1, minsize=100)
1226
- button_scrollable_frame.scrollable_frame.grid_columnconfigure(2, weight=1, minsize=100)
1227
-
971
+ btn_col = 0
972
+ btn_row = 1
973
+
1228
974
  if run:
1229
- run_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue, settings_type), style='Custom.TButton')
1230
- run_button.grid(row=btn_row, column=0, pady=5, padx=5, sticky='ew')
1231
- if abort:
1232
- abort_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, style='Custom.TButton')
1233
- abort_button.grid(row=btn_row, column=1, pady=5, padx=5, sticky='ew')
1234
- btn_row += 1
1235
- if download:
1236
- download_dataset_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Download", command=download_hug_dataset, style='Custom.TButton')
1237
- download_dataset_button.grid(row=btn_row, column=0, pady=5, padx=5, sticky='ew')
975
+ run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue, settings_type), font=('Helvetica', 12))
976
+ run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
977
+ btn_row += 1
978
+
979
+ if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
980
+ abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 12))
981
+ abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
982
+ btn_row += 1
983
+
984
+ if download and settings_type in ['mask']:
985
+ download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Download", command=download_hug_dataset, font=('Helvetica', 12))
986
+ download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
987
+ btn_row += 1
988
+
1238
989
  if import_btn:
1239
- import_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(settings_row, settings_type), style='Custom.TButton')
1240
- import_button.grid(row=btn_row, column=1, pady=5, padx=5, sticky='ew')
1241
- btn_row += 1
1242
- if progress:
1243
- progress_label = ttk.Label(button_scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white")
1244
- progress_label.grid(row=btn_row, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
990
+ import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(settings_row, settings_type), font=('Helvetica', 12))
991
+ import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
1245
992
 
1246
993
  # Call toggle_settings after vars_dict is initialized
1247
994
  if vars_dict is not None:
1248
995
  toggle_settings(button_scrollable_frame)
1249
-
1250
- return progress_label
1251
-
1252
-
1253
- def setup_console(vertical_container):
1254
- global console_output
1255
- print("Setting up console frame")
1256
- console_frame = tk.Frame(vertical_container, bg='black')
1257
- vertical_container.add(console_frame, stretch="always")
1258
- console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
1259
- console_label.grid(row=0, column=0, pady=10, padx=10)
1260
- console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
1261
- console_output.grid(row=1, column=0, sticky="nsew")
1262
- console_frame.grid_rowconfigure(1, weight=1)
1263
- console_frame.grid_columnconfigure(0, weight=1)
1264
- print("Console setup complete")
1265
- return console_output
996
+ return button_scrollable_frame
1266
997
 
1267
998
  def toggle_test_mode():
1268
999
  global vars_dict, test_mode_button
@@ -1276,24 +1007,10 @@ def toggle_test_mode():
1276
1007
 
1277
1008
  def toggle_settings(button_scrollable_frame):
1278
1009
  global vars_dict
1010
+ from .settings import categories
1279
1011
 
1280
1012
  if vars_dict is None:
1281
1013
  raise ValueError("vars_dict is not initialized.")
1282
-
1283
- categories = {
1284
- "General": ["src", "input_folder", "metadata_type", "custom_regex", "experiment", "channels", "magnification"],
1285
- "Nucleus": ["nucleus_channel", "nucleus_background", "nucleus_Signal_to_noise", "nucleus_CP_prob", "nucleus_FT", "remove_background_nucleus", "nucleus_min_size", "nucleus_mask_dim", "nucleus_loc"],
1286
- "Cell": ["cell_channel", "cell_background", "cell_Signal_to_noise", "cell_CP_prob", "cell_FT", "remove_background_cell", "cell_min_size", "cell_mask_dim", "cytoplasm", "cytoplasm_min_size", "include_uninfected", "merge_edge_pathogen_cells", "adjust_cells"],
1287
- "Pathogen": ["pathogen_channel", "pathogen_background", "pathogen_Signal_to_noise", "pathogen_CP_prob", "pathogen_FT", "pathogen_model", "remove_background_pathogen", "pathogen_min_size", "pathogen_mask_dim"],
1288
- "Timelapse": ["timelapse", "fps", "timelapse_displacement", "timelapse_memory", "timelapse_frame_limits", "timelapse_remove_transient", "timelapse_mode", "timelapse_objects", "compartments"],
1289
- "Plot": ["plot_filtration", "examples_to_plot", "normalize_plots", "normalize", "cmap", "figuresize", "plot"],
1290
- "Object Image": ["save_png", "dialate_pngs", "dialate_png_ratios", "png_size", "png_dims", "save_arrays", "normalize_by", "dialate_png_ratios", "crop_mode", "dialate_pngs", "normalize", "use_bounding_box"],
1291
- "Annotate Data": ["treatment_loc", "cells", "cell_loc", "pathogens", "pathogen_loc", "channel_of_interest", "measurement", "treatments", "representative_images", "um_per_pixel", "nr_imgs"],
1292
- "Measurements": ["homogeneity", "homogeneity_distances", "radial_dist", "calculate_correlation", "manders_thresholds", "save_measurements"],
1293
- "Advanced": ["preprocess", "remove_background", "normalize", "lower_percentile", "merge_pathogens", "batch_size", "filter", "save", "masks", "verbose", "randomize", "max_workers", "workers"],
1294
- "Miscellaneous": ["all_to_mip", "pick_slice", "skip_mode", "upscale", "upscale_factor"],
1295
- "Test": ["test_mode", "test_images", "random_test", "test_nr"]
1296
- }
1297
1014
 
1298
1015
  def toggle_category(settings, var):
1299
1016
  for setting in settings:
@@ -1307,27 +1024,32 @@ def toggle_settings(button_scrollable_frame):
1307
1024
  widget.grid()
1308
1025
 
1309
1026
  row = 1
1310
- col = 2 # Start from column 2 to avoid overlap with buttons
1027
+ col = 2
1311
1028
  category_idx = 0
1312
1029
 
1313
1030
  for category, settings in categories.items():
1314
1031
  if any(setting in vars_dict for setting in settings):
1315
1032
  category_var = tk.IntVar(value=0)
1316
1033
  vars_dict[category] = (None, None, category_var)
1317
- toggle = ttk.Checkbutton(
1034
+ toggle = spacrCheckbutton(
1318
1035
  button_scrollable_frame.scrollable_frame,
1319
1036
  text=category,
1320
1037
  variable=category_var,
1321
- command=lambda cat=settings, var=category_var: toggle_category(cat, var),
1322
- style='TCheckbutton'
1038
+ command=lambda cat=settings, var=category_var: toggle_category(cat, var)
1323
1039
  )
1040
+ # Directly set the style
1041
+ style = ttk.Style()
1042
+ font_style = tkFont.Font(family="Helvetica", size=12, weight="bold")
1043
+ style.configure('Spacr.TCheckbutton', font=font_style, background='black', foreground='#ffffff', indicatoron=False, relief='flat')
1044
+ style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
1045
+ toggle.configure(style='Spacr.TCheckbutton')
1324
1046
  toggle.grid(row=row, column=col, sticky="w", pady=2, padx=2)
1325
1047
  col += 1
1326
1048
  category_idx += 1
1327
1049
 
1328
1050
  if category_idx % 4 == 0:
1329
1051
  row += 1
1330
- col = 2 # Reset column to 2
1052
+ col = 2
1331
1053
 
1332
1054
  for settings in categories.values():
1333
1055
  for setting in settings:
@@ -1336,59 +1058,6 @@ def toggle_settings(button_scrollable_frame):
1336
1058
  label.grid_remove()
1337
1059
  widget.grid_remove()
1338
1060
 
1339
- def initiate_abort():
1340
- global thread_control
1341
- if thread_control.get("stop_requested") is not None:
1342
- thread_control["stop_requested"].value = 1
1343
-
1344
- if thread_control.get("run_thread") is not None:
1345
- thread_control["run_thread"].join(timeout=5)
1346
- if thread_control["run_thread"].is_alive():
1347
- thread_control["run_thread"].terminate()
1348
- thread_control["run_thread"] = None
1349
-
1350
- def run_mask_gui(settings, q, fig_queue, stop_requested):
1351
- process_stdout_stderr(q)
1352
- try:
1353
- preprocess_generate_masks_wrapper(settings, q, fig_queue)
1354
- except Exception as e:
1355
- q.put(f"Error during processing: {e}")
1356
- traceback.print_exc()
1357
- finally:
1358
- stop_requested.value = 1
1359
-
1360
- def start_process(q, fig_queue, settings_type='mask'):
1361
- global thread_control, vars_dict
1362
- settings = check_settings(vars_dict)
1363
- if thread_control.get("run_thread") is not None:
1364
- initiate_abort()
1365
- stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
1366
- thread_control["stop_requested"] = stop_requested
1367
- if settings_type == 'mask':
1368
- thread_control["run_thread"] = Process(target=run_mask_gui, args=(settings, q, fig_queue, stop_requested))
1369
- elif settings_type == 'measure':
1370
- thread_control["run_thread"] = Process(target=run_measure_gui, args=(settings, q, fig_queue, stop_requested))
1371
- elif settings_type == 'classify':
1372
- thread_control["run_thread"] = Process(target=run_classify_gui, args=(settings, q, fig_queue, stop_requested))
1373
- thread_control["run_thread"].start()
1374
-
1375
- def import_settings(settings_type='mask'):
1376
- global vars_dict, scrollable_frame
1377
- csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
1378
- csv_settings = read_settings_from_csv(csv_file_path)
1379
- if settings_type == 'mask':
1380
- settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
1381
- elif settings_type == 'measure':
1382
- settings = get_measure_crop_settings(settings={})
1383
- elif settings_type == 'classify':
1384
- settings = set_default_train_test_model(settings={})
1385
- else:
1386
- raise ValueError(f"Invalid settings type: {settings_type}")
1387
-
1388
- variables = convert_settings_dict_for_gui(settings)
1389
- new_settings = update_settings_from_csv(variables, csv_settings)
1390
- vars_dict = generate_fields(new_settings, scrollable_frame)
1391
-
1392
1061
  def process_fig_queue():
1393
1062
  global canvas, fig_queue, canvas_widget, parent_frame
1394
1063
  try:
@@ -1421,25 +1090,35 @@ def process_console_queue():
1421
1090
  after_id = console_output.after(100, process_console_queue)
1422
1091
  parent_frame.after_tasks.append(after_id)
1423
1092
 
1424
- def setup_frame(parent_frame):
1425
- style = ttk.Style(parent_frame)
1426
- set_dark_style(style)
1427
- set_default_font(parent_frame, font_name="Helvetica", size=8)
1428
- parent_frame.configure(bg='black')
1429
- parent_frame.grid_rowconfigure(0, weight=1)
1430
- parent_frame.grid_columnconfigure(0, weight=1)
1431
- vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, bg='black')
1432
- vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
1433
- horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, bg='black')
1434
- vertical_container.add(horizontal_container, stretch="always")
1435
- horizontal_container.grid_columnconfigure(0, weight=1)
1436
- horizontal_container.grid_columnconfigure(1, weight=1)
1437
- settings_frame = tk.Frame(horizontal_container, bg='black')
1438
- settings_frame.grid_rowconfigure(0, weight=0)
1439
- settings_frame.grid_rowconfigure(1, weight=1)
1440
- settings_frame.grid_columnconfigure(0, weight=1)
1441
- horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
1442
- return parent_frame, vertical_container, horizontal_container
1093
+ def run_mask_gui(settings, q, fig_queue, stop_requested):
1094
+ process_stdout_stderr(q)
1095
+ try:
1096
+ preprocess_generate_masks_wrapper(settings, q, fig_queue)
1097
+ except Exception as e:
1098
+ q.put(f"Error during processing: {e}")
1099
+ traceback.print_exc()
1100
+ finally:
1101
+ stop_requested.value = 1
1102
+
1103
+ def run_sequencing_gui(settings, q, fig_queue, stop_requested):
1104
+ process_stdout_stderr(q)
1105
+ try:
1106
+ sequencing_wrapper(settings, q, fig_queue)
1107
+ except Exception as e:
1108
+ q.put(f"Error during processing: {e}")
1109
+ traceback.print_exc()
1110
+ finally:
1111
+ stop_requested.value = 1
1112
+
1113
+ def run_umap_gui(settings, q, fig_queue, stop_requested):
1114
+ process_stdout_stderr(q)
1115
+ try:
1116
+ umap_wrapper(settings, q, fig_queue)
1117
+ except Exception as e:
1118
+ q.put(f"Error during processing: {e}")
1119
+ traceback.print_exc()
1120
+ finally:
1121
+ stop_requested.value = 1
1443
1122
 
1444
1123
  def run_measure_gui(settings, q, fig_queue, stop_requested):
1445
1124
  process_stdout_stderr(q)
@@ -1475,7 +1154,7 @@ def set_globals(q_var, console_output_var, parent_frame_var, vars_dict_var, canv
1475
1154
  fig_queue = fig_queue_var
1476
1155
 
1477
1156
  def initiate_root(parent, settings_type='mask'):
1478
- global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, progress_label
1157
+ global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, progress_label, button_scrollable_frame
1479
1158
  print("Initializing root with settings_type:", settings_type)
1480
1159
  parent_frame = parent
1481
1160
 
@@ -1493,9 +1172,15 @@ def initiate_root(parent, settings_type='mask'):
1493
1172
  fig_queue = Queue()
1494
1173
  parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
1495
1174
  scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type) # Adjust height and width as needed
1496
- progress_label = setup_button_section(horizontal_container, settings_type)
1175
+ button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
1497
1176
  canvas, canvas_widget = setup_plot_section(vertical_container)
1498
1177
  console_output = setup_console(vertical_container)
1178
+
1179
+ if settings_type in ['mask', 'measure', 'classify', 'sequencing']:
1180
+ progress_output = setup_progress_frame(vertical_container)
1181
+ else:
1182
+ progress_output = None
1183
+
1499
1184
  set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue)
1500
1185
  process_console_queue()
1501
1186
  process_fig_queue()
@@ -1504,7 +1189,6 @@ def initiate_root(parent, settings_type='mask'):
1504
1189
  print("Root initialization complete")
1505
1190
  return parent_frame, vars_dict
1506
1191
 
1507
-
1508
1192
  def cancel_after_tasks(frame):
1509
1193
  if hasattr(frame, 'after_tasks'):
1510
1194
  for task in frame.after_tasks:
@@ -1522,4 +1206,7 @@ def start_gui_app(settings_type='mask'):
1522
1206
  print("Starting GUI app with settings_type:", settings_type)
1523
1207
  initiate_root(root.content_frame, settings_type)
1524
1208
  create_menu_bar(root)
1525
- root.mainloop()
1209
+ root.mainloop()
1210
+
1211
+
1212
+