spacr 0.2.56__py3-none-any.whl → 0.2.61__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,185 @@ 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
 
82
86
  try:
83
87
  while not fig_queue.empty():
84
- time.sleep(1)
85
- clear_canvas(canvas)
86
88
  fig = fig_queue.get_nowait()
87
-
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
93
-
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')
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()
89
+ figures.append(fig)
90
+ figure_index = len(figures) - 1
91
+ display_figure(fig)
102
92
  except Exception as e:
103
93
  traceback.print_exc()
104
94
  finally:
105
- after_id = canvas_widget.after(1, process_fig_queue)
95
+ after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
106
96
  parent_frame.after_tasks.append(after_id)
107
97
 
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
98
+ def display_figure(fig):
99
+ global canvas, canvas_widget
100
+
101
+ # Clear previous canvas content
102
+ canvas.get_tk_widget().destroy()
103
+
104
+ # Create a new canvas for the figure
105
+ new_canvas = FigureCanvasTkAgg(fig, master=canvas_widget.master)
106
+ new_canvas.draw()
107
+ new_canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
108
+
109
+ # Update the global canvas and canvas_widget references
110
+ canvas = new_canvas
111
+ canvas_widget = new_canvas.get_tk_widget()
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
+ # Define the save functions for the context menu
119
+ def save_figure_as_format(format):
120
+ file_path = filedialog.asksaveasfilename(defaultextension=f".{format}", filetypes=[(f"{format.upper()} files", f"*.{format}"), ("All files", "*.*")])
121
+ if file_path:
122
+ fig.savefig(file_path, format=format)
123
+
124
+ def modify_figure():
125
+ def apply_modifications():
126
+ try:
127
+ x_width = float(x_width_var.get())
128
+ y_height = float(y_height_var.get())
129
+ line_width = float(line_width_var.get())
130
+ font_size = int(font_size_var.get())
131
+ modify_figure_properties(fig, x_width=x_width, y_height=y_height, line_width=line_width)
132
+ for ax in fig.get_axes():
133
+ for label in ax.get_xticklabels() + ax.get_yticklabels():
134
+ label.set_fontsize(font_size)
135
+ ax.title.set_fontsize(font_size)
136
+ ax.xaxis.label.set_fontsize(font_size)
137
+ ax.yaxis.label.set_fontsize(font_size)
138
+ canvas.draw_idle() # Redraw the canvas after modifications
139
+ except ValueError:
140
+ print("Invalid input; please enter numeric values.")
141
+
142
+ # Create a new window for user input
143
+ modify_window = tk.Toplevel()
144
+ modify_window.title("Modify Figure Properties")
145
+
146
+ # Apply dark style to the popup window
147
+ modify_window.configure(bg=bg_color)
148
+
149
+ # Create and style the input fields
150
+ tk.Label(modify_window, text="X Axis Width:", bg=bg_color, fg=fg_color).grid(row=0, column=0, padx=10, pady=5)
151
+ x_width_var = tk.StringVar()
152
+ tk.Entry(modify_window, textvariable=x_width_var, bg=bg_color, fg=fg_color).grid(row=0, column=1, padx=10, pady=5)
153
+
154
+ tk.Label(modify_window, text="Y Axis Height:", bg=bg_color, fg=fg_color).grid(row=1, column=0, padx=10, pady=5)
155
+ y_height_var = tk.StringVar()
156
+ tk.Entry(modify_window, textvariable=y_height_var, bg=bg_color, fg=fg_color).grid(row=1, column=1, padx=10, pady=5)
157
+
158
+ tk.Label(modify_window, text="Line Width:", bg=bg_color, fg=fg_color).grid(row=2, column=0, padx=10, pady=5)
159
+ line_width_var = tk.StringVar()
160
+ tk.Entry(modify_window, textvariable=line_width_var, bg=bg_color, fg=fg_color).grid(row=2, column=1, padx=10, pady=5)
161
+
162
+ tk.Label(modify_window, text="Font Size:", bg=bg_color, fg=fg_color).grid(row=3, column=0, padx=10, pady=5)
163
+ font_size_var = tk.StringVar()
164
+ tk.Entry(modify_window, textvariable=font_size_var, bg=bg_color, fg=fg_color).grid(row=3, column=1, padx=10, pady=5)
165
+
166
+ # Apply button
167
+ apply_button = tk.Button(modify_window, text="Apply", command=apply_modifications, bg=bg_color, fg=fg_color)
168
+ apply_button.grid(row=4, column=0, columnspan=2, pady=10)
169
+
170
+ # Create the context menu
171
+ context_menu = tk.Menu(canvas_widget, tearoff=0, bg=bg_color, fg=fg_color)
172
+ context_menu.add_command(label="Save Figure as PDF", command=lambda: save_figure_as_format('pdf'))
173
+ context_menu.add_command(label="Save Figure as PNG", command=lambda: save_figure_as_format('png'))
174
+ context_menu.add_command(label="Modify Figure", command=modify_figure)
175
+
176
+ def on_right_click(event):
177
+ context_menu.post(event.x_root, event.y_root)
178
+
179
+ new_canvas.get_tk_widget().bind("<Button-3>", on_right_click)
180
+
181
+ def clear_unused_figures():
182
+ global figures, figure_index
183
+
184
+ lower_bound = max(0, figure_index - 20)
185
+ upper_bound = min(len(figures), figure_index + 20)
186
+
187
+ # Clear figures outside of the +/- 20 range
188
+ figures = deque([fig for i, fig in enumerate(figures) if lower_bound <= i <= upper_bound])
189
+
190
+ # Update the figure index after clearing
191
+ figure_index = min(max(figure_index, 0), len(figures) - 1)
192
+
193
+ def show_previous_figure():
194
+ global figure_index, figures
195
+ if figure_index is not None and figure_index > 0:
196
+ figure_index -= 1
197
+ display_figure(figures[figure_index])
198
+ clear_unused_figures()
199
+
200
+ def show_next_figure():
201
+ global figure_index, figures
202
+ if figure_index is not None and figure_index < len(figures) - 1:
203
+ figure_index += 1
204
+ display_figure(figures[figure_index])
205
+ clear_unused_figures()
206
+
207
+ def save_figure_as_format(fig, file_format):
208
+ file_path = filedialog.asksaveasfilename(defaultextension=f".{file_format}", filetypes=[(f"{file_format.upper()} files", f"*.{file_format}"), ("All files", "*.*")])
209
+ if file_path:
210
+ try:
211
+ fig.savefig(file_path, format=file_format)
212
+ print(f"Figure saved as {file_format.upper()} at {file_path}")
213
+ except Exception as e:
214
+ print(f"Error saving figure: {e}")
215
+
216
+ def modify_figure_properties(fig, x_width=None, y_height=None, line_width=None):
217
+ """
218
+ Modifies the properties of the figure, including axis dimensions and line widths.
219
+
220
+ Parameters:
221
+ - fig: The matplotlib figure object to modify.
222
+ - x_width: Desired width for the x-axis (optional).
223
+ - y_height: Desired height for the y-axis (optional).
224
+ - line_width: Desired line width for all lines (optional).
225
+ """
226
+ for ax in fig.get_axes():
227
+ # Scaling the figure
228
+ if x_width is not None or y_height is not None:
229
+ # Get the current axis limits
230
+ current_xlim = ax.get_xlim()
231
+ current_ylim = ax.get_ylim()
232
+
233
+ # Set new limits
234
+ if x_width is not None:
235
+ scale_factor_x = x_width / (current_xlim[1] - current_xlim[0])
236
+ else:
237
+ scale_factor_x = 1
238
+
239
+ if y_height is not None:
240
+ scale_factor_y = y_height / (current_ylim[1] - current_ylim[0])
241
+ else:
242
+ scale_factor_y = 1
243
+
244
+ # Adjust the figure size and elements proportionally
245
+ fig.set_size_inches(fig.get_size_inches() * scale_factor_x * scale_factor_y, forward=True)
246
+ ax.set_xlim(left=current_xlim[0] * scale_factor_x, right=current_xlim[1] * scale_factor_x)
247
+ ax.set_ylim(bottom=current_ylim[0] * scale_factor_y, top=current_ylim[1] * scale_factor_y)
248
+
249
+ # Adjust line width and other elements if specified
250
+ if line_width is not None:
251
+ for line in ax.get_lines():
252
+ line.set_linewidth(line_width)
253
+ for spine in ax.spines.values(): # Modify width of spines (e.g., scale bars)
254
+ spine.set_linewidth(line_width)
255
+ ax.tick_params(width=line_width) # Modify width of ticks
256
+ for text in ax.get_xticklabels() + ax.get_yticklabels():
257
+ text.set_fontsize(ax.get_xticklabels()[0].get_fontsize())
258
+
259
+ fig.canvas.draw_idle()
260
+
261
+ 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):
262
+ 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
263
  thread_control = thread_control_var
111
264
  q = q_var
112
265
  console_output = console_output_var
@@ -116,13 +269,17 @@ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var,
116
269
  canvas_widget = canvas_widget_var
117
270
  scrollable_frame = scrollable_frame_var
118
271
  fig_queue = fig_queue_var
272
+ figures = figures_var
273
+ figure_index = figure_index_var
119
274
  progress_bar = progress_bar_var
120
275
  usage_bars = usage_bars_var
276
+ fig_memory_limit = fig_memory_limit_var
277
+ figure_current_memory_usage = figure_current_memory_usage_var
121
278
 
122
279
  def import_settings(settings_type='mask'):
123
280
  from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
124
281
  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
282
+ 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
283
 
127
284
  def read_settings_from_csv(csv_file_path):
128
285
  settings = {}
@@ -161,6 +318,8 @@ def import_settings(settings_type='mask'):
161
318
  settings = set_default_generate_barecode_mapping(settings={})
162
319
  elif settings_type == 'umap':
163
320
  settings = set_default_umap_image_settings(settings={})
321
+ elif settings_type == 'recruitment':
322
+ settings = get_analyze_recruitment_default_settings(settings={})
164
323
  else:
165
324
  raise ValueError(f"Invalid settings type: {settings_type}")
166
325
 
@@ -230,21 +389,28 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
230
389
  return scrollable_frame, vars_dict
231
390
 
232
391
  def setup_plot_section(vertical_container):
233
- global canvas, canvas_widget
392
+ global canvas, canvas_widget, figures, figure_index
393
+
394
+ from .gui_elements import set_element_size
395
+
396
+ # Initialize deque for storing figures and the current index
397
+ figures = deque()
398
+ figure_index = -1
234
399
 
235
400
  # Create a frame for the plot section
236
- plot_frame = tk.Frame(vertical_container, bg='lightgrey')
401
+ plot_frame = tk.Frame(vertical_container)
237
402
  vertical_container.add(plot_frame, stretch="always")
238
403
 
239
404
  # Set up the plot
240
405
  figure = Figure(figsize=(30, 4), dpi=100)
241
406
  plot = figure.add_subplot(111)
242
- plot.plot([], []) # This creates an empty plot.
407
+ plot.plot([], [])
243
408
  plot.axis('off')
409
+
244
410
  canvas = FigureCanvasTkAgg(figure, master=plot_frame)
245
411
  canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
246
412
  canvas_widget = canvas.get_tk_widget()
247
- canvas_widget.grid(row=0, column=0, sticky="nsew") # Use grid for the canvas widget
413
+ canvas_widget.grid(row=0, column=0, sticky="nsew")
248
414
 
249
415
  plot_frame.grid_rowconfigure(0, weight=1)
250
416
  plot_frame.grid_columnconfigure(0, weight=1)
@@ -260,6 +426,19 @@ def setup_plot_section(vertical_container):
260
426
  style = ttk.Style(vertical_container)
261
427
  _ = set_dark_style(style, containers=containers, widgets=widgets)
262
428
 
429
+ # Add navigation buttons using spacrButton
430
+ button_frame = tk.Frame(plot_frame, bg=style_out['bg_color'])
431
+ button_frame.grid(row=1, column=0, sticky='ew', pady=5)
432
+
433
+ size_dict = set_element_size()
434
+
435
+ btn_size = int(size_dict['btn_size']*0.75)
436
+ prev_button = spacrButton(button_frame, text="Previous", command=show_previous_figure, bg=style_out['bg_color'], show_text=False, size=btn_size, animation=False)
437
+ prev_button.pack(side='left', padx=5)
438
+
439
+ next_button = spacrButton(button_frame, text="Next", command=show_next_figure, bg=style_out['bg_color'], show_text=False, size=btn_size, animation=False)
440
+ next_button.pack(side='right', padx=5)
441
+
263
442
  return canvas, canvas_widget
264
443
 
265
444
  def setup_console(vertical_container):
@@ -280,7 +459,7 @@ def setup_console(vertical_container):
280
459
 
281
460
  # Create the scrollable frame (which is a Text widget) with white text
282
461
  family = style_out['font_family']
283
- font_size = style_out['font_size'] - 2
462
+ font_size = style_out['font_size']
284
463
  font_loader = style_out['font_loader']
285
464
  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
465
 
@@ -331,9 +510,6 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
331
510
  button_section_height = size_dict['panel_height']
332
511
  button_frame = tk.Frame(horizontal_container, height=button_section_height)
333
512
 
334
- # Prevent the frame from resizing based on the child widget
335
- #button_frame.pack_propagate(False)
336
-
337
513
  horizontal_container.add(button_frame, stretch="always", sticky="nsew")
338
514
  button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
339
515
  button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
@@ -366,7 +542,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
366
542
  widgets.append(import_button)
367
543
  btn_row += 1
368
544
 
369
- # Add the progress bar under the settings category menu
545
+ # Add the batch progress bar
370
546
  progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
371
547
  progress_bar.grid(row=btn_row, column=0, columnspan=7, pady=5, padx=5, sticky='ew')
372
548
  progress_bar.set_label_position() # Set the label position after grid placement
@@ -380,7 +556,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
380
556
 
381
557
  return button_scrollable_frame, btn_col
382
558
 
383
- def setup_usage_panel(horizontal_container, btn_col):
559
+ def setup_usage_panel(horizontal_container, btn_col, uppdate_frequency):
384
560
  global usage_bars
385
561
  from .gui_elements import set_dark_style, set_element_size
386
562
 
@@ -406,7 +582,7 @@ def setup_usage_panel(horizontal_container, btn_col):
406
582
  bar['value'] = usage
407
583
 
408
584
  # 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)
585
+ parent_frame.after(uppdate_frequency, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
410
586
 
411
587
  size_dict = set_element_size()
412
588
  usage_panel_height = size_dict['panel_height']
@@ -423,7 +599,7 @@ def setup_usage_panel(horizontal_container, btn_col):
423
599
  widgets = [usage_scrollable_frame.scrollable_frame]
424
600
 
425
601
  usage_bars = []
426
- max_elements_per_column = 4
602
+ max_elements_per_column = 6
427
603
  row = 0
428
604
  col = 0
429
605
 
@@ -441,7 +617,7 @@ def setup_usage_panel(horizontal_container, btn_col):
441
617
  try:
442
618
  ram_info = psutil.virtual_memory()
443
619
  ram_label_text = f"RAM"
444
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=ram_label_text, anchor='w', style="usage.TLabel")
620
+ 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
621
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
446
622
  ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
447
623
  ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -458,7 +634,7 @@ def setup_usage_panel(horizontal_container, btn_col):
458
634
  if gpus:
459
635
  gpu = gpus[0]
460
636
  vram_label_text = f"VRAM"
461
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=vram_label_text, anchor='w', style="usage.TLabel")
637
+ 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
638
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
463
639
  vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
464
640
  vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -468,7 +644,7 @@ def setup_usage_panel(horizontal_container, btn_col):
468
644
  row += 1
469
645
 
470
646
  gpu_label_text = f"GPU"
471
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=gpu_label_text, anchor='w', style="usage.TLabel")
647
+ 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
648
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
473
649
  gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
474
650
  gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -488,7 +664,8 @@ def setup_usage_panel(horizontal_container, btn_col):
488
664
  if row > 0 and row % max_elements_per_column == 0:
489
665
  col += 1
490
666
  row = 0
491
- label = ttk.Label(usage_scrollable_frame.scrollable_frame, text=f"Core {core+1}", anchor='w', style="usage.TLabel")
667
+
668
+ 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
669
  label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
493
670
  bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
494
671
  bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
@@ -525,11 +702,10 @@ def initiate_abort():
525
702
 
526
703
  thread_control = {"run_thread": None, "stop_requested": False}
527
704
 
528
-
529
705
  def start_process(q=None, fig_queue=None, settings_type='mask'):
530
706
  global thread_control, vars_dict, parent_frame
531
707
  from .settings import check_settings, expected_types
532
- from .gui_utils import run_function_gui
708
+ from .gui_utils import run_function_gui, set_high_priority, set_cpu_affinity, initialize_cuda
533
709
 
534
710
  if q is None:
535
711
  q = Queue()
@@ -547,21 +723,32 @@ def start_process(q=None, fig_queue=None, settings_type='mask'):
547
723
  stop_requested = Value('i', 0)
548
724
  thread_control["stop_requested"] = stop_requested
549
725
 
726
+ # Initialize CUDA in the main process
727
+ initialize_cuda()
728
+
550
729
  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)
730
+ if settings_type in ['mask', 'umap', 'measure', 'simulation', 'sequencing',
731
+ 'classify', 'cellpose_dataset', 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
732
+ 'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores', 'vision_dataset']:
733
+
734
+ # Start the process
735
+ process = Process(target=run_function_gui, args=process_args)
736
+ process.start()
737
+
738
+ # Set high priority for the process
739
+ #set_high_priority(process)
740
+
741
+ # Set CPU affinity if necessary
742
+ set_cpu_affinity(process)
743
+
744
+ # Store the process in thread_control for future reference
745
+ thread_control["run_thread"] = process
558
746
  else:
559
747
  q.put(f"Error: Unknown settings type '{settings_type}'")
560
748
  return
561
- thread_control["run_thread"].start()
562
749
 
563
750
  def process_console_queue():
564
- global q, console_output, parent_frame, progress_bar
751
+ global q, console_output, parent_frame, progress_bar, process_console_queue
565
752
 
566
753
  # Initialize function attribute if it doesn't exist
567
754
  if not hasattr(process_console_queue, "completed_tasks"):
@@ -574,21 +761,21 @@ def process_console_queue():
574
761
  while not q.empty():
575
762
  message = q.get_nowait()
576
763
  clean_message = ansi_escape_pattern.sub('', message)
577
- console_output.insert(tk.END, clean_message + "\n")
578
- console_output.see(tk.END)
764
+ #console_output.insert(tk.END, clean_message + "\n")
765
+ #console_output.see(tk.END)
579
766
 
580
767
  # Check if the message contains progress information
581
768
  if clean_message.startswith("Progress:"):
582
769
  try:
583
770
  # Extract the progress information
584
- match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*)(.*)', clean_message)
771
+ match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*),(.*)', clean_message)
585
772
 
586
773
  if match:
587
774
  current_progress = int(match.group(1))
588
775
  total_progress = int(match.group(2))
589
776
  operation_type = match.group(3).strip()
590
- time_info = match.group(4).strip()
591
-
777
+ additional_info = match.group(4).strip() # Capture everything after operation_type
778
+
592
779
  # Check if the maximum value has changed
593
780
  if process_console_queue.current_maximum != total_progress:
594
781
  process_console_queue.current_maximum = total_progress
@@ -599,55 +786,77 @@ def process_console_queue():
599
786
 
600
787
  # Calculate the unique progress count
601
788
  unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
602
-
789
+
603
790
  # Update the progress bar
604
791
  if progress_bar:
605
792
  progress_bar['maximum'] = total_progress
606
793
  progress_bar['value'] = unique_progress_count
607
794
 
608
- # Extract and update additional information
795
+ # Store operation type and additional info
609
796
  if operation_type:
610
797
  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))
798
+ progress_bar.additional_info = additional_info
623
799
 
624
800
  # Update the progress label
625
801
  if progress_bar.progress_label:
626
802
  progress_bar.update_label()
627
-
803
+
628
804
  # Clear completed tasks when progress is complete
629
805
  if unique_progress_count >= total_progress:
630
806
  process_console_queue.completed_tasks.clear()
631
807
 
632
808
  except Exception as e:
633
809
  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)
810
+ else:
811
+ # Only insert messages that do not start with "Progress:"
812
+ console_output.insert(tk.END, clean_message + "\n")
813
+ console_output.see(tk.END)
638
814
 
639
- after_id = console_output.after(1, process_console_queue)
815
+ after_id = console_output.after(uppdate_frequency, process_console_queue)
640
816
  parent_frame.after_tasks.append(after_id)
641
817
 
818
+ def main_thread_update_function(root, q, fig_queue, canvas_widget):
819
+ global uppdate_frequency
820
+ try:
821
+ while not q.empty():
822
+ message = q.get_nowait()
823
+ except Exception as e:
824
+ print(f"Error updating GUI canvas: {e}")
825
+ finally:
826
+ root.after(uppdate_frequency, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget))
642
827
 
643
828
  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
829
+ """
830
+ Initializes the root window and sets up the GUI components based on the specified settings type.
831
+
832
+ Args:
833
+ parent (tkinter.Tk or tkinter.Toplevel): The parent window for the GUI.
834
+ settings_type (str, optional): The type of settings to be displayed in the GUI. Defaults to 'mask'.
835
+
836
+ Returns:
837
+ tuple: A tuple containing the parent frame and the dictionary of variables used in the GUI.
838
+ """
839
+
840
+ 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
841
+
842
+ from .gui_utils import setup_frame
646
843
  from .settings import descriptions
647
844
 
845
+ uppdate_frequency = 1000
846
+
847
+ # Start tracemalloc and initialize global variables
848
+ tracemalloc.start()
849
+
648
850
  set_start_method('spawn', force=True)
851
+ #set_start_method('forkserver', force=True)
649
852
  print("Initializing root with settings_type:", settings_type)
650
853
 
854
+ # Initialize global variables
855
+ figures = deque()
856
+ figure_index = -1
857
+ fig_memory_limit = 200 * 1024 * 1024 # 200 MB limit
858
+ figure_current_memory_usage = 0
859
+
651
860
  parent_frame = parent
652
861
 
653
862
  if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
@@ -676,9 +885,9 @@ def initiate_root(parent, settings_type='mask'):
676
885
  canvas, canvas_widget = setup_plot_section(vertical_container)
677
886
  console_output, _ = setup_console(vertical_container)
678
887
  button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
679
- _, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col)
888
+ _, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col, uppdate_frequency)
680
889
 
681
- set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar, usage_bars)
890
+ 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
891
  description_text = descriptions.get(settings_type, "No description available for this module.")
683
892
 
684
893
  q.put(f"Console")
@@ -687,7 +896,7 @@ def initiate_root(parent, settings_type='mask'):
687
896
 
688
897
  process_console_queue()
689
898
  process_fig_queue()
690
- after_id = parent_window.after(100, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
899
+ after_id = parent_window.after(uppdate_frequency, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
691
900
  parent_window.after_tasks.append(after_id)
692
901
 
693
902
  print("Root initialization complete")