spacr 0.2.4__py3-none-any.whl → 0.2.8__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 -11
- spacr/core.py +277 -349
- spacr/deep_spacr.py +248 -269
- spacr/gui.py +58 -54
- spacr/gui_core.py +689 -535
- spacr/gui_elements.py +1002 -153
- spacr/gui_utils.py +452 -107
- spacr/io.py +158 -91
- spacr/measure.py +199 -151
- spacr/plot.py +159 -47
- spacr/resources/font/open_sans/OFL.txt +93 -0
- spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
- spacr/resources/font/open_sans/README.txt +100 -0
- spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
- spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
- spacr/resources/icons/logo.pdf +2786 -6
- spacr/resources/icons/logo_spacr.png +0 -0
- spacr/resources/icons/logo_spacr_1.png +0 -0
- spacr/sequencing.py +477 -587
- spacr/settings.py +217 -144
- spacr/utils.py +46 -46
- {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/METADATA +46 -35
- spacr-0.2.8.dist-info/RECORD +100 -0
- {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/WHEEL +1 -1
- spacr-0.2.4.dist-info/RECORD +0 -58
- {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/LICENSE +0 -0
- {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/entry_points.txt +0 -0
- {spacr-0.2.4.dist-info → spacr-0.2.8.dist-info}/top_level.txt +0 -0
spacr/gui_core.py
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
-
import
|
2
|
-
import matplotlib.pyplot as plt
|
3
|
-
matplotlib.use('Agg')
|
1
|
+
import traceback, ctypes, csv, re, platform, time
|
4
2
|
import tkinter as tk
|
5
3
|
from tkinter import ttk
|
6
4
|
from tkinter import filedialog
|
7
5
|
from multiprocessing import Process, Value, Queue, set_start_method
|
8
|
-
from multiprocessing.sharedctypes import Synchronized
|
9
6
|
from tkinter import ttk, scrolledtext
|
10
7
|
from matplotlib.figure import Figure
|
11
8
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
12
|
-
from huggingface_hub import list_repo_files
|
13
9
|
import numpy as np
|
10
|
+
import psutil
|
11
|
+
import GPUtil
|
12
|
+
from collections import deque
|
13
|
+
import tracemalloc
|
14
|
+
from tkinter import Menu
|
15
|
+
import io
|
16
|
+
|
14
17
|
try:
|
15
18
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
16
19
|
except AttributeError:
|
17
20
|
pass
|
18
21
|
|
19
|
-
from .
|
20
|
-
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu ,set_dark_style, set_default_font
|
22
|
+
from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , spacrSlider, set_dark_style, standardize_figure
|
21
23
|
|
22
24
|
# Define global variables
|
23
25
|
q = None
|
@@ -29,148 +31,375 @@ canvas_widget = None
|
|
29
31
|
scrollable_frame = None
|
30
32
|
progress_label = None
|
31
33
|
fig_queue = None
|
34
|
+
figures = None
|
35
|
+
figure_index = None
|
36
|
+
progress_bar = None
|
37
|
+
usage_bars = None
|
32
38
|
|
33
39
|
thread_control = {"run_thread": None, "stop_requested": False}
|
34
40
|
|
35
|
-
def
|
36
|
-
global
|
37
|
-
|
38
|
-
|
39
|
-
if
|
40
|
-
|
41
|
-
thread_control["run_thread"].join()
|
42
|
-
thread_control["run_thread"] = None
|
41
|
+
def toggle_settings(button_scrollable_frame):
|
42
|
+
global vars_dict
|
43
|
+
from .settings import categories
|
44
|
+
from .gui_utils import hide_all_settings
|
45
|
+
if vars_dict is None:
|
46
|
+
raise ValueError("vars_dict is not initialized.")
|
43
47
|
|
44
|
-
|
45
|
-
"""
|
46
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
47
|
-
"""
|
48
|
-
fig = plt.gcf()
|
49
|
-
if fig_queue:
|
50
|
-
fig_queue.put(fig)
|
51
|
-
else:
|
52
|
-
fig.show()
|
53
|
-
plt.close(fig)
|
48
|
+
active_categories = set()
|
54
49
|
|
55
|
-
def
|
50
|
+
def toggle_category(settings):
|
51
|
+
for setting in settings:
|
52
|
+
if setting in vars_dict:
|
53
|
+
label, widget, _, frame = vars_dict[setting]
|
54
|
+
if widget.grid_info():
|
55
|
+
label.grid_remove()
|
56
|
+
widget.grid_remove()
|
57
|
+
frame.grid_remove()
|
58
|
+
else:
|
59
|
+
label.grid()
|
60
|
+
widget.grid()
|
61
|
+
frame.grid()
|
56
62
|
|
57
|
-
|
58
|
-
|
63
|
+
def on_category_select(selected_category):
|
64
|
+
if selected_category == "Select Category":
|
65
|
+
return
|
66
|
+
if selected_category in categories:
|
67
|
+
toggle_category(categories[selected_category])
|
68
|
+
if selected_category in active_categories:
|
69
|
+
active_categories.remove(selected_category)
|
70
|
+
else:
|
71
|
+
active_categories.add(selected_category)
|
72
|
+
category_dropdown.update_styles(active_categories)
|
73
|
+
category_var.set("Select Category")
|
74
|
+
|
75
|
+
category_var = tk.StringVar()
|
76
|
+
non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
|
77
|
+
category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
|
78
|
+
category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
|
79
|
+
vars_dict = hide_all_settings(vars_dict, categories)
|
80
|
+
|
81
|
+
def display_figure(fig):
|
82
|
+
global canvas, canvas_widget
|
83
|
+
|
84
|
+
from .gui_elements import save_figure_as_format, modify_figure
|
85
|
+
|
86
|
+
# Apply the dark style to the context menu
|
87
|
+
style_out = set_dark_style(ttk.Style())
|
88
|
+
bg_color = style_out['bg_color']
|
89
|
+
fg_color = style_out['fg_color']
|
90
|
+
|
91
|
+
# Initialize the scale factor for zooming
|
92
|
+
scale_factor = 1.0
|
93
|
+
|
94
|
+
# Save the original x and y limits of the first axis (assuming all axes have the same limits)
|
95
|
+
original_xlim = [ax.get_xlim() for ax in fig.get_axes()]
|
96
|
+
original_ylim = [ax.get_ylim() for ax in fig.get_axes()]
|
97
|
+
|
98
|
+
# Clear previous canvas content
|
99
|
+
if canvas:
|
100
|
+
canvas.get_tk_widget().destroy()
|
101
|
+
|
102
|
+
# Create a new canvas for the figure
|
103
|
+
new_canvas = FigureCanvasTkAgg(fig, master=canvas_widget.master)
|
104
|
+
new_canvas.draw()
|
105
|
+
new_canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
|
106
|
+
|
107
|
+
# Update the global canvas and canvas_widget references
|
108
|
+
canvas = new_canvas
|
109
|
+
canvas_widget = new_canvas.get_tk_widget()
|
110
|
+
canvas_widget.configure(bg=bg_color)
|
111
|
+
|
112
|
+
# Create the context menu
|
113
|
+
context_menu = tk.Menu(canvas_widget, tearoff=0, bg=bg_color, fg=fg_color)
|
114
|
+
context_menu.add_command(label="Save Figure as PDF", command=lambda: save_figure_as_format(fig, 'pdf'))
|
115
|
+
context_menu.add_command(label="Save Figure as PNG", command=lambda: save_figure_as_format(fig, 'png'))
|
116
|
+
context_menu.add_command(label="Modify Figure", command=lambda: modify_figure(fig))
|
117
|
+
context_menu.add_command(label="Reset Zoom", command=lambda: reset_zoom(fig)) # Add Reset Zoom option
|
118
|
+
|
119
|
+
def reset_zoom(fig):
|
120
|
+
global scale_factor
|
121
|
+
scale_factor = 1.0 # Reset the scale factor
|
122
|
+
|
123
|
+
for i, ax in enumerate(fig.get_axes()):
|
124
|
+
ax.set_xlim(original_xlim[i])
|
125
|
+
ax.set_ylim(original_ylim[i])
|
126
|
+
fig.canvas.draw_idle()
|
127
|
+
|
128
|
+
def on_right_click(event):
|
129
|
+
context_menu.post(event.x_root, event.y_root)
|
130
|
+
|
131
|
+
def on_hover(event):
|
132
|
+
widget_width = event.widget.winfo_width()
|
133
|
+
x_position = event.x
|
134
|
+
|
135
|
+
if x_position < widget_width / 2:
|
136
|
+
canvas_widget.config(cursor="hand2")
|
137
|
+
else:
|
138
|
+
canvas_widget.config(cursor="hand2")
|
139
|
+
|
140
|
+
def on_leave(event):
|
141
|
+
canvas_widget.config(cursor="arrow")
|
142
|
+
|
143
|
+
def flash_feedback(side):
|
144
|
+
flash = tk.Toplevel(canvas_widget.master)
|
145
|
+
flash.overrideredirect(True)
|
146
|
+
flash_width = int(canvas_widget.winfo_width() / 2)
|
147
|
+
flash_height = canvas_widget.winfo_height()
|
148
|
+
flash.configure(bg='white')
|
149
|
+
flash.attributes('-alpha', 0.9)
|
150
|
+
|
151
|
+
if side == "left":
|
152
|
+
flash.geometry(f"{flash_width}x{flash_height}+{canvas_widget.winfo_rootx()}+{canvas_widget.winfo_rooty()}")
|
153
|
+
else:
|
154
|
+
flash.geometry(f"{flash_width}x{flash_height}+{canvas_widget.winfo_rootx() + flash_width}+{canvas_widget.winfo_rooty()}")
|
155
|
+
|
156
|
+
flash.lift()
|
157
|
+
|
158
|
+
# Ensure the flash covers the correct area only
|
159
|
+
flash.update_idletasks()
|
160
|
+
flash.after(100, flash.destroy)
|
161
|
+
|
162
|
+
def on_click(event):
|
163
|
+
widget_width = event.widget.winfo_width()
|
164
|
+
x_position = event.x
|
165
|
+
|
166
|
+
if x_position < widget_width / 2:
|
167
|
+
#flash_feedback("left")
|
168
|
+
show_previous_figure()
|
169
|
+
else:
|
170
|
+
#flash_feedback("right")
|
171
|
+
show_next_figure()
|
172
|
+
|
173
|
+
def zoom_v1(event):
|
174
|
+
nonlocal scale_factor
|
175
|
+
|
176
|
+
zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
|
177
|
+
|
178
|
+
# Adjust zoom factor based on the operating system and mouse event
|
179
|
+
if event.num == 4 or event.delta > 0: # Scroll up
|
180
|
+
scale_factor *= (1 + zoom_speed)
|
181
|
+
elif event.num == 5 or event.delta < 0: # Scroll down
|
182
|
+
scale_factor /= (1 + zoom_speed)
|
183
|
+
|
184
|
+
# Get mouse position relative to the figure
|
185
|
+
x_mouse, y_mouse = event.x, event.y
|
186
|
+
x_ratio = x_mouse / canvas_widget.winfo_width()
|
187
|
+
y_ratio = y_mouse / canvas_widget.winfo_height()
|
188
|
+
|
189
|
+
for ax in fig.get_axes():
|
190
|
+
xlim = ax.get_xlim()
|
191
|
+
ylim = ax.get_ylim()
|
192
|
+
|
193
|
+
# Calculate the new limits
|
194
|
+
x_center = xlim[0] + x_ratio * (xlim[1] - xlim[0])
|
195
|
+
y_center = ylim[0] + (1 - y_ratio) * (ylim[1] - ylim[0])
|
196
|
+
|
197
|
+
x_range = (xlim[1] - xlim[0]) * scale_factor
|
198
|
+
y_range = (ylim[1] - ylim[0]) * scale_factor
|
199
|
+
|
200
|
+
ax.set_xlim([x_center - x_range * x_ratio, x_center + x_range * (1 - x_ratio)])
|
201
|
+
ax.set_ylim([y_center - y_range * (1 - y_ratio), y_center + y_range * y_ratio])
|
202
|
+
|
203
|
+
# Redraw the figure
|
204
|
+
fig.canvas.draw_idle()
|
205
|
+
|
206
|
+
def zoom(event):
|
207
|
+
nonlocal scale_factor
|
208
|
+
|
209
|
+
zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
|
210
|
+
|
211
|
+
# Determine the zoom direction based on the scroll event
|
212
|
+
if event.num == 4 or event.delta > 0: # Scroll up (zoom in)
|
213
|
+
scale_factor /= (1 + zoom_speed) # Divide to zoom in
|
214
|
+
elif event.num == 5 or event.delta < 0: # Scroll down (zoom out)
|
215
|
+
scale_factor *= (1 + zoom_speed) # Multiply to zoom out
|
216
|
+
|
217
|
+
# Adjust the axes limits based on the new scale factor
|
218
|
+
for ax in canvas.figure.get_axes():
|
219
|
+
xlim = ax.get_xlim()
|
220
|
+
ylim = ax.get_ylim()
|
221
|
+
|
222
|
+
x_center = (xlim[1] + xlim[0]) / 2
|
223
|
+
y_center = (ylim[1] + ylim[0]) / 2
|
224
|
+
|
225
|
+
x_range = (xlim[1] - xlim[0]) * scale_factor
|
226
|
+
y_range = (ylim[1] - ylim[0]) * scale_factor
|
227
|
+
|
228
|
+
# Set the new limits
|
229
|
+
ax.set_xlim([x_center - x_range / 2, x_center + x_range / 2])
|
230
|
+
ax.set_ylim([y_center - y_range / 2, y_center + y_range / 2])
|
231
|
+
|
232
|
+
# Redraw the figure efficiently
|
233
|
+
canvas.draw_idle()
|
234
|
+
|
235
|
+
|
236
|
+
# Bind events for hover, click interactions, and zoom
|
237
|
+
canvas_widget.bind("<Motion>", on_hover)
|
238
|
+
canvas_widget.bind("<Leave>", on_leave)
|
239
|
+
canvas_widget.bind("<Button-1>", on_click)
|
240
|
+
canvas_widget.bind("<Button-3>", on_right_click)
|
241
|
+
|
242
|
+
|
243
|
+
# Detect the operating system and bind the appropriate mouse wheel events
|
244
|
+
current_os = platform.system()
|
245
|
+
|
246
|
+
if current_os == "Windows":
|
247
|
+
canvas_widget.bind("<MouseWheel>", zoom) # Windows
|
248
|
+
elif current_os == "Darwin": # macOS
|
249
|
+
canvas_widget.bind("<MouseWheel>", zoom)
|
250
|
+
canvas_widget.bind("<Button-4>", zoom) # Scroll up
|
251
|
+
canvas_widget.bind("<Button-5>", zoom) # Scroll down
|
252
|
+
elif current_os == "Linux":
|
253
|
+
canvas_widget.bind("<Button-4>", zoom) # Linux Scroll up
|
254
|
+
canvas_widget.bind("<Button-5>", zoom) # Linux Scroll down
|
255
|
+
|
256
|
+
def clear_unused_figures():
|
257
|
+
global figures, figure_index
|
258
|
+
|
259
|
+
lower_bound = max(0, figure_index - 20)
|
260
|
+
upper_bound = min(len(figures), figure_index + 20)
|
261
|
+
# Clear figures outside of the +/- 20 range
|
262
|
+
figures = deque([fig for i, fig in enumerate(figures) if lower_bound <= i <= upper_bound])
|
263
|
+
# Update the figure index after clearing
|
264
|
+
figure_index = min(max(figure_index, 0), len(figures) - 1)
|
265
|
+
|
266
|
+
def show_previous_figure():
|
267
|
+
global figure_index, figures, fig_queue
|
59
268
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
269
|
+
if figure_index is not None and figure_index > 0:
|
270
|
+
figure_index -= 1
|
271
|
+
display_figure(figures[figure_index])
|
272
|
+
clear_unused_figures()
|
273
|
+
|
274
|
+
def show_next_figure():
|
275
|
+
global figure_index, figures, fig_queue
|
276
|
+
if figure_index is not None and figure_index < len(figures) - 1:
|
277
|
+
figure_index += 1
|
278
|
+
display_figure(figures[figure_index])
|
279
|
+
clear_unused_figures()
|
280
|
+
elif figure_index == len(figures) - 1 and not fig_queue.empty():
|
281
|
+
fig = fig_queue.get_nowait()
|
282
|
+
figures.append(fig)
|
283
|
+
figure_index += 1
|
284
|
+
display_figure(fig)
|
65
285
|
|
66
|
-
|
67
|
-
|
68
|
-
plt.show = lambda: spacrFigShow(fig_queue)
|
286
|
+
def process_fig_queue():
|
287
|
+
global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index, index_control
|
69
288
|
|
289
|
+
from .gui_elements import standardize_figure
|
70
290
|
try:
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
291
|
+
while not fig_queue.empty():
|
292
|
+
fig = fig_queue.get_nowait()
|
293
|
+
|
294
|
+
if fig is None:
|
295
|
+
print("Warning: Retrieved a None figure from fig_queue.")
|
296
|
+
continue # Skip processing if the figure is None
|
297
|
+
|
298
|
+
# Standardize the figure appearance before adding it to the list
|
299
|
+
fig = standardize_figure(fig)
|
300
|
+
|
301
|
+
figures.append(fig)
|
302
|
+
|
303
|
+
# Update the slider range and set the value to the latest figure index
|
304
|
+
index_control.set_to(len(figures) - 1)
|
305
|
+
|
306
|
+
if figure_index == -1:
|
307
|
+
figure_index += 1
|
308
|
+
display_figure(figures[figure_index])
|
309
|
+
index_control.set(figure_index)
|
310
|
+
|
75
311
|
except Exception as e:
|
76
|
-
# Send the error message to the GUI via the queue
|
77
|
-
errorMessage = f"Error during processing: {e}"
|
78
|
-
q.put(errorMessage)
|
79
312
|
traceback.print_exc()
|
80
313
|
finally:
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
from .sim import run_multiple_simulations
|
90
|
-
from .deep_spacr import train_test_model
|
91
|
-
from .sequencing import analyze_reads, map_barcodes_folder, perform_regression
|
92
|
-
process_stdout_stderr(q)
|
93
|
-
|
94
|
-
print(f'run_function_gui settings_type: {settings_type}')
|
314
|
+
after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
|
315
|
+
parent_frame.after_tasks.append(after_id)
|
316
|
+
|
317
|
+
def update_figure(value):
|
318
|
+
global figure_index, figures
|
319
|
+
|
320
|
+
# Convert the value to an integer
|
321
|
+
index = int(value)
|
95
322
|
|
96
|
-
if
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
function = measure_crop
|
101
|
-
imports = 1
|
102
|
-
elif settings_type == 'simulation':
|
103
|
-
function = run_multiple_simulations
|
104
|
-
imports = 1
|
105
|
-
elif settings_type == 'sequencing':
|
106
|
-
function = analyze_reads
|
107
|
-
imports = 1
|
108
|
-
elif settings_type == 'classify':
|
109
|
-
function = train_test_model
|
110
|
-
imports = 2
|
111
|
-
elif settings_type == 'train_cellpose':
|
112
|
-
function = train_cellpose
|
113
|
-
imports = 1
|
114
|
-
elif settings_type == 'ml_analyze':
|
115
|
-
function = generate_ml_scores
|
116
|
-
imports = 2
|
117
|
-
elif settings_type == 'cellpose_masks':
|
118
|
-
function = identify_masks_finetune
|
119
|
-
imports = 1
|
120
|
-
elif settings_type == 'cellpose_all':
|
121
|
-
function = check_cellpose_models
|
122
|
-
imports = 1
|
123
|
-
elif settings_type == 'map_barcodes':
|
124
|
-
function = map_barcodes_folder
|
125
|
-
imports = 2
|
126
|
-
elif settings_type == 'regression':
|
127
|
-
function = perform_regression
|
128
|
-
imports = 2
|
129
|
-
elif settings_type == 'recruitment':
|
130
|
-
function = analyze_recruitment
|
131
|
-
imports = 2
|
132
|
-
else:
|
133
|
-
raise ValueError(f"Invalid settings type: {settings_type}")
|
134
|
-
try:
|
135
|
-
function_gui_wrapper(function, settings, q, fig_queue, imports)
|
136
|
-
except Exception as e:
|
137
|
-
q.put(f"Error during processing: {e}")
|
138
|
-
traceback.print_exc()
|
139
|
-
finally:
|
140
|
-
stop_requested.value = 1
|
323
|
+
# Check if the index is valid
|
324
|
+
if 0 <= index < len(figures):
|
325
|
+
figure_index = index
|
326
|
+
display_figure(figures[figure_index])
|
141
327
|
|
142
|
-
|
143
|
-
|
144
|
-
|
328
|
+
# Update the index control widget's range and value
|
329
|
+
index_control.set_to(len(figures) - 1)
|
330
|
+
index_control.set(figure_index)
|
145
331
|
|
146
|
-
|
147
|
-
|
148
|
-
if fig_queue is None:
|
149
|
-
fig_queue = Queue()
|
332
|
+
def setup_plot_section(vertical_container):
|
333
|
+
global canvas, canvas_widget, figures, figure_index, index_control
|
150
334
|
|
151
|
-
|
152
|
-
|
153
|
-
except ValueError as e:
|
154
|
-
q.put(f"Error: {e}")
|
155
|
-
return
|
335
|
+
# Initialize deque for storing figures and the current index
|
336
|
+
figures = deque()
|
156
337
|
|
157
|
-
|
158
|
-
|
338
|
+
# Create a frame for the plot section
|
339
|
+
plot_frame = tk.Frame(vertical_container)
|
340
|
+
vertical_container.add(plot_frame, stretch="always")
|
341
|
+
|
342
|
+
# Set up the plot
|
343
|
+
figure = Figure(figsize=(30, 4), dpi=100)
|
344
|
+
plot = figure.add_subplot(111)
|
345
|
+
plot.plot([], [])
|
346
|
+
plot.axis('off')
|
159
347
|
|
160
|
-
|
161
|
-
|
348
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
349
|
+
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
350
|
+
canvas_widget = canvas.get_tk_widget()
|
351
|
+
canvas_widget.grid(row=0, column=0, sticky="nsew")
|
162
352
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
353
|
+
plot_frame.grid_rowconfigure(0, weight=1)
|
354
|
+
plot_frame.grid_columnconfigure(0, weight=1)
|
355
|
+
|
356
|
+
canvas.draw()
|
357
|
+
canvas.figure = figure # Ensure that the figure is linked to the canvas
|
358
|
+
style_out = set_dark_style(ttk.Style())
|
359
|
+
bg = style_out['bg_color']
|
360
|
+
fg = style_out['fg_color']
|
361
|
+
|
362
|
+
figure.patch.set_facecolor(bg)
|
363
|
+
plot.set_facecolor(bg)
|
364
|
+
containers = [plot_frame]
|
365
|
+
|
366
|
+
# Create slider
|
367
|
+
control_frame = tk.Frame(plot_frame, height=15*2, bg=bg) # Fixed height based on knob_radius
|
368
|
+
control_frame.grid(row=1, column=0, sticky="ew", padx=10, pady=5)
|
369
|
+
control_frame.grid_propagate(False) # Prevent the frame from resizing
|
370
|
+
|
371
|
+
# Pass the update_figure function as the command to spacrSlider
|
372
|
+
index_control = spacrSlider(control_frame, from_=0, to=0, value=0, thickness=2, knob_radius=10, position="center", show_index=True, command=update_figure)
|
373
|
+
index_control.grid(row=0, column=0, sticky="ew")
|
374
|
+
control_frame.grid_columnconfigure(0, weight=1)
|
375
|
+
|
376
|
+
widgets = [canvas_widget, index_control]
|
377
|
+
style = ttk.Style(vertical_container)
|
378
|
+
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
379
|
+
|
380
|
+
return canvas, canvas_widget
|
381
|
+
|
382
|
+
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, index_control_var, progress_bar_var, usage_bars_var):
|
383
|
+
global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, index_control
|
384
|
+
thread_control = thread_control_var
|
385
|
+
q = q_var
|
386
|
+
console_output = console_output_var
|
387
|
+
parent_frame = parent_frame_var
|
388
|
+
vars_dict = vars_dict_var
|
389
|
+
canvas = canvas_var
|
390
|
+
canvas_widget = canvas_widget_var
|
391
|
+
scrollable_frame = scrollable_frame_var
|
392
|
+
fig_queue = fig_queue_var
|
393
|
+
figures = figures_var
|
394
|
+
figure_index = figure_index_var
|
395
|
+
progress_bar = progress_bar_var
|
396
|
+
usage_bars = usage_bars_var
|
397
|
+
index_control = index_control_var
|
170
398
|
|
171
399
|
def import_settings(settings_type='mask'):
|
400
|
+
from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
|
172
401
|
global vars_dict, scrollable_frame, button_scrollable_frame
|
173
|
-
from .settings import generate_fields
|
402
|
+
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
|
174
403
|
|
175
404
|
def read_settings_from_csv(csv_file_path):
|
176
405
|
settings = {}
|
@@ -206,9 +435,11 @@ def import_settings(settings_type='mask'):
|
|
206
435
|
elif settings_type == 'classify':
|
207
436
|
settings = set_default_train_test_model(settings={})
|
208
437
|
elif settings_type == 'sequencing':
|
209
|
-
settings =
|
438
|
+
settings = set_default_generate_barecode_mapping(settings={})
|
210
439
|
elif settings_type == 'umap':
|
211
440
|
settings = set_default_umap_image_settings(settings={})
|
441
|
+
elif settings_type == 'recruitment':
|
442
|
+
settings = get_analyze_recruitment_default_settings(settings={})
|
212
443
|
else:
|
213
444
|
raise ValueError(f"Invalid settings type: {settings_type}")
|
214
445
|
|
@@ -217,74 +448,24 @@ def import_settings(settings_type='mask'):
|
|
217
448
|
vars_dict = generate_fields(new_settings, scrollable_frame)
|
218
449
|
vars_dict = hide_all_settings(vars_dict, categories=None)
|
219
450
|
|
220
|
-
def
|
221
|
-
from torchvision import models as torch_models
|
222
|
-
torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
|
223
|
-
chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
|
224
|
-
chans_v2 = [0, 1, 2, 3, None]
|
225
|
-
variables = {}
|
226
|
-
special_cases = {
|
227
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
228
|
-
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
229
|
-
'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
230
|
-
'cell_mask_dim': ('combo', chans, None),
|
231
|
-
'cell_chann_dim': ('combo', chans, None),
|
232
|
-
'nucleus_mask_dim': ('combo', chans, None),
|
233
|
-
'nucleus_chann_dim': ('combo', chans, None),
|
234
|
-
'pathogen_mask_dim': ('combo', chans, None),
|
235
|
-
'pathogen_chann_dim': ('combo', chans, None),
|
236
|
-
'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
237
|
-
'magnification': ('combo', [20, 40, 60], 20),
|
238
|
-
'nucleus_channel': ('combo', chans_v2, None),
|
239
|
-
'cell_channel': ('combo', chans_v2, None),
|
240
|
-
'channel_of_interest': ('combo', chans_v2, None),
|
241
|
-
'pathogen_channel': ('combo', chans_v2, None),
|
242
|
-
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
243
|
-
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
244
|
-
'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
|
245
|
-
'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
|
246
|
-
'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
|
247
|
-
'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
|
248
|
-
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
249
|
-
'model_type': ('combo', torchvision_models, 'resnet50'),
|
250
|
-
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
251
|
-
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
252
|
-
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
253
|
-
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
254
|
-
'agg_type': ('combo', ['mean', 'median'], 'mean'),
|
255
|
-
'grouping': ('combo', ['mean', 'median'], 'mean'),
|
256
|
-
'min_max': ('combo', ['allq', 'all'], 'allq'),
|
257
|
-
'transform': ('combo', ['log', 'sqrt', 'square', None], None)
|
258
|
-
}
|
259
|
-
|
260
|
-
for key, value in settings.items():
|
261
|
-
if key in special_cases:
|
262
|
-
variables[key] = special_cases[key]
|
263
|
-
elif isinstance(value, bool):
|
264
|
-
variables[key] = ('check', None, value)
|
265
|
-
elif isinstance(value, int) or isinstance(value, float):
|
266
|
-
variables[key] = ('entry', None, value)
|
267
|
-
elif isinstance(value, str):
|
268
|
-
variables[key] = ('entry', None, value)
|
269
|
-
elif value is None:
|
270
|
-
variables[key] = ('entry', None, value)
|
271
|
-
elif isinstance(value, list):
|
272
|
-
variables[key] = ('entry', None, str(value))
|
273
|
-
else:
|
274
|
-
variables[key] = ('entry', None, str(value))
|
275
|
-
return variables
|
276
|
-
|
277
|
-
def setup_settings_panel(vertical_container, settings_type='mask', window_dimensions=[500, 1000]):
|
451
|
+
def setup_settings_panel(vertical_container, settings_type='mask'):
|
278
452
|
global vars_dict, scrollable_frame
|
279
|
-
from .settings import get_identify_masks_finetune_default_settings, set_default_analyze_screen, set_default_settings_preprocess_generate_masks, get_measure_crop_settings,
|
453
|
+
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
|
454
|
+
from .gui_utils import convert_settings_dict_for_gui
|
455
|
+
from .gui_elements import set_element_size
|
456
|
+
|
457
|
+
size_dict = set_element_size()
|
458
|
+
settings_width = size_dict['settings_width']
|
459
|
+
|
460
|
+
# Create a PanedWindow for the settings panel
|
461
|
+
settings_paned_window = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, width=size_dict['settings_width'])
|
462
|
+
vertical_container.add(settings_paned_window, stretch="always")
|
280
463
|
|
281
|
-
|
282
|
-
|
464
|
+
settings_frame = tk.Frame(settings_paned_window, width=settings_width)
|
465
|
+
settings_frame.pack_propagate(False) # Prevent the frame from resizing based on its children
|
466
|
+
|
467
|
+
settings_paned_window.add(settings_frame)
|
283
468
|
|
284
|
-
settings_frame = tk.Frame(vertical_container)
|
285
|
-
vertical_container.add(settings_frame, stretch="always")
|
286
|
-
settings_label = spacrLabel(settings_frame, text="Settings", anchor='center', justify='center', align="center")
|
287
|
-
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
288
469
|
scrollable_frame = spacrFrame(settings_frame)
|
289
470
|
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
290
471
|
settings_frame.grid_rowconfigure(1, weight=1)
|
@@ -295,9 +476,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
295
476
|
elif settings_type == 'measure':
|
296
477
|
settings = get_measure_crop_settings(settings={})
|
297
478
|
elif settings_type == 'classify':
|
298
|
-
settings =
|
299
|
-
elif settings_type == 'sequencing':
|
300
|
-
settings = get_analyze_reads_default_settings(settings={})
|
479
|
+
settings = deep_spacr_defaults(settings={})
|
301
480
|
elif settings_type == 'umap':
|
302
481
|
settings = set_default_umap_image_settings(settings={})
|
303
482
|
elif settings_type == 'train_cellpose':
|
@@ -309,7 +488,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
309
488
|
elif settings_type == 'cellpose_all':
|
310
489
|
settings = get_check_cellpose_models_default_settings(settings={})
|
311
490
|
elif settings_type == 'map_barcodes':
|
312
|
-
settings =
|
491
|
+
settings = set_default_generate_barecode_mapping(settings={})
|
313
492
|
elif settings_type == 'regression':
|
314
493
|
settings = get_perform_regression_default_settings(settings={})
|
315
494
|
elif settings_type == 'recruitment':
|
@@ -321,7 +500,7 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
321
500
|
vars_dict = generate_fields(variables, scrollable_frame)
|
322
501
|
|
323
502
|
containers = [settings_frame]
|
324
|
-
widgets = [
|
503
|
+
widgets = [scrollable_frame]
|
325
504
|
|
326
505
|
style = ttk.Style(vertical_container)
|
327
506
|
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
@@ -329,448 +508,414 @@ def setup_settings_panel(vertical_container, settings_type='mask', window_dimens
|
|
329
508
|
print("Settings panel setup complete")
|
330
509
|
return scrollable_frame, vars_dict
|
331
510
|
|
332
|
-
def setup_plot_section(vertical_container):
|
333
|
-
global canvas, canvas_widget
|
334
|
-
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
335
|
-
vertical_container.add(plot_frame, stretch="always")
|
336
|
-
figure = Figure(figsize=(30, 4), dpi=100)
|
337
|
-
plot = figure.add_subplot(111)
|
338
|
-
plot.plot([], []) # This creates an empty plot.
|
339
|
-
plot.axis('off')
|
340
|
-
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
341
|
-
canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
|
342
|
-
canvas_widget = canvas.get_tk_widget()
|
343
|
-
plot_frame.add(canvas_widget, stretch="always")
|
344
|
-
canvas.draw()
|
345
|
-
canvas.figure = figure
|
346
|
-
style_out = set_dark_style(ttk.Style())
|
347
|
-
|
348
|
-
figure.patch.set_facecolor(style_out['bg_color'])
|
349
|
-
plot.set_facecolor(style_out['bg_color'])
|
350
|
-
containers = [plot_frame]
|
351
|
-
widgets = [canvas_widget]
|
352
|
-
style = ttk.Style(vertical_container)
|
353
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
354
|
-
return canvas, canvas_widget
|
355
|
-
|
356
511
|
def setup_console(vertical_container):
|
357
512
|
global console_output
|
358
|
-
|
359
|
-
vertical_container.add(console_frame, stretch="always")
|
360
|
-
console_label = spacrLabel(console_frame, text="Console", anchor='center', justify='center', align="center")
|
361
|
-
console_label.grid(row=0, column=0, pady=10, padx=10)
|
362
|
-
console_output = scrolledtext.ScrolledText(console_frame, height=10)
|
363
|
-
console_output.grid(row=1, column=0, sticky="nsew")
|
364
|
-
console_frame.grid_rowconfigure(1, weight=1)
|
365
|
-
console_frame.grid_columnconfigure(0, weight=1)
|
366
|
-
containers = [console_frame]
|
367
|
-
widgets = [console_label, console_output]
|
368
|
-
style = ttk.Style(vertical_container)
|
369
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
370
|
-
return console_output
|
371
|
-
|
372
|
-
def setup_progress_frame(vertical_container):
|
373
|
-
global progress_output
|
374
|
-
progress_frame = tk.Frame(vertical_container)
|
375
|
-
vertical_container.add(progress_frame, stretch="always")
|
376
|
-
label_frame = tk.Frame(progress_frame)
|
377
|
-
label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
|
378
|
-
progress_label = spacrLabel(label_frame, text="Processing: 0%", font=('Helvetica', 12), anchor='w', justify='left', align="left")
|
379
|
-
progress_label.grid(row=0, column=0, sticky="w")
|
380
|
-
progress_output = scrolledtext.ScrolledText(progress_frame, height=10)
|
381
|
-
progress_output.grid(row=1, column=0, sticky="nsew")
|
382
|
-
progress_frame.grid_rowconfigure(1, weight=1)
|
383
|
-
progress_frame.grid_columnconfigure(0, weight=1)
|
384
|
-
containers = [progress_frame, label_frame]
|
385
|
-
widgets = [progress_label, progress_output]
|
386
|
-
style = ttk.Style(vertical_container)
|
387
|
-
_ = set_dark_style(style, containers=containers, widgets=widgets)
|
388
|
-
return progress_output
|
513
|
+
from .gui_elements import set_dark_style
|
389
514
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
settings_repo_id = "einarolafsson/spacr_settings"
|
394
|
-
dataset_subfolder = "plate1"
|
395
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
|
396
|
-
|
397
|
-
# Download the dataset
|
398
|
-
try:
|
399
|
-
dataset_path = download_dataset(dataset_repo_id, dataset_subfolder, local_dir)
|
400
|
-
if 'src' in vars_dict:
|
401
|
-
vars_dict['src'][2].set(dataset_path)
|
402
|
-
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
403
|
-
q.put(f"Dataset downloaded to: {dataset_path}\n")
|
404
|
-
except Exception as e:
|
405
|
-
q.put(f"Failed to download dataset: {e}\n")
|
515
|
+
# Apply dark style and get style output
|
516
|
+
style = ttk.Style()
|
517
|
+
style_out = set_dark_style(style)
|
406
518
|
|
407
|
-
#
|
408
|
-
|
409
|
-
|
410
|
-
q.put(f"Settings downloaded to: {settings_path}\n")
|
411
|
-
except Exception as e:
|
412
|
-
q.put(f"Failed to download settings: {e}\n")
|
519
|
+
# Create a frame for the console section
|
520
|
+
console_frame = tk.Frame(vertical_container, bg=style_out['bg_color'])
|
521
|
+
vertical_container.add(console_frame, stretch="always")
|
413
522
|
|
414
|
-
|
415
|
-
|
416
|
-
""
|
417
|
-
Downloads a dataset or settings files from Hugging Face and returns the local path.
|
523
|
+
# Create a thicker frame at the top for the hover effect
|
524
|
+
top_border = tk.Frame(console_frame, height=5, bg=style_out['bg_color'])
|
525
|
+
top_border.grid(row=0, column=0, sticky="ew", pady=(0, 2))
|
418
526
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
527
|
+
# Create the scrollable frame (which is a Text widget) with white text
|
528
|
+
family = style_out['font_family']
|
529
|
+
font_size = style_out['font_size']
|
530
|
+
font_loader = style_out['font_loader']
|
531
|
+
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)
|
532
|
+
|
533
|
+
console_output.grid(row=1, column=0, sticky="nsew") # Use grid for console_output
|
425
534
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
if local_dir is None:
|
430
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
431
|
-
|
432
|
-
local_subfolder_dir = os.path.join(local_dir, subfolder if subfolder else "settings")
|
433
|
-
if not os.path.exists(local_subfolder_dir):
|
434
|
-
os.makedirs(local_subfolder_dir)
|
435
|
-
elif len(os.listdir(local_subfolder_dir)) > 0:
|
436
|
-
q.put(f"Files already downloaded to: {local_subfolder_dir}")
|
437
|
-
return local_subfolder_dir
|
438
|
-
|
439
|
-
attempt = 0
|
440
|
-
while attempt < retries:
|
441
|
-
try:
|
442
|
-
files = list_repo_files(repo_id, repo_type="dataset")
|
443
|
-
subfolder_files = [file for file in files if file.startswith(subfolder) or (subfolder == "" and file.endswith('.csv'))]
|
444
|
-
|
445
|
-
for file_name in subfolder_files:
|
446
|
-
for download_attempt in range(retries):
|
447
|
-
try:
|
448
|
-
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
449
|
-
response = requests.get(url, stream=True)
|
450
|
-
response.raise_for_status()
|
451
|
-
|
452
|
-
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
453
|
-
with open(local_file_path, 'wb') as file:
|
454
|
-
for chunk in response.iter_content(chunk_size=8192):
|
455
|
-
file.write(chunk)
|
456
|
-
q.put(f"Downloaded file: {file_name}")
|
457
|
-
break
|
458
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
459
|
-
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
460
|
-
time.sleep(delay)
|
461
|
-
else:
|
462
|
-
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
535
|
+
# Configure the grid to allow expansion
|
536
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
537
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
463
538
|
|
464
|
-
|
539
|
+
def on_enter(event):
|
540
|
+
top_border.config(bg=style_out['active_color'])
|
465
541
|
|
466
|
-
|
467
|
-
|
468
|
-
attempt += 1
|
469
|
-
time.sleep(delay)
|
542
|
+
def on_leave(event):
|
543
|
+
top_border.config(bg=style_out['bg_color'])
|
470
544
|
|
471
|
-
|
545
|
+
console_output.bind("<Enter>", on_enter)
|
546
|
+
console_output.bind("<Leave>", on_leave)
|
472
547
|
|
473
|
-
|
474
|
-
global button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
475
|
-
from .settings import descriptions
|
548
|
+
return console_output, console_frame
|
476
549
|
|
477
|
-
|
478
|
-
|
550
|
+
def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
|
551
|
+
global thread_control, parent_frame, button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
|
552
|
+
from .gui_utils import download_hug_dataset
|
553
|
+
from .gui_elements import set_element_size
|
479
554
|
|
480
|
-
|
555
|
+
size_dict = set_element_size()
|
556
|
+
button_section_height = size_dict['panel_height']
|
557
|
+
button_frame = tk.Frame(horizontal_container, height=button_section_height)
|
558
|
+
|
481
559
|
horizontal_container.add(button_frame, stretch="always", sticky="nsew")
|
482
|
-
button_frame
|
483
|
-
button_frame.grid_rowconfigure(1, weight=1)
|
484
|
-
button_frame.grid_columnconfigure(0, weight=1)
|
485
|
-
|
486
|
-
categories_label = spacrLabel(button_frame, text="Categories", anchor='center', justify='center', align="center")
|
487
|
-
categories_label.grid(row=0, column=0, pady=10, padx=10)
|
488
|
-
button_scrollable_frame = spacrFrame(button_frame)
|
560
|
+
button_scrollable_frame = spacrFrame(button_frame, scrollbar=False)
|
489
561
|
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
490
|
-
|
491
|
-
widgets = [categories_label, button_scrollable_frame.scrollable_frame]
|
562
|
+
widgets = [button_scrollable_frame.scrollable_frame]
|
492
563
|
|
493
564
|
btn_col = 0
|
494
|
-
btn_row =
|
565
|
+
btn_row = 0
|
495
566
|
|
496
567
|
if run:
|
497
|
-
|
498
|
-
run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type))
|
568
|
+
run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type), show_text=False, size=size_dict['btn_size'], animation=False)
|
499
569
|
run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
500
570
|
widgets.append(run_button)
|
501
|
-
|
571
|
+
btn_col += 1
|
502
572
|
|
503
|
-
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
|
504
|
-
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=initiate_abort)
|
573
|
+
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap', 'map_barcodes']:
|
574
|
+
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=lambda: initiate_abort(), show_text=False, size=size_dict['btn_size'], animation=False)
|
505
575
|
abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
506
576
|
widgets.append(abort_button)
|
507
|
-
|
577
|
+
btn_col += 1
|
508
578
|
|
509
579
|
if download and settings_type in ['mask']:
|
510
|
-
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset)
|
580
|
+
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=lambda: download_hug_dataset(q, vars_dict), show_text=False, size=size_dict['btn_size'], animation=False)
|
511
581
|
download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
512
582
|
widgets.append(download_dataset_button)
|
513
|
-
|
583
|
+
btn_col += 1
|
514
584
|
|
515
585
|
if import_btn:
|
516
|
-
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type))
|
586
|
+
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type), show_text=False, size=size_dict['btn_size'], animation=False)
|
517
587
|
import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
518
588
|
widgets.append(import_button)
|
519
589
|
btn_row += 1
|
520
590
|
|
521
|
-
# Add the progress bar
|
591
|
+
# Add the batch progress bar
|
522
592
|
progress_bar = spacrProgressBar(button_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate')
|
523
|
-
progress_bar.grid(row=
|
593
|
+
progress_bar.grid(row=btn_row, column=0, columnspan=7, pady=5, padx=5, sticky='ew')
|
594
|
+
progress_bar.set_label_position() # Set the label position after grid placement
|
524
595
|
widgets.append(progress_bar)
|
525
596
|
|
526
597
|
if vars_dict is not None:
|
527
598
|
toggle_settings(button_scrollable_frame)
|
528
599
|
|
529
|
-
description_frame = tk.Frame(horizontal_container)
|
530
|
-
horizontal_container.add(description_frame, stretch="always", sticky="nsew")
|
531
|
-
description_frame.grid_columnconfigure(0, weight=1)
|
532
|
-
description_frame.grid_rowconfigure(0, weight=1) # Add this line to make the row expandable
|
533
|
-
|
534
|
-
description_label = tk.Label(description_frame, text="Module Description", anchor='nw', justify='left', wraplength=width - 50)
|
535
|
-
description_label.grid(row=0, column=0, pady=50, padx=20, sticky='nsew')
|
536
|
-
description_text = descriptions.get(settings_type, "No description available for this module.")
|
537
|
-
description_label.config(text=description_text)
|
538
|
-
|
539
|
-
def update_wraplength(event):
|
540
|
-
new_width = event.width - 40 # Adjust as needed
|
541
|
-
description_label.config(wraplength=new_width)
|
542
|
-
|
543
|
-
description_label.bind('<Configure>', update_wraplength)
|
544
|
-
|
545
|
-
containers = [button_frame, description_frame]
|
546
|
-
widgets.extend([description_label])
|
547
|
-
|
548
600
|
style = ttk.Style(horizontal_container)
|
549
|
-
_ = set_dark_style(style, containers=
|
601
|
+
_ = set_dark_style(style, containers=[button_frame], widgets=widgets)
|
602
|
+
|
603
|
+
return button_scrollable_frame, btn_col
|
604
|
+
|
605
|
+
def setup_usage_panel(horizontal_container, btn_col, uppdate_frequency):
|
606
|
+
global usage_bars
|
607
|
+
from .gui_elements import set_dark_style, set_element_size
|
608
|
+
|
609
|
+
usg_col = 1
|
610
|
+
|
611
|
+
def update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame):
|
612
|
+
# Update RAM usage
|
613
|
+
ram_usage = psutil.virtual_memory().percent
|
614
|
+
ram_bar['value'] = ram_usage
|
615
|
+
|
616
|
+
# Update GPU and VRAM usage
|
617
|
+
gpus = GPUtil.getGPUs()
|
618
|
+
if gpus:
|
619
|
+
gpu = gpus[0]
|
620
|
+
vram_usage = gpu.memoryUtil * 100
|
621
|
+
gpu_usage = gpu.load * 100
|
622
|
+
vram_bar['value'] = vram_usage
|
623
|
+
gpu_bar['value'] = gpu_usage
|
624
|
+
|
625
|
+
# Update CPU usage for each core
|
626
|
+
cpu_percentages = psutil.cpu_percent(percpu=True)
|
627
|
+
for bar, usage in zip(usage_bars[3:], cpu_percentages):
|
628
|
+
bar['value'] = usage
|
629
|
+
|
630
|
+
# Schedule the function to run again after 1000 ms (1 second)
|
631
|
+
parent_frame.after(uppdate_frequency, update_usage, ram_bar, vram_bar, gpu_bar, usage_bars, parent_frame)
|
632
|
+
|
633
|
+
size_dict = set_element_size()
|
634
|
+
usage_panel_height = size_dict['panel_height']
|
635
|
+
usage_frame = tk.Frame(horizontal_container, height=usage_panel_height)
|
636
|
+
horizontal_container.add(usage_frame)
|
637
|
+
|
638
|
+
usage_frame.grid_rowconfigure(0, weight=0)
|
639
|
+
usage_frame.grid_rowconfigure(1, weight=1)
|
640
|
+
usage_frame.grid_columnconfigure(0, weight=1)
|
641
|
+
usage_frame.grid_columnconfigure(1, weight=1)
|
642
|
+
|
643
|
+
usage_scrollable_frame = spacrFrame(usage_frame, scrollbar=False)
|
644
|
+
usage_scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
|
645
|
+
widgets = [usage_scrollable_frame.scrollable_frame]
|
646
|
+
|
647
|
+
usage_bars = []
|
648
|
+
max_elements_per_column = 6
|
649
|
+
row = 0
|
650
|
+
col = 0
|
550
651
|
|
551
|
-
|
652
|
+
# Initialize RAM, VRAM, and GPU bars as None
|
653
|
+
ram_bar, vram_bar, gpu_bar = None, None, None
|
654
|
+
|
655
|
+
# Configure the style for the label
|
656
|
+
style = ttk.Style()
|
657
|
+
style_out = set_dark_style(style)
|
658
|
+
font_loader = style_out['font_loader']
|
659
|
+
font_size = style_out['font_size'] - 2
|
660
|
+
style.configure("usage.TLabel", font=font_loader.get_font(size=font_size), foreground=style_out['fg_color'])
|
661
|
+
|
662
|
+
# Try adding RAM bar
|
663
|
+
try:
|
664
|
+
ram_info = psutil.virtual_memory()
|
665
|
+
ram_label_text = f"RAM"
|
666
|
+
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'])
|
667
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
668
|
+
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
669
|
+
ram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
670
|
+
widgets.append(label)
|
671
|
+
widgets.append(ram_bar)
|
672
|
+
usage_bars.append(ram_bar)
|
673
|
+
row += 1
|
674
|
+
except Exception as e:
|
675
|
+
print(f"Could not add RAM usage bar: {e}")
|
552
676
|
|
677
|
+
# Try adding VRAM and GPU usage bars
|
678
|
+
try:
|
679
|
+
gpus = GPUtil.getGPUs()
|
680
|
+
if gpus:
|
681
|
+
gpu = gpus[0]
|
682
|
+
vram_label_text = f"VRAM"
|
683
|
+
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'])
|
684
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
685
|
+
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
686
|
+
vram_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
687
|
+
widgets.append(label)
|
688
|
+
widgets.append(vram_bar)
|
689
|
+
usage_bars.append(vram_bar)
|
690
|
+
row += 1
|
691
|
+
|
692
|
+
gpu_label_text = f"GPU"
|
693
|
+
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'])
|
694
|
+
label.grid(row=row, column=2 * col, pady=5, padx=5, sticky='w')
|
695
|
+
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
696
|
+
gpu_bar.grid(row=row, column=2 * col + 1, pady=5, padx=5, sticky='ew')
|
697
|
+
widgets.append(label)
|
698
|
+
widgets.append(gpu_bar)
|
699
|
+
usage_bars.append(gpu_bar)
|
700
|
+
row += 1
|
701
|
+
except Exception as e:
|
702
|
+
print(f"Could not add VRAM or GPU usage bars: {e}")
|
553
703
|
|
554
|
-
|
555
|
-
|
556
|
-
|
704
|
+
# Add CPU core usage bars
|
705
|
+
try:
|
706
|
+
cpu_cores = psutil.cpu_count(logical=True)
|
707
|
+
cpu_freq = psutil.cpu_freq()
|
708
|
+
|
709
|
+
for core in range(cpu_cores):
|
710
|
+
if row > 0 and row % max_elements_per_column == 0:
|
711
|
+
col += 1
|
712
|
+
row = 0
|
713
|
+
|
714
|
+
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'])
|
715
|
+
label.grid(row=row, column=2 * col, pady=2, padx=5, sticky='w')
|
716
|
+
bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
717
|
+
bar.grid(row=row, column=2 * col + 1, pady=2, padx=5, sticky='ew')
|
718
|
+
widgets.append(label)
|
719
|
+
widgets.append(bar)
|
720
|
+
usage_bars.append(bar)
|
721
|
+
row += 1
|
722
|
+
except Exception as e:
|
723
|
+
print(f"Could not add CPU core usage bars: {e}")
|
557
724
|
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
725
|
+
style = ttk.Style(horizontal_container)
|
726
|
+
_ = set_dark_style(style, containers=[usage_frame], widgets=widgets)
|
727
|
+
|
728
|
+
if ram_bar is None:
|
729
|
+
ram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
730
|
+
if vram_bar is None:
|
731
|
+
vram_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
732
|
+
if gpu_bar is None:
|
733
|
+
gpu_bar = spacrProgressBar(usage_scrollable_frame.scrollable_frame, orient='horizontal', mode='determinate', length=size_dict['bar_size'], label=False)
|
734
|
+
|
735
|
+
update_usage(ram_bar, vram_bar, gpu_bar, usage_bars, usage_frame)
|
736
|
+
return usage_scrollable_frame, usage_bars, usg_col
|
562
737
|
|
563
|
-
|
564
|
-
|
738
|
+
def initiate_abort():
|
739
|
+
global thread_control, q, parent_frame
|
740
|
+
if thread_control.get("run_thread") is not None:
|
741
|
+
try:
|
742
|
+
q.put("Aborting processes...")
|
743
|
+
thread_control.get("run_thread").terminate()
|
744
|
+
thread_control["run_thread"] = None
|
745
|
+
q.put("Processes aborted.")
|
746
|
+
except Exception as e:
|
747
|
+
q.put(f"Error aborting process: {e}")
|
565
748
|
|
566
|
-
|
567
|
-
if any(setting in vars_dict for setting in settings):
|
568
|
-
vars_dict[category] = (None, None, tk.IntVar(value=0))
|
569
|
-
|
570
|
-
# Initially hide all settings
|
571
|
-
for setting in settings:
|
572
|
-
if setting in vars_dict:
|
573
|
-
label, widget, _ = vars_dict[setting]
|
574
|
-
label.grid_remove()
|
575
|
-
widget.grid_remove()
|
576
|
-
return vars_dict
|
749
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
577
750
|
|
578
|
-
def
|
579
|
-
global vars_dict
|
580
|
-
from .settings import
|
751
|
+
def start_process(q=None, fig_queue=None, settings_type='mask'):
|
752
|
+
global thread_control, vars_dict, parent_frame
|
753
|
+
from .settings import check_settings, expected_types
|
754
|
+
from .gui_utils import run_function_gui, set_high_priority, set_cpu_affinity, initialize_cuda
|
581
755
|
|
582
|
-
if
|
583
|
-
|
756
|
+
if q is None:
|
757
|
+
q = Queue()
|
758
|
+
if fig_queue is None:
|
759
|
+
fig_queue = Queue()
|
760
|
+
try:
|
761
|
+
settings = check_settings(vars_dict, expected_types, q)
|
762
|
+
except ValueError as e:
|
763
|
+
q.put(f"Error: {e}")
|
764
|
+
return
|
584
765
|
|
585
|
-
|
766
|
+
if thread_control.get("run_thread") is not None:
|
767
|
+
initiate_abort()
|
768
|
+
|
769
|
+
stop_requested = Value('i', 0)
|
770
|
+
thread_control["stop_requested"] = stop_requested
|
586
771
|
|
587
|
-
|
588
|
-
|
589
|
-
if setting in vars_dict:
|
590
|
-
label, widget, _ = vars_dict[setting]
|
591
|
-
if widget.grid_info():
|
592
|
-
label.grid_remove()
|
593
|
-
widget.grid_remove()
|
594
|
-
else:
|
595
|
-
label.grid()
|
596
|
-
widget.grid()
|
772
|
+
# Initialize CUDA in the main process
|
773
|
+
initialize_cuda()
|
597
774
|
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
toggle_category(categories[selected_category])
|
603
|
-
if selected_category in active_categories:
|
604
|
-
active_categories.remove(selected_category)
|
605
|
-
else:
|
606
|
-
active_categories.add(selected_category)
|
607
|
-
category_dropdown.update_styles(active_categories)
|
608
|
-
category_var.set("Select Category")
|
775
|
+
process_args = (settings_type, settings, q, fig_queue, stop_requested)
|
776
|
+
if settings_type in ['mask', 'umap', 'measure', 'simulation', 'sequencing',
|
777
|
+
'classify', 'cellpose_dataset', 'train_cellpose', 'ml_analyze', 'cellpose_masks', 'cellpose_all', 'map_barcodes',
|
778
|
+
'regression', 'recruitment', 'plaques', 'cellpose_compare', 'vision_scores', 'vision_dataset']:
|
609
779
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
category_dropdown.grid(row=7, column=0, sticky="ew", pady=2, padx=2)
|
614
|
-
vars_dict = hide_all_settings(vars_dict, categories)
|
780
|
+
# Start the process
|
781
|
+
process = Process(target=run_function_gui, args=process_args)
|
782
|
+
process.start()
|
615
783
|
|
616
|
-
|
617
|
-
|
784
|
+
# Set high priority for the process
|
785
|
+
#set_high_priority(process)
|
618
786
|
|
619
|
-
|
620
|
-
|
621
|
-
ax.clear()
|
622
|
-
canvas.draw_idle()
|
787
|
+
# Set CPU affinity if necessary
|
788
|
+
set_cpu_affinity(process)
|
623
789
|
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
ax.set_xticks([]) # Remove x-axis ticks
|
630
|
-
ax.set_yticks([]) # Remove y-axis ticks
|
631
|
-
ax.xaxis.set_visible(False) # Hide the x-axis
|
632
|
-
ax.yaxis.set_visible(False) # Hide the y-axis
|
633
|
-
fig.tight_layout()
|
634
|
-
fig.set_facecolor('black')
|
635
|
-
canvas.figure = fig
|
636
|
-
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
637
|
-
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
638
|
-
canvas.draw_idle()
|
639
|
-
except Exception as e:
|
640
|
-
traceback.print_exc()
|
641
|
-
finally:
|
642
|
-
after_id = canvas_widget.after(100, process_fig_queue)
|
643
|
-
parent_frame.after_tasks.append(after_id)
|
790
|
+
# Store the process in thread_control for future reference
|
791
|
+
thread_control["run_thread"] = process
|
792
|
+
else:
|
793
|
+
q.put(f"Error: Unknown settings type '{settings_type}'")
|
794
|
+
return
|
644
795
|
|
645
796
|
def process_console_queue():
|
646
|
-
global q, console_output, parent_frame, progress_bar
|
797
|
+
global q, console_output, parent_frame, progress_bar, process_console_queue
|
647
798
|
|
648
799
|
# Initialize function attribute if it doesn't exist
|
649
800
|
if not hasattr(process_console_queue, "completed_tasks"):
|
650
801
|
process_console_queue.completed_tasks = []
|
802
|
+
if not hasattr(process_console_queue, "current_maximum"):
|
803
|
+
process_console_queue.current_maximum = None
|
651
804
|
|
652
805
|
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
653
|
-
|
806
|
+
|
654
807
|
while not q.empty():
|
655
808
|
message = q.get_nowait()
|
656
809
|
clean_message = ansi_escape_pattern.sub('', message)
|
657
|
-
console_output.insert(tk.END, clean_message + "\n")
|
658
|
-
console_output.see(tk.END)
|
810
|
+
#console_output.insert(tk.END, clean_message + "\n")
|
811
|
+
#console_output.see(tk.END)
|
659
812
|
|
660
813
|
# Check if the message contains progress information
|
661
|
-
if clean_message.startswith("Progress"):
|
814
|
+
if clean_message.startswith("Progress:"):
|
662
815
|
try:
|
663
816
|
# Extract the progress information
|
664
|
-
match = re.search(r'(\d+)/(\d+)', clean_message)
|
817
|
+
match = re.search(r'Progress: (\d+)/(\d+), operation_type: ([\w\s]*),(.*)', clean_message)
|
818
|
+
|
665
819
|
if match:
|
666
820
|
current_progress = int(match.group(1))
|
667
821
|
total_progress = int(match.group(2))
|
822
|
+
operation_type = match.group(3).strip()
|
823
|
+
additional_info = match.group(4).strip() # Capture everything after operation_type
|
824
|
+
|
825
|
+
# Check if the maximum value has changed
|
826
|
+
if process_console_queue.current_maximum != total_progress:
|
827
|
+
process_console_queue.current_maximum = total_progress
|
828
|
+
process_console_queue.completed_tasks = []
|
668
829
|
|
669
830
|
# Add the task to the completed set
|
670
831
|
process_console_queue.completed_tasks.append(current_progress)
|
671
832
|
|
672
833
|
# Calculate the unique progress count
|
673
834
|
unique_progress_count = len(np.unique(process_console_queue.completed_tasks))
|
674
|
-
|
835
|
+
|
675
836
|
# Update the progress bar
|
676
837
|
if progress_bar:
|
677
838
|
progress_bar['maximum'] = total_progress
|
678
839
|
progress_bar['value'] = unique_progress_count
|
679
840
|
|
680
|
-
#
|
681
|
-
|
682
|
-
|
683
|
-
progress_bar.
|
684
|
-
|
685
|
-
time_image_match = re.search(r'Time/image: ([\d.]+) sec', clean_message)
|
686
|
-
if time_image_match:
|
687
|
-
progress_bar.time_image = float(time_image_match.group(1))
|
688
|
-
|
689
|
-
time_batch_match = re.search(r'Time/batch: ([\d.]+) sec', clean_message)
|
690
|
-
if time_batch_match:
|
691
|
-
progress_bar.time_batch = float(time_batch_match.group(1))
|
692
|
-
|
693
|
-
time_left_match = re.search(r'Time_left: ([\d.]+) min', clean_message)
|
694
|
-
if time_left_match:
|
695
|
-
progress_bar.time_left = float(time_left_match.group(1))
|
841
|
+
# Store operation type and additional info
|
842
|
+
if operation_type:
|
843
|
+
progress_bar.operation_type = operation_type
|
844
|
+
progress_bar.additional_info = additional_info
|
696
845
|
|
697
846
|
# Update the progress label
|
698
847
|
if progress_bar.progress_label:
|
699
848
|
progress_bar.update_label()
|
700
|
-
|
849
|
+
|
701
850
|
# Clear completed tasks when progress is complete
|
702
851
|
if unique_progress_count >= total_progress:
|
703
852
|
process_console_queue.completed_tasks.clear()
|
853
|
+
|
704
854
|
except Exception as e:
|
705
855
|
print(f"Error parsing progress message: {e}")
|
706
|
-
|
707
|
-
|
856
|
+
else:
|
857
|
+
# Only insert messages that do not start with "Progress:"
|
858
|
+
console_output.insert(tk.END, clean_message + "\n")
|
859
|
+
console_output.see(tk.END)
|
860
|
+
|
861
|
+
after_id = console_output.after(uppdate_frequency, process_console_queue)
|
708
862
|
parent_frame.after_tasks.append(after_id)
|
709
863
|
|
710
|
-
def
|
711
|
-
global
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
fig_queue = fig_queue_var
|
720
|
-
progress_bar = progress_bar_var
|
864
|
+
def main_thread_update_function(root, q, fig_queue, canvas_widget):
|
865
|
+
global uppdate_frequency
|
866
|
+
try:
|
867
|
+
while not q.empty():
|
868
|
+
message = q.get_nowait()
|
869
|
+
except Exception as e:
|
870
|
+
print(f"Error updating GUI canvas: {e}")
|
871
|
+
finally:
|
872
|
+
root.after(uppdate_frequency, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget))
|
721
873
|
|
722
|
-
def
|
723
|
-
|
724
|
-
|
725
|
-
settings_frame = tk.Frame(horizontal_container)
|
726
|
-
return vertical_container, horizontal_container, settings_frame
|
874
|
+
def initiate_root(parent, settings_type='mask'):
|
875
|
+
"""
|
876
|
+
Initializes the root window and sets up the GUI components based on the specified settings type.
|
727
877
|
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
878
|
+
Args:
|
879
|
+
parent (tkinter.Tk or tkinter.Toplevel): The parent window for the GUI.
|
880
|
+
settings_type (str, optional): The type of settings to be displayed in the GUI. Defaults to 'mask'.
|
881
|
+
|
882
|
+
Returns:
|
883
|
+
tuple: A tuple containing the parent frame and the dictionary of variables used in the GUI.
|
884
|
+
"""
|
735
885
|
|
736
|
-
|
737
|
-
vertical_container.add(horizontal_container, stretch="always")
|
738
|
-
horizontal_container.grid_columnconfigure(0, weight=1)
|
739
|
-
horizontal_container.grid_columnconfigure(1, weight=1)
|
740
|
-
settings_frame.grid_rowconfigure(0, weight=0)
|
741
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
742
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
743
|
-
horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
|
886
|
+
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, index_control, usage_bars
|
744
887
|
|
745
|
-
|
888
|
+
from .gui_utils import setup_frame
|
889
|
+
from .settings import descriptions
|
890
|
+
|
891
|
+
uppdate_frequency = 500
|
892
|
+
|
893
|
+
# Start tracemalloc and initialize global variables
|
894
|
+
tracemalloc.start()
|
746
895
|
|
747
|
-
def initiate_root(parent, settings_type='mask'):
|
748
|
-
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar
|
749
|
-
from .gui_utils import main_thread_update_function
|
750
|
-
from .gui import gui_app
|
751
896
|
set_start_method('spawn', force=True)
|
897
|
+
#set_start_method('forkserver', force=True)
|
752
898
|
print("Initializing root with settings_type:", settings_type)
|
753
899
|
|
900
|
+
# Initialize global variables
|
901
|
+
figures = deque()
|
902
|
+
figure_index = -1
|
903
|
+
|
754
904
|
parent_frame = parent
|
755
|
-
parent_frame.update_idletasks()
|
756
|
-
frame_width = int(parent_frame.winfo_width())
|
757
|
-
frame_height = int(parent_frame.winfo_height())
|
758
|
-
print(frame_width, frame_height)
|
759
|
-
dims = [frame_width, frame_height]
|
760
905
|
|
761
|
-
if not
|
762
|
-
|
906
|
+
if not isinstance(parent_frame, (tk.Tk, tk.Toplevel)):
|
907
|
+
parent_window = parent_frame.winfo_toplevel()
|
908
|
+
else:
|
909
|
+
parent_window = parent_frame
|
763
910
|
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
except tk.TclError as e:
|
769
|
-
print(f"Error destroying widget: {e}")
|
911
|
+
parent_window.update_idletasks()
|
912
|
+
|
913
|
+
if not hasattr(parent_window, 'after_tasks'):
|
914
|
+
parent_window.after_tasks = []
|
770
915
|
|
771
916
|
q = Queue()
|
772
917
|
fig_queue = Queue()
|
773
|
-
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
918
|
+
parent_frame, vertical_container, horizontal_container, settings_container = setup_frame(parent_frame)
|
774
919
|
|
775
920
|
if settings_type == 'annotate':
|
776
921
|
from .app_annotate import initiate_annotation_app
|
@@ -779,17 +924,26 @@ def initiate_root(parent, settings_type='mask'):
|
|
779
924
|
from .app_make_masks import initiate_make_mask_app
|
780
925
|
initiate_make_mask_app(horizontal_container)
|
781
926
|
else:
|
782
|
-
scrollable_frame, vars_dict = setup_settings_panel(
|
783
|
-
|
927
|
+
scrollable_frame, vars_dict = setup_settings_panel(settings_container, settings_type)
|
928
|
+
print('setup_settings_panel')
|
784
929
|
canvas, canvas_widget = setup_plot_section(vertical_container)
|
785
|
-
console_output = setup_console(vertical_container)
|
930
|
+
console_output, _ = setup_console(vertical_container)
|
931
|
+
button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
|
932
|
+
_, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col, uppdate_frequency)
|
786
933
|
|
787
|
-
set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, progress_bar)
|
934
|
+
set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, index_control, progress_bar, usage_bars)
|
935
|
+
description_text = descriptions.get(settings_type, "No description available for this module.")
|
936
|
+
|
937
|
+
q.put(f"Console")
|
938
|
+
q.put(f" ")
|
939
|
+
q.put(description_text)
|
940
|
+
|
788
941
|
process_console_queue()
|
789
942
|
process_fig_queue()
|
790
|
-
after_id =
|
791
|
-
|
943
|
+
after_id = parent_window.after(uppdate_frequency, lambda: main_thread_update_function(parent_window, q, fig_queue, canvas_widget))
|
944
|
+
parent_window.after_tasks.append(after_id)
|
792
945
|
|
793
946
|
print("Root initialization complete")
|
794
947
|
return parent_frame, vars_dict
|
795
948
|
|
949
|
+
|