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/core.py +135 -472
- spacr/deep_spacr.py +189 -270
- spacr/gui_core.py +296 -87
- spacr/gui_elements.py +34 -81
- spacr/gui_utils.py +61 -47
- spacr/io.py +104 -41
- spacr/plot.py +47 -1
- spacr/settings.py +27 -31
- spacr/utils.py +14 -13
- {spacr-0.2.56.dist-info → spacr-0.2.61.dist-info}/METADATA +1 -1
- {spacr-0.2.56.dist-info → spacr-0.2.61.dist-info}/RECORD +15 -15
- {spacr-0.2.56.dist-info → spacr-0.2.61.dist-info}/LICENSE +0 -0
- {spacr-0.2.56.dist-info → spacr-0.2.61.dist-info}/WHEEL +0 -0
- {spacr-0.2.56.dist-info → spacr-0.2.61.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.56.dist-info → spacr-0.2.61.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,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
|
-
|
89
|
-
|
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(
|
95
|
+
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
106
96
|
parent_frame.after_tasks.append(after_id)
|
107
97
|
|
108
|
-
def
|
109
|
-
global
|
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
|
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([], [])
|
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")
|
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']
|
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
|
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(
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
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(
|
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
|
-
|
645
|
-
|
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(
|
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")
|