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/__init__.py +1 -3
- spacr/core.py +135 -472
- spacr/deep_spacr.py +189 -270
- spacr/gui_core.py +294 -86
- spacr/gui_elements.py +335 -82
- spacr/gui_utils.py +100 -49
- spacr/io.py +104 -41
- spacr/plot.py +51 -5
- spacr/sequencing.py +4 -8
- spacr/settings.py +27 -31
- spacr/utils.py +15 -14
- {spacr-0.2.56.dist-info → spacr-0.2.65.dist-info}/METADATA +1 -1
- {spacr-0.2.56.dist-info → spacr-0.2.65.dist-info}/RECORD +17 -17
- {spacr-0.2.56.dist-info → spacr-0.2.65.dist-info}/LICENSE +0 -0
- {spacr-0.2.56.dist-info → spacr-0.2.65.dist-info}/WHEEL +0 -0
- {spacr-0.2.56.dist-info → spacr-0.2.65.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.56.dist-info → spacr-0.2.65.dist-info}/top_level.txt +0 -0
spacr/gui_core.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import traceback, ctypes, csv, re
|
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 .
|
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
|
-
|
89
|
-
|
90
|
-
|
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
|
-
#
|
95
|
-
fig
|
96
|
-
fig.set_facecolor('black')
|
95
|
+
# Standardize the figure appearance before adding it to the list
|
96
|
+
standardize_figure(fig)
|
97
97
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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(
|
105
|
+
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
106
106
|
parent_frame.after_tasks.append(after_id)
|
107
107
|
|
108
|
-
def
|
109
|
-
global
|
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
|
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([], [])
|
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")
|
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']
|
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
|
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(
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
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(
|
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
|
-
|
645
|
-
|
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(
|
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")
|