spacr 0.2.53__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.py CHANGED
@@ -27,7 +27,7 @@ class MainApp(tk.Tk):
27
27
  }
28
28
 
29
29
  self.additional_gui_apps = {
30
- "Sequencing": (lambda frame: initiate_root(self, 'sequencing'), "Analyze sequencing data."),
30
+ #"Sequencing": (lambda frame: initiate_root(self, 'sequencing'), "Analyze sequencing data."),
31
31
  "Umap": (lambda frame: initiate_root(self, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
32
32
  "Train Cellpose": (lambda frame: initiate_root(self, 'train_cellpose'), "Train custom Cellpose models."),
33
33
  "ML Analyze": (lambda frame: initiate_root(self, 'ml_analyze'), "Machine learning analysis of data."),
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, get_analyze_reads_default_settings, 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
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 = {}
@@ -158,9 +315,11 @@ def import_settings(settings_type='mask'):
158
315
  elif settings_type == 'classify':
159
316
  settings = set_default_train_test_model(settings={})
160
317
  elif settings_type == 'sequencing':
161
- settings = get_analyze_reads_default_settings(settings={})
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
 
@@ -171,7 +330,7 @@ def import_settings(settings_type='mask'):
171
330
 
172
331
  def setup_settings_panel(vertical_container, settings_type='mask'):
173
332
  global vars_dict, scrollable_frame
174
- from .settings import get_identify_masks_finetune_default_settings, set_default_analyze_screen, 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, get_perform_regression_default_settings, get_train_cellpose_default_settings, get_map_barcodes_default_settings, get_analyze_recruitment_default_settings, get_check_cellpose_models_default_settings
333
+ from .settings import get_identify_masks_finetune_default_settings, set_default_analyze_screen, set_default_settings_preprocess_generate_masks, get_measure_crop_settings, deep_spacr_defaults, set_default_generate_barecode_mapping, set_default_umap_image_settings, generate_fields, get_perform_regression_default_settings, get_train_cellpose_default_settings, get_map_barcodes_default_settings, get_analyze_recruitment_default_settings, get_check_cellpose_models_default_settings
175
334
  from .gui_utils import convert_settings_dict_for_gui
176
335
  from .gui_elements import set_element_size
177
336
 
@@ -197,9 +356,7 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
197
356
  elif settings_type == 'measure':
198
357
  settings = get_measure_crop_settings(settings={})
199
358
  elif settings_type == 'classify':
200
- settings = set_default_train_test_model(settings={})
201
- elif settings_type == 'sequencing':
202
- settings = get_analyze_reads_default_settings(settings={})
359
+ settings = deep_spacr_defaults(settings={})
203
360
  elif settings_type == 'umap':
204
361
  settings = set_default_umap_image_settings(settings={})
205
362
  elif settings_type == 'train_cellpose':
@@ -211,7 +368,7 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
211
368
  elif settings_type == 'cellpose_all':
212
369
  settings = get_check_cellpose_models_default_settings(settings={})
213
370
  elif settings_type == 'map_barcodes':
214
- settings = get_map_barcodes_default_settings(settings={})
371
+ settings = set_default_generate_barecode_mapping(settings={})
215
372
  elif settings_type == 'regression':
216
373
  settings = get_perform_regression_default_settings(settings={})
217
374
  elif settings_type == 'recruitment':
@@ -232,21 +389,28 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
232
389
  return scrollable_frame, vars_dict
233
390
 
234
391
  def setup_plot_section(vertical_container):
235
- 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
236
399
 
237
400
  # Create a frame for the plot section
238
- plot_frame = tk.Frame(vertical_container, bg='lightgrey')
401
+ plot_frame = tk.Frame(vertical_container)
239
402
  vertical_container.add(plot_frame, stretch="always")
240
403
 
241
404
  # Set up the plot
242
405
  figure = Figure(figsize=(30, 4), dpi=100)
243
406
  plot = figure.add_subplot(111)
244
- plot.plot([], []) # This creates an empty plot.
407
+ plot.plot([], [])
245
408
  plot.axis('off')
409
+
246
410
  canvas = FigureCanvasTkAgg(figure, master=plot_frame)
247
411
  canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
248
412
  canvas_widget = canvas.get_tk_widget()
249
- 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")
250
414
 
251
415
  plot_frame.grid_rowconfigure(0, weight=1)
252
416
  plot_frame.grid_columnconfigure(0, weight=1)
@@ -262,6 +426,19 @@ def setup_plot_section(vertical_container):
262
426
  style = ttk.Style(vertical_container)
263
427
  _ = set_dark_style(style, containers=containers, widgets=widgets)
264
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
+
265
442
  return canvas, canvas_widget
266
443
 
267
444
  def setup_console(vertical_container):
@@ -282,7 +459,7 @@ def setup_console(vertical_container):
282
459
 
283
460
  # Create the scrollable frame (which is a Text widget) with white text
284
461
  family = style_out['font_family']
285
- font_size = style_out['font_size'] - 2
462
+ font_size = style_out['font_size']
286
463
  font_loader = style_out['font_loader']
287
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)
288
465
 
@@ -333,9 +510,6 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
333
510
  button_section_height = size_dict['panel_height']
334
511
  button_frame = tk.Frame(horizontal_container, height=button_section_height)
335
512
 
336
- # Prevent the frame from resizing based on the child widget
337
- #button_frame.pack_propagate(False)
338
-
339
513
  horizontal_container.add(button_frame, stretch="always", sticky="nsew")
340
514
  button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
341
515
  button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
@@ -350,7 +524,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
350
524
  widgets.append(run_button)
351
525
  btn_col += 1
352
526
 
353
- if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
527
+ if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap', 'map_barcodes']:
354
528
  abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=lambda: initiate_abort(), show_text=False, size=size_dict['btn_size'], animation=False)
355
529
  abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
356
530
  widgets.append(abort_button)
@@ -368,7 +542,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
368
542
  widgets.append(import_button)
369
543
  btn_row += 1
370
544
 
371
- # Add the progress bar under the settings category menu
545
+ # Add the batch progress bar
372
546
  progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
373
547
  progress_bar.grid(row=btn_row, column=0, columnspan=7, pady=5, padx=5, sticky='ew')
374
548
  progress_bar.set_label_position() # Set the label position after grid placement
@@ -382,7 +556,7 @@ def setup_button_section(horizontal_container, settings_type='mask', run=True, a
382
556
 
383
557
  return button_scrollable_frame, btn_col
384
558
 
385
- def setup_usage_panel(horizontal_container, btn_col):
559
+ def setup_usage_panel(horizontal_container, btn_col, uppdate_frequency):
386
560
  global usage_bars
387
561
  from .gui_elements import set_dark_style, set_element_size
388
562
 
@@ -408,7 +582,7 @@ def setup_usage_panel(horizontal_container, btn_col):
408
582
  bar['value'] = usage
409
583
 
410
584
  # Schedule the function to run again after 1000 ms (1 second)
411
- 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)
412
586
 
413
587
  size_dict = set_element_size()
414
588
  usage_panel_height = size_dict['panel_height']
@@ -425,7 +599,7 @@ def setup_usage_panel(horizontal_container, btn_col):
425
599
  widgets = [usage_scrollable_frame.scrollable_frame]
426
600
 
427
601
  usage_bars = []
428
- max_elements_per_column = 4
602
+ max_elements_per_column = 6
429
603
  row = 0
430
604
  col = 0
431
605
 
@@ -443,7 +617,7 @@ def setup_usage_panel(horizontal_container, btn_col):
443
617
  try:
444
618
  ram_info = psutil.virtual_memory()
445
619
  ram_label_text = f"RAM"
446
- 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'])
447
621
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
448
622
  ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
449
623
  ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -460,7 +634,7 @@ def setup_usage_panel(horizontal_container, btn_col):
460
634
  if gpus:
461
635
  gpu = gpus[0]
462
636
  vram_label_text = f"VRAM"
463
- 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'])
464
638
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
465
639
  vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
466
640
  vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -470,7 +644,7 @@ def setup_usage_panel(horizontal_container, btn_col):
470
644
  row += 1
471
645
 
472
646
  gpu_label_text = f"GPU"
473
- 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'])
474
648
  label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
475
649
  gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
476
650
  gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
@@ -490,7 +664,8 @@ def setup_usage_panel(horizontal_container, btn_col):
490
664
  if row > 0 and row % max_elements_per_column == 0:
491
665
  col += 1
492
666
  row = 0
493
- 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'])
494
669
  label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
495
670
  bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
496
671
  bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
@@ -527,11 +702,10 @@ def initiate_abort():
527
702
 
528
703
  thread_control = {"run_thread": None, "stop_requested": False}
529
704
 
530
-
531
705
  def start_process(q=None, fig_queue=None, settings_type='mask'):
532
706
  global thread_control, vars_dict, parent_frame
533
707
  from .settings import check_settings, expected_types
534
- from .gui_utils import run_function_gui
708
+ from .gui_utils import run_function_gui, set_high_priority, set_cpu_affinity, initialize_cuda
535
709
 
536
710
  if q is None:
537
711
  q = Queue()
@@ -549,21 +723,32 @@ def start_process(q=None, fig_queue=None, settings_type='mask'):
549
723
  stop_requested = Value('i', 0)
550
724
  thread_control["stop_requested"] = stop_requested
551
725
 
726
+ # Initialize CUDA in the main process
727
+ initialize_cuda()
728
+
552
729
  process_args = (settings_type, settings, q, fig_queue, stop_requested)
553
- if settings_type in [
554
- 'mask', 'measure', 'simulation', 'sequencing', 'classify', 'cellpose_dataset',
555
- 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
556
- 'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores',
557
- 'vision_dataset'
558
- ]:
559
- 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
560
746
  else:
561
747
  q.put(f"Error: Unknown settings type '{settings_type}'")
562
748
  return
563
- thread_control["run_thread"].start()
564
749
 
565
750
  def process_console_queue():
566
- global q, console_output, parent_frame, progress_bar
751
+ global q, console_output, parent_frame, progress_bar, process_console_queue
567
752
 
568
753
  # Initialize function attribute if it doesn't exist
569
754
  if not hasattr(process_console_queue, "completed_tasks"):
@@ -576,21 +761,21 @@ def process_console_queue():
576
761
  while not q.empty():
577
762
  message = q.get_nowait()
578
763
  clean_message = ansi_escape_pattern.sub('', message)
579
- console_output.insert(tk.END, clean_message + "\n")
580
- console_output.see(tk.END)
764
+ #console_output.insert(tk.END, clean_message + "\n")
765
+ #console_output.see(tk.END)
581
766
 
582
767
  # Check if the message contains progress information
583
768
  if clean_message.startswith("Progress:"):
584
769
  try:
585
770
  # Extract the progress information
586
- 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)
587
772
 
588
773
  if match:
589
774
  current_progress = int(match.group(1))
590
775
  total_progress = int(match.group(2))
591
776
  operation_type = match.group(3).strip()
592
- time_info = match.group(4).strip()
593
-
777
+ additional_info = match.group(4).strip() # Capture everything after operation_type
778
+
594
779
  # Check if the maximum value has changed
595
780
  if process_console_queue.current_maximum != total_progress:
596
781
  process_console_queue.current_maximum = total_progress
@@ -601,55 +786,77 @@ def process_console_queue():
601
786
 
602
787
  # Calculate the unique progress count
603
788
  unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
604
-
789
+
605
790
  # Update the progress bar
606
791
  if progress_bar:
607
792
  progress_bar['maximum'] = total_progress
608
793
  progress_bar['value'] = unique_progress_count
609
794
 
610
- # Extract and update additional information
795
+ # Store operation type and additional info
611
796
  if operation_type:
612
797
  progress_bar.operation_type = operation_type
613
-
614
- time_image_match = re.search(r'Time/image: ([\d.]+) sec', time_info)
615
- if time_image_match:
616
- progress_bar.time_image = float(time_image_match.group(1))
617
-
618
- time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', time_info)
619
- if time_batch_match:
620
- progress_bar.time_batch = float(time_batch_match.group(1))
621
-
622
- time_left_match = re.search(r'Time_left: ([\d.]+) min', time_info)
623
- if time_left_match:
624
- progress_bar.time_left = float(time_left_match.group(1))
798
+ progress_bar.additional_info = additional_info
625
799
 
626
800
  # Update the progress label
627
801
  if progress_bar.progress_label:
628
802
  progress_bar.update_label()
629
-
803
+
630
804
  # Clear completed tasks when progress is complete
631
805
  if unique_progress_count >= total_progress:
632
806
  process_console_queue.completed_tasks.clear()
633
807
 
634
808
  except Exception as e:
635
809
  print(f"Error parsing progress message: {e}")
636
- #else:
637
- # # Only insert messages that do not start with "Progress:"
638
- # console_output.insert(tk.END, clean_message + "\n")
639
- # 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)
640
814
 
641
- after_id = console_output.after(1, process_console_queue)
815
+ after_id = console_output.after(uppdate_frequency, process_console_queue)
642
816
  parent_frame.after_tasks.append(after_id)
643
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))
644
827
 
645
828
  def initiate_root(parent, settings_type='mask'):
646
- global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
647
- 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
648
843
  from .settings import descriptions
649
844
 
845
+ uppdate_frequency = 1000
846
+
847
+ # Start tracemalloc and initialize global variables
848
+ tracemalloc.start()
849
+
650
850
  set_start_method('spawn', force=True)
851
+ #set_start_method('forkserver', force=True)
651
852
  print("Initializing root with settings_type:", settings_type)
652
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
+
653
860
  parent_frame = parent
654
861
 
655
862
  if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
@@ -678,9 +885,9 @@ def initiate_root(parent, settings_type='mask'):
678
885
  canvas, canvas_widget = setup_plot_section(vertical_container)
679
886
  console_output, _ = setup_console(vertical_container)
680
887
  button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
681
- _, 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)
682
889
 
683
- 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)
684
891
  description_text = descriptions.get(settings_type, "No description available for this module.")
685
892
 
686
893
  q.put(f"Console")
@@ -689,7 +896,7 @@ def initiate_root(parent, settings_type='mask'):
689
896
 
690
897
  process_console_queue()
691
898
  process_fig_queue()
692
- 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))
693
900
  parent_window.after_tasks.append(after_id)
694
901
 
695
902
  print("Root initialization complete")