spacr 0.2.56__py3-none-any.whl → 0.2.65__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_core.py CHANGED
@@ -1,4 +1,4 @@
1
- import traceback, ctypes, csv, re, time
1
+ import traceback, ctypes, csv, re
2
2
  import tkinter as tk
3
3
  from tkinter import ttk
4
4
  from tkinter import filedialog
@@ -9,14 +9,17 @@ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
9
9
  import numpy as np
10
10
  import psutil
11
11
  import GPUtil
12
+ from collections import deque
13
+ import tracemalloc
14
+ from tkinter import Menu
15
+ import io
12
16
 
13
17
  try:
14
18
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
15
19
  except AttributeError:
16
20
  pass
17
21
 
18
- from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks, set_default_generate_barecode_mapping, set_default_umap_image_settings
19
- from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style
22
+ from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , set_dark_style
20
23
 
21
24
  # Define global variables
22
25
  q = None
@@ -28,6 +31,12 @@ canvas_widget = None
28
31
  scrollable_frame = None
29
32
  progress_label = None
30
33
  fig_queue = None
34
+ figures = None
35
+ figure_index = None
36
+ progress_bar = None
37
+ usage_bars = None
38
+ fig_memory_limit = None
39
+ figure_current_memory_usage = None
31
40
 
32
41
  thread_control = {"run_thread": None, "stop_requested": False}
33
42
 
@@ -72,41 +81,197 @@ def toggle_settings(button_scrollable_frame):
72
81
  vars_dict = hide_all_settings(vars_dict, categories)
73
82
 
74
83
  def process_fig_queue():
75
- global canvas, fig_queue, canvas_widget, parent_frame
76
-
77
- def clear_canvas(canvas):
78
- for ax in canvas.figure.get_axes():
79
- ax.clear()
80
- canvas.draw_idle()
84
+ global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index
81
85
 
86
+ from .gui_elements import standardize_figure
82
87
  try:
83
88
  while not fig_queue.empty():
84
- time.sleep(1)
85
- clear_canvas(canvas)
86
89
  fig = fig_queue.get_nowait()
87
90
 
88
- for ax in fig.get_axes():
89
- ax.set_xticks([]) # Remove x-axis ticks
90
- ax.set_yticks([]) # Remove y-axis ticks
91
- ax.xaxis.set_visible(False) # Hide the x-axis
92
- ax.yaxis.set_visible(False) # Hide the y-axis
91
+ if fig is None:
92
+ print("Warning: Retrieved a None figure from fig_queue.")
93
+ continue # Skip processing if the figure is None
93
94
 
94
- # Adjust layout to minimize spacing between axes
95
- fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0.01, hspace=0.01)
96
- fig.set_facecolor('black')
95
+ # Standardize the figure appearance before adding it to the list
96
+ standardize_figure(fig)
97
97
 
98
- canvas.figure = fig
99
- fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
100
- fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
101
- canvas.draw_idle()
98
+ figures.append(fig)
99
+ if figure_index == len(figures) - 2:
100
+ figure_index += 1
101
+ display_figure(fig)
102
102
  except Exception as e:
103
103
  traceback.print_exc()
104
104
  finally:
105
- after_id = canvas_widget.after(1, process_fig_queue)
105
+ after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
106
106
  parent_frame.after_tasks.append(after_id)
107
107
 
108
- def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, progress_bar_var, usage_bars_var):
109
- global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars
108
+ def display_figure(fig):
109
+ global canvas, canvas_widget
110
+
111
+ from .gui_elements import modify_figure_properties, save_figure_as_format, modify_figure
112
+
113
+ # Apply the dark style to the context menu
114
+ style_out = set_dark_style(ttk.Style())
115
+ bg_color = style_out['bg_color']
116
+ fg_color = style_out['fg_color']
117
+
118
+ # Initialize the scale factor for zooming
119
+ scale_factor = 1.0
120
+
121
+ # Save the original x and y limits of the first axis (assuming all axes have the same limits)
122
+ original_xlim = [ax.get_xlim() for ax in fig.get_axes()]
123
+ original_ylim = [ax.get_ylim() for ax in fig.get_axes()]
124
+
125
+ # Clear previous canvas content
126
+ if canvas:
127
+ canvas.get_tk_widget().destroy()
128
+
129
+ # Create a new canvas for the figure
130
+ new_canvas = FigureCanvasTkAgg(fig, master=canvas_widget.master)
131
+ new_canvas.draw()
132
+ new_canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
133
+
134
+ # Update the global canvas and canvas_widget references
135
+ canvas = new_canvas
136
+ canvas_widget = new_canvas.get_tk_widget()
137
+ canvas_widget.configure(bg=bg_color)
138
+
139
+ # Create the context menu
140
+ context_menu = tk.Menu(canvas_widget, tearoff=0, bg=bg_color, fg=fg_color)
141
+ context_menu.add_command(label="Save Figure as PDF", command=lambda: save_figure_as_format(fig, 'pdf'))
142
+ context_menu.add_command(label="Save Figure as PNG", command=lambda: save_figure_as_format(fig, 'png'))
143
+ context_menu.add_command(label="Modify Figure", command=lambda: modify_figure(fig))
144
+ context_menu.add_command(label="Reset Zoom", command=lambda: reset_zoom(fig)) # Add Reset Zoom option
145
+
146
+ def reset_zoom(fig):
147
+ global scale_factor
148
+ scale_factor = 1.0 # Reset the scale factor
149
+
150
+ for i, ax in enumerate(fig.get_axes()):
151
+ ax.set_xlim(original_xlim[i])
152
+ ax.set_ylim(original_ylim[i])
153
+ fig.canvas.draw_idle()
154
+
155
+ def on_right_click(event):
156
+ context_menu.post(event.x_root, event.y_root)
157
+
158
+ def on_hover(event):
159
+ widget_width = event.widget.winfo_width()
160
+ x_position = event.x
161
+
162
+ if x_position < widget_width / 2:
163
+ canvas_widget.config(cursor="hand2")
164
+ else:
165
+ canvas_widget.config(cursor="hand2")
166
+
167
+ def on_leave(event):
168
+ canvas_widget.config(cursor="arrow")
169
+
170
+ def flash_feedback(side):
171
+ flash = tk.Toplevel(canvas_widget.master)
172
+ flash.overrideredirect(True)
173
+ flash_width = int(canvas_widget.winfo_width() / 2)
174
+ flash_height = canvas_widget.winfo_height()
175
+ flash.configure(bg='white')
176
+ flash.attributes('-alpha', 0.9)
177
+
178
+ if side == "left":
179
+ flash.geometry(f"{flash_width}x{flash_height}+{canvas_widget.winfo_rootx()}+{canvas_widget.winfo_rooty()}")
180
+ else:
181
+ flash.geometry(f"{flash_width}x{flash_height}+{canvas_widget.winfo_rootx() + flash_width}+{canvas_widget.winfo_rooty()}")
182
+
183
+ flash.lift()
184
+
185
+ # Ensure the flash covers the correct area only
186
+ flash.update_idletasks()
187
+ flash.after(100, flash.destroy)
188
+
189
+ def on_click(event):
190
+ widget_width = event.widget.winfo_width()
191
+ x_position = event.x
192
+
193
+ if x_position < widget_width / 2:
194
+ #flash_feedback("left")
195
+ show_previous_figure()
196
+ else:
197
+ #flash_feedback("right")
198
+ show_next_figure()
199
+
200
+ def zoom(event):
201
+ nonlocal scale_factor
202
+
203
+ zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
204
+
205
+ # Adjust zoom factor based on the operating system and mouse event
206
+ if event.num == 4 or event.delta > 0: # Scroll up
207
+ scale_factor *= (1 + zoom_speed)
208
+ elif event.num == 5 or event.delta < 0: # Scroll down
209
+ scale_factor /= (1 + zoom_speed)
210
+
211
+ # Get mouse position relative to the figure
212
+ x_mouse, y_mouse = event.x, event.y
213
+ x_ratio = x_mouse / canvas_widget.winfo_width()
214
+ y_ratio = y_mouse / canvas_widget.winfo_height()
215
+
216
+ for ax in fig.get_axes():
217
+ xlim = ax.get_xlim()
218
+ ylim = ax.get_ylim()
219
+
220
+ # Calculate the new limits
221
+ x_center = xlim[0] + x_ratio * (xlim[1] - xlim[0])
222
+ y_center = ylim[0] + (1 - y_ratio) * (ylim[1] - ylim[0])
223
+
224
+ x_range = (xlim[1] - xlim[0]) * scale_factor
225
+ y_range = (ylim[1] - ylim[0]) * scale_factor
226
+
227
+ ax.set_xlim([x_center - x_range * x_ratio, x_center + x_range * (1 - x_ratio)])
228
+ ax.set_ylim([y_center - y_range * (1 - y_ratio), y_center + y_range * y_ratio])
229
+
230
+ # Redraw the figure
231
+ fig.canvas.draw_idle()
232
+
233
+ # Bind events for hover, click interactions, and zoom
234
+ canvas_widget.bind("<Motion>", on_hover)
235
+ canvas_widget.bind("<Leave>", on_leave)
236
+ canvas_widget.bind("<Button-1>", on_click)
237
+ canvas_widget.bind("<Button-3>", on_right_click)
238
+
239
+ # Bind mouse wheel for zooming (cross-platform)
240
+ canvas_widget.bind("<MouseWheel>", zoom) # Windows
241
+ canvas_widget.bind("<Button-4>", zoom) # Linux/macOS Scroll Up
242
+ canvas_widget.bind("<Button-5>", zoom) # Linux/macOS Scroll Down
243
+
244
+ def clear_unused_figures():
245
+ global figures, figure_index
246
+
247
+ lower_bound = max(0, figure_index - 20)
248
+ upper_bound = min(len(figures), figure_index + 20)
249
+ # Clear figures outside of the +/- 20 range
250
+ figures = deque([fig for i, fig in enumerate(figures) if lower_bound <= i <= upper_bound])
251
+ # Update the figure index after clearing
252
+ figure_index = min(max(figure_index, 0), len(figures) - 1)
253
+
254
+ def show_previous_figure():
255
+ global figure_index, figures, fig_queue
256
+ if figure_index is not None and figure_index > 0:
257
+ figure_index -= 1
258
+ display_figure(figures[figure_index])
259
+ clear_unused_figures()
260
+
261
+ def show_next_figure():
262
+ global figure_index, figures, fig_queue
263
+ if figure_index is not None and figure_index < len(figures) - 1:
264
+ figure_index += 1
265
+ display_figure(figures[figure_index])
266
+ clear_unused_figures()
267
+ elif figure_index == len(figures) - 1 and not fig_queue.empty():
268
+ fig = fig_queue.get_nowait()
269
+ figures.append(fig)
270
+ figure_index += 1
271
+ display_figure(fig)
272
+
273
+ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, figures_var, figure_index_var, progress_bar_var, usage_bars_var, fig_memory_limit_var, figure_current_memory_usage_var):
274
+ global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, fig_memory_limit, figure_current_memory_usage
110
275
  thread_control = thread_control_var
111
276
  q = q_var
112
277
  console_output = console_output_var
@@ -116,13 +281,17 @@ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var,
116
281
  canvas_widget = canvas_widget_var
117
282
  scrollable_frame = scrollable_frame_var
118
283
  fig_queue = fig_queue_var
284
+ figures = figures_var
285
+ figure_index = figure_index_var
119
286
  progress_bar = progress_bar_var
120
287
  usage_bars = usage_bars_var
288
+ fig_memory_limit = fig_memory_limit_var
289
+ figure_current_memory_usage = figure_current_memory_usage_var
121
290
 
122
291
  def import_settings(settings_type='mask'):
123
292
  from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
124
293
  global vars_dict, scrollable_frame, button_scrollable_frame
125
- from .settings import generate_fields, set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model, set_default_generate_barecode_mapping, set_default_umap_image_settings
294
+ from .settings import generate_fields, set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model, set_default_generate_barecode_mapping, set_default_umap_image_settings, get_analyze_recruitment_default_settings
126
295
 
127
296
  def read_settings_from_csv(csv_file_path):
128
297
  settings = {}
@@ -161,6 +330,8 @@ def import_settings(settings_type='mask'):
161
330
  settings = set_default_generate_barecode_mapping(settings={})
162
331
  elif settings_type == 'umap':
163
332
  settings = set_default_umap_image_settings(settings={})
333
+ elif settings_type == 'recruitment':
334
+ settings = get_analyze_recruitment_default_settings(settings={})
164
335
  else:
165
336
  raise ValueError(f"Invalid settings type: {settings_type}")
166
337
 
@@ -230,27 +401,34 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
230
401
  return scrollable_frame, vars_dict
231
402
 
232
403
  def setup_plot_section(vertical_container):
233
- global canvas, canvas_widget
404
+ global canvas, canvas_widget, figures, figure_index
405
+
406
+ from .gui_elements import set_element_size
407
+
408
+ # Initialize deque for storing figures and the current index
409
+ figures = deque()
410
+ figure_index = -1
234
411
 
235
412
  # Create a frame for the plot section
236
- plot_frame = tk.Frame(vertical_container, bg='lightgrey')
413
+ plot_frame = tk.Frame(vertical_container)
237
414
  vertical_container.add(plot_frame, stretch="always")
238
415
 
239
416
  # Set up the plot
240
417
  figure = Figure(figsize=(30, 4), dpi=100)
241
418
  plot = figure.add_subplot(111)
242
- plot.plot([], []) # This creates an empty plot.
419
+ plot.plot([], [])
243
420
  plot.axis('off')
421
+
244
422
  canvas = FigureCanvasTkAgg(figure, master=plot_frame)
245
423
  canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
246
424
  canvas_widget = canvas.get_tk_widget()
247
- canvas_widget.grid(row=0, column=0, sticky="nsew") # Use grid for the canvas widget
425
+ canvas_widget.grid(row=0, column=0, sticky="nsew")
248
426
 
249
427
  plot_frame.grid_rowconfigure(0, weight=1)
250
428
  plot_frame.grid_columnconfigure(0, weight=1)
251
429
 
252
430
  canvas.draw()
253
- canvas.figure = figure
431
+ canvas.figure = figure # Ensure that the figure is linked to the canvas
254
432
  style_out = set_dark_style(ttk.Style())
255
433
 
256
434
  figure.patch.set_facecolor(style_out['bg_color'])
@@ -259,7 +437,7 @@ def setup_plot_section(vertical_container):
259
437
  widgets = [canvas_widget]
260
438
  style = ttk.Style(vertical_container)
261
439
  _ = set_dark_style(style, containers=containers, widgets=widgets)
262
-
440
+
263
441
  return canvas, canvas_widget
264
442
 
265
443
  def setup_console(vertical_container):
@@ -280,7 +458,7 @@ def setup_console(vertical_container):
280
458
 
281
459
  # Create the scrollable frame (which is a Text widget) with white text
282
460
  family = style_out['font_family']
283
- font_size = style_out['font_size'] - 2
461
+ font_size = style_out['font_size']
284
462
  font_loader = style_out['font_loader']
285
463
  console_output = tk.Text(console_frame, bg=style_out['bg_color'], fg=style_out['fg_color'], font=font_loader.get_font(size=font_size), bd=0, highlightthickness=0)
286
464
 
@@ -331,9 +509,6 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
331
509
  button_section_height = size_dict['panel_height']
332
510
  button_frame = tk.Frame(horizontal_container, height=button_section_height)
333
511
 
334
- # Prevent the frame from resizing based on the child widget
335
- #button_frame.pack_propagate(False)
336
-
337
512
  horizontal_container.add(button_frame, stretch="always", sticky="nsew")
338
513
  button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
339
514
  button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
@@ -366,7 +541,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
366
541
  widgets.append(import_button)
367
542
  btn_row += 1
368
543
 
369
- # Add the progress bar under the settings category menu
544
+ # Add the batch progress bar
370
545
  progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
371
546
  progress_bar.grid(row=btn_row, column=0, columnspan=7, pady=5, padx=5, sticky='ew')
372
547
  progress_bar.set_label_position() # Set the label position after grid placement
@@ -380,7 +555,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
380
555
 
381
556
  return button_scrollable_frame, btn_col
382
557
 
383
- def setup_usage_panel(horizontal_container, btn_col):
558
+ def setup_usage_panel(horizontal_container, btn_col, uppdate_frequency):
384
559
  global usage_bars
385
560
  from .gui_elements import set_dark_style, set_element_size
386
561
 
@@ -406,7 +581,7 @@ def setup_usage_panel(horizontal_container, btn_col):
406
581
  bar['value'] = usage
407
582
 
408
583
  # Schedule the function to run again after 1000 ms (1 second)
409
- parent_frame.after(1000, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
584
+ parent_frame.after(uppdate_frequency, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
410
585
 
411
586
  size_dict = set_element_size()
412
587
  usage_panel_height = size_dict['panel_height']
@@ -423,7 +598,7 @@ def setup_usage_panel(horizontal_container, btn_col):
423
598
  widgets = [usage_scrollable_frame.scrollable_frame]
424
599
 
425
600
  usage_bars = []
426
- max_elements_per_column = 4
601
+ max_elements_per_column = 6
427
602
  row = 0
428
603
  col = 0
429
604
 
@@ -441,7 +616,7 @@ def setup_usage_panel(horizontal_container, btn_col):
441
616
  try:
442
617
  ram_info = psutil.virtual_memory()
443
618
  ram_label_text = f"RAM"
444
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w', style="usage.TLabel")
619
+ label = tk.Label(usage_scrollable_frame.scrollable_frame,text=ram_label_text,anchor='w',font=font_loader.get_font(size=font_size),bg=style_out['bg_color'],fg=style_out['fg_color'])
445
620
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
446
621
  ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
447
622
  ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -458,7 +633,7 @@ def setup_usage_panel(horizontal_container, btn_col):
458
633
  if gpus:
459
634
  gpu = gpus[0]
460
635
  vram_label_text = f"VRAM"
461
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w', style="usage.TLabel")
636
+ label = tk.Label(usage_scrollable_frame.scrollable_frame,text=vram_label_text,anchor='w',font=font_loader.get_font(size=font_size),bg=style_out['bg_color'],fg=style_out['fg_color'])
462
637
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
463
638
  vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
464
639
  vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -468,7 +643,7 @@ def setup_usage_panel(horizontal_container, btn_col):
468
643
  row += 1
469
644
 
470
645
  gpu_label_text = f"GPU"
471
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w', style="usage.TLabel")
646
+ label = tk.Label(usage_scrollable_frame.scrollable_frame,text=gpu_label_text,anchor='w',font=font_loader.get_font(size=font_size),bg=style_out['bg_color'],fg=style_out['fg_color'])
472
647
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
473
648
  gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
474
649
  gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -488,7 +663,8 @@ def setup_usage_panel(horizontal_container, btn_col):
488
663
  if row > 0 and row % max_elements_per_column == 0:
489
664
  col += 1
490
665
  row = 0
491
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w', style="usage.TLabel")
666
+
667
+ label = tk.Label(usage_scrollable_frame.scrollable_frame,text=f"C{core+1}",anchor='w',font=font_loader.get_font(size=font_size),bg=style_out['bg_color'],fg=style_out['fg_color'])
492
668
  label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
493
669
  bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
494
670
  bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
@@ -525,11 +701,10 @@ def initiate_abort():
525
701
 
526
702
  thread_control = {"run_thread": None, "stop_requested": False}
527
703
 
528
-
529
704
  def start_process(q=None, fig_queue=None, settings_type='mask'):
530
705
  global thread_control, vars_dict, parent_frame
531
706
  from .settings import check_settings, expected_types
532
- from .gui_utils import run_function_gui
707
+ from .gui_utils import run_function_gui, set_high_priority, set_cpu_affinity, initialize_cuda
533
708
 
534
709
  if q is None:
535
710
  q = Queue()
@@ -547,21 +722,32 @@ def start_process(q=None, fig_queue=None, settings_type='mask'):
547
722
  stop_requested = Value('i', 0)
548
723
  thread_control["stop_requested"] = stop_requested
549
724
 
725
+ # Initialize CUDA in the main process
726
+ initialize_cuda()
727
+
550
728
  process_args = (settings_type, settings, q, fig_queue, stop_requested)
551
- if settings_type in [
552
- 'mask', 'umap', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
553
- 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
554
- 'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
555
- 'vision_dataset'
556
- ]:
557
- thread_control["run_thread"] = Process(target=run_function_gui, args=process_args)
729
+ if settings_type in ['mask', 'umap', 'measure', 'simulation', 'sequencing',
730
+ 'classify', 'cellpose_dataset', 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
731
+ 'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores', 'vision_dataset']:
732
+
733
+ # Start the process
734
+ process = Process(target=run_function_gui, args=process_args)
735
+ process.start()
736
+
737
+ # Set high priority for the process
738
+ #set_high_priority(process)
739
+
740
+ # Set CPU affinity if necessary
741
+ set_cpu_affinity(process)
742
+
743
+ # Store the process in thread_control for future reference
744
+ thread_control["run_thread"] = process
558
745
  else:
559
746
  q.put(f"Error: Unknown settings type '{settings_type}'")
560
747
  return
561
- thread_control["run_thread"].start()
562
748
 
563
749
  def process_console_queue():
564
- global q, console_output, parent_frame, progress_bar
750
+ global q, console_output, parent_frame, progress_bar, process_console_queue
565
751
 
566
752
  # Initialize function attribute if it doesn't exist
567
753
  if not hasattr(process_console_queue, "completed_tasks"):
@@ -574,21 +760,21 @@ def process_console_queue():
574
760
  while not q.empty():
575
761
  message = q.get_nowait()
576
762
  clean_message = ansi_escape_pattern.sub('', message)
577
- console_output.insert(tk.END, clean_message + "\n")
578
- console_output.see(tk.END)
763
+ #console_output.insert(tk.END, clean_message + "\n")
764
+ #console_output.see(tk.END)
579
765
 
580
766
  # Check if the message contains progress information
581
767
  if clean_message.startswith("Progress:"):
582
768
  try:
583
769
  # Extract the progress information
584
- match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
770
+ match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*),(.*)', clean_message)
585
771
 
586
772
  if match:
587
773
  current_progress = int(match.group(1))
588
774
  total_progress = int(match.group(2))
589
775
  operation_type = match.group(3).strip()
590
- time_info = match.group(4).strip()
591
-
776
+ additional_info = match.group(4).strip() # Capture everything after operation_type
777
+
592
778
  # Check if the maximum value has changed
593
779
  if process_console_queue.current_maximum != total_progress:
594
780
  process_console_queue.current_maximum = total_progress
@@ -599,55 +785,77 @@ def process_console_queue():
599
785
 
600
786
  # Calculate the unique progress count
601
787
  unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
602
-
788
+
603
789
  # Update the progress bar
604
790
  if progress_bar:
605
791
  progress_bar['maximum'] = total_progress
606
792
  progress_bar['value'] = unique_progress_count
607
793
 
608
- # Extract and update additional information
794
+ # Store operation type and additional info
609
795
  if operation_type:
610
796
  progress_bar.operation_type = operation_type
611
-
612
- time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
613
- if time_image_match:
614
- progress_bar.time_image = float(time_image_match.group(1))
615
-
616
- time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
617
- if time_batch_match:
618
- progress_bar.time_batch = float(time_batch_match.group(1))
619
-
620
- time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
621
- if time_left_match:
622
- progress_bar.time_left = float(time_left_match.group(1))
797
+ progress_bar.additional_info = additional_info
623
798
 
624
799
  # Update the progress label
625
800
  if progress_bar.progress_label:
626
801
  progress_bar.update_label()
627
-
802
+
628
803
  # Clear completed tasks when progress is complete
629
804
  if unique_progress_count >= total_progress:
630
805
  process_console_queue.completed_tasks.clear()
631
806
 
632
807
  except Exception as e:
633
808
  print(f"Error parsing progress message: {e}")
634
- #else:
635
- # # Only insert messages that do not start with "Progress:"
636
- # console_output.insert(tk.END, clean_message + "\n")
637
- # console_output.see(tk.END)
809
+ else:
810
+ # Only insert messages that do not start with "Progress:"
811
+ console_output.insert(tk.END, clean_message + "\n")
812
+ console_output.see(tk.END)
638
813
 
639
- after_id = console_output.after(1, process_console_queue)
814
+ after_id = console_output.after(uppdate_frequency, process_console_queue)
640
815
  parent_frame.after_tasks.append(after_id)
641
816
 
817
+ def main_thread_update_function(root, q, fig_queue, canvas_widget):
818
+ global uppdate_frequency
819
+ try:
820
+ while not q.empty():
821
+ message = q.get_nowait()
822
+ except Exception as e:
823
+ print(f"Error updating GUI canvas: {e}")
824
+ finally:
825
+ root.after(uppdate_frequency, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget))
642
826
 
643
827
  def initiate_root(parent, settings_type='mask'):
644
- global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
645
- from .gui_utils import main_thread_update_function, setup_frame
828
+ """
829
+ Initializes the root window and sets up the GUI components based on the specified settings type.
830
+
831
+ Args:
832
+ parent (tkinter.Tk or tkinter.Toplevel): The parent window for the GUI.
833
+ settings_type (str, optional): The type of settings to be displayed in the GUI. Defaults to 'mask'.
834
+
835
+ Returns:
836
+ tuple: A tuple containing the parent frame and the dictionary of variables used in the GUI.
837
+ """
838
+
839
+ global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index, fig_memory_limit, figure_current_memory_usage
840
+
841
+ from .gui_utils import setup_frame
646
842
  from .settings import descriptions
647
843
 
844
+ uppdate_frequency = 100
845
+
846
+ # Start tracemalloc and initialize global variables
847
+ tracemalloc.start()
848
+
648
849
  set_start_method('spawn', force=True)
850
+ #set_start_method('forkserver', force=True)
649
851
  print("Initializing root with settings_type:", settings_type)
650
852
 
853
+ # Initialize global variables
854
+ figures = deque()
855
+ figure_index = -1
856
+ fig_memory_limit = 200 * 1024 * 1024 # 200 MB limit
857
+ figure_current_memory_usage = 0
858
+
651
859
  parent_frame = parent
652
860
 
653
861
  if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
@@ -676,9 +884,9 @@ def initiate_root(parent, settings_type='mask'):
676
884
  canvas, canvas_widget = setup_plot_section(vertical_container)
677
885
  console_output, _ = setup_console(vertical_container)
678
886
  button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
679
- _, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col)
887
+ _, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col, uppdate_frequency)
680
888
 
681
- set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
889
+ set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, fig_memory_limit, figure_current_memory_usage)
682
890
  description_text = descriptions.get(settings_type, "No description available for this module.")
683
891
 
684
892
  q.put(f"Console")
@@ -687,7 +895,7 @@ def initiate_root(parent, settings_type='mask'):
687
895
 
688
896
  process_console_queue()
689
897
  process_fig_queue()
690
- after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
898
+ after_id = parent_window.after(uppdate_frequency, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
691
899
  parent_window.after_tasks.append(after_id)
692
900
 
693
901
  print("Root initialization complete")