spacr 0.1.55__py3-none-any.whl → 0.1.62__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 +4 -1
- spacr/app_annotate.py +1 -1
- spacr/app_make_masks.py +1 -4
- spacr/core.py +1 -1
- spacr/gui.py +3 -3
- spacr/gui_core.py +608 -0
- spacr/gui_elements.py +324 -0
- spacr/gui_run.py +58 -0
- spacr/gui_utils.py +3 -1076
- spacr/gui_wrappers.py +137 -0
- spacr/measure.py +6 -8
- spacr/plot.py +53 -1
- spacr/settings.py +2 -1
- spacr/utils.py +19 -11
- {spacr-0.1.55.dist-info → spacr-0.1.62.dist-info}/METADATA +1 -1
- {spacr-0.1.55.dist-info → spacr-0.1.62.dist-info}/RECORD +20 -16
- {spacr-0.1.55.dist-info → spacr-0.1.62.dist-info}/LICENSE +0 -0
- {spacr-0.1.55.dist-info → spacr-0.1.62.dist-info}/WHEEL +0 -0
- {spacr-0.1.55.dist-info → spacr-0.1.62.dist-info}/entry_points.txt +0 -0
- {spacr-0.1.55.dist-info → spacr-0.1.62.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py
CHANGED
@@ -1,383 +1,15 @@
|
|
1
|
-
import
|
2
|
-
import matplotlib.pyplot as plt
|
3
|
-
matplotlib.use('Agg')
|
1
|
+
import io, sys, ast, ctypes, re, csv, ast
|
4
2
|
import tkinter as tk
|
5
3
|
from tkinter import ttk
|
6
|
-
import tkinter.font as tkFont
|
7
|
-
from tkinter import filedialog
|
8
|
-
from tkinter import Checkbutton
|
9
|
-
from tkinter import font as tkFont
|
10
|
-
from multiprocessing import Process, Value, Queue, Manager, set_start_method
|
11
|
-
from tkinter import ttk, scrolledtext
|
12
|
-
from matplotlib.figure import Figure
|
13
|
-
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
14
|
-
import time
|
15
|
-
import requests
|
16
|
-
from huggingface_hub import list_repo_files, hf_hub_download
|
17
4
|
|
18
|
-
from .
|
19
|
-
from .
|
5
|
+
from . gui_core import initiate_root
|
6
|
+
from .gui_elements import spacrLabel, spacrCheckbutton, set_dark_style
|
20
7
|
|
21
8
|
try:
|
22
9
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
23
10
|
except AttributeError:
|
24
11
|
pass
|
25
12
|
|
26
|
-
# Define global variables
|
27
|
-
q = None
|
28
|
-
console_output = None
|
29
|
-
parent_frame = None
|
30
|
-
vars_dict = None
|
31
|
-
canvas = None
|
32
|
-
canvas_widget = None
|
33
|
-
scrollable_frame = None
|
34
|
-
progress_label = None
|
35
|
-
fig_queue = None
|
36
|
-
|
37
|
-
thread_control = {"run_thread": None, "stop_requested": False}
|
38
|
-
|
39
|
-
class spacrCheckbutton(ttk.Checkbutton):
|
40
|
-
def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
|
41
|
-
super().__init__(parent, *args, **kwargs)
|
42
|
-
self.text = text
|
43
|
-
self.variable = variable if variable else tk.BooleanVar()
|
44
|
-
self.command = command
|
45
|
-
self.configure(text=self.text, variable=self.variable, command=self.command, style='Spacr.TCheckbutton')
|
46
|
-
|
47
|
-
class spacrFrame(ttk.Frame):
|
48
|
-
def __init__(self, container, width=None, *args, bg='black', **kwargs):
|
49
|
-
super().__init__(container, *args, **kwargs)
|
50
|
-
self.configure(style='TFrame')
|
51
|
-
if width is None:
|
52
|
-
screen_width = self.winfo_screenwidth()
|
53
|
-
width = screen_width // 4
|
54
|
-
canvas = tk.Canvas(self, bg=bg, width=width)
|
55
|
-
scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
|
56
|
-
|
57
|
-
self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
|
58
|
-
self.scrollable_frame.bind(
|
59
|
-
"<Configure>",
|
60
|
-
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
61
|
-
)
|
62
|
-
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
63
|
-
canvas.configure(yscrollcommand=scrollbar.set)
|
64
|
-
|
65
|
-
canvas.grid(row=0, column=0, sticky="nsew")
|
66
|
-
scrollbar.grid(row=0, column=1, sticky="ns")
|
67
|
-
|
68
|
-
self.grid_rowconfigure(0, weight=1)
|
69
|
-
self.grid_columnconfigure(0, weight=1)
|
70
|
-
self.grid_columnconfigure(1, weight=0)
|
71
|
-
|
72
|
-
for child in self.scrollable_frame.winfo_children():
|
73
|
-
child.configure(bg='black')
|
74
|
-
|
75
|
-
class spacrLabel(tk.Frame):
|
76
|
-
def __init__(self, parent, text="", font=None, style=None, align="right", **kwargs):
|
77
|
-
label_kwargs = {k: v for k, v in kwargs.items() if k in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
|
78
|
-
for key in label_kwargs.keys():
|
79
|
-
kwargs.pop(key)
|
80
|
-
super().__init__(parent, **kwargs)
|
81
|
-
self.text = text
|
82
|
-
self.kwargs = label_kwargs
|
83
|
-
self.align = align
|
84
|
-
screen_height = self.winfo_screenheight()
|
85
|
-
label_height = screen_height // 50
|
86
|
-
label_width = label_height * 10
|
87
|
-
self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=self.kwargs.get("background", "black"))
|
88
|
-
self.canvas.grid(row=0, column=0, sticky="ew")
|
89
|
-
|
90
|
-
self.font_style = font if font else tkFont.Font(family=self.kwargs.get("font_family", "Helvetica"), size=self.kwargs.get("font_size", 12), weight=tkFont.NORMAL)
|
91
|
-
self.style = style
|
92
|
-
|
93
|
-
if self.align == "center":
|
94
|
-
anchor_value = tk.CENTER
|
95
|
-
text_anchor = 'center'
|
96
|
-
else: # default to right alignment
|
97
|
-
anchor_value = tk.E
|
98
|
-
text_anchor = 'e'
|
99
|
-
|
100
|
-
if self.style:
|
101
|
-
ttk_style = ttk.Style()
|
102
|
-
ttk_style.configure(self.style, **label_kwargs)
|
103
|
-
self.label_text = ttk.Label(self.canvas, text=self.text, style=self.style, anchor=text_anchor, justify=text_anchor)
|
104
|
-
self.label_text.pack(fill=tk.BOTH, expand=True)
|
105
|
-
else:
|
106
|
-
self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
|
107
|
-
label_height // 2, text=self.text, fill=self.kwargs.get("foreground", "white"),
|
108
|
-
font=self.font_style, anchor=anchor_value, justify=tk.RIGHT)
|
109
|
-
|
110
|
-
def set_text(self, text):
|
111
|
-
if self.style:
|
112
|
-
self.label_text.config(text=text)
|
113
|
-
else:
|
114
|
-
self.canvas.itemconfig(self.label_text, text=text)
|
115
|
-
|
116
|
-
class spacrButton(tk.Frame):
|
117
|
-
def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
|
118
|
-
super().__init__(parent, *args, **kwargs)
|
119
|
-
self.text = text
|
120
|
-
self.command = command
|
121
|
-
screen_height = self.winfo_screenheight()
|
122
|
-
button_height = screen_height // 50
|
123
|
-
button_width = button_height * 3
|
124
|
-
|
125
|
-
# Increase the canvas size to accommodate the button and the rim
|
126
|
-
self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg="black")
|
127
|
-
self.canvas.grid(row=0, column=0)
|
128
|
-
|
129
|
-
self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
|
130
|
-
|
131
|
-
self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
|
132
|
-
self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill="white", font=self.font_style)
|
133
|
-
|
134
|
-
self.bind("<Enter>", self.on_enter)
|
135
|
-
self.bind("<Leave>", self.on_leave)
|
136
|
-
self.bind("<Button-1>", self.on_click)
|
137
|
-
self.canvas.bind("<Enter>", self.on_enter)
|
138
|
-
self.canvas.bind("<Leave>", self.on_leave)
|
139
|
-
self.canvas.bind("<Button-1>", self.on_click)
|
140
|
-
|
141
|
-
def on_enter(self, event=None):
|
142
|
-
self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
|
143
|
-
|
144
|
-
def on_leave(self, event=None):
|
145
|
-
self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
|
146
|
-
|
147
|
-
def on_click(self, event=None):
|
148
|
-
if self.command:
|
149
|
-
self.command()
|
150
|
-
|
151
|
-
def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
|
152
|
-
points = [
|
153
|
-
x1 + radius, y1,
|
154
|
-
x1 + radius, y1,
|
155
|
-
x2 - radius, y1,
|
156
|
-
x2 - radius, y1,
|
157
|
-
x2, y1,
|
158
|
-
x2, y1 + radius,
|
159
|
-
x2, y1 + radius,
|
160
|
-
x2, y2 - radius,
|
161
|
-
x2, y2 - radius,
|
162
|
-
x2, y2,
|
163
|
-
x2 - radius, y2,
|
164
|
-
x2 - radius, y2,
|
165
|
-
x1 + radius, y2,
|
166
|
-
x1 + radius, y2,
|
167
|
-
x1, y2,
|
168
|
-
x1, y2 - radius,
|
169
|
-
x1, y2 - radius,
|
170
|
-
x1, y1 + radius,
|
171
|
-
x1, y1 + radius,
|
172
|
-
x1, y1
|
173
|
-
]
|
174
|
-
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
175
|
-
|
176
|
-
|
177
|
-
class spacrSwitch(ttk.Frame):
|
178
|
-
def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
|
179
|
-
super().__init__(parent, *args, **kwargs)
|
180
|
-
self.text = text
|
181
|
-
self.variable = variable if variable else tk.BooleanVar()
|
182
|
-
self.command = command
|
183
|
-
self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
|
184
|
-
self.canvas.grid(row=0, column=1, padx=(10, 0))
|
185
|
-
self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
|
186
|
-
self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
|
187
|
-
self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
|
188
|
-
self.label.grid(row=0, column=0, padx=(0, 10))
|
189
|
-
self.bind("<Button-1>", self.toggle)
|
190
|
-
self.canvas.bind("<Button-1>", self.toggle)
|
191
|
-
self.label.bind("<Button-1>", self.toggle)
|
192
|
-
self.update_switch()
|
193
|
-
|
194
|
-
def toggle(self, event=None):
|
195
|
-
self.variable.set(not self.variable.get())
|
196
|
-
self.animate_switch()
|
197
|
-
if self.command:
|
198
|
-
self.command()
|
199
|
-
|
200
|
-
def update_switch(self):
|
201
|
-
if self.variable.get():
|
202
|
-
self.canvas.itemconfig(self.switch, fill="#008080") # Teal
|
203
|
-
self.canvas.coords(self.switch, 24, 4, 36, 16) # Move switch to the right
|
204
|
-
else:
|
205
|
-
self.canvas.itemconfig(self.switch, fill="#800080") # Purple
|
206
|
-
self.canvas.coords(self.switch, 4, 4, 16, 16) # Move switch to the left
|
207
|
-
|
208
|
-
def animate_switch(self):
|
209
|
-
if self.variable.get():
|
210
|
-
start_x, end_x = 4, 24
|
211
|
-
final_color = "#008080" # Teal
|
212
|
-
else:
|
213
|
-
start_x, end_x = 24, 4
|
214
|
-
final_color = "#800080" # Purple
|
215
|
-
|
216
|
-
self.animate_movement(start_x, end_x, final_color)
|
217
|
-
|
218
|
-
def animate_movement(self, start_x, end_x, final_color):
|
219
|
-
step = 1 if start_x < end_x else -1
|
220
|
-
for i in range(start_x, end_x, step):
|
221
|
-
self.canvas.coords(self.switch, i, 4, i + 12, 16)
|
222
|
-
self.canvas.update()
|
223
|
-
self.after(10) # Small delay for smooth animation
|
224
|
-
self.canvas.itemconfig(self.switch, fill=final_color)
|
225
|
-
|
226
|
-
def get(self):
|
227
|
-
return self.variable.get()
|
228
|
-
|
229
|
-
def set(self, value):
|
230
|
-
self.variable.set(value)
|
231
|
-
self.update_switch()
|
232
|
-
|
233
|
-
def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs): # Smaller radius for smaller switch
|
234
|
-
points = [x1 + radius, y1,
|
235
|
-
x1 + radius, y1,
|
236
|
-
x2 - radius, y1,
|
237
|
-
x2 - radius, y1,
|
238
|
-
x2, y1,
|
239
|
-
x2, y1 + radius,
|
240
|
-
x2, y1 + radius,
|
241
|
-
x2, y2 - radius,
|
242
|
-
x2, y2 - radius,
|
243
|
-
x2, y2,
|
244
|
-
x2 - radius, y2,
|
245
|
-
x2 - radius, y2,
|
246
|
-
x1 + radius, y2,
|
247
|
-
x1 + radius, y2,
|
248
|
-
x1, y2,
|
249
|
-
x1, y2 - radius,
|
250
|
-
x1, y2 - radius,
|
251
|
-
x1, y1 + radius,
|
252
|
-
x1, y1 + radius,
|
253
|
-
x1, y1]
|
254
|
-
|
255
|
-
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
256
|
-
|
257
|
-
class spacrToolTip:
|
258
|
-
def __init__(self, widget, text):
|
259
|
-
self.widget = widget
|
260
|
-
self.text = text
|
261
|
-
self.tooltip_window = None
|
262
|
-
widget.bind("<Enter>", self.show_tooltip)
|
263
|
-
widget.bind("<Leave>", self.hide_tooltip)
|
264
|
-
|
265
|
-
def show_tooltip(self, event):
|
266
|
-
x = event.x_root + 20
|
267
|
-
y = event.y_root + 10
|
268
|
-
self.tooltip_window = tk.Toplevel(self.widget)
|
269
|
-
self.tooltip_window.wm_overrideredirect(True)
|
270
|
-
self.tooltip_window.wm_geometry(f"+{x}+{y}")
|
271
|
-
self.tooltip_window.configure(bg='black')
|
272
|
-
label = tk.Label(self.tooltip_window, text=self.text, background="#333333", foreground="white", relief='flat', borderwidth=0)
|
273
|
-
label.grid(row=0, column=0, padx=5, pady=5)
|
274
|
-
|
275
|
-
def hide_tooltip(self, event):
|
276
|
-
if self.tooltip_window:
|
277
|
-
self.tooltip_window.destroy()
|
278
|
-
self.tooltip_window = None
|
279
|
-
|
280
|
-
def initiate_abort():
|
281
|
-
global thread_control
|
282
|
-
if thread_control.get("stop_requested") is not None:
|
283
|
-
thread_control["stop_requested"].value = 1
|
284
|
-
|
285
|
-
if thread_control.get("run_thread") is not None:
|
286
|
-
thread_control["run_thread"].join(timeout=5)
|
287
|
-
if thread_control["run_thread"].is_alive():
|
288
|
-
thread_control["run_thread"].terminate()
|
289
|
-
thread_control["run_thread"] = None
|
290
|
-
|
291
|
-
def start_process(q, fig_queue, settings_type='mask'):
|
292
|
-
global thread_control, vars_dict
|
293
|
-
from .settings import check_settings
|
294
|
-
|
295
|
-
settings = check_settings(vars_dict)
|
296
|
-
if thread_control.get("run_thread") is not None:
|
297
|
-
initiate_abort()
|
298
|
-
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
299
|
-
thread_control["stop_requested"] = stop_requested
|
300
|
-
if settings_type == 'mask':
|
301
|
-
thread_control["run_thread"] = Process(target=run_mask_gui, args=(settings, q, fig_queue, stop_requested))
|
302
|
-
elif settings_type == 'measure':
|
303
|
-
thread_control["run_thread"] = Process(target=run_measure_gui, args=(settings, q, fig_queue, stop_requested))
|
304
|
-
elif settings_type == 'classify':
|
305
|
-
thread_control["run_thread"] = Process(target=run_classify_gui, args=(settings, q, fig_queue, stop_requested))
|
306
|
-
elif settings_type == 'sequencing':
|
307
|
-
thread_control["run_thread"] = Process(target=run_sequencing_gui, args=(settings, q, fig_queue, stop_requested))
|
308
|
-
elif settings_type == 'umap':
|
309
|
-
thread_control["run_thread"] = Process(target=run_umap_gui, args=(settings, q, fig_queue, stop_requested))
|
310
|
-
thread_control["run_thread"].start()
|
311
|
-
|
312
|
-
def import_settings(settings_type='mask'):
|
313
|
-
global vars_dict, scrollable_frame
|
314
|
-
from .settings import generate_fields
|
315
|
-
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
316
|
-
csv_settings = read_settings_from_csv(csv_file_path)
|
317
|
-
if settings_type == 'mask':
|
318
|
-
settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
|
319
|
-
elif settings_type == 'measure':
|
320
|
-
settings = get_measure_crop_settings(settings={})
|
321
|
-
elif settings_type == 'classify':
|
322
|
-
settings = set_default_train_test_model(settings={})
|
323
|
-
elif settings_type == 'sequencing':
|
324
|
-
settings = get_analyze_reads_default_settings(settings={})
|
325
|
-
elif settings_type == 'umap':
|
326
|
-
settings = set_default_umap_image_settings(settings={})
|
327
|
-
else:
|
328
|
-
raise ValueError(f"Invalid settings type: {settings_type}")
|
329
|
-
|
330
|
-
variables = convert_settings_dict_for_gui(settings)
|
331
|
-
new_settings = update_settings_from_csv(variables, csv_settings)
|
332
|
-
vars_dict = generate_fields(new_settings, scrollable_frame)
|
333
|
-
|
334
|
-
def set_dark_style(style):
|
335
|
-
font_style = tkFont.Font(family="Helvetica", size=24)
|
336
|
-
|
337
|
-
# Entry
|
338
|
-
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
|
339
|
-
|
340
|
-
# Combobox
|
341
|
-
style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
|
342
|
-
style.map('TCombobox', fieldbackground=[('readonly', 'black')], foreground=[('readonly', '#ffffff')])
|
343
|
-
|
344
|
-
# Custom Button
|
345
|
-
style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
|
346
|
-
style.map('Custom.TButton', background=[('active', 'teal'), ('!active', 'black')], foreground=[('active', 'white'), ('!active', 'white')], bordercolor=[('active', 'white'), ('!active', 'white')])
|
347
|
-
|
348
|
-
# Custom Label
|
349
|
-
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style)
|
350
|
-
|
351
|
-
# Checkbutton
|
352
|
-
style.configure('Spacr.TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font="15")
|
353
|
-
style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
|
354
|
-
|
355
|
-
# General Label
|
356
|
-
style.configure('TLabel', background='black', foreground='#ffffff', font=font_style)
|
357
|
-
|
358
|
-
# Frame
|
359
|
-
style.configure('TFrame', background='black')
|
360
|
-
|
361
|
-
# PanedWindow
|
362
|
-
style.configure('TPanedwindow', background='black')
|
363
|
-
|
364
|
-
# Notebook
|
365
|
-
style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0])
|
366
|
-
style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
|
367
|
-
style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
|
368
|
-
|
369
|
-
# Button (regular)
|
370
|
-
style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style)
|
371
|
-
style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
|
372
|
-
|
373
|
-
# Scrollbar
|
374
|
-
style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black')
|
375
|
-
style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black')
|
376
|
-
|
377
|
-
# LabelFrame
|
378
|
-
style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
|
379
|
-
style.configure('Custom.TLabelFrame.Label', background='black', foreground='white', font=('Helvetica', 10, 'bold'))
|
380
|
-
|
381
13
|
def set_default_font(root, font_name="Helvetica", size=12):
|
382
14
|
default_font = (font_name, size)
|
383
15
|
root.option_add("*Font", default_font)
|
@@ -385,37 +17,6 @@ def set_default_font(root, font_name="Helvetica", size=12):
|
|
385
17
|
root.option_add("*TLabel.Font", default_font)
|
386
18
|
root.option_add("*TEntry.Font", default_font)
|
387
19
|
|
388
|
-
def create_menu_bar(root):
|
389
|
-
from .app_annotate import initiate_annotation_app_root
|
390
|
-
from .app_make_masks import initiate_mask_app_root
|
391
|
-
|
392
|
-
gui_apps = {
|
393
|
-
"Mask": 'mask',
|
394
|
-
"Measure": 'measure',
|
395
|
-
"Annotate": initiate_annotation_app_root,
|
396
|
-
"Make Masks": initiate_mask_app_root,
|
397
|
-
"Classify": 'classify',
|
398
|
-
"Sequencing": 'sequencing',
|
399
|
-
"Umap": 'umap'
|
400
|
-
}
|
401
|
-
|
402
|
-
def load_app_wrapper(app_name, app_func):
|
403
|
-
load_app(root, app_name, app_func)
|
404
|
-
|
405
|
-
# Create the menu bar
|
406
|
-
menu_bar = tk.Menu(root, bg="#008080", fg="white")
|
407
|
-
# Create a "SpaCr Applications" menu
|
408
|
-
app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
|
409
|
-
menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
|
410
|
-
# Add options to the "SpaCr Applications" menu
|
411
|
-
for app_name, app_func in gui_apps.items():
|
412
|
-
app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
|
413
|
-
# Add a separator and an exit option
|
414
|
-
app_menu.add_separator()
|
415
|
-
app_menu.add_command(label="Exit", command=root.quit)
|
416
|
-
# Configure the menu for the root window
|
417
|
-
root.config(menu=menu_bar)
|
418
|
-
|
419
20
|
def proceed_with_app(root, app_name, app_func):
|
420
21
|
from .app_annotate import gui_annotate
|
421
22
|
from .app_make_masks import gui_make_masks
|
@@ -467,26 +68,6 @@ def load_app(root, app_name, app_func):
|
|
467
68
|
else:
|
468
69
|
proceed_with_app(root, app_name, app_func)
|
469
70
|
|
470
|
-
def read_settings_from_csv(csv_file_path):
|
471
|
-
settings = {}
|
472
|
-
with open(csv_file_path, newline='') as csvfile:
|
473
|
-
reader = csv.DictReader(csvfile)
|
474
|
-
for row in reader:
|
475
|
-
key = row['Key']
|
476
|
-
value = row['Value']
|
477
|
-
settings[key] = value
|
478
|
-
return settings
|
479
|
-
|
480
|
-
def update_settings_from_csv(variables, csv_settings):
|
481
|
-
new_settings = variables.copy() # Start with a copy of the original settings
|
482
|
-
for key, value in csv_settings.items():
|
483
|
-
if key in new_settings:
|
484
|
-
# Get the variable type and options from the original settings
|
485
|
-
var_type, options, _ = new_settings[key]
|
486
|
-
# Update the default value with the CSV value, keeping the type and options unchanged
|
487
|
-
new_settings[key] = (var_type, options, value)
|
488
|
-
return new_settings
|
489
|
-
|
490
71
|
def parse_list(value):
|
491
72
|
try:
|
492
73
|
parsed_value = ast.literal_eval(value)
|
@@ -530,33 +111,6 @@ def create_input_field(frame, label_text, row, var_type='entry', options=None, d
|
|
530
111
|
var = None # Placeholder in case of an undefined var_type
|
531
112
|
return (label, None, var)
|
532
113
|
|
533
|
-
def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label):
|
534
|
-
try:
|
535
|
-
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
536
|
-
while not q.empty():
|
537
|
-
message = q.get_nowait()
|
538
|
-
clean_message = ansi_escape_pattern.sub('', message)
|
539
|
-
if clean_message.startswith("Progress"):
|
540
|
-
progress_label.config(text=clean_message)
|
541
|
-
if clean_message.startswith("\rProgress"):
|
542
|
-
progress_label.config(text=clean_message)
|
543
|
-
elif clean_message.startswith("Successfully"):
|
544
|
-
progress_label.config(text=clean_message)
|
545
|
-
elif clean_message.startswith("Processing"):
|
546
|
-
progress_label.config(text=clean_message)
|
547
|
-
elif clean_message.startswith("scale"):
|
548
|
-
pass
|
549
|
-
elif clean_message.startswith("plot_cropped_arrays"):
|
550
|
-
pass
|
551
|
-
elif clean_message == "" or clean_message == "\r" or clean_message.strip() == "":
|
552
|
-
pass
|
553
|
-
else:
|
554
|
-
print(clean_message)
|
555
|
-
except Exception as e:
|
556
|
-
print(f"Error updating GUI canvas: {e}")
|
557
|
-
finally:
|
558
|
-
root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
|
559
|
-
|
560
114
|
def process_stdout_stderr(q):
|
561
115
|
"""
|
562
116
|
Redirect stdout and stderr to the queue q.
|
@@ -578,635 +132,8 @@ class WriteToQueue(io.TextIOBase):
|
|
578
132
|
def flush(self):
|
579
133
|
pass
|
580
134
|
|
581
|
-
def clear_canvas(canvas):
|
582
|
-
# Clear each plot (axes) in the figure
|
583
|
-
for ax in canvas.figure.get_axes():
|
584
|
-
ax.clear()
|
585
|
-
|
586
|
-
# Redraw the now empty canvas without changing its size
|
587
|
-
canvas.draw_idle()
|
588
|
-
|
589
|
-
def my_show():
|
590
|
-
"""
|
591
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
592
|
-
"""
|
593
|
-
fig = plt.gcf()
|
594
|
-
fig_queue.put(fig)
|
595
|
-
plt.close(fig)
|
596
|
-
|
597
|
-
def measure_crop_wrapper(settings, q, fig_queue):
|
598
|
-
"""
|
599
|
-
Wraps the measure_crop function to integrate with GUI processes.
|
600
|
-
|
601
|
-
Parameters:
|
602
|
-
- settings: dict, The settings for the measure_crop function.
|
603
|
-
- q: multiprocessing.Queue, Queue for logging messages to the GUI.
|
604
|
-
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
605
|
-
"""
|
606
|
-
|
607
|
-
# Temporarily override plt.show
|
608
|
-
original_show = plt.show
|
609
|
-
plt.show = my_show
|
610
|
-
|
611
|
-
try:
|
612
|
-
print('start')
|
613
|
-
spacr.measure.measure_crop(settings=settings)
|
614
|
-
except Exception as e:
|
615
|
-
errorMessage = f"Error during processing: {e}"
|
616
|
-
q.put(errorMessage)
|
617
|
-
traceback.print_exc()
|
618
|
-
finally:
|
619
|
-
plt.show = original_show
|
620
|
-
|
621
|
-
def preprocess_generate_masks_wrapper(settings, q, fig_queue):
|
622
|
-
"""
|
623
|
-
Wraps the measure_crop function to integrate with GUI processes.
|
624
|
-
|
625
|
-
Parameters:
|
626
|
-
- settings: dict, The settings for the measure_crop function.
|
627
|
-
- q: multiprocessing.Queue, Queue for logging messages to the GUI.
|
628
|
-
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
629
|
-
"""
|
630
|
-
|
631
|
-
# Temporarily override plt.show
|
632
|
-
original_show = plt.show
|
633
|
-
plt.show = my_show
|
634
|
-
|
635
|
-
try:
|
636
|
-
spacr.core.preprocess_generate_masks(src=settings['src'], settings=settings)
|
637
|
-
except Exception as e:
|
638
|
-
errorMessage = f"Error during processing: {e}"
|
639
|
-
q.put(errorMessage)
|
640
|
-
traceback.print_exc()
|
641
|
-
finally:
|
642
|
-
plt.show = original_show
|
643
|
-
|
644
|
-
def sequencing_wrapper(settings, q, fig_queue):
|
645
|
-
|
646
|
-
# Temporarily override plt.show
|
647
|
-
original_show = plt.show
|
648
|
-
plt.show = my_show
|
649
|
-
|
650
|
-
try:
|
651
|
-
spacr.sequencing.analyze_reads(settings=settings)
|
652
|
-
except Exception as e:
|
653
|
-
errorMessage = f"Error during processing: {e}"
|
654
|
-
q.put(errorMessage)
|
655
|
-
traceback.print_exc()
|
656
|
-
finally:
|
657
|
-
plt.show = original_show
|
658
|
-
|
659
|
-
def umap_wrapper(settings, q, fig_queue):
|
660
|
-
|
661
|
-
# Temporarily override plt.show
|
662
|
-
original_show = plt.show
|
663
|
-
plt.show = my_show
|
664
|
-
|
665
|
-
try:
|
666
|
-
spacr.core.generate_image_umap(settings=settings)
|
667
|
-
except Exception as e:
|
668
|
-
errorMessage = f"Error during processing: {e}"
|
669
|
-
q.put(errorMessage)
|
670
|
-
traceback.print_exc()
|
671
|
-
finally:
|
672
|
-
plt.show = original_show
|
673
|
-
|
674
|
-
def train_test_model_wrapper(settings, q, fig_queue):
|
675
|
-
"""
|
676
|
-
Wraps the measure_crop function to integrate with GUI processes.
|
677
|
-
|
678
|
-
Parameters:
|
679
|
-
- settings: dict, The settings for the measure_crop function.
|
680
|
-
- q: multiprocessing.Queue, Queue for logging messages to the GUI.
|
681
|
-
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
682
|
-
"""
|
683
|
-
|
684
|
-
def my_show():
|
685
|
-
"""
|
686
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
687
|
-
"""
|
688
|
-
fig = plt.gcf()
|
689
|
-
fig_queue.put(fig) # Queue the figure for GUI display
|
690
|
-
plt.close(fig) # Prevent the figure from being shown by plt.show()
|
691
|
-
|
692
|
-
# Temporarily override plt.show
|
693
|
-
original_show = plt.show
|
694
|
-
plt.show = my_show
|
695
|
-
|
696
|
-
try:
|
697
|
-
spacr.core.train_test_model(settings['src'], settings=settings)
|
698
|
-
except Exception as e:
|
699
|
-
errorMessage = f"Error during processing: {e}"
|
700
|
-
q.put(errorMessage) # Send the error message to the GUI via the queue
|
701
|
-
traceback.print_exc()
|
702
|
-
finally:
|
703
|
-
plt.show = original_show # Restore the original plt.show function
|
704
|
-
|
705
|
-
|
706
|
-
def run_multiple_simulations_wrapper(settings, q, fig_queue):
|
707
|
-
"""
|
708
|
-
Wraps the run_multiple_simulations function to integrate with GUI processes.
|
709
|
-
|
710
|
-
Parameters:
|
711
|
-
- settings: dict, The settings for the run_multiple_simulations function.
|
712
|
-
- q: multiprocessing.Queue, Queue for logging messages to the GUI.
|
713
|
-
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
714
|
-
"""
|
715
|
-
|
716
|
-
def my_show():
|
717
|
-
"""
|
718
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
719
|
-
"""
|
720
|
-
fig = plt.gcf()
|
721
|
-
fig_queue.put(fig) # Queue the figure for GUI display
|
722
|
-
plt.close(fig) # Prevent the figure from being shown by plt.show()
|
723
|
-
|
724
|
-
# Temporarily override plt.show
|
725
|
-
original_show = plt.show
|
726
|
-
plt.show = my_show
|
727
|
-
|
728
|
-
try:
|
729
|
-
spacr.sim.run_multiple_simulations(settings=settings)
|
730
|
-
except Exception as e:
|
731
|
-
errorMessage = f"Error during processing: {e}"
|
732
|
-
q.put(errorMessage) # Send the error message to the GUI via the queue
|
733
|
-
traceback.print_exc()
|
734
|
-
finally:
|
735
|
-
plt.show = original_show # Restore the original plt.show function
|
736
|
-
|
737
|
-
def convert_settings_dict_for_gui(settings):
|
738
|
-
variables = {}
|
739
|
-
special_cases = {
|
740
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
741
|
-
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
742
|
-
'cell_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
|
743
|
-
'nucleus_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
|
744
|
-
'pathogen_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
|
745
|
-
#'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
746
|
-
'magnification': ('combo', [20, 40, 60], 20),
|
747
|
-
'nucleus_channel': ('combo', [0, 1, 2, 3, None], None),
|
748
|
-
'cell_channel': ('combo', [0, 1, 2, 3, None], None),
|
749
|
-
'pathogen_channel': ('combo', [0, 1, 2, 3, None], None),
|
750
|
-
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
751
|
-
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
752
|
-
'model_type': ('combo', ['resnet50', 'other_model'], 'resnet50'),
|
753
|
-
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
754
|
-
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
755
|
-
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
756
|
-
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
757
|
-
}
|
758
|
-
|
759
|
-
for key, value in settings.items():
|
760
|
-
if key in special_cases:
|
761
|
-
variables[key] = special_cases[key]
|
762
|
-
elif isinstance(value, bool):
|
763
|
-
variables[key] = ('check', None, value)
|
764
|
-
elif isinstance(value, int) or isinstance(value, float):
|
765
|
-
variables[key] = ('entry', None, value)
|
766
|
-
elif isinstance(value, str):
|
767
|
-
variables[key] = ('entry', None, value)
|
768
|
-
elif value is None:
|
769
|
-
variables[key] = ('entry', None, value)
|
770
|
-
elif isinstance(value, list):
|
771
|
-
variables[key] = ('entry', None, str(value))
|
772
|
-
else:
|
773
|
-
variables[key] = ('entry', None, str(value))
|
774
|
-
return variables
|
775
|
-
|
776
|
-
def setup_frame(parent_frame):
|
777
|
-
style = ttk.Style(parent_frame)
|
778
|
-
set_dark_style(style)
|
779
|
-
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
780
|
-
parent_frame.configure(bg='black')
|
781
|
-
parent_frame.grid_rowconfigure(0, weight=1)
|
782
|
-
parent_frame.grid_columnconfigure(0, weight=1)
|
783
|
-
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, bg='black')
|
784
|
-
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
785
|
-
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, bg='black')
|
786
|
-
vertical_container.add(horizontal_container, stretch="always")
|
787
|
-
horizontal_container.grid_columnconfigure(0, weight=1)
|
788
|
-
horizontal_container.grid_columnconfigure(1, weight=1)
|
789
|
-
settings_frame = tk.Frame(horizontal_container, bg='black')
|
790
|
-
settings_frame.grid_rowconfigure(0, weight=0)
|
791
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
792
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
793
|
-
horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
|
794
|
-
return parent_frame, vertical_container, horizontal_container
|
795
|
-
|
796
|
-
def setup_settings_panel(vertical_container, settings_type='mask', frame_height=500, frame_width=1000):
|
797
|
-
global vars_dict, scrollable_frame
|
798
|
-
from .settings import set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model, get_analyze_reads_default_settings, set_default_umap_image_settings, generate_fields
|
799
|
-
|
800
|
-
print("Setting up settings panel")
|
801
|
-
|
802
|
-
# Create settings frame
|
803
|
-
settings_frame = tk.Frame(vertical_container, bg='black', height=frame_height, width=frame_width)
|
804
|
-
vertical_container.add(settings_frame, stretch="always")
|
805
|
-
|
806
|
-
# Add settings label
|
807
|
-
settings_label = spacrLabel(settings_frame, text="Settings", background="black", foreground="white", anchor='center', justify='center', align="center")
|
808
|
-
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
809
|
-
|
810
|
-
# Create a spacrFrame inside the settings_frame
|
811
|
-
scrollable_frame = spacrFrame(settings_frame, bg='black', width=frame_width)
|
812
|
-
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
813
|
-
|
814
|
-
# Configure the weights for resizing
|
815
|
-
settings_frame.grid_rowconfigure(1, weight=1)
|
816
|
-
settings_frame.grid_columnconfigure(0, weight=1)
|
817
|
-
|
818
|
-
# Load settings based on type
|
819
|
-
if settings_type == 'mask':
|
820
|
-
settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
|
821
|
-
elif settings_type == 'measure':
|
822
|
-
settings = get_measure_crop_settings(settings={})
|
823
|
-
elif settings_type == 'classify':
|
824
|
-
settings = set_default_train_test_model(settings={})
|
825
|
-
elif settings_type == 'sequencing':
|
826
|
-
settings = get_analyze_reads_default_settings(settings={})
|
827
|
-
elif settings_type == 'umap':
|
828
|
-
settings = set_default_umap_image_settings(settings={})
|
829
|
-
else:
|
830
|
-
raise ValueError(f"Invalid settings type: {settings_type}")
|
831
|
-
|
832
|
-
# Generate fields for settings
|
833
|
-
variables = convert_settings_dict_for_gui(settings)
|
834
|
-
vars_dict = generate_fields(variables, scrollable_frame)
|
835
|
-
print("Settings panel setup complete")
|
836
|
-
return scrollable_frame, vars_dict
|
837
|
-
|
838
|
-
def setup_plot_section(vertical_container):
|
839
|
-
global canvas, canvas_widget
|
840
|
-
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
841
|
-
vertical_container.add(plot_frame, stretch="always")
|
842
|
-
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
843
|
-
plot = figure.add_subplot(111)
|
844
|
-
plot.plot([], []) # This creates an empty plot.
|
845
|
-
plot.axis('off')
|
846
|
-
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
847
|
-
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
848
|
-
canvas_widget = canvas.get_tk_widget()
|
849
|
-
plot_frame.add(canvas_widget, stretch="always")
|
850
|
-
canvas.draw()
|
851
|
-
canvas.figure = figure
|
852
|
-
return canvas, canvas_widget
|
853
|
-
|
854
|
-
def setup_console(vertical_container):
|
855
|
-
global console_output
|
856
|
-
print("Setting up console frame")
|
857
|
-
console_frame = tk.Frame(vertical_container, bg='black')
|
858
|
-
vertical_container.add(console_frame, stretch="always")
|
859
|
-
console_label = spacrLabel(console_frame, text="Console", background="black", foreground="white", anchor='center', justify='center', align="center")
|
860
|
-
console_label.grid(row=0, column=0, pady=10, padx=10)
|
861
|
-
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
862
|
-
console_output.grid(row=1, column=0, sticky="nsew")
|
863
|
-
console_frame.grid_rowconfigure(1, weight=1)
|
864
|
-
console_frame.grid_columnconfigure(0, weight=1)
|
865
|
-
print("Console setup complete")
|
866
|
-
return console_output
|
867
|
-
|
868
|
-
def setup_progress_frame(vertical_container):
|
869
|
-
global progress_output
|
870
|
-
progress_frame = tk.Frame(vertical_container, bg='black')
|
871
|
-
vertical_container.add(progress_frame, stretch="always")
|
872
|
-
label_frame = tk.Frame(progress_frame, bg='black')
|
873
|
-
label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
|
874
|
-
progress_label = spacrLabel(label_frame, text="Processing: 0%", background="black", foreground="white", font=('Helvetica', 12), anchor='w', justify='left', align="left")
|
875
|
-
progress_label.grid(row=0, column=0, sticky="w")
|
876
|
-
progress_output = scrolledtext.ScrolledText(progress_frame, height=10, bg='black', fg='white', insertbackground='white')
|
877
|
-
progress_output.grid(row=1, column=0, sticky="nsew")
|
878
|
-
progress_frame.grid_rowconfigure(1, weight=1)
|
879
|
-
progress_frame.grid_columnconfigure(0, weight=1)
|
880
|
-
print("Progress frame setup complete")
|
881
|
-
return progress_output
|
882
|
-
|
883
|
-
def download_hug_dataset():
|
884
|
-
global vars_dict, q
|
885
|
-
repo_id = "einarolafsson/toxo_mito"
|
886
|
-
subfolder = "plate1"
|
887
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
|
888
|
-
try:
|
889
|
-
local_path = download_dataset(repo_id, subfolder, local_dir)
|
890
|
-
if 'src' in vars_dict:
|
891
|
-
vars_dict['src'][2].set(local_path) # Assuming vars_dict['src'] is a tuple and the 3rd element is a StringVar
|
892
|
-
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
893
|
-
q.put(f"Dataset downloaded to: {local_path}\n")
|
894
|
-
except Exception as e:
|
895
|
-
q.put(f"Failed to download dataset: {e}\n")
|
896
|
-
|
897
|
-
def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
898
|
-
global q
|
899
|
-
"""
|
900
|
-
Downloads a dataset from Hugging Face and returns the local path.
|
901
|
-
|
902
|
-
Args:
|
903
|
-
repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito').
|
904
|
-
subfolder (str): The subfolder path within the repository (e.g., 'plate1').
|
905
|
-
local_dir (str): The local directory where the dataset will be saved. Defaults to the user's home directory.
|
906
|
-
retries (int): Number of retry attempts in case of failure.
|
907
|
-
delay (int): Delay in seconds between retries.
|
908
|
-
|
909
|
-
Returns:
|
910
|
-
str: The local path to the downloaded dataset.
|
911
|
-
"""
|
912
|
-
if local_dir is None:
|
913
|
-
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
914
|
-
|
915
|
-
local_subfolder_dir = os.path.join(local_dir, subfolder)
|
916
|
-
if not os.path.exists(local_subfolder_dir):
|
917
|
-
os.makedirs(local_subfolder_dir)
|
918
|
-
elif len(os.listdir(local_subfolder_dir)) == 40:
|
919
|
-
q.put(f"Dataset already downloaded to: {local_subfolder_dir}")
|
920
|
-
return local_subfolder_dir
|
921
|
-
|
922
|
-
attempt = 0
|
923
|
-
while attempt < retries:
|
924
|
-
try:
|
925
|
-
files = list_repo_files(repo_id, repo_type="dataset")
|
926
|
-
subfolder_files = [file for file in files if file.startswith(subfolder)]
|
927
|
-
|
928
|
-
for file_name in subfolder_files:
|
929
|
-
for attempt in range(retries):
|
930
|
-
try:
|
931
|
-
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
932
|
-
response = requests.get(url, stream=True)
|
933
|
-
response.raise_for_status()
|
934
|
-
|
935
|
-
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
936
|
-
with open(local_file_path, 'wb') as file:
|
937
|
-
for chunk in response.iter_content(chunk_size=8192):
|
938
|
-
file.write(chunk)
|
939
|
-
q.put(f"Downloaded file: {file_name}")
|
940
|
-
break
|
941
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
942
|
-
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
943
|
-
time.sleep(delay)
|
944
|
-
else:
|
945
|
-
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
946
|
-
|
947
|
-
return local_subfolder_dir
|
948
|
-
|
949
|
-
except (requests.HTTPError, requests.Timeout) as e:
|
950
|
-
q.put(f"Error downloading dataset: {e}. Retrying in {delay} seconds...")
|
951
|
-
attempt += 1
|
952
|
-
time.sleep(delay)
|
953
|
-
|
954
|
-
raise Exception("Failed to download dataset after multiple attempts.")
|
955
|
-
|
956
|
-
def setup_button_section(horizontal_container, settings_type='mask', settings_row=5, run=True, abort=True, download=True, import_btn=True):
|
957
|
-
global button_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict
|
958
|
-
|
959
|
-
button_frame = tk.Frame(horizontal_container, bg='black')
|
960
|
-
horizontal_container.add(button_frame, stretch="always", sticky="nsew")
|
961
|
-
button_frame.grid_rowconfigure(0, weight=0)
|
962
|
-
button_frame.grid_rowconfigure(1, weight=1)
|
963
|
-
button_frame.grid_columnconfigure(0, weight=1)
|
964
|
-
|
965
|
-
categories_label = spacrLabel(button_frame, text="Categories", background="black", foreground="white", font=('Helvetica', 12), anchor='center', justify='center', align="center") # Increase font size
|
966
|
-
categories_label.grid(row=0, column=0, pady=10, padx=10)
|
967
|
-
|
968
|
-
button_scrollable_frame = spacrFrame(button_frame, bg='black')
|
969
|
-
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
970
|
-
|
971
|
-
btn_col = 0
|
972
|
-
btn_row = 1
|
973
|
-
|
974
|
-
if run:
|
975
|
-
run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue, settings_type), font=('Helvetica', 12))
|
976
|
-
run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
977
|
-
btn_row += 1
|
978
|
-
|
979
|
-
if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
|
980
|
-
abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 12))
|
981
|
-
abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
982
|
-
btn_row += 1
|
983
|
-
|
984
|
-
if download and settings_type in ['mask']:
|
985
|
-
download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Download", command=download_hug_dataset, font=('Helvetica', 12))
|
986
|
-
download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
987
|
-
btn_row += 1
|
988
|
-
|
989
|
-
if import_btn:
|
990
|
-
import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(settings_row, settings_type), font=('Helvetica', 12))
|
991
|
-
import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
|
992
|
-
|
993
|
-
# Call toggle_settings after vars_dict is initialized
|
994
|
-
if vars_dict is not None:
|
995
|
-
toggle_settings(button_scrollable_frame)
|
996
|
-
return button_scrollable_frame
|
997
|
-
|
998
|
-
def toggle_test_mode():
|
999
|
-
global vars_dict, test_mode_button
|
1000
|
-
current_state = vars_dict['test_mode'][2].get()
|
1001
|
-
new_state = not current_state
|
1002
|
-
vars_dict['test_mode'][2].set(new_state)
|
1003
|
-
if new_state:
|
1004
|
-
test_mode_button.config(bg="blue")
|
1005
|
-
else:
|
1006
|
-
test_mode_button.config(bg="gray")
|
1007
|
-
|
1008
|
-
def toggle_settings(button_scrollable_frame):
|
1009
|
-
global vars_dict
|
1010
|
-
from .settings import categories
|
1011
|
-
|
1012
|
-
if vars_dict is None:
|
1013
|
-
raise ValueError("vars_dict is not initialized.")
|
1014
|
-
|
1015
|
-
def toggle_category(settings, var):
|
1016
|
-
for setting in settings:
|
1017
|
-
if setting in vars_dict:
|
1018
|
-
label, widget, _ = vars_dict[setting]
|
1019
|
-
if var.get() == 0:
|
1020
|
-
label.grid_remove()
|
1021
|
-
widget.grid_remove()
|
1022
|
-
else:
|
1023
|
-
label.grid()
|
1024
|
-
widget.grid()
|
1025
|
-
|
1026
|
-
row = 1
|
1027
|
-
col = 2
|
1028
|
-
category_idx = 0
|
1029
|
-
|
1030
|
-
for category, settings in categories.items():
|
1031
|
-
if any(setting in vars_dict for setting in settings):
|
1032
|
-
category_var = tk.IntVar(value=0)
|
1033
|
-
vars_dict[category] = (None, None, category_var)
|
1034
|
-
toggle = spacrCheckbutton(
|
1035
|
-
button_scrollable_frame.scrollable_frame,
|
1036
|
-
text=category,
|
1037
|
-
variable=category_var,
|
1038
|
-
command=lambda cat=settings, var=category_var: toggle_category(cat, var)
|
1039
|
-
)
|
1040
|
-
# Directly set the style
|
1041
|
-
style = ttk.Style()
|
1042
|
-
font_style = tkFont.Font(family="Helvetica", size=12, weight="bold")
|
1043
|
-
style.configure('Spacr.TCheckbutton', font=font_style, background='black', foreground='#ffffff', indicatoron=False, relief='flat')
|
1044
|
-
style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
|
1045
|
-
toggle.configure(style='Spacr.TCheckbutton')
|
1046
|
-
toggle.grid(row=row, column=col, sticky="w", pady=2, padx=2)
|
1047
|
-
col += 1
|
1048
|
-
category_idx += 1
|
1049
|
-
|
1050
|
-
if category_idx % 4 == 0:
|
1051
|
-
row += 1
|
1052
|
-
col = 2
|
1053
|
-
|
1054
|
-
for settings in categories.values():
|
1055
|
-
for setting in settings:
|
1056
|
-
if setting in vars_dict:
|
1057
|
-
label, widget, _ = vars_dict[setting]
|
1058
|
-
label.grid_remove()
|
1059
|
-
widget.grid_remove()
|
1060
|
-
|
1061
|
-
def process_fig_queue():
|
1062
|
-
global canvas, fig_queue, canvas_widget, parent_frame
|
1063
|
-
try:
|
1064
|
-
while not fig_queue.empty():
|
1065
|
-
clear_canvas(canvas)
|
1066
|
-
fig = fig_queue.get_nowait()
|
1067
|
-
for ax in fig.get_axes():
|
1068
|
-
ax.set_xticks([]) # Remove x-axis ticks
|
1069
|
-
ax.set_yticks([]) # Remove y-axis ticks
|
1070
|
-
ax.xaxis.set_visible(False) # Hide the x-axis
|
1071
|
-
ax.yaxis.set_visible(False) # Hide the y-axis
|
1072
|
-
fig.tight_layout()
|
1073
|
-
fig.set_facecolor('black')
|
1074
|
-
canvas.figure = fig
|
1075
|
-
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
1076
|
-
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
1077
|
-
canvas.draw_idle()
|
1078
|
-
except Exception as e:
|
1079
|
-
traceback.print_exc()
|
1080
|
-
finally:
|
1081
|
-
after_id = canvas_widget.after(100, process_fig_queue)
|
1082
|
-
parent_frame.after_tasks.append(after_id)
|
1083
|
-
|
1084
|
-
def process_console_queue():
|
1085
|
-
global q, console_output, parent_frame
|
1086
|
-
while not q.empty():
|
1087
|
-
message = q.get_nowait()
|
1088
|
-
console_output.insert(tk.END, message)
|
1089
|
-
console_output.see(tk.END)
|
1090
|
-
after_id = console_output.after(100, process_console_queue)
|
1091
|
-
parent_frame.after_tasks.append(after_id)
|
1092
|
-
|
1093
|
-
def run_mask_gui(settings, q, fig_queue, stop_requested):
|
1094
|
-
process_stdout_stderr(q)
|
1095
|
-
try:
|
1096
|
-
preprocess_generate_masks_wrapper(settings, q, fig_queue)
|
1097
|
-
except Exception as e:
|
1098
|
-
q.put(f"Error during processing: {e}")
|
1099
|
-
traceback.print_exc()
|
1100
|
-
finally:
|
1101
|
-
stop_requested.value = 1
|
1102
|
-
|
1103
|
-
def run_sequencing_gui(settings, q, fig_queue, stop_requested):
|
1104
|
-
process_stdout_stderr(q)
|
1105
|
-
try:
|
1106
|
-
sequencing_wrapper(settings, q, fig_queue)
|
1107
|
-
except Exception as e:
|
1108
|
-
q.put(f"Error during processing: {e}")
|
1109
|
-
traceback.print_exc()
|
1110
|
-
finally:
|
1111
|
-
stop_requested.value = 1
|
1112
|
-
|
1113
|
-
def run_umap_gui(settings, q, fig_queue, stop_requested):
|
1114
|
-
process_stdout_stderr(q)
|
1115
|
-
try:
|
1116
|
-
umap_wrapper(settings, q, fig_queue)
|
1117
|
-
except Exception as e:
|
1118
|
-
q.put(f"Error during processing: {e}")
|
1119
|
-
traceback.print_exc()
|
1120
|
-
finally:
|
1121
|
-
stop_requested.value = 1
|
1122
|
-
|
1123
|
-
def run_measure_gui(settings, q, fig_queue, stop_requested):
|
1124
|
-
process_stdout_stderr(q)
|
1125
|
-
try:
|
1126
|
-
settings['input_folder'] = settings['src']
|
1127
|
-
measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
|
1128
|
-
except Exception as e:
|
1129
|
-
q.put(f"Error during processing: {e}")
|
1130
|
-
traceback.print_exc()
|
1131
|
-
finally:
|
1132
|
-
stop_requested.value = 1
|
1133
|
-
|
1134
|
-
def run_classify_gui(settings, q, fig_queue, stop_requested):
|
1135
|
-
process_stdout_stderr(q)
|
1136
|
-
try:
|
1137
|
-
train_test_model_wrapper(settings['src'], settings)
|
1138
|
-
except Exception as e:
|
1139
|
-
q.put(f"Error during processing: {e}")
|
1140
|
-
traceback.print_exc()
|
1141
|
-
finally:
|
1142
|
-
stop_requested.value = 1
|
1143
|
-
|
1144
|
-
def set_globals(q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, progress_label_var, fig_queue_var):
|
1145
|
-
global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue
|
1146
|
-
q = q_var
|
1147
|
-
console_output = console_output_var
|
1148
|
-
parent_frame = parent_frame_var
|
1149
|
-
vars_dict = vars_dict_var
|
1150
|
-
canvas = canvas_var
|
1151
|
-
canvas_widget = canvas_widget_var
|
1152
|
-
scrollable_frame = scrollable_frame_var
|
1153
|
-
progress_label = progress_label_var
|
1154
|
-
fig_queue = fig_queue_var
|
1155
|
-
|
1156
|
-
def initiate_root(parent, settings_type='mask'):
|
1157
|
-
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, progress_label, button_scrollable_frame
|
1158
|
-
print("Initializing root with settings_type:", settings_type)
|
1159
|
-
parent_frame = parent
|
1160
|
-
|
1161
|
-
if not hasattr(parent_frame, 'after_tasks'):
|
1162
|
-
parent_frame.after_tasks = []
|
1163
|
-
|
1164
|
-
for widget in parent_frame.winfo_children():
|
1165
|
-
if widget.winfo_exists():
|
1166
|
-
try:
|
1167
|
-
widget.destroy()
|
1168
|
-
except tk.TclError as e:
|
1169
|
-
print(f"Error destroying widget: {e}")
|
1170
|
-
|
1171
|
-
q = Queue()
|
1172
|
-
fig_queue = Queue()
|
1173
|
-
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
1174
|
-
scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type) # Adjust height and width as needed
|
1175
|
-
button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
|
1176
|
-
canvas, canvas_widget = setup_plot_section(vertical_container)
|
1177
|
-
console_output = setup_console(vertical_container)
|
1178
|
-
|
1179
|
-
if settings_type in ['mask', 'measure', 'classify', 'sequencing']:
|
1180
|
-
progress_output = setup_progress_frame(vertical_container)
|
1181
|
-
else:
|
1182
|
-
progress_output = None
|
1183
|
-
|
1184
|
-
set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue)
|
1185
|
-
process_console_queue()
|
1186
|
-
process_fig_queue()
|
1187
|
-
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
1188
|
-
parent_frame.after_tasks.append(after_id)
|
1189
|
-
print("Root initialization complete")
|
1190
|
-
return parent_frame, vars_dict
|
1191
|
-
|
1192
135
|
def cancel_after_tasks(frame):
|
1193
136
|
if hasattr(frame, 'after_tasks'):
|
1194
137
|
for task in frame.after_tasks:
|
1195
138
|
frame.after_cancel(task)
|
1196
139
|
frame.after_tasks.clear()
|
1197
|
-
|
1198
|
-
def start_gui_app(settings_type='mask'):
|
1199
|
-
global q, fig_queue, parent_frame, scrollable_frame, vars_dict, canvas, canvas_widget, progress_label
|
1200
|
-
root = tk.Tk()
|
1201
|
-
width = root.winfo_screenwidth()
|
1202
|
-
height = root.winfo_screenheight()
|
1203
|
-
root.geometry(f"{width}x{height}")
|
1204
|
-
root.title(f"SpaCr: {settings_type.capitalize()}")
|
1205
|
-
root.content_frame = tk.Frame(root)
|
1206
|
-
print("Starting GUI app with settings_type:", settings_type)
|
1207
|
-
initiate_root(root.content_frame, settings_type)
|
1208
|
-
create_menu_bar(root)
|
1209
|
-
root.mainloop()
|
1210
|
-
|
1211
|
-
|
1212
|
-
|