spacr 0.1.55__py3-none-any.whl → 0.1.62__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,383 +1,15 @@
1
- import os, spacr, traceback, io, sys, ast, ctypes, matplotlib, re, csv, requests, ast
2
- import matplotlib.pyplot as plt
3
- matplotlib.use('Agg')
1
+ import io, sys, ast, ctypes, re, csv, ast
4
2
  import tkinter as tk
5
3
  from tkinter import ttk
6
- import tkinter.font as tkFont
7
- from tkinter import filedialog
8
- from tkinter import Checkbutton
9
- from tkinter import font as tkFont
10
- from multiprocessing import Process, Value, Queue, Manager, set_start_method
11
- from tkinter import ttk, scrolledtext
12
- from matplotlib.figure import Figure
13
- from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
14
- import time
15
- import requests
16
- from huggingface_hub import list_repo_files, hf_hub_download
17
4
 
18
- from .logger import log_function_call
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
5
+ from . gui_core import initiate_root
6
+ from .gui_elements import spacrLabel, spacrCheckbutton, set_dark_style
20
7
 
21
8
  try:
22
9
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
23
10
  except AttributeError:
24
11
  pass
25
12
 
26
- # Define global variables
27
- q = None
28
- console_output = None
29
- parent_frame = None
30
- vars_dict = None
31
- canvas = None
32
- canvas_widget = None
33
- scrollable_frame = None
34
- progress_label = None
35
- fig_queue = None
36
-
37
- thread_control = {"run_thread": None, "stop_requested": False}
38
-
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')
46
-
47
- class spacrFrame(ttk.Frame):
48
- def __init__(self, container, width=None, *args, bg='black', **kwargs):
49
- super().__init__(container, *args, **kwargs)
50
- self.configure(style='TFrame')
51
- if width is None:
52
- screen_width = self.winfo_screenwidth()
53
- width = screen_width // 4
54
- canvas = tk.Canvas(self, bg=bg, width=width)
55
- scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
56
-
57
- self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
58
- self.scrollable_frame.bind(
59
- "<Configure>",
60
- lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
61
- )
62
- canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
63
- canvas.configure(yscrollcommand=scrollbar.set)
64
-
65
- canvas.grid(row=0, column=0, sticky="nsew")
66
- scrollbar.grid(row=0, column=1, sticky="ns")
67
-
68
- self.grid_rowconfigure(0, weight=1)
69
- self.grid_columnconfigure(0, weight=1)
70
- self.grid_columnconfigure(1, weight=0)
71
-
72
- for child in self.scrollable_frame.winfo_children():
73
- child.configure(bg='black')
74
-
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)
109
-
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)
115
-
116
- class spacrButton(tk.Frame):
117
- def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
118
- super().__init__(parent, *args, **kwargs)
119
- self.text = text
120
- self.command = command
121
- screen_height = self.winfo_screenheight()
122
- button_height = screen_height // 50
123
- button_width = button_height * 3
124
-
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")
127
- self.canvas.grid(row=0, column=0)
128
-
129
- self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
130
-
131
- self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
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)
133
-
134
- self.bind("<Enter>", self.on_enter)
135
- self.bind("<Leave>", self.on_leave)
136
- self.bind("<Button-1>", self.on_click)
137
- self.canvas.bind("<Enter>", self.on_enter)
138
- self.canvas.bind("<Leave>", self.on_leave)
139
- self.canvas.bind("<Button-1>", self.on_click)
140
-
141
- def on_enter(self, event=None):
142
- self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
143
-
144
- def on_leave(self, event=None):
145
- self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
146
-
147
- def on_click(self, event=None):
148
- if self.command:
149
- self.command()
150
-
151
- def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
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
- ]
174
- return self.canvas.create_polygon(points, **kwargs, smooth=True)
175
-
176
-
177
- class spacrSwitch(ttk.Frame):
178
- def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
179
- super().__init__(parent, *args, **kwargs)
180
- self.text = text
181
- self.variable = variable if variable else tk.BooleanVar()
182
- self.command = command
183
- self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
184
- self.canvas.grid(row=0, column=1, padx=(10, 0))
185
- self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
186
- self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
187
- self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
188
- self.label.grid(row=0, column=0, padx=(0, 10))
189
- self.bind("<Button-1>", self.toggle)
190
- self.canvas.bind("<Button-1>", self.toggle)
191
- self.label.bind("<Button-1>", self.toggle)
192
- self.update_switch()
193
-
194
- def toggle(self, event=None):
195
- self.variable.set(not self.variable.get())
196
- self.animate_switch()
197
- if self.command:
198
- self.command()
199
-
200
- def update_switch(self):
201
- if self.variable.get():
202
- self.canvas.itemconfig(self.switch, fill="#008080") # Teal
203
- self.canvas.coords(self.switch, 24, 4, 36, 16) # Move switch to the right
204
- else:
205
- self.canvas.itemconfig(self.switch, fill="#800080") # Purple
206
- self.canvas.coords(self.switch, 4, 4, 16, 16) # Move switch to the left
207
-
208
- def animate_switch(self):
209
- if self.variable.get():
210
- start_x, end_x = 4, 24
211
- final_color = "#008080" # Teal
212
- else:
213
- start_x, end_x = 24, 4
214
- final_color = "#800080" # Purple
215
-
216
- self.animate_movement(start_x, end_x, final_color)
217
-
218
- def animate_movement(self, start_x, end_x, final_color):
219
- step = 1 if start_x < end_x else -1
220
- for i in range(start_x, end_x, step):
221
- self.canvas.coords(self.switch, i, 4, i + 12, 16)
222
- self.canvas.update()
223
- self.after(10) # Small delay for smooth animation
224
- self.canvas.itemconfig(self.switch, fill=final_color)
225
-
226
- def get(self):
227
- return self.variable.get()
228
-
229
- def set(self, value):
230
- self.variable.set(value)
231
- self.update_switch()
232
-
233
- def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs): # Smaller radius for smaller switch
234
- points = [x1 + radius, y1,
235
- x1 + radius, y1,
236
- x2 - radius, y1,
237
- x2 - radius, y1,
238
- x2, y1,
239
- x2, y1 + radius,
240
- x2, y1 + radius,
241
- x2, y2 - radius,
242
- x2, y2 - radius,
243
- x2, y2,
244
- x2 - radius, y2,
245
- x2 - radius, y2,
246
- x1 + radius, y2,
247
- x1 + radius, y2,
248
- x1, y2,
249
- x1, y2 - radius,
250
- x1, y2 - radius,
251
- x1, y1 + radius,
252
- x1, y1 + radius,
253
- x1, y1]
254
-
255
- return self.canvas.create_polygon(points, **kwargs, smooth=True)
256
-
257
- class spacrToolTip:
258
- def __init__(self, widget, text):
259
- self.widget = widget
260
- self.text = text
261
- self.tooltip_window = None
262
- widget.bind("<Enter>", self.show_tooltip)
263
- widget.bind("<Leave>", self.hide_tooltip)
264
-
265
- def show_tooltip(self, event):
266
- x = event.x_root + 20
267
- y = event.y_root + 10
268
- self.tooltip_window = tk.Toplevel(self.widget)
269
- self.tooltip_window.wm_overrideredirect(True)
270
- self.tooltip_window.wm_geometry(f"+{x}+{y}")
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)
273
- label.grid(row=0, column=0, padx=5, pady=5)
274
-
275
- def hide_tooltip(self, event):
276
- if self.tooltip_window:
277
- self.tooltip_window.destroy()
278
- self.tooltip_window = None
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
13
  def set_default_font(root, font_name="Helvetica", size=12):
382
14
  default_font = (font_name, size)
383
15
  root.option_add("*Font", default_font)
@@ -385,37 +17,6 @@ def set_default_font(root, font_name="Helvetica", size=12):
385
17
  root.option_add("*TLabel.Font", default_font)
386
18
  root.option_add("*TEntry.Font", default_font)
387
19
 
388
- def create_menu_bar(root):
389
- from .app_annotate import initiate_annotation_app_root
390
- from .app_make_masks import initiate_mask_app_root
391
-
392
- gui_apps = {
393
- "Mask": 'mask',
394
- "Measure": 'measure',
395
- "Annotate": initiate_annotation_app_root,
396
- "Make Masks": initiate_mask_app_root,
397
- "Classify": 'classify',
398
- "Sequencing": 'sequencing',
399
- "Umap": 'umap'
400
- }
401
-
402
- def load_app_wrapper(app_name, app_func):
403
- load_app(root, app_name, app_func)
404
-
405
- # Create the menu bar
406
- menu_bar = tk.Menu(root, bg="#008080", fg="white")
407
- # Create a "SpaCr Applications" menu
408
- app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
409
- menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
410
- # Add options to the "SpaCr Applications" menu
411
- for app_name, app_func in gui_apps.items():
412
- app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
413
- # Add a separator and an exit option
414
- app_menu.add_separator()
415
- app_menu.add_command(label="Exit", command=root.quit)
416
- # Configure the menu for the root window
417
- root.config(menu=menu_bar)
418
-
419
20
  def proceed_with_app(root, app_name, app_func):
420
21
  from .app_annotate import gui_annotate
421
22
  from .app_make_masks import gui_make_masks
@@ -467,26 +68,6 @@ def load_app(root, app_name, app_func):
467
68
  else:
468
69
  proceed_with_app(root, app_name, app_func)
469
70
 
470
- def read_settings_from_csv(csv_file_path):
471
- settings = {}
472
- with open(csv_file_path, newline='') as csvfile:
473
- reader = csv.DictReader(csvfile)
474
- for row in reader:
475
- key = row['Key']
476
- value = row['Value']
477
- settings[key] = value
478
- return settings
479
-
480
- def update_settings_from_csv(variables, csv_settings):
481
- new_settings = variables.copy() # Start with a copy of the original settings
482
- for key, value in csv_settings.items():
483
- if key in new_settings:
484
- # Get the variable type and options from the original settings
485
- var_type, options, _ = new_settings[key]
486
- # Update the default value with the CSV value, keeping the type and options unchanged
487
- new_settings[key] = (var_type, options, value)
488
- return new_settings
489
-
490
71
  def parse_list(value):
491
72
  try:
492
73
  parsed_value = ast.literal_eval(value)
@@ -530,33 +111,6 @@ def create_input_field(frame, label_text, row, var_type='entry', options=None, d
530
111
  var = None # Placeholder in case of an undefined var_type
531
112
  return (label, None, var)
532
113
 
533
- def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label):
534
- try:
535
- ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
536
- while not q.empty():
537
- message = q.get_nowait()
538
- clean_message = ansi_escape_pattern.sub('', message)
539
- if clean_message.startswith("Progress"):
540
- progress_label.config(text=clean_message)
541
- if clean_message.startswith("\rProgress"):
542
- progress_label.config(text=clean_message)
543
- elif clean_message.startswith("Successfully"):
544
- progress_label.config(text=clean_message)
545
- elif clean_message.startswith("Processing"):
546
- progress_label.config(text=clean_message)
547
- elif clean_message.startswith("scale"):
548
- pass
549
- elif clean_message.startswith("plot_cropped_arrays"):
550
- pass
551
- elif clean_message == "" or clean_message == "\r" or clean_message.strip() == "":
552
- pass
553
- else:
554
- print(clean_message)
555
- except Exception as e:
556
- print(f"Error updating GUI canvas: {e}")
557
- finally:
558
- root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
559
-
560
114
  def process_stdout_stderr(q):
561
115
  """
562
116
  Redirect stdout and stderr to the queue q.
@@ -578,635 +132,8 @@ class WriteToQueue(io.TextIOBase):
578
132
  def flush(self):
579
133
  pass
580
134
 
581
- def clear_canvas(canvas):
582
- # Clear each plot (axes) in the figure
583
- for ax in canvas.figure.get_axes():
584
- ax.clear()
585
-
586
- # Redraw the now empty canvas without changing its size
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)
596
-
597
- def measure_crop_wrapper(settings, q, fig_queue):
598
- """
599
- Wraps the measure_crop function to integrate with GUI processes.
600
-
601
- Parameters:
602
- - settings: dict, The settings for the measure_crop function.
603
- - q: multiprocessing.Queue, Queue for logging messages to the GUI.
604
- - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
605
- """
606
-
607
- # Temporarily override plt.show
608
- original_show = plt.show
609
- plt.show = my_show
610
-
611
- try:
612
- print('start')
613
- spacr.measure.measure_crop(settings=settings)
614
- except Exception as e:
615
- errorMessage = f"Error during processing: {e}"
616
- q.put(errorMessage)
617
- traceback.print_exc()
618
- finally:
619
- plt.show = original_show
620
-
621
- def preprocess_generate_masks_wrapper(settings, q, fig_queue):
622
- """
623
- Wraps the measure_crop function to integrate with GUI processes.
624
-
625
- Parameters:
626
- - settings: dict, The settings for the measure_crop function.
627
- - q: multiprocessing.Queue, Queue for logging messages to the GUI.
628
- - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
629
- """
630
-
631
- # Temporarily override plt.show
632
- original_show = plt.show
633
- plt.show = my_show
634
-
635
- try:
636
- spacr.core.preprocess_generate_masks(src=settings['src'], settings=settings)
637
- except Exception as e:
638
- errorMessage = f"Error during processing: {e}"
639
- q.put(errorMessage)
640
- traceback.print_exc()
641
- finally:
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
673
-
674
- def train_test_model_wrapper(settings, q, fig_queue):
675
- """
676
- Wraps the measure_crop function to integrate with GUI processes.
677
-
678
- Parameters:
679
- - settings: dict, The settings for the measure_crop function.
680
- - q: multiprocessing.Queue, Queue for logging messages to the GUI.
681
- - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
682
- """
683
-
684
- def my_show():
685
- """
686
- Replacement for plt.show() that queues figures instead of displaying them.
687
- """
688
- fig = plt.gcf()
689
- fig_queue.put(fig) # Queue the figure for GUI display
690
- plt.close(fig) # Prevent the figure from being shown by plt.show()
691
-
692
- # Temporarily override plt.show
693
- original_show = plt.show
694
- plt.show = my_show
695
-
696
- try:
697
- spacr.core.train_test_model(settings['src'], settings=settings)
698
- except Exception as e:
699
- errorMessage = f"Error during processing: {e}"
700
- q.put(errorMessage) # Send the error message to the GUI via the queue
701
- traceback.print_exc()
702
- finally:
703
- plt.show = original_show # Restore the original plt.show function
704
-
705
-
706
- def run_multiple_simulations_wrapper(settings, q, fig_queue):
707
- """
708
- Wraps the run_multiple_simulations function to integrate with GUI processes.
709
-
710
- Parameters:
711
- - settings: dict, The settings for the run_multiple_simulations function.
712
- - q: multiprocessing.Queue, Queue for logging messages to the GUI.
713
- - fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
714
- """
715
-
716
- def my_show():
717
- """
718
- Replacement for plt.show() that queues figures instead of displaying them.
719
- """
720
- fig = plt.gcf()
721
- fig_queue.put(fig) # Queue the figure for GUI display
722
- plt.close(fig) # Prevent the figure from being shown by plt.show()
723
-
724
- # Temporarily override plt.show
725
- original_show = plt.show
726
- plt.show = my_show
727
-
728
- try:
729
- spacr.sim.run_multiple_simulations(settings=settings)
730
- except Exception as e:
731
- errorMessage = f"Error during processing: {e}"
732
- q.put(errorMessage) # Send the error message to the GUI via the queue
733
- traceback.print_exc()
734
- finally:
735
- plt.show = original_show # Restore the original plt.show function
736
-
737
- def convert_settings_dict_for_gui(settings):
738
- variables = {}
739
- special_cases = {
740
- 'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
741
- 'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
742
- 'cell_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
743
- 'nucleus_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
744
- 'pathogen_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
745
- #'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
746
- 'magnification': ('combo', [20, 40, 60], 20),
747
- 'nucleus_channel': ('combo', [0, 1, 2, 3, None], None),
748
- 'cell_channel': ('combo', [0, 1, 2, 3, None], None),
749
- 'pathogen_channel': ('combo', [0, 1, 2, 3, None], None),
750
- 'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
751
- 'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
752
- 'model_type': ('combo', ['resnet50', 'other_model'], 'resnet50'),
753
- 'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
754
- 'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
755
- 'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
756
- 'normalize_by': ('combo', ['fov', 'png'], 'png'),
757
- }
758
-
759
- for key, value in settings.items():
760
- if key in special_cases:
761
- variables[key] = special_cases[key]
762
- elif isinstance(value, bool):
763
- variables[key] = ('check', None, value)
764
- elif isinstance(value, int) or isinstance(value, float):
765
- variables[key] = ('entry', None, value)
766
- elif isinstance(value, str):
767
- variables[key] = ('entry', None, value)
768
- elif value is None:
769
- variables[key] = ('entry', None, value)
770
- elif isinstance(value, list):
771
- variables[key] = ('entry', None, str(value))
772
- else:
773
- variables[key] = ('entry', None, str(value))
774
- return variables
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
-
796
- def setup_settings_panel(vertical_container, settings_type='mask', frame_height=500, frame_width=1000):
797
- global vars_dict, scrollable_frame
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
799
-
800
- print("Setting up settings panel")
801
-
802
- # Create settings frame
803
- settings_frame = tk.Frame(vertical_container, bg='black', height=frame_height, width=frame_width)
804
- vertical_container.add(settings_frame, stretch="always")
805
-
806
- # Add settings label
807
- settings_label = spacrLabel(settings_frame, text="Settings", background="black", foreground="white", anchor='center', justify='center', align="center")
808
- settings_label.grid(row=0, column=0, pady=10, padx=10)
809
-
810
- # Create a spacrFrame inside the settings_frame
811
- scrollable_frame = spacrFrame(settings_frame, bg='black', width=frame_width)
812
- scrollable_frame.grid(row=1, column=0, sticky="nsew")
813
-
814
- # Configure the weights for resizing
815
- settings_frame.grid_rowconfigure(1, weight=1)
816
- settings_frame.grid_columnconfigure(0, weight=1)
817
-
818
- # Load settings based on type
819
- if settings_type == 'mask':
820
- settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
821
- elif settings_type == 'measure':
822
- settings = get_measure_crop_settings(settings={})
823
- elif settings_type == 'classify':
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={})
829
- else:
830
- raise ValueError(f"Invalid settings type: {settings_type}")
831
-
832
- # Generate fields for settings
833
- variables = convert_settings_dict_for_gui(settings)
834
- vars_dict = generate_fields(variables, scrollable_frame)
835
- print("Settings panel setup complete")
836
- return scrollable_frame, vars_dict
837
-
838
- def setup_plot_section(vertical_container):
839
- global canvas, canvas_widget
840
- plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
841
- vertical_container.add(plot_frame, stretch="always")
842
- figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
843
- plot = figure.add_subplot(111)
844
- plot.plot([], []) # This creates an empty plot.
845
- plot.axis('off')
846
- canvas = FigureCanvasTkAgg(figure, master=plot_frame)
847
- canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
848
- canvas_widget = canvas.get_tk_widget()
849
- plot_frame.add(canvas_widget, stretch="always")
850
- canvas.draw()
851
- canvas.figure = figure
852
- return canvas, canvas_widget
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
-
883
- def download_hug_dataset():
884
- global vars_dict, q
885
- repo_id = "einarolafsson/toxo_mito"
886
- subfolder = "plate1"
887
- local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
888
- try:
889
- local_path = download_dataset(repo_id, subfolder, local_dir)
890
- if 'src' in vars_dict:
891
- vars_dict['src'][2].set(local_path) # Assuming vars_dict['src'] is a tuple and the 3rd element is a StringVar
892
- q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
893
- q.put(f"Dataset downloaded to: {local_path}\n")
894
- except Exception as e:
895
- q.put(f"Failed to download dataset: {e}\n")
896
-
897
- def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
898
- global q
899
- """
900
- Downloads a dataset from Hugging Face and returns the local path.
901
-
902
- Args:
903
- repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito').
904
- subfolder (str): The subfolder path within the repository (e.g., 'plate1').
905
- local_dir (str): The local directory where the dataset will be saved. Defaults to the user's home directory.
906
- retries (int): Number of retry attempts in case of failure.
907
- delay (int): Delay in seconds between retries.
908
-
909
- Returns:
910
- str: The local path to the downloaded dataset.
911
- """
912
- if local_dir is None:
913
- local_dir = os.path.join(os.path.expanduser("~"), "datasets")
914
-
915
- local_subfolder_dir = os.path.join(local_dir, subfolder)
916
- if not os.path.exists(local_subfolder_dir):
917
- os.makedirs(local_subfolder_dir)
918
- elif len(os.listdir(local_subfolder_dir)) == 40:
919
- q.put(f"Dataset already downloaded to: {local_subfolder_dir}")
920
- return local_subfolder_dir
921
-
922
- attempt = 0
923
- while attempt < retries:
924
- try:
925
- files = list_repo_files(repo_id, repo_type="dataset")
926
- subfolder_files = [file for file in files if file.startswith(subfolder)]
927
-
928
- for file_name in subfolder_files:
929
- for attempt in range(retries):
930
- try:
931
- url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
932
- response = requests.get(url, stream=True)
933
- response.raise_for_status()
934
-
935
- local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
936
- with open(local_file_path, 'wb') as file:
937
- for chunk in response.iter_content(chunk_size=8192):
938
- file.write(chunk)
939
- q.put(f"Downloaded file: {file_name}")
940
- break
941
- except (requests.HTTPError, requests.Timeout) as e:
942
- q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
943
- time.sleep(delay)
944
- else:
945
- raise Exception(f"Failed to download {file_name} after multiple attempts.")
946
-
947
- return local_subfolder_dir
948
-
949
- except (requests.HTTPError, requests.Timeout) as e:
950
- q.put(f"Error downloading dataset: {e}. Retrying in {delay} seconds...")
951
- attempt += 1
952
- time.sleep(delay)
953
-
954
- raise Exception("Failed to download dataset after multiple attempts.")
955
-
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
958
-
959
- button_frame = tk.Frame(horizontal_container, bg='black')
960
- horizontal_container.add(button_frame, stretch="always", sticky="nsew")
961
- button_frame.grid_rowconfigure(0, weight=0)
962
- button_frame.grid_rowconfigure(1, weight=1)
963
- button_frame.grid_columnconfigure(0, weight=1)
964
-
965
- categories_label = spacrLabel(button_frame, text="Categories", background="black", foreground="white", font=('Helvetica', 12), anchor='center', justify='center', align="center") # Increase font size
966
- categories_label.grid(row=0, column=0, pady=10, padx=10)
967
-
968
- button_scrollable_frame = spacrFrame(button_frame, bg='black')
969
- button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
970
-
971
- btn_col = 0
972
- btn_row = 1
973
-
974
- if run:
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
-
989
- if import_btn:
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')
992
-
993
- # Call toggle_settings after vars_dict is initialized
994
- if vars_dict is not None:
995
- toggle_settings(button_scrollable_frame)
996
- return button_scrollable_frame
997
-
998
- def toggle_test_mode():
999
- global vars_dict, test_mode_button
1000
- current_state = vars_dict['test_mode'][2].get()
1001
- new_state = not current_state
1002
- vars_dict['test_mode'][2].set(new_state)
1003
- if new_state:
1004
- test_mode_button.config(bg="blue")
1005
- else:
1006
- test_mode_button.config(bg="gray")
1007
-
1008
- def toggle_settings(button_scrollable_frame):
1009
- global vars_dict
1010
- from .settings import categories
1011
-
1012
- if vars_dict is None:
1013
- raise ValueError("vars_dict is not initialized.")
1014
-
1015
- def toggle_category(settings, var):
1016
- for setting in settings:
1017
- if setting in vars_dict:
1018
- label, widget, _ = vars_dict[setting]
1019
- if var.get() == 0:
1020
- label.grid_remove()
1021
- widget.grid_remove()
1022
- else:
1023
- label.grid()
1024
- widget.grid()
1025
-
1026
- row = 1
1027
- col = 2
1028
- category_idx = 0
1029
-
1030
- for category, settings in categories.items():
1031
- if any(setting in vars_dict for setting in settings):
1032
- category_var = tk.IntVar(value=0)
1033
- vars_dict[category] = (None, None, category_var)
1034
- toggle = spacrCheckbutton(
1035
- button_scrollable_frame.scrollable_frame,
1036
- text=category,
1037
- variable=category_var,
1038
- command=lambda cat=settings, var=category_var: toggle_category(cat, var)
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')
1046
- toggle.grid(row=row, column=col, sticky="w", pady=2, padx=2)
1047
- col += 1
1048
- category_idx += 1
1049
-
1050
- if category_idx % 4 == 0:
1051
- row += 1
1052
- col = 2
1053
-
1054
- for settings in categories.values():
1055
- for setting in settings:
1056
- if setting in vars_dict:
1057
- label, widget, _ = vars_dict[setting]
1058
- label.grid_remove()
1059
- widget.grid_remove()
1060
-
1061
- def process_fig_queue():
1062
- global canvas, fig_queue, canvas_widget, parent_frame
1063
- try:
1064
- while not fig_queue.empty():
1065
- clear_canvas(canvas)
1066
- fig = fig_queue.get_nowait()
1067
- for ax in fig.get_axes():
1068
- ax.set_xticks([]) # Remove x-axis ticks
1069
- ax.set_yticks([]) # Remove y-axis ticks
1070
- ax.xaxis.set_visible(False) # Hide the x-axis
1071
- ax.yaxis.set_visible(False) # Hide the y-axis
1072
- fig.tight_layout()
1073
- fig.set_facecolor('black')
1074
- canvas.figure = fig
1075
- fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
1076
- fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
1077
- canvas.draw_idle()
1078
- except Exception as e:
1079
- traceback.print_exc()
1080
- finally:
1081
- after_id = canvas_widget.after(100, process_fig_queue)
1082
- parent_frame.after_tasks.append(after_id)
1083
-
1084
- def process_console_queue():
1085
- global q, console_output, parent_frame
1086
- while not q.empty():
1087
- message = q.get_nowait()
1088
- console_output.insert(tk.END, message)
1089
- console_output.see(tk.END)
1090
- after_id = console_output.after(100, process_console_queue)
1091
- parent_frame.after_tasks.append(after_id)
1092
-
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
1122
-
1123
- def run_measure_gui(settings, q, fig_queue, stop_requested):
1124
- process_stdout_stderr(q)
1125
- try:
1126
- settings['input_folder'] = settings['src']
1127
- measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
1128
- except Exception as e:
1129
- q.put(f"Error during processing: {e}")
1130
- traceback.print_exc()
1131
- finally:
1132
- stop_requested.value = 1
1133
-
1134
- def run_classify_gui(settings, q, fig_queue, stop_requested):
1135
- process_stdout_stderr(q)
1136
- try:
1137
- train_test_model_wrapper(settings['src'], settings)
1138
- except Exception as e:
1139
- q.put(f"Error during processing: {e}")
1140
- traceback.print_exc()
1141
- finally:
1142
- stop_requested.value = 1
1143
-
1144
- def set_globals(q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, progress_label_var, fig_queue_var):
1145
- global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue
1146
- q = q_var
1147
- console_output = console_output_var
1148
- parent_frame = parent_frame_var
1149
- vars_dict = vars_dict_var
1150
- canvas = canvas_var
1151
- canvas_widget = canvas_widget_var
1152
- scrollable_frame = scrollable_frame_var
1153
- progress_label = progress_label_var
1154
- fig_queue = fig_queue_var
1155
-
1156
- def initiate_root(parent, settings_type='mask'):
1157
- global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, progress_label, button_scrollable_frame
1158
- print("Initializing root with settings_type:", settings_type)
1159
- parent_frame = parent
1160
-
1161
- if not hasattr(parent_frame, 'after_tasks'):
1162
- parent_frame.after_tasks = []
1163
-
1164
- for widget in parent_frame.winfo_children():
1165
- if widget.winfo_exists():
1166
- try:
1167
- widget.destroy()
1168
- except tk.TclError as e:
1169
- print(f"Error destroying widget: {e}")
1170
-
1171
- q = Queue()
1172
- fig_queue = Queue()
1173
- parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
1174
- scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type) # Adjust height and width as needed
1175
- button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
1176
- canvas, canvas_widget = setup_plot_section(vertical_container)
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
-
1184
- set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue)
1185
- process_console_queue()
1186
- process_fig_queue()
1187
- after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
1188
- parent_frame.after_tasks.append(after_id)
1189
- print("Root initialization complete")
1190
- return parent_frame, vars_dict
1191
-
1192
135
  def cancel_after_tasks(frame):
1193
136
  if hasattr(frame, 'after_tasks'):
1194
137
  for task in frame.after_tasks:
1195
138
  frame.after_cancel(task)
1196
139
  frame.after_tasks.clear()
1197
-
1198
- def start_gui_app(settings_type='mask'):
1199
- global q, fig_queue, parent_frame, scrollable_frame, vars_dict, canvas, canvas_widget, progress_label
1200
- root = tk.Tk()
1201
- width = root.winfo_screenwidth()
1202
- height = root.winfo_screenheight()
1203
- root.geometry(f"{width}x{height}")
1204
- root.title(f"SpaCr: {settings_type.capitalize()}")
1205
- root.content_frame = tk.Frame(root)
1206
- print("Starting GUI app with settings_type:", settings_type)
1207
- initiate_root(root.content_frame, settings_type)
1208
- create_menu_bar(root)
1209
- root.mainloop()
1210
-
1211
-
1212
-