spacr 0.1.16__py3-none-any.whl → 0.1.50__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/app_annotate.py +1 -1
- spacr/app_classify.py +5 -203
- spacr/app_make_masks.py +1 -1
- spacr/app_make_masks_v2.py +2 -2
- spacr/app_mask.py +5 -250
- spacr/app_measure.py +5 -250
- spacr/deep_spacr.py +3 -1
- spacr/gui.py +22 -13
- spacr/gui_utils.py +1001 -681
- spacr/measure.py +24 -3
- spacr/settings.py +13 -2
- spacr/utils.py +59 -5
- {spacr-0.1.16.dist-info → spacr-0.1.50.dist-info}/METADATA +2 -1
- {spacr-0.1.16.dist-info → spacr-0.1.50.dist-info}/RECORD +18 -18
- spacr-0.1.50.dist-info/entry_points.txt +8 -0
- spacr-0.1.16.dist-info/entry_points.txt +0 -8
- {spacr-0.1.16.dist-info → spacr-0.1.50.dist-info}/LICENSE +0 -0
- {spacr-0.1.16.dist-info → spacr-0.1.50.dist-info}/WHEEL +0 -0
- {spacr-0.1.16.dist-info → spacr-0.1.50.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py
CHANGED
@@ -1,184 +1,124 @@
|
|
1
|
-
import os, spacr, inspect, traceback, io, sys, ast, ctypes, matplotlib, re, csv, requests
|
1
|
+
import os, spacr, inspect, traceback, io, sys, ast, ctypes, matplotlib, re, csv, requests, ast
|
2
2
|
import matplotlib.pyplot as plt
|
3
3
|
matplotlib.use('Agg')
|
4
4
|
import numpy as np
|
5
5
|
import tkinter as tk
|
6
6
|
from tkinter import ttk, messagebox
|
7
7
|
import tkinter.font as tkFont
|
8
|
+
from tkinter import filedialog
|
9
|
+
from tkinter import Checkbutton
|
10
|
+
from tkinter import font as tkFont
|
8
11
|
from torchvision import models
|
9
|
-
#from ttf_opensans import opensans
|
10
12
|
|
11
|
-
from
|
13
|
+
from multiprocessing import Process, Value, Queue, Manager, set_start_method
|
14
|
+
import multiprocessing as mp
|
15
|
+
|
16
|
+
from tkinter import ttk, scrolledtext
|
17
|
+
from matplotlib.figure import Figure
|
18
|
+
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
19
|
+
import time
|
20
|
+
import requests
|
21
|
+
from requests.exceptions import HTTPError, Timeout
|
22
|
+
from huggingface_hub import list_repo_files, hf_hub_download
|
12
23
|
|
13
24
|
from .logger import log_function_call
|
25
|
+
from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks
|
14
26
|
|
15
27
|
try:
|
16
28
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
17
29
|
except AttributeError:
|
18
30
|
pass
|
19
31
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
y = event.y_root + 10
|
31
|
-
self.tooltip_window = tk.Toplevel(self.widget)
|
32
|
-
self.tooltip_window.wm_overrideredirect(True)
|
33
|
-
self.tooltip_window.wm_geometry(f"+{x}+{y}")
|
34
|
-
label = tk.Label(self.tooltip_window, text=self.text, background="yellow", relief='solid', borderwidth=1)
|
35
|
-
label.pack()
|
36
|
-
|
37
|
-
def hide_tooltip(self, event):
|
38
|
-
if self.tooltip_window:
|
39
|
-
self.tooltip_window.destroy()
|
40
|
-
self.tooltip_window = None
|
41
|
-
|
42
|
-
def load_app(root, app_name, app_func):
|
43
|
-
# Cancel all scheduled after tasks
|
44
|
-
if hasattr(root, 'after_tasks'):
|
45
|
-
for task in root.after_tasks:
|
46
|
-
root.after_cancel(task)
|
47
|
-
root.after_tasks = []
|
48
|
-
|
49
|
-
# Exit functionality only for the annotation app
|
50
|
-
if app_name == "Annotate" and hasattr(root, 'current_app_exit_func'):
|
51
|
-
root.current_app_exit_func()
|
52
|
-
|
53
|
-
# Clear the current content frame
|
54
|
-
if hasattr(root, 'content_frame'):
|
55
|
-
for widget in root.content_frame.winfo_children():
|
56
|
-
widget.destroy()
|
57
|
-
else:
|
58
|
-
root.content_frame = tk.Frame(root)
|
59
|
-
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
60
|
-
root.grid_rowconfigure(1, weight=1)
|
61
|
-
root.grid_columnconfigure(0, weight=1)
|
62
|
-
|
63
|
-
# Initialize the new app in the content frame
|
64
|
-
app_func(root.content_frame)
|
65
|
-
|
66
|
-
def create_menu_bar(root):
|
67
|
-
from .app_mask import initiate_mask_root
|
68
|
-
from .app_measure import initiate_measure_root
|
69
|
-
from .app_annotate import initiate_annotation_app_root
|
70
|
-
from .app_make_masks import initiate_mask_app_root
|
71
|
-
from .app_classify import initiate_classify_root
|
72
|
-
|
73
|
-
gui_apps = {
|
74
|
-
"Mask": initiate_mask_root,
|
75
|
-
"Measure": initiate_measure_root,
|
76
|
-
"Annotate": initiate_annotation_app_root,
|
77
|
-
"Make Masks": initiate_mask_app_root,
|
78
|
-
"Classify": initiate_classify_root
|
79
|
-
}
|
80
|
-
|
81
|
-
def load_app_wrapper(app_name, app_func):
|
82
|
-
load_app(root, app_name, app_func)
|
83
|
-
|
84
|
-
# Create the menu bar
|
85
|
-
menu_bar = tk.Menu(root, bg="#008080", fg="white")
|
86
|
-
# Create a "SpaCr Applications" menu
|
87
|
-
app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
|
88
|
-
menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
|
89
|
-
# Add options to the "SpaCr Applications" menu
|
90
|
-
for app_name, app_func in gui_apps.items():
|
91
|
-
app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
|
92
|
-
# Add a separator and an exit option
|
93
|
-
app_menu.add_separator()
|
94
|
-
app_menu.add_command(label="Exit", command=root.quit)
|
95
|
-
# Configure the menu for the root window
|
96
|
-
root.config(menu=menu_bar)
|
97
|
-
|
98
|
-
def proceed_with_app(root, app_name, app_func):
|
32
|
+
# Define global variables
|
33
|
+
q = None
|
34
|
+
console_output = None
|
35
|
+
parent_frame = None
|
36
|
+
vars_dict = None
|
37
|
+
canvas = None
|
38
|
+
canvas_widget = None
|
39
|
+
scrollable_frame = None
|
40
|
+
progress_label = None
|
41
|
+
fig_queue = None
|
99
42
|
|
100
|
-
|
101
|
-
from .app_measure import gui_measure
|
102
|
-
from .app_annotate import gui_annotate
|
103
|
-
from .app_make_masks import gui_make_masks
|
104
|
-
from .app_classify import gui_classify
|
105
|
-
from .gui import gui_app
|
43
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
106
44
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
45
|
+
def set_dark_style(style):
|
46
|
+
font_style = tkFont.Font(family="Helvetica", size=10)
|
47
|
+
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style) # Entry
|
48
|
+
style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style) # Combobox
|
49
|
+
style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
|
50
|
+
style.map('Custom.TButton',
|
51
|
+
background=[('active', 'teal'), ('!active', 'black')],
|
52
|
+
foreground=[('active', 'white'), ('!active', 'white')],
|
53
|
+
bordercolor=[('active', 'white'), ('!active', 'white')])
|
54
|
+
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style) # Custom Label
|
55
|
+
style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style) # Checkbutton
|
56
|
+
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
57
|
+
style.configure('TLabel', background='black', foreground='#ffffff', font=font_style) # Label
|
58
|
+
style.configure('TFrame', background='black') # Frame
|
59
|
+
style.configure('TPanedwindow', background='black') # PanedWindow
|
60
|
+
style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0]) # Notebook
|
61
|
+
style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
|
62
|
+
style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')])
|
63
|
+
style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style) # Button (regular)
|
64
|
+
style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
|
65
|
+
style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black') # Scrollbar
|
66
|
+
style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black') # Scrollbar
|
116
67
|
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
elif app_name == "Mask":
|
122
|
-
gui_mask()
|
123
|
-
elif app_name == "Measure":
|
124
|
-
gui_measure()
|
125
|
-
elif app_name == "Annotate":
|
126
|
-
gui_annotate()
|
127
|
-
elif app_name == "Make Masks":
|
128
|
-
gui_make_masks()
|
129
|
-
elif app_name == "Classify":
|
130
|
-
gui_classify()
|
131
|
-
else:
|
132
|
-
raise ValueError(f"Invalid app name: {app_name}")
|
68
|
+
# Define custom LabelFrame style
|
69
|
+
style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
|
70
|
+
style.configure('Custom.TLabelFrame.Label', background='black', foreground='white') # Style for the Label inside LabelFrame
|
71
|
+
style.configure('Custom.TLabelFrame.Label', font=('Helvetica', 10, 'bold'))
|
133
72
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
root.
|
73
|
+
def set_default_font(root, font_name="Helvetica", size=12):
|
74
|
+
default_font = (font_name, size)
|
75
|
+
root.option_add("*Font", default_font)
|
76
|
+
root.option_add("*TButton.Font", default_font)
|
77
|
+
root.option_add("*TLabel.Font", default_font)
|
78
|
+
root.option_add("*TEntry.Font", default_font)
|
140
79
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
80
|
+
class ScrollableFrame(ttk.Frame):
|
81
|
+
def __init__(self, container, width=None, *args, bg='black', **kwargs):
|
82
|
+
super().__init__(container, *args, **kwargs)
|
83
|
+
self.configure(style='TFrame')
|
84
|
+
if width is None:
|
85
|
+
screen_width = self.winfo_screenwidth()
|
86
|
+
width = screen_width // 4
|
87
|
+
canvas = tk.Canvas(self, bg=bg, width=width)
|
88
|
+
scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
|
89
|
+
|
90
|
+
self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
|
91
|
+
self.scrollable_frame.bind(
|
92
|
+
"<Configure>",
|
93
|
+
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
94
|
+
)
|
95
|
+
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
96
|
+
canvas.configure(yscrollcommand=scrollbar.set)
|
97
|
+
|
98
|
+
canvas.grid(row=0, column=0, sticky="nsew")
|
99
|
+
scrollbar.grid(row=0, column=1, sticky="ns")
|
148
100
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
from .gui import gui_app
|
101
|
+
self.grid_rowconfigure(0, weight=1)
|
102
|
+
self.grid_columnconfigure(0, weight=1)
|
103
|
+
self.grid_columnconfigure(1, weight=0)
|
104
|
+
|
105
|
+
for child in self.scrollable_frame.winfo_children():
|
106
|
+
child.configure(bg='black')
|
156
107
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
"Measure": initiate_measure_root,
|
161
|
-
"Annotate": initiate_annotation_app_root,
|
162
|
-
"Make Masks": initiate_mask_app_root,
|
163
|
-
"Classify": initiate_classify_root
|
164
|
-
}
|
108
|
+
class StdoutRedirector:
|
109
|
+
def __init__(self, text_widget):
|
110
|
+
self.text_widget = text_widget
|
165
111
|
|
166
|
-
def
|
167
|
-
|
112
|
+
def write(self, string):
|
113
|
+
try:
|
114
|
+
if self.text_widget.winfo_exists():
|
115
|
+
self.text_widget.insert(tk.END, string)
|
116
|
+
self.text_widget.see(tk.END)
|
117
|
+
except tk.TclError:
|
118
|
+
pass # Handle or log the error as needed
|
168
119
|
|
169
|
-
|
170
|
-
|
171
|
-
# Create a "SpaCr Applications" menu
|
172
|
-
app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
|
173
|
-
menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
|
174
|
-
# Add options to the "SpaCr Applications" menu
|
175
|
-
for app_name, app_func in gui_apps.items():
|
176
|
-
app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
|
177
|
-
# Add a separator and an exit option
|
178
|
-
app_menu.add_separator()
|
179
|
-
app_menu.add_command(label="Exit", command=root.destroy) # Use root.destroy instead of root.quit
|
180
|
-
# Configure the menu for the root window
|
181
|
-
root.config(menu=menu_bar)
|
120
|
+
def flush(self):
|
121
|
+
pass
|
182
122
|
|
183
123
|
class CustomButton(tk.Frame):
|
184
124
|
def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
|
@@ -328,77 +268,104 @@ class ToggleSwitch(ttk.Frame):
|
|
328
268
|
x1, y1]
|
329
269
|
|
330
270
|
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
331
|
-
|
332
|
-
def set_default_font(root, font_name="Helvetica", size=12):
|
333
|
-
default_font = (font_name, size)
|
334
|
-
root.option_add("*Font", default_font)
|
335
|
-
root.option_add("*TButton.Font", default_font)
|
336
|
-
root.option_add("*TLabel.Font", default_font)
|
337
|
-
root.option_add("*TEntry.Font", default_font)
|
338
271
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
272
|
+
class ToolTip:
|
273
|
+
def __init__(self, widget, text):
|
274
|
+
self.widget = widget
|
275
|
+
self.text = text
|
276
|
+
self.tooltip_window = None
|
277
|
+
widget.bind("<Enter>", self.show_tooltip)
|
278
|
+
widget.bind("<Leave>", self.hide_tooltip)
|
279
|
+
|
280
|
+
def show_tooltip(self, event):
|
281
|
+
x = event.x_root + 20
|
282
|
+
y = event.y_root + 10
|
283
|
+
self.tooltip_window = tk.Toplevel(self.widget)
|
284
|
+
self.tooltip_window.wm_overrideredirect(True)
|
285
|
+
self.tooltip_window.wm_geometry(f"+{x}+{y}")
|
286
|
+
label = tk.Label(self.tooltip_window, text=self.text, background="yellow", relief='solid', borderwidth=1)
|
287
|
+
label.grid(row=0, column=0, padx=5, pady=5)
|
288
|
+
|
289
|
+
def hide_tooltip(self, event):
|
290
|
+
if self.tooltip_window:
|
291
|
+
self.tooltip_window.destroy()
|
292
|
+
self.tooltip_window = None
|
293
|
+
|
294
|
+
def create_menu_bar(root):
|
295
|
+
from .app_annotate import initiate_annotation_app_root
|
296
|
+
from .app_make_masks import initiate_mask_app_root
|
297
|
+
|
298
|
+
gui_apps = {
|
299
|
+
"Mask": 'mask',
|
300
|
+
"Measure": 'measure',
|
301
|
+
"Annotate": initiate_annotation_app_root,
|
302
|
+
"Make Masks": initiate_mask_app_root,
|
303
|
+
"Classify": 'classify'
|
304
|
+
}
|
305
|
+
|
306
|
+
def load_app_wrapper(app_name, app_func):
|
307
|
+
load_app(root, app_name, app_func)
|
308
|
+
|
309
|
+
# Create the menu bar
|
310
|
+
menu_bar = tk.Menu(root, bg="#008080", fg="white")
|
311
|
+
# Create a "SpaCr Applications" menu
|
312
|
+
app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
|
313
|
+
menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
|
314
|
+
# Add options to the "SpaCr Applications" menu
|
315
|
+
for app_name, app_func in gui_apps.items():
|
316
|
+
app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
|
317
|
+
# Add a separator and an exit option
|
318
|
+
app_menu.add_separator()
|
319
|
+
app_menu.add_command(label="Exit", command=root.quit)
|
320
|
+
# Configure the menu for the root window
|
321
|
+
root.config(menu=menu_bar)
|
322
|
+
|
323
|
+
def proceed_with_app(root, app_name, app_func):
|
324
|
+
from .app_annotate import gui_annotate
|
325
|
+
from .app_make_masks import gui_make_masks
|
326
|
+
from .gui import gui_app
|
327
|
+
|
328
|
+
# Clear the current content frame
|
329
|
+
if hasattr(root, 'content_frame'):
|
330
|
+
for widget in root.content_frame.winfo_children():
|
331
|
+
try:
|
332
|
+
widget.destroy()
|
333
|
+
except tk.TclError as e:
|
334
|
+
print(f"Error destroying widget: {e}")
|
364
335
|
else:
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
def set_dark_style_v1(style):
|
370
|
-
font_style = tkFont.Font(family="Helvetica", size=10)
|
371
|
-
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
|
372
|
-
style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
|
373
|
-
style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
|
374
|
-
style.map('Custom.TButton',
|
375
|
-
background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
|
376
|
-
foreground=[('active', '#ffffff'), ('disabled', '#888888')])
|
377
|
-
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style)
|
378
|
-
style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
|
379
|
-
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
336
|
+
root.content_frame = tk.Frame(root)
|
337
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
338
|
+
root.grid_rowconfigure(1, weight=1)
|
339
|
+
root.grid_columnconfigure(0, weight=1)
|
380
340
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
341
|
+
# Initialize the new app in the content frame
|
342
|
+
if app_name == "Mask":
|
343
|
+
initiate_root(root.content_frame, 'mask')
|
344
|
+
elif app_name == "Measure":
|
345
|
+
initiate_root(root.content_frame, 'measure')
|
346
|
+
elif app_name == "Classify":
|
347
|
+
initiate_root(root.content_frame, 'classify')
|
348
|
+
elif app_name == "Annotate":
|
349
|
+
gui_annotate()
|
350
|
+
elif app_name == "Make Masks":
|
351
|
+
gui_make_masks()
|
352
|
+
else:
|
353
|
+
raise ValueError(f"Invalid app name: {app_name}")
|
354
|
+
|
355
|
+
def load_app(root, app_name, app_func):
|
356
|
+
# Cancel all scheduled after tasks
|
357
|
+
if hasattr(root, 'after_tasks'):
|
358
|
+
for task in root.after_tasks:
|
359
|
+
root.after_cancel(task)
|
360
|
+
root.after_tasks = []
|
361
|
+
|
362
|
+
# Exit functionality only for the annotation app
|
363
|
+
if app_name != "Annotate" and hasattr(root, 'current_app_exit_func'):
|
364
|
+
root.next_app_func = proceed_with_app
|
365
|
+
root.next_app_args = (app_name, app_func) # Ensure correct arguments
|
366
|
+
root.current_app_exit_func()
|
367
|
+
else:
|
368
|
+
proceed_with_app(root, app_name, app_func)
|
402
369
|
|
403
370
|
def read_settings_from_csv(csv_file_path):
|
404
371
|
settings = {}
|
@@ -420,353 +387,321 @@ def update_settings_from_csv(variables, csv_settings):
|
|
420
387
|
new_settings[key] = (var_type, options, value)
|
421
388
|
return new_settings
|
422
389
|
|
423
|
-
def
|
390
|
+
def parse_list(value):
|
424
391
|
try:
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
# If it fails, return the value as it is (a string)
|
429
|
-
return value
|
430
|
-
|
431
|
-
def disable_interactivity(fig):
|
432
|
-
if hasattr(fig.canvas, 'toolbar'):
|
433
|
-
fig.canvas.toolbar.pack_forget()
|
434
|
-
|
435
|
-
event_handlers = fig.canvas.callbacks.callbacks
|
436
|
-
for event, handlers in list(event_handlers.items()):
|
437
|
-
for handler_id in list(handlers.keys()):
|
438
|
-
fig.canvas.mpl_disconnect(handler_id)
|
439
|
-
|
440
|
-
class ScrollableFrame_v1(ttk.Frame):
|
441
|
-
def __init__(self, container, *args, bg='black', **kwargs):
|
442
|
-
super().__init__(container, *args, **kwargs)
|
443
|
-
self.configure(style='TFrame')
|
444
|
-
screen_width = self.winfo_screenwidth()
|
445
|
-
frame_width = screen_width // 4 # Set the frame width to 1/4th of the screen width
|
446
|
-
canvas = tk.Canvas(self, bg=bg, width=frame_width) # Set canvas background to match dark mode
|
447
|
-
scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
|
448
|
-
self.scrollable_frame = ttk.Frame(canvas, style='TFrame', padding=5) # Ensure it uses the styled frame
|
449
|
-
self.scrollable_frame.bind(
|
450
|
-
"<Configure>",
|
451
|
-
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
452
|
-
)
|
453
|
-
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
454
|
-
canvas.configure(yscrollcommand=scrollbar.set)
|
455
|
-
canvas.pack(side="left", fill="both", expand=True)
|
456
|
-
scrollbar.pack(side="right", fill="y")
|
457
|
-
|
458
|
-
class ScrollableFrame(ttk.Frame):
|
459
|
-
def __init__(self, container, width=None, *args, bg='black', **kwargs):
|
460
|
-
super().__init__(container, *args, **kwargs)
|
461
|
-
self.configure(style='TFrame')
|
462
|
-
if width is None:
|
463
|
-
screen_width = self.winfo_screenwidth()
|
464
|
-
width = screen_width // 4
|
465
|
-
canvas = tk.Canvas(self, bg=bg, width=width)
|
466
|
-
scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
|
467
|
-
|
468
|
-
self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
|
469
|
-
self.scrollable_frame.bind(
|
470
|
-
"<Configure>",
|
471
|
-
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
472
|
-
)
|
473
|
-
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
474
|
-
canvas.configure(yscrollcommand=scrollbar.set)
|
475
|
-
canvas.pack(side="left", fill="both", expand=True)
|
476
|
-
scrollbar.pack(side="right", fill="y")
|
477
|
-
for child in self.scrollable_frame.winfo_children():
|
478
|
-
child.configure(bg='black')
|
479
|
-
|
480
|
-
class StdoutRedirector:
|
481
|
-
def __init__(self, text_widget):
|
482
|
-
self.text_widget = text_widget
|
483
|
-
|
484
|
-
def write(self, string):
|
485
|
-
try:
|
486
|
-
if self.text_widget.winfo_exists():
|
487
|
-
self.text_widget.insert(tk.END, string)
|
488
|
-
self.text_widget.see(tk.END)
|
489
|
-
except tk.TclError:
|
490
|
-
pass # Handle or log the error as needed
|
491
|
-
|
492
|
-
def flush(self):
|
493
|
-
pass
|
494
|
-
|
495
|
-
def check_mask_gui_settings(vars_dict):
|
496
|
-
settings = {}
|
497
|
-
for key, var in vars_dict.items():
|
498
|
-
value = var.get()
|
499
|
-
|
500
|
-
# Handle conversion for specific types if necessary
|
501
|
-
if key in ['channels', 'timelapse_frame_limits']: # Assuming these should be lists
|
502
|
-
try:
|
503
|
-
# Convert string representation of a list into an actual list
|
504
|
-
settings[key] = eval(value)
|
505
|
-
except:
|
506
|
-
messagebox.showerror("Error", f"Invalid format for {key}. Please enter a valid list.")
|
507
|
-
return
|
508
|
-
elif key in ['nucleus_channel', 'cell_channel', 'pathogen_channel', 'examples_to_plot', 'batch_size', 'timelapse_memory', 'workers', 'fps', 'magnification']: # Assuming these should be integers
|
509
|
-
try:
|
510
|
-
settings[key] = int(value) if value else None
|
511
|
-
except ValueError:
|
512
|
-
messagebox.showerror("Error", f"Invalid number for {key}.")
|
513
|
-
return
|
514
|
-
elif key in ['nucleus_background', 'cell_background', 'pathogen_background', 'nucleus_Signal_to_noise', 'cell_Signal_to_noise', 'pathogen_Signal_to_noise', 'nucleus_CP_prob', 'cell_CP_prob', 'pathogen_CP_prob', 'lower_quantile']: # Assuming these should be floats
|
515
|
-
try:
|
516
|
-
settings[key] = float(value) if value else None
|
517
|
-
except ValueError:
|
518
|
-
messagebox.showerror("Error", f"Invalid number for {key}.")
|
519
|
-
return
|
392
|
+
parsed_value = ast.literal_eval(value)
|
393
|
+
if isinstance(parsed_value, list):
|
394
|
+
return parsed_value
|
520
395
|
else:
|
521
|
-
|
522
|
-
|
396
|
+
raise ValueError
|
397
|
+
except (ValueError, SyntaxError):
|
398
|
+
raise ValueError("Invalid format for list")
|
399
|
+
|
523
400
|
|
524
|
-
def
|
401
|
+
def check_settings(vars_dict):
|
402
|
+
global q
|
525
403
|
settings = {}
|
526
|
-
for key,
|
527
|
-
|
404
|
+
# Define the expected types for each key, including None where applicable
|
405
|
+
expected_types = {
|
406
|
+
"src": str,
|
407
|
+
"metadata_type": str,
|
408
|
+
"custom_regex": (str, type(None)),
|
409
|
+
"experiment": str,
|
410
|
+
"channels": list,
|
411
|
+
"magnification": int,
|
412
|
+
"nucleus_channel": (int, type(None)),
|
413
|
+
"nucleus_background": int,
|
414
|
+
"nucleus_Signal_to_noise": float,
|
415
|
+
"nucleus_CP_prob": float,
|
416
|
+
"nucleus_FT": float,
|
417
|
+
"cell_channel": (int, type(None)),
|
418
|
+
"cell_background": (int, float),
|
419
|
+
"cell_Signal_to_noise": (int, float),
|
420
|
+
"cell_CP_prob": (int, float),
|
421
|
+
"cell_FT": (int, float),
|
422
|
+
"pathogen_channel": (int, type(None)),
|
423
|
+
"pathogen_background": (int, float),
|
424
|
+
"pathogen_Signal_to_noise": (int, float),
|
425
|
+
"pathogen_CP_prob": (int, float),
|
426
|
+
"pathogen_FT": (int, float),
|
427
|
+
"preprocess": bool,
|
428
|
+
"masks": bool,
|
429
|
+
"examples_to_plot": int,
|
430
|
+
"randomize": bool,
|
431
|
+
"batch_size": int,
|
432
|
+
"timelapse": bool,
|
433
|
+
"timelapse_displacement": int,
|
434
|
+
"timelapse_memory": int,
|
435
|
+
"timelapse_frame_limits": list, # This can be a list of lists
|
436
|
+
"timelapse_remove_transient": bool,
|
437
|
+
"timelapse_mode": str,
|
438
|
+
"timelapse_objects": list,
|
439
|
+
"fps": int,
|
440
|
+
"remove_background": bool,
|
441
|
+
"lower_percentile": (int, float),
|
442
|
+
"merge_pathogens": bool,
|
443
|
+
"normalize_plots": bool,
|
444
|
+
"all_to_mip": bool,
|
445
|
+
"pick_slice": bool,
|
446
|
+
"skip_mode": str,
|
447
|
+
"save": bool,
|
448
|
+
"plot": bool,
|
449
|
+
"workers": int,
|
450
|
+
"verbose": bool,
|
451
|
+
"input_folder": str,
|
452
|
+
"cell_mask_dim": int,
|
453
|
+
"cell_min_size": int,
|
454
|
+
"cytoplasm_min_size": int,
|
455
|
+
"nucleus_mask_dim": int,
|
456
|
+
"nucleus_min_size": int,
|
457
|
+
"pathogen_mask_dim": int,
|
458
|
+
"pathogen_min_size": int,
|
459
|
+
"save_png": bool,
|
460
|
+
"crop_mode": list,
|
461
|
+
"use_bounding_box": bool,
|
462
|
+
"png_size": list, # This can be a list of lists
|
463
|
+
"normalize": bool,
|
464
|
+
"png_dims": list,
|
465
|
+
"normalize_by": str,
|
466
|
+
"save_measurements": bool,
|
467
|
+
"representative_images": bool,
|
468
|
+
"plot_filtration": bool,
|
469
|
+
"include_uninfected": bool,
|
470
|
+
"dialate_pngs": bool,
|
471
|
+
"dialate_png_ratios": list,
|
472
|
+
"max_workers": int,
|
473
|
+
"cells": list,
|
474
|
+
"cell_loc": list,
|
475
|
+
"pathogens": list,
|
476
|
+
"pathogen_loc": (list, list), # This can be a list of lists
|
477
|
+
"treatments": list,
|
478
|
+
"treatment_loc": (list, list), # This can be a list of lists
|
479
|
+
"channel_of_interest": int,
|
480
|
+
"compartments": list,
|
481
|
+
"measurement": str,
|
482
|
+
"nr_imgs": int,
|
483
|
+
"um_per_pixel": (int, float),
|
484
|
+
# Additional settings based on provided defaults
|
485
|
+
"include_noninfected": bool,
|
486
|
+
"include_multiinfected": bool,
|
487
|
+
"include_multinucleated": bool,
|
488
|
+
"filter_min_max": (list, type(None)),
|
489
|
+
"channel_dims": list,
|
490
|
+
"backgrounds": list,
|
491
|
+
"outline_thickness": int,
|
492
|
+
"outline_color": str,
|
493
|
+
"overlay_chans": list,
|
494
|
+
"overlay": bool,
|
495
|
+
"normalization_percentiles": list,
|
496
|
+
"print_object_number": bool,
|
497
|
+
"nr": int,
|
498
|
+
"figuresize": int,
|
499
|
+
"cmap": str,
|
500
|
+
"test_mode": bool,
|
501
|
+
"test_images": int,
|
502
|
+
"remove_background_cell": bool,
|
503
|
+
"remove_background_nucleus": bool,
|
504
|
+
"remove_background_pathogen": bool,
|
505
|
+
"pathogen_model": (str, type(None)),
|
506
|
+
"filter": bool,
|
507
|
+
"upscale": bool,
|
508
|
+
"upscale_factor": float,
|
509
|
+
"adjust_cells": bool,
|
510
|
+
"row_limit": int,
|
511
|
+
"tables": list,
|
512
|
+
"visualize": str,
|
513
|
+
"image_nr": int,
|
514
|
+
"dot_size": int,
|
515
|
+
"n_neighbors": int,
|
516
|
+
"min_dist": float,
|
517
|
+
"metric": str,
|
518
|
+
"eps": float,
|
519
|
+
"min_samples": int,
|
520
|
+
"filter_by": str,
|
521
|
+
"img_zoom": float,
|
522
|
+
"plot_by_cluster": bool,
|
523
|
+
"plot_cluster_grids": bool,
|
524
|
+
"remove_cluster_noise": bool,
|
525
|
+
"remove_highly_correlated": bool,
|
526
|
+
"log_data": bool,
|
527
|
+
"black_background": bool,
|
528
|
+
"remove_image_canvas": bool,
|
529
|
+
"plot_outlines": bool,
|
530
|
+
"plot_points": bool,
|
531
|
+
"smooth_lines": bool,
|
532
|
+
"clustering": str,
|
533
|
+
"exclude": (str, type(None)),
|
534
|
+
"col_to_compare": str,
|
535
|
+
"pos": str,
|
536
|
+
"neg": str,
|
537
|
+
"embedding_by_controls": bool,
|
538
|
+
"plot_images": bool,
|
539
|
+
"reduction_method": str,
|
540
|
+
"save_figure": bool,
|
541
|
+
"color_by": (str, type(None)),
|
542
|
+
"analyze_clusters": bool,
|
543
|
+
"resnet_features": bool,
|
544
|
+
"test_nr": int,
|
545
|
+
"radial_dist": bool,
|
546
|
+
"calculate_correlation": bool,
|
547
|
+
"manders_thresholds": list,
|
548
|
+
"homogeneity": bool,
|
549
|
+
"homogeneity_distances": list,
|
550
|
+
"save_arrays": bool,
|
551
|
+
"cytoplasm": bool,
|
552
|
+
"merge_edge_pathogen_cells": bool,
|
553
|
+
"cells_per_well": int,
|
554
|
+
"pathogen_size_range": list,
|
555
|
+
"nucleus_size_range": list,
|
556
|
+
"cell_size_range": list,
|
557
|
+
"pathogen_intensity_range": list,
|
558
|
+
"nucleus_intensity_range": list,
|
559
|
+
"cell_intensity_range": list,
|
560
|
+
"target_intensity_min": int,
|
561
|
+
"model_type": str,
|
562
|
+
"heatmap_feature": str,
|
563
|
+
"grouping": str,
|
564
|
+
"min_max": str,
|
565
|
+
"minimum_cell_count": int,
|
566
|
+
"n_estimators": int,
|
567
|
+
"test_size": float,
|
568
|
+
"location_column": str,
|
569
|
+
"positive_control": str,
|
570
|
+
"negative_control": str,
|
571
|
+
"n_repeats": int,
|
572
|
+
"top_features": int,
|
573
|
+
"remove_low_variance_features": bool,
|
574
|
+
"n_jobs": int,
|
575
|
+
"classes": list,
|
576
|
+
"schedule": str,
|
577
|
+
"loss_type": str,
|
578
|
+
"image_size": int,
|
579
|
+
"epochs": int,
|
580
|
+
"val_split": float,
|
581
|
+
"train_mode": str,
|
582
|
+
"learning_rate": float,
|
583
|
+
"weight_decay": float,
|
584
|
+
"dropout_rate": float,
|
585
|
+
"init_weights": bool,
|
586
|
+
"amsgrad": bool,
|
587
|
+
"use_checkpoint": bool,
|
588
|
+
"gradient_accumulation": bool,
|
589
|
+
"gradient_accumulation_steps": int,
|
590
|
+
"intermedeate_save": bool,
|
591
|
+
"pin_memory": bool,
|
592
|
+
"num_workers": int,
|
593
|
+
"augment": bool,
|
594
|
+
"target": str,
|
595
|
+
"cell_types": list,
|
596
|
+
"cell_plate_metadata": (list, type(None)),
|
597
|
+
"pathogen_types": list,
|
598
|
+
"pathogen_plate_metadata": (list, list), # This can be a list of lists
|
599
|
+
"treatment_plate_metadata": (list, list), # This can be a list of lists
|
600
|
+
"metadata_types": list,
|
601
|
+
"cell_chann_dim": int,
|
602
|
+
"nucleus_chann_dim": int,
|
603
|
+
"pathogen_chann_dim": int,
|
604
|
+
"plot_nr": int,
|
605
|
+
"plot_control": bool,
|
606
|
+
"remove_background": bool,
|
607
|
+
"target": str,
|
608
|
+
"upstream": str,
|
609
|
+
"downstream": str,
|
610
|
+
"barecode_length_1": int,
|
611
|
+
"barecode_length_2": int,
|
612
|
+
"chunk_size": int,
|
613
|
+
"grna": str,
|
614
|
+
"barcodes": str,
|
615
|
+
"plate_dict": dict,
|
616
|
+
"pc": str,
|
617
|
+
"pc_loc": str,
|
618
|
+
"nc": str,
|
619
|
+
"nc_loc": str,
|
620
|
+
"dependent_variable": str,
|
621
|
+
"transform": (str, type(None)),
|
622
|
+
"agg_type": str,
|
623
|
+
"min_cell_count": int,
|
624
|
+
"regression_type": str,
|
625
|
+
"remove_row_column_effect": bool,
|
626
|
+
"alpha": float,
|
627
|
+
"fraction_threshold": float,
|
628
|
+
"class_1_threshold": (float, type(None)),
|
629
|
+
"batch_size": int,
|
630
|
+
"CP_prob": float,
|
631
|
+
"flow_threshold": float,
|
632
|
+
"percentiles": (list, type(None)),
|
633
|
+
"circular": bool,
|
634
|
+
"invert": bool,
|
635
|
+
"diameter": int,
|
636
|
+
"grayscale": bool,
|
637
|
+
"resize": bool,
|
638
|
+
"target_height": (int, type(None)),
|
639
|
+
"target_width": (int, type(None)),
|
640
|
+
"rescale": bool,
|
641
|
+
"resample": bool,
|
642
|
+
"model_name": str,
|
643
|
+
"Signal_to_noise": int,
|
644
|
+
"learning_rate": float,
|
645
|
+
"weight_decay": float,
|
646
|
+
"batch_size": int,
|
647
|
+
"n_epochs": int,
|
648
|
+
"from_scratch": bool,
|
649
|
+
"width_height": list,
|
650
|
+
"resize": bool,
|
651
|
+
"gene_weights_csv": str,
|
652
|
+
"fraction_threshold": float,
|
653
|
+
}
|
528
654
|
|
529
|
-
|
530
|
-
|
531
|
-
|
655
|
+
for key, (label, widget, var) in vars_dict.items():
|
656
|
+
if key not in expected_types:
|
657
|
+
if key not in ["General","Nucleus","Cell","Pathogen","Timelapse","Plot","Object Image","Annotate Data","Measurements","Advanced","Miscellaneous","Test"]:
|
532
658
|
|
533
|
-
|
534
|
-
|
535
|
-
settings[key] = [list(map(str, sublist)) for sublist in ast.literal_eval(value)] if value else []
|
536
|
-
|
537
|
-
elif key == 'dialate_png_ratios':
|
538
|
-
settings[key] = [float(num) for num in ast.literal_eval(value)] if value else []
|
539
|
-
|
540
|
-
elif key == 'normalize':
|
541
|
-
settings[key] = [int(num) for num in ast.literal_eval(value)] if value else []
|
542
|
-
|
543
|
-
# Directly assign string values for these specific keys
|
544
|
-
elif key in ['normalize_by', 'experiment', 'measurement', 'input_folder']:
|
545
|
-
settings[key] = value
|
546
|
-
|
547
|
-
elif key == 'png_size':
|
548
|
-
settings[key] = [list(map(int, dim)) for dim in ast.literal_eval(value)] if value else []
|
549
|
-
|
550
|
-
# Ensure these are lists of strings, converting from tuples if necessary
|
551
|
-
elif key in ['timelapse_objects', 'crop_mode', 'cells', 'pathogens', 'treatments']:
|
552
|
-
eval_value = ast.literal_eval(value) if value else []
|
553
|
-
settings[key] = list(map(str, eval_value)) if isinstance(eval_value, (list, tuple)) else [str(eval_value)]
|
554
|
-
|
555
|
-
# Handling for single non-string values (int, float, bool)
|
556
|
-
elif key in ['cell_mask_dim', 'cell_min_size', 'nucleus_mask_dim', 'nucleus_min_size', 'pathogen_mask_dim', 'pathogen_min_size', 'cytoplasm_min_size', 'max_workers', 'channel_of_interest', 'nr_imgs']:
|
557
|
-
settings[key] = int(value) if value else None
|
558
|
-
|
559
|
-
elif key == 'um_per_pixel':
|
560
|
-
settings[key] = float(value) if value else None
|
561
|
-
|
562
|
-
# Handling boolean values based on checkboxes
|
563
|
-
elif key in ['save_png', 'use_bounding_box', 'save_measurements', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'timelapse', 'representative_images']:
|
564
|
-
settings[key] = var.get()
|
565
|
-
|
566
|
-
except SyntaxError as e:
|
567
|
-
print(f"Syntax error processing {key}: {str(e)}")
|
568
|
-
#messagebox.showerror("Error", f"Syntax error processing {key}: {str(e)}")
|
569
|
-
return None
|
570
|
-
except Exception as e:
|
571
|
-
print(f"Error processing {key}: {str(e)}")
|
572
|
-
#messagebox.showerror("Error", f"Error processing {key}: {str(e)}")
|
573
|
-
return None
|
574
|
-
|
575
|
-
return settings
|
576
|
-
|
577
|
-
def check_classify_gui_settings(vars_dict):
|
578
|
-
settings = {}
|
579
|
-
for key, var in vars_dict.items():
|
580
|
-
value = var.get() # This retrieves the string representation for entries or the actual value for checkboxes and combos
|
581
|
-
|
582
|
-
try:
|
583
|
-
if key in ['src', 'measurement']:
|
584
|
-
# Directly assign string values
|
585
|
-
settings[key] = str(value)
|
586
|
-
elif key in ['cell_mask_dim', 'image_size', 'batch_size', 'epochs', 'gradient_accumulation_steps', 'num_workers']:
|
587
|
-
# Convert to integer
|
588
|
-
settings[key] = int(value)
|
589
|
-
elif key in ['val_split', 'learning_rate', 'weight_decay', 'dropout_rate']:
|
590
|
-
# Convert to float
|
591
|
-
settings[key] = float(value)
|
592
|
-
elif key == 'classes':
|
593
|
-
# Evaluate as list
|
594
|
-
settings[key] = ast.literal_eval(value)
|
595
|
-
|
596
|
-
elif key in ['model_type','optimizer_type','schedule','loss_type','train_mode']:
|
597
|
-
settings[key] = value
|
598
|
-
|
599
|
-
elif key in ['gradient_accumulation','normalize','save','plot', 'init_weights','amsgrad','use_checkpoint','intermedeate_save','pin_memory', 'num_workers','verbose']:
|
600
|
-
settings[key] = bool(value)
|
601
|
-
|
602
|
-
except SyntaxError as e:
|
603
|
-
messagebox.showerror("Error", f"Syntax error processing {key}: {str(e)}")
|
604
|
-
return None
|
605
|
-
except Exception as e:
|
606
|
-
messagebox.showerror("Error", f"Error processing {key}: {str(e)}")
|
607
|
-
return None
|
608
|
-
|
609
|
-
return settings
|
659
|
+
q.put(f"Key {key} not found in expected types.")
|
660
|
+
continue
|
610
661
|
|
611
|
-
|
612
|
-
|
613
|
-
for key, var in vars_dict.items():
|
614
|
-
value = var.get() # This retrieves the string representation for entries or the actual value for checkboxes and combos
|
662
|
+
value = var.get()
|
663
|
+
expected_type = expected_types.get(key, str)
|
615
664
|
|
616
665
|
try:
|
617
|
-
if key in [
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
if len(ls) == 3 and ls[2] > 0:
|
625
|
-
list_of_integers = list(range(ls[0], ls[1], ls[2]))
|
626
|
-
list_of_integers = [num + 1 if num == 0 else num for num in list_of_integers]
|
666
|
+
if key in ["png_size", "pathogen_plate_metadata", "treatment_plate_metadata"]:
|
667
|
+
parsed_value = ast.literal_eval(value) if value else None
|
668
|
+
if isinstance(parsed_value, list):
|
669
|
+
if all(isinstance(i, list) for i in parsed_value) or all(not isinstance(i, list) for i in parsed_value):
|
670
|
+
settings[key] = parsed_value
|
671
|
+
else:
|
672
|
+
raise ValueError("Invalid format: Mixed list and list of lists")
|
627
673
|
else:
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
elif
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
674
|
+
raise ValueError("Invalid format for list or list of lists")
|
675
|
+
elif expected_type == list:
|
676
|
+
settings[key] = parse_list(value) if value else None
|
677
|
+
elif expected_type == bool:
|
678
|
+
settings[key] = value if isinstance(value, bool) else value.lower() in ['true', '1', 't', 'y', 'yes']
|
679
|
+
elif expected_type == (int, type(None)):
|
680
|
+
settings[key] = int(value) if value else None
|
681
|
+
elif expected_type == (float, type(None)):
|
682
|
+
settings[key] = float(value) if value else None
|
683
|
+
elif expected_type == (int, float):
|
684
|
+
settings[key] = float(value) if '.' in value else int(value)
|
685
|
+
elif expected_type == (str, type(None)):
|
686
|
+
settings[key] = str(value) if value else None
|
687
|
+
elif isinstance(expected_type, tuple):
|
688
|
+
for typ in expected_type:
|
689
|
+
try:
|
690
|
+
settings[key] = typ(value) if value else None
|
691
|
+
break
|
692
|
+
except (ValueError, TypeError):
|
693
|
+
continue
|
638
694
|
else:
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
elif key in ['number_of_control_genes', 'replicates', 'max_workers']:
|
647
|
-
# Convert to integer
|
648
|
-
settings[key] = int(value)
|
649
|
-
|
650
|
-
except SyntaxError as e:
|
651
|
-
messagebox.showerror("Error", f"Syntax error processing {key}: {str(e)}")
|
652
|
-
return None
|
653
|
-
except Exception as e:
|
654
|
-
messagebox.showerror("Error", f"Error processing {key}: {str(e)}")
|
655
|
-
return None
|
656
|
-
|
657
|
-
return settings
|
658
|
-
|
659
|
-
def sim_variables():
|
660
|
-
variables = {
|
661
|
-
'name':('entry', None, 'plates_2_4_8'),
|
662
|
-
'variable':('entry', None, 'all'),
|
663
|
-
'src':('entry', None, '/home/olafsson/Desktop/simulations'),
|
664
|
-
'number_of_control_genes':('entry', None, 30),
|
665
|
-
'replicates':('entry', None, 4),
|
666
|
-
'max_workers':('entry', None, 1),
|
667
|
-
'plot':('check', None, True),
|
668
|
-
'random_seed':('check', None, True),
|
669
|
-
'nr_plates': ('entry', None, '[8,8,0]'),# '[2,2,8]'
|
670
|
-
'number_of_genes': ('entry', None, '[100, 100, 0]'), #[1384, 1384, 0]
|
671
|
-
'number_of_active_genes': ('entry', None, '[10,10,0]'),
|
672
|
-
'avg_genes_per_well': ('entry', None, '[2, 10, 2]'),
|
673
|
-
'avg_cells_per_well': ('entry', None, '[100, 100, 0]'),
|
674
|
-
'positive_mean': ('entry', None, '[0.8, 0.8, 0]'),
|
675
|
-
'avg_reads_per_gene': ('entry', None, '[1000,1000, 0]'),
|
676
|
-
'sequencing_error': ('entry', None, '[0.01, 0.01, 0]'),
|
677
|
-
'well_ineq_coeff': ('entry', None, '[0.3,0.3,0]'),
|
678
|
-
'gene_ineq_coeff': ('entry', None, '[0.8,0.8,0]'),
|
679
|
-
}
|
680
|
-
return variables
|
695
|
+
raise ValueError
|
696
|
+
else:
|
697
|
+
settings[key] = expected_type(value) if value else None
|
698
|
+
except (ValueError, SyntaxError):
|
699
|
+
expected_type_name = ' or '.join([t.__name__ for t in expected_type]) if isinstance(expected_type, tuple) else expected_type.__name__
|
700
|
+
q.put(f"Error: Invalid format for {key}. Expected type: {expected_type_name}.")
|
701
|
+
return
|
681
702
|
|
682
|
-
def add_measure_gui_defaults(settings):
|
683
|
-
settings['compartments'] = ['pathogen', 'cytoplasm']
|
684
703
|
return settings
|
685
704
|
|
686
|
-
def measure_variables():
|
687
|
-
variables = {
|
688
|
-
'input_folder':('entry', None, '/mnt/data/CellVoyager/40x/einar/mitotrackerHeLaToxoDsRed_20240224_123156/test_gui/merged'),
|
689
|
-
'channels': ('combo', ['[0,1,2,3]','[0,1,2]','[0,1]','[0]'], '[0,1,2,3]'),
|
690
|
-
'cell_mask_dim':('entry', None, 4),
|
691
|
-
'cell_min_size':('entry', None, 0),
|
692
|
-
'cytoplasm_min_size':('entry', None, 0),
|
693
|
-
'nucleus_mask_dim':('entry', None, 5),
|
694
|
-
'nucleus_min_size':('entry', None, 0),
|
695
|
-
'pathogen_mask_dim':('entry', None, 6),
|
696
|
-
'pathogen_min_size':('entry', None, 0),
|
697
|
-
'save_png':('check', None, True),
|
698
|
-
'crop_mode':('entry', None, '["cell"]'),
|
699
|
-
'use_bounding_box':('check', None, True),
|
700
|
-
'png_size': ('entry', None, '[[224,224]]'),
|
701
|
-
'normalize':('entry', None, '[2,98]'),
|
702
|
-
'png_dims':('entry', None, '[1,2,3]'),
|
703
|
-
'normalize_by':('combo', ['fov', 'png'], 'png'),
|
704
|
-
'save_measurements':('check', None, True),
|
705
|
-
'representative_images':('check', None, True),
|
706
|
-
'plot':('check', None, True),
|
707
|
-
'plot_filtration':('check', None, True),
|
708
|
-
'include_uninfected':('check', None, True),
|
709
|
-
'dialate_pngs':('check', None, False),
|
710
|
-
'dialate_png_ratios':('entry', None, '[0.2]'),
|
711
|
-
'timelapse':('check', None, False),
|
712
|
-
'timelapse_objects':('combo', ['["cell"]', '["nucleus"]', '["pathogen"]', '["cytoplasm"]'], '["cell"]'),
|
713
|
-
'max_workers':('entry', None, 30),
|
714
|
-
'experiment':('entry', None, 'experiment name'),
|
715
|
-
'cells':('entry', None, ['HeLa']),
|
716
|
-
'cell_loc': ('entry', None, '[["c1","c2"], ["c3","c4"]]'),
|
717
|
-
'pathogens':('entry', None, '["wt","mutant"]'),
|
718
|
-
'pathogen_loc': ('entry', None, '[["c1","c2"], ["c3","c4"]]'),
|
719
|
-
'treatments': ('entry', None, '["cm","lovastatin_20uM"]'),
|
720
|
-
'treatment_loc': ('entry', None, '[["c1","c2"], ["c3","c4"]]'),
|
721
|
-
'channel_of_interest':('entry', None, 3),
|
722
|
-
'compartments':('entry', None, '["pathogen","cytoplasm"]'),
|
723
|
-
'measurement':('entry', None, 'mean_intensity'),
|
724
|
-
'nr_imgs':('entry', None, 32),
|
725
|
-
'um_per_pixel':('entry', None, 0.1)
|
726
|
-
}
|
727
|
-
return variables
|
728
|
-
|
729
|
-
def classify_variables():
|
730
|
-
|
731
|
-
def get_torchvision_models():
|
732
|
-
# Fetch all public callable attributes from torchvision.models that are functions
|
733
|
-
model_names = [name for name, obj in inspect.getmembers(models)
|
734
|
-
if inspect.isfunction(obj) and not name.startswith("__")]
|
735
|
-
return model_names
|
736
|
-
|
737
|
-
model_names = get_torchvision_models()
|
738
|
-
variables = {
|
739
|
-
'src':('entry', None, '/mnt/data/CellVoyager/40x/einar/mitotrackerHeLaToxoDsRed_20240224_123156/test_gui/merged'),
|
740
|
-
'cell_mask_dim':('entry', None, 4),
|
741
|
-
'classes':('entry', None, '["nc","pc"]'),
|
742
|
-
'measurement':('entry', None, 'mean_intensity'),
|
743
|
-
'model_type': ('combo', model_names, 'resnet50'),
|
744
|
-
'optimizer_type': ('combo', ['adamw','adam'], 'adamw'),
|
745
|
-
'schedule': ('combo', ['reduce_lr_on_plateau','step_lr'], 'reduce_lr_on_plateau'),
|
746
|
-
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
747
|
-
'image_size': ('entry', None, 224),
|
748
|
-
'batch_size': ('entry', None, 12),
|
749
|
-
'epochs': ('entry', None, 2),
|
750
|
-
'val_split': ('entry', None, 0.1),
|
751
|
-
'train_mode': ('combo', ['erm', 'irm'], 'erm'),
|
752
|
-
'learning_rate': ('entry', None, 0.0001),
|
753
|
-
'weight_decay': ('entry', None, 0.00001),
|
754
|
-
'dropout_rate': ('entry', None, 0.1),
|
755
|
-
'gradient_accumulation': ('check', None, True),
|
756
|
-
'gradient_accumulation_steps': ('entry', None, 4),
|
757
|
-
'normalize': ('check', None, True),
|
758
|
-
'save': ('check', None, True),
|
759
|
-
'plot': ('check', None, True),
|
760
|
-
'init_weights': ('check', None, True),
|
761
|
-
'amsgrad': ('check', None, True),
|
762
|
-
'use_checkpoint': ('check', None, True),
|
763
|
-
'intermedeate_save': ('check', None, True),
|
764
|
-
'pin_memory': ('check', None, True),
|
765
|
-
'num_workers': ('entry', None, 30),
|
766
|
-
'verbose': ('check', None, True),
|
767
|
-
}
|
768
|
-
return variables
|
769
|
-
|
770
705
|
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
771
706
|
label = ttk.Label(frame, text=label_text, style='Custom.TLabel') # Apply Custom.TLabel style for labels
|
772
707
|
label.grid(column=0, row=row, sticky=tk.W, padx=5, pady=5)
|
@@ -778,7 +713,7 @@ def create_input_field(frame, label_text, row, var_type='entry', options=None, d
|
|
778
713
|
return (label, entry, var) # Return both the label and the entry, and the variable
|
779
714
|
elif var_type == 'check':
|
780
715
|
var = tk.BooleanVar(value=default_value) # Set default value (True/False)
|
781
|
-
check =
|
716
|
+
check = Checkbutton(frame, text="", variable=var)
|
782
717
|
check.grid(column=1, row=row, sticky=tk.W, padx=5)
|
783
718
|
return (label, check, var) # Return both the label and the checkbutton, and the variable
|
784
719
|
elif var_type == 'combo':
|
@@ -792,115 +727,12 @@ def create_input_field(frame, label_text, row, var_type='entry', options=None, d
|
|
792
727
|
var = None # Placeholder in case of an undefined var_type
|
793
728
|
return (label, None, var)
|
794
729
|
|
795
|
-
def convert_settings_dict_for_gui(settings):
|
796
|
-
variables = {}
|
797
|
-
special_cases = {
|
798
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
799
|
-
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
800
|
-
'magnification': ('combo', [20, 40, 60], 20),
|
801
|
-
'nucleus_channel': ('combo', [0, 1, 2, 3, None], None),
|
802
|
-
'cell_channel': ('combo', [0, 1, 2, 3, None], None),
|
803
|
-
'pathogen_channel': ('combo', [0, 1, 2, 3, None], None),
|
804
|
-
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
805
|
-
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
806
|
-
'model_type': ('combo', ['resnet50', 'other_model'], 'resnet50'),
|
807
|
-
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
808
|
-
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
809
|
-
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
810
|
-
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
811
|
-
}
|
812
|
-
for key, value in settings.items():
|
813
|
-
if key in special_cases:
|
814
|
-
variables[key] = special_cases[key]
|
815
|
-
elif isinstance(value, bool):
|
816
|
-
variables[key] = ('check', None, value)
|
817
|
-
elif isinstance(value, int) or isinstance(value, float):
|
818
|
-
variables[key] = ('entry', None, value)
|
819
|
-
elif isinstance(value, str):
|
820
|
-
variables[key] = ('entry', None, value)
|
821
|
-
elif value is None:
|
822
|
-
variables[key] = ('entry', None, value)
|
823
|
-
elif isinstance(value, list):
|
824
|
-
variables[key] = ('entry', None, str(value))
|
825
|
-
return variables
|
826
|
-
|
827
|
-
def mask_variables():
|
828
|
-
variables = {
|
829
|
-
'src': ('entry', None, 'path/to/images'),
|
830
|
-
'pathogen_model': ('entry', None, 'path/to/model'),
|
831
|
-
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
832
|
-
'custom_regex': ('entry', None, None),
|
833
|
-
'experiment': ('entry', None, 'exp'),
|
834
|
-
'channels': ('combo', ['[0,1,2,3]','[0,1,2]','[0,1]','[0]'], '[0,1,2,3]'),
|
835
|
-
'magnification': ('combo', [20, 40, 60,], 40),
|
836
|
-
'nucleus_channel': ('combo', [0,1,2,3, None], 0),
|
837
|
-
'nucleus_background': ('entry', None, 100),
|
838
|
-
'nucleus_Signal_to_noise': ('entry', None, 5),
|
839
|
-
'nucleus_CP_prob': ('entry', None, 0),
|
840
|
-
'remove_background_nucleus': ('check', None, False),
|
841
|
-
'cell_channel': ('combo', [0,1,2,3, None], 3),
|
842
|
-
'cell_background': ('entry', None, 100),
|
843
|
-
'cell_Signal_to_noise': ('entry', None, 5),
|
844
|
-
'cell_CP_prob': ('entry', None, 0),
|
845
|
-
'remove_background_cell': ('check', None, False),
|
846
|
-
'pathogen_channel': ('combo', [0,1,2,3, None], 2),
|
847
|
-
'pathogen_background': ('entry', None, 100),
|
848
|
-
'pathogen_Signal_to_noise': ('entry', None, 3),
|
849
|
-
'pathogen_CP_prob': ('entry', None, 0),
|
850
|
-
'remove_background_pathogen': ('check', None, False),
|
851
|
-
'preprocess': ('check', None, True),
|
852
|
-
'masks': ('check', None, True),
|
853
|
-
'examples_to_plot': ('entry', None, 1),
|
854
|
-
'randomize': ('check', None, True),
|
855
|
-
'batch_size': ('entry', None, 50),
|
856
|
-
'timelapse': ('check', None, False),
|
857
|
-
'timelapse_displacement': ('entry', None, None),
|
858
|
-
'timelapse_memory': ('entry', None, 0),
|
859
|
-
'timelapse_frame_limits': ('entry', None, '[0,1000]'),
|
860
|
-
'timelapse_remove_transient': ('check', None, True),
|
861
|
-
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
862
|
-
'timelapse_objects': ('combo', ['cell','nucleus','pathogen','cytoplasm', None], None),
|
863
|
-
'fps': ('entry', None, 2),
|
864
|
-
'remove_background': ('check', None, True),
|
865
|
-
'lower_quantile': ('entry', None, 0.01),
|
866
|
-
#'merge': ('check', None, False),
|
867
|
-
'normalize_plots': ('check', None, True),
|
868
|
-
'all_to_mip': ('check', None, False),
|
869
|
-
'pick_slice': ('check', None, False),
|
870
|
-
'skip_mode': ('entry', None, None),
|
871
|
-
'save': ('check', None, True),
|
872
|
-
'plot': ('check', None, True),
|
873
|
-
'workers': ('entry', None, 30),
|
874
|
-
'verbose': ('check', None, True),
|
875
|
-
'filter': ('check', None, True),
|
876
|
-
'merge_pathogens': ('check', None, True),
|
877
|
-
'adjust_cells': ('check', None, True),
|
878
|
-
'test_images': ('entry', None, 10),
|
879
|
-
'random_test': ('check', None, True),
|
880
|
-
}
|
881
|
-
return variables
|
882
|
-
|
883
|
-
def add_mask_gui_defaults(settings):
|
884
|
-
settings['remove_background'] = True
|
885
|
-
settings['fps'] = 2
|
886
|
-
settings['all_to_mip'] = False
|
887
|
-
settings['pick_slice'] = False
|
888
|
-
settings['merge'] = False
|
889
|
-
settings['skip_mode'] = ''
|
890
|
-
settings['verbose'] = False
|
891
|
-
settings['normalize_plots'] = True
|
892
|
-
settings['randomize'] = True
|
893
|
-
settings['preprocess'] = True
|
894
|
-
settings['masks'] = True
|
895
|
-
settings['examples_to_plot'] = 1
|
896
|
-
return settings
|
897
|
-
|
898
730
|
def generate_fields(variables, scrollable_frame):
|
731
|
+
row = 1
|
899
732
|
vars_dict = {}
|
900
|
-
row = 5
|
901
733
|
tooltips = {
|
902
734
|
"src": "Path to the folder containing the images.",
|
903
|
-
"metadata_type": "Type of metadata to expect in the images. This will determine how the images are processed. If 'custom' is selected, you can provide a custom regex pattern to extract metadata from the image names",
|
735
|
+
"metadata_type": "Type of metadata to expect in the images. This will determine how the images are processed. If 'custom' is selected, you can provide a custom regex pattern to extract metadata from the image names.",
|
904
736
|
"custom_regex": "Custom regex pattern to extract metadata from the image names. This will only be used if 'custom' is selected for 'metadata_type'.",
|
905
737
|
"experiment": "Name of the experiment. This will be used to name the output files.",
|
906
738
|
"channels": "List of channels to use for the analysis. The first channel is 0, the second is 1, and so on. For example, [0,1,2] will use channels 0, 1, and 2.",
|
@@ -909,17 +741,20 @@ def generate_fields(variables, scrollable_frame):
|
|
909
741
|
"nucleus_background": "The background intensity for the nucleus channel. This will be used to remove background noise.",
|
910
742
|
"nucleus_Signal_to_noise": "The signal-to-noise ratio for the nucleus channel. This will be used to determine the range of intensities to normalize images to for nucleus segmentation.",
|
911
743
|
"nucleus_CP_prob": "The cellpose probability threshold for the nucleus channel. This will be used to segment the nucleus.",
|
744
|
+
"nucleus_FT": "The flow threshold for nucleus objects. This will be used in nuclues segmentation.",
|
912
745
|
"cell_channel": "The channel to use for the cell. If None, the cell will not be segmented.",
|
913
746
|
"cell_background": "The background intensity for the cell channel. This will be used to remove background noise.",
|
914
747
|
"cell_Signal_to_noise": "The signal-to-noise ratio for the cell channel. This will be used to determine the range of intensities to normalize images to for cell segmentation.",
|
915
|
-
"cell_CP_prob": "The cellpose probability threshold for the cell channel. This will be used
|
748
|
+
"cell_CP_prob": "The cellpose probability threshold for the cell channel. This will be used in cell segmentation.",
|
749
|
+
"cell_FT": "The flow threshold for cell objects. This will be used to segment the cells.",
|
916
750
|
"pathogen_channel": "The channel to use for the pathogen. If None, the pathogen will not be segmented.",
|
917
751
|
"pathogen_background": "The background intensity for the pathogen channel. This will be used to remove background noise.",
|
918
752
|
"pathogen_Signal_to_noise": "The signal-to-noise ratio for the pathogen channel. This will be used to determine the range of intensities to normalize images to for pathogen segmentation.",
|
919
753
|
"pathogen_CP_prob": "The cellpose probability threshold for the pathogen channel. This will be used to segment the pathogen.",
|
754
|
+
"pathogen_FT": "The flow threshold for pathogen objects. This will be used in pathogen segmentation.",
|
920
755
|
"preprocess": "Whether to preprocess the images before segmentation. This includes background removal and normalization. Set to False only if this step has already been done.",
|
921
756
|
"masks": "Whether to generate masks for the segmented objects. If True, masks will be generated for the nucleus, cell, and pathogen.",
|
922
|
-
"examples_to_plot": "The number of images to plot for each segmented object. This will be used to visually inspect the segmentation results and normalization
|
757
|
+
"examples_to_plot": "The number of images to plot for each segmented object. This will be used to visually inspect the segmentation results and normalization.",
|
923
758
|
"randomize": "Whether to randomize the order of the images before processing. Recommended to avoid bias in the segmentation.",
|
924
759
|
"batch_size": "The batch size to use for processing the images. This will determine how many images are processed at once. Images are normalized and segmented in batches. Lower if application runs out of RAM or VRAM.",
|
925
760
|
"timelapse": "Whether to process the images as a timelapse.",
|
@@ -931,41 +766,41 @@ def generate_fields(variables, scrollable_frame):
|
|
931
766
|
"timelapse_objects": "The objects to track in the timelapse (cell, nucleus or pathogen). This will determine which objects are tracked over time. If None, all objects will be tracked.",
|
932
767
|
"fps": "Frames per second of the automatically generated timelapse movies.",
|
933
768
|
"remove_background": "Whether to remove background noise from the images. This will help improve the quality of the segmentation.",
|
934
|
-
"
|
935
|
-
"merge_pathogens": "Whether to merge pathogen objects that share more than 75% of their
|
769
|
+
"lower_percentile": "The lower quantile to use for normalizing the images. This will be used to determine the range of intensities to normalize images to.",
|
770
|
+
"merge_pathogens": "Whether to merge pathogen objects that share more than 75% of their perimeter.",
|
936
771
|
"normalize_plots": "Whether to normalize the plots.",
|
937
772
|
"all_to_mip": "Whether to convert all images to maximum intensity projections before processing.",
|
938
773
|
"pick_slice": "Whether to pick a single slice from the z-stack images. If False, the maximum intensity projection will be used.",
|
939
774
|
"skip_mode": "The mode to use for skipping images. This will determine how to handle images that cannot be processed.",
|
940
775
|
"save": "Whether to save the results to disk.",
|
776
|
+
"merge_edge_pathogen_cells": "Whether to merge cells that share pathogen objects.",
|
941
777
|
"plot": "Whether to plot the results.",
|
942
778
|
"workers": "The number of workers to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
|
943
779
|
"verbose": "Whether to print verbose output during processing.",
|
944
780
|
"input_folder": "Path to the folder containing the images.",
|
945
781
|
"cell_mask_dim": "The dimension of the array the cell mask is saved in.",
|
946
|
-
"cell_min_size": "The minimum size of cell objects in
|
947
|
-
"
|
782
|
+
"cell_min_size": "The minimum size of cell objects in pixels^2.",
|
783
|
+
"cytoplasm": "Whether to segment the cytoplasm (Cell - Nucleus + Pathogen).",
|
784
|
+
"cytoplasm_min_size": "The minimum size of cytoplasm objects in pixels^2.",
|
948
785
|
"nucleus_mask_dim": "The dimension of the array the nucleus mask is saved in.",
|
949
|
-
"nucleus_min_size": "The minimum size of nucleus objects in
|
786
|
+
"nucleus_min_size": "The minimum size of nucleus objects in pixels^2.",
|
950
787
|
"pathogen_mask_dim": "The dimension of the array the pathogen mask is saved in.",
|
951
|
-
"pathogen_min_size": "The minimum size of pathogen objects in
|
788
|
+
"pathogen_min_size": "The minimum size of pathogen objects in pixels^2.",
|
952
789
|
"save_png": "Whether to save the segmented objects as PNG images.",
|
953
790
|
"crop_mode": "The mode to use for cropping the images. This will determine which objects are cropped from the images (cell, nucleus, pathogen, cytoplasm).",
|
954
791
|
"use_bounding_box": "Whether to use the bounding box of the objects for cropping. If False, only the object itself will be cropped.",
|
955
792
|
"png_size": "The size of the PNG images to save. This will determine the size of the saved images.",
|
956
|
-
"normalize": "The percentiles to use for normalizing the images. This will be used to determine the range of intensities to normalize images to
|
793
|
+
"normalize": "The percentiles to use for normalizing the images. This will be used to determine the range of intensities to normalize images to. If None, no normalization is done.",
|
957
794
|
"png_dims": "The dimensions of the PNG images to save. This will determine the dimensions of the saved images. Maximum of 3 dimensions e.g. [1,2,3].",
|
958
795
|
"normalize_by": "Whether to normalize the images by field of view (fov) or by PNG image (png).",
|
959
796
|
"save_measurements": "Whether to save the measurements to disk.",
|
960
797
|
"representative_images": "Whether to save representative images of the segmented objects (Not working yet).",
|
961
|
-
"plot": "Whether to plot results.",
|
962
798
|
"plot_filtration": "Whether to plot the filtration steps.",
|
963
799
|
"include_uninfected": "Whether to include uninfected cells in the analysis.",
|
964
|
-
"dialate_pngs": "Whether to
|
965
|
-
"dialate_png_ratios": "The ratios to use for
|
966
|
-
"timelapse_objects": "The objects to track in the timelapse (cell, nucleus or pathogen). This will determine which objects are tracked over time. If None, all objects will be tracked.",
|
800
|
+
"dialate_pngs": "Whether to dilate the PNG images before saving.",
|
801
|
+
"dialate_png_ratios": "The ratios to use for dilating the PNG images. This will determine the amount of dilation applied to the images before cropping.",
|
967
802
|
"max_workers": "The number of workers to use for processing the images. This will determine how many images are processed in parallel. Increase to speed up processing.",
|
968
|
-
"cells
|
803
|
+
"cells": "The cell types to include in the analysis.",
|
969
804
|
"cell_loc": "The locations of the cell types in the images.",
|
970
805
|
"pathogens": "The pathogen types to include in the analysis.",
|
971
806
|
"pathogen_loc": "The locations of the pathogen types in the images.",
|
@@ -975,7 +810,7 @@ def generate_fields(variables, scrollable_frame):
|
|
975
810
|
"compartments": "The compartments to measure in the images.",
|
976
811
|
"measurement": "The measurement to use for the analysis.",
|
977
812
|
"nr_imgs": "The number of images to plot.",
|
978
|
-
"um_per_pixel": "The micrometers per pixel for the images."
|
813
|
+
"um_per_pixel": "The micrometers per pixel for the images."
|
979
814
|
}
|
980
815
|
|
981
816
|
for key, (var_type, options, default_value) in variables.items():
|
@@ -985,7 +820,6 @@ def generate_fields(variables, scrollable_frame):
|
|
985
820
|
# Add tooltip to the label if it exists in the tooltips dictionary
|
986
821
|
if key in tooltips:
|
987
822
|
ToolTip(label, tooltips[key])
|
988
|
-
|
989
823
|
row += 1
|
990
824
|
return vars_dict
|
991
825
|
|
@@ -1133,7 +967,7 @@ def preprocess_generate_masks_wrapper(settings, q, fig_queue):
|
|
1133
967
|
plt.show = my_show
|
1134
968
|
|
1135
969
|
try:
|
1136
|
-
spacr.core.preprocess_generate_masks(settings['src'], settings=settings)
|
970
|
+
spacr.core.preprocess_generate_masks(src=settings['src'], settings=settings)
|
1137
971
|
except Exception as e:
|
1138
972
|
errorMessage = f"Error during processing: {e}"
|
1139
973
|
q.put(errorMessage) # Send the error message to the GUI via the queue
|
@@ -1202,4 +1036,490 @@ def run_multiple_simulations_wrapper(settings, q, fig_queue):
|
|
1202
1036
|
q.put(errorMessage) # Send the error message to the GUI via the queue
|
1203
1037
|
traceback.print_exc()
|
1204
1038
|
finally:
|
1205
|
-
plt.show = original_show # Restore the original plt.show function
|
1039
|
+
plt.show = original_show # Restore the original plt.show function
|
1040
|
+
|
1041
|
+
def convert_settings_dict_for_gui(settings):
|
1042
|
+
variables = {}
|
1043
|
+
special_cases = {
|
1044
|
+
'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
|
1045
|
+
'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
|
1046
|
+
'cell_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
|
1047
|
+
'nucleus_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
|
1048
|
+
'pathogen_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
|
1049
|
+
#'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
|
1050
|
+
'magnification': ('combo', [20, 40, 60], 20),
|
1051
|
+
'nucleus_channel': ('combo', [0, 1, 2, 3, None], None),
|
1052
|
+
'cell_channel': ('combo', [0, 1, 2, 3, None], None),
|
1053
|
+
'pathogen_channel': ('combo', [0, 1, 2, 3, None], None),
|
1054
|
+
'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
|
1055
|
+
'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
|
1056
|
+
'model_type': ('combo', ['resnet50', 'other_model'], 'resnet50'),
|
1057
|
+
'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
|
1058
|
+
'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
|
1059
|
+
'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
|
1060
|
+
'normalize_by': ('combo', ['fov', 'png'], 'png'),
|
1061
|
+
}
|
1062
|
+
|
1063
|
+
for key, value in settings.items():
|
1064
|
+
if key in special_cases:
|
1065
|
+
variables[key] = special_cases[key]
|
1066
|
+
elif isinstance(value, bool):
|
1067
|
+
variables[key] = ('check', None, value)
|
1068
|
+
elif isinstance(value, int) or isinstance(value, float):
|
1069
|
+
variables[key] = ('entry', None, value)
|
1070
|
+
elif isinstance(value, str):
|
1071
|
+
variables[key] = ('entry', None, value)
|
1072
|
+
elif value is None:
|
1073
|
+
variables[key] = ('entry', None, value)
|
1074
|
+
elif isinstance(value, list):
|
1075
|
+
variables[key] = ('entry', None, str(value))
|
1076
|
+
else:
|
1077
|
+
variables[key] = ('entry', None, str(value))
|
1078
|
+
return variables
|
1079
|
+
|
1080
|
+
def setup_settings_panel(vertical_container, settings_type='mask', frame_height=500, frame_width=1000):
|
1081
|
+
global vars_dict, scrollable_frame
|
1082
|
+
from .settings import set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model
|
1083
|
+
|
1084
|
+
print("Setting up settings panel")
|
1085
|
+
|
1086
|
+
# Create settings frame
|
1087
|
+
settings_frame = tk.Frame(vertical_container, bg='black', height=frame_height, width=frame_width)
|
1088
|
+
vertical_container.add(settings_frame, stretch="always")
|
1089
|
+
|
1090
|
+
# Add settings label
|
1091
|
+
settings_label = ttk.Label(settings_frame, text="Settings", style="Custom.TLabel", background="black", foreground="white")
|
1092
|
+
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
1093
|
+
|
1094
|
+
# Create a ScrollableFrame inside the settings_frame
|
1095
|
+
scrollable_frame = ScrollableFrame(settings_frame, bg='black', width=frame_width)
|
1096
|
+
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
1097
|
+
|
1098
|
+
# Configure the weights for resizing
|
1099
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
1100
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
1101
|
+
|
1102
|
+
# Load settings based on type
|
1103
|
+
if settings_type == 'mask':
|
1104
|
+
settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
|
1105
|
+
elif settings_type == 'measure':
|
1106
|
+
settings = get_measure_crop_settings(settings={})
|
1107
|
+
elif settings_type == 'classify':
|
1108
|
+
settings = set_default_train_test_model(settings={})
|
1109
|
+
else:
|
1110
|
+
raise ValueError(f"Invalid settings type: {settings_type}")
|
1111
|
+
|
1112
|
+
# Generate fields for settings
|
1113
|
+
variables = convert_settings_dict_for_gui(settings)
|
1114
|
+
vars_dict = generate_fields(variables, scrollable_frame)
|
1115
|
+
|
1116
|
+
print("Settings panel setup complete")
|
1117
|
+
return scrollable_frame, vars_dict
|
1118
|
+
|
1119
|
+
|
1120
|
+
def setup_plot_section(vertical_container):
|
1121
|
+
global canvas, canvas_widget
|
1122
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
1123
|
+
vertical_container.add(plot_frame, stretch="always")
|
1124
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
1125
|
+
plot = figure.add_subplot(111)
|
1126
|
+
plot.plot([], []) # This creates an empty plot.
|
1127
|
+
plot.axis('off')
|
1128
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
1129
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
1130
|
+
canvas_widget = canvas.get_tk_widget()
|
1131
|
+
plot_frame.add(canvas_widget, stretch="always")
|
1132
|
+
canvas.draw()
|
1133
|
+
canvas.figure = figure
|
1134
|
+
return canvas, canvas_widget
|
1135
|
+
|
1136
|
+
def download_hug_dataset():
|
1137
|
+
global vars_dict, q
|
1138
|
+
repo_id = "einarolafsson/toxo_mito"
|
1139
|
+
subfolder = "plate1"
|
1140
|
+
local_dir = os.path.join(os.path.expanduser("~"), "datasets") # Set to the home directory
|
1141
|
+
try:
|
1142
|
+
local_path = download_dataset(repo_id, subfolder, local_dir)
|
1143
|
+
if 'src' in vars_dict:
|
1144
|
+
vars_dict['src'][2].set(local_path) # Assuming vars_dict['src'] is a tuple and the 3rd element is a StringVar
|
1145
|
+
q.put(f"Set source path to: {vars_dict['src'][2].get()}\n")
|
1146
|
+
q.put(f"Dataset downloaded to: {local_path}\n")
|
1147
|
+
except Exception as e:
|
1148
|
+
q.put(f"Failed to download dataset: {e}\n")
|
1149
|
+
|
1150
|
+
def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
1151
|
+
global q
|
1152
|
+
"""
|
1153
|
+
Downloads a dataset from Hugging Face and returns the local path.
|
1154
|
+
|
1155
|
+
Args:
|
1156
|
+
repo_id (str): The repository ID (e.g., 'einarolafsson/toxo_mito').
|
1157
|
+
subfolder (str): The subfolder path within the repository (e.g., 'plate1').
|
1158
|
+
local_dir (str): The local directory where the dataset will be saved. Defaults to the user's home directory.
|
1159
|
+
retries (int): Number of retry attempts in case of failure.
|
1160
|
+
delay (int): Delay in seconds between retries.
|
1161
|
+
|
1162
|
+
Returns:
|
1163
|
+
str: The local path to the downloaded dataset.
|
1164
|
+
"""
|
1165
|
+
if local_dir is None:
|
1166
|
+
local_dir = os.path.join(os.path.expanduser("~"), "datasets")
|
1167
|
+
|
1168
|
+
local_subfolder_dir = os.path.join(local_dir, subfolder)
|
1169
|
+
if not os.path.exists(local_subfolder_dir):
|
1170
|
+
os.makedirs(local_subfolder_dir)
|
1171
|
+
elif len(os.listdir(local_subfolder_dir)) == 40:
|
1172
|
+
q.put(f"Dataset already downloaded to: {local_subfolder_dir}")
|
1173
|
+
return local_subfolder_dir
|
1174
|
+
|
1175
|
+
attempt = 0
|
1176
|
+
while attempt < retries:
|
1177
|
+
try:
|
1178
|
+
files = list_repo_files(repo_id, repo_type="dataset")
|
1179
|
+
subfolder_files = [file for file in files if file.startswith(subfolder)]
|
1180
|
+
|
1181
|
+
for file_name in subfolder_files:
|
1182
|
+
for attempt in range(retries):
|
1183
|
+
try:
|
1184
|
+
url = f"https://huggingface.co/datasets/{repo_id}/resolve/main/{file_name}?download=true"
|
1185
|
+
response = requests.get(url, stream=True)
|
1186
|
+
response.raise_for_status()
|
1187
|
+
|
1188
|
+
local_file_path = os.path.join(local_subfolder_dir, os.path.basename(file_name))
|
1189
|
+
with open(local_file_path, 'wb') as file:
|
1190
|
+
for chunk in response.iter_content(chunk_size=8192):
|
1191
|
+
file.write(chunk)
|
1192
|
+
q.put(f"Downloaded file: {file_name}")
|
1193
|
+
break
|
1194
|
+
except (requests.HTTPError, requests.Timeout) as e:
|
1195
|
+
q.put(f"Error downloading {file_name}: {e}. Retrying in {delay} seconds...")
|
1196
|
+
time.sleep(delay)
|
1197
|
+
else:
|
1198
|
+
raise Exception(f"Failed to download {file_name} after multiple attempts.")
|
1199
|
+
|
1200
|
+
return local_subfolder_dir
|
1201
|
+
|
1202
|
+
except (requests.HTTPError, requests.Timeout) as e:
|
1203
|
+
q.put(f"Error downloading dataset: {e}. Retrying in {delay} seconds...")
|
1204
|
+
attempt += 1
|
1205
|
+
time.sleep(delay)
|
1206
|
+
|
1207
|
+
raise Exception("Failed to download dataset after multiple attempts.")
|
1208
|
+
|
1209
|
+
def setup_button_section(horizontal_container, settings_type='mask', btn_row=1, settings_row=5, run=True, abort=True, download=True, import_btn=True, progress=True):
|
1210
|
+
global button_frame, run_button, abort_button, download_dataset_button, import_button, progress_label, q, fig_queue, vars_dict
|
1211
|
+
|
1212
|
+
button_frame = tk.Frame(horizontal_container, bg='black')
|
1213
|
+
horizontal_container.add(button_frame, stretch="always", sticky="nsew")
|
1214
|
+
button_frame.grid_rowconfigure(0, weight=0)
|
1215
|
+
button_frame.grid_rowconfigure(1, weight=1)
|
1216
|
+
button_frame.grid_columnconfigure(0, weight=1)
|
1217
|
+
|
1218
|
+
categories_label = ttk.Label(button_frame, text="Categories", style="Custom.TLabel", background="black", foreground="white")
|
1219
|
+
categories_label.grid(row=0, column=0, pady=10, padx=10)
|
1220
|
+
|
1221
|
+
button_scrollable_frame = ScrollableFrame(button_frame, bg='black')
|
1222
|
+
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
1223
|
+
|
1224
|
+
button_scrollable_frame.scrollable_frame.grid_columnconfigure(0, weight=1, minsize=100)
|
1225
|
+
button_scrollable_frame.scrollable_frame.grid_columnconfigure(1, weight=1, minsize=100)
|
1226
|
+
button_scrollable_frame.scrollable_frame.grid_columnconfigure(2, weight=1, minsize=100)
|
1227
|
+
|
1228
|
+
if run:
|
1229
|
+
run_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue, settings_type), style='Custom.TButton')
|
1230
|
+
run_button.grid(row=btn_row, column=0, pady=5, padx=5, sticky='ew')
|
1231
|
+
if abort:
|
1232
|
+
abort_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, style='Custom.TButton')
|
1233
|
+
abort_button.grid(row=btn_row, column=1, pady=5, padx=5, sticky='ew')
|
1234
|
+
btn_row += 1
|
1235
|
+
if download:
|
1236
|
+
download_dataset_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Download", command=download_hug_dataset, style='Custom.TButton')
|
1237
|
+
download_dataset_button.grid(row=btn_row, column=0, pady=5, padx=5, sticky='ew')
|
1238
|
+
if import_btn:
|
1239
|
+
import_button = ttk.Button(button_scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(settings_row, settings_type), style='Custom.TButton')
|
1240
|
+
import_button.grid(row=btn_row, column=1, pady=5, padx=5, sticky='ew')
|
1241
|
+
btn_row += 1
|
1242
|
+
if progress:
|
1243
|
+
progress_label = ttk.Label(button_scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white")
|
1244
|
+
progress_label.grid(row=btn_row, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
1245
|
+
|
1246
|
+
# Call toggle_settings after vars_dict is initialized
|
1247
|
+
if vars_dict is not None:
|
1248
|
+
toggle_settings(button_scrollable_frame)
|
1249
|
+
|
1250
|
+
return progress_label
|
1251
|
+
|
1252
|
+
|
1253
|
+
def setup_console(vertical_container):
|
1254
|
+
global console_output
|
1255
|
+
print("Setting up console frame")
|
1256
|
+
console_frame = tk.Frame(vertical_container, bg='black')
|
1257
|
+
vertical_container.add(console_frame, stretch="always")
|
1258
|
+
console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
|
1259
|
+
console_label.grid(row=0, column=0, pady=10, padx=10)
|
1260
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
1261
|
+
console_output.grid(row=1, column=0, sticky="nsew")
|
1262
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
1263
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
1264
|
+
print("Console setup complete")
|
1265
|
+
return console_output
|
1266
|
+
|
1267
|
+
def toggle_test_mode():
|
1268
|
+
global vars_dict, test_mode_button
|
1269
|
+
current_state = vars_dict['test_mode'][2].get()
|
1270
|
+
new_state = not current_state
|
1271
|
+
vars_dict['test_mode'][2].set(new_state)
|
1272
|
+
if new_state:
|
1273
|
+
test_mode_button.config(bg="blue")
|
1274
|
+
else:
|
1275
|
+
test_mode_button.config(bg="gray")
|
1276
|
+
|
1277
|
+
def toggle_settings(button_scrollable_frame):
|
1278
|
+
global vars_dict
|
1279
|
+
|
1280
|
+
if vars_dict is None:
|
1281
|
+
raise ValueError("vars_dict is not initialized.")
|
1282
|
+
|
1283
|
+
categories = {
|
1284
|
+
"General": ["src", "input_folder", "metadata_type", "custom_regex", "experiment", "channels", "magnification"],
|
1285
|
+
"Nucleus": ["nucleus_channel", "nucleus_background", "nucleus_Signal_to_noise", "nucleus_CP_prob", "nucleus_FT", "remove_background_nucleus", "nucleus_min_size", "nucleus_mask_dim", "nucleus_loc"],
|
1286
|
+
"Cell": ["cell_channel", "cell_background", "cell_Signal_to_noise", "cell_CP_prob", "cell_FT", "remove_background_cell", "cell_min_size", "cell_mask_dim", "cytoplasm", "cytoplasm_min_size", "include_uninfected", "merge_edge_pathogen_cells", "adjust_cells"],
|
1287
|
+
"Pathogen": ["pathogen_channel", "pathogen_background", "pathogen_Signal_to_noise", "pathogen_CP_prob", "pathogen_FT", "pathogen_model", "remove_background_pathogen", "pathogen_min_size", "pathogen_mask_dim"],
|
1288
|
+
"Timelapse": ["timelapse", "fps", "timelapse_displacement", "timelapse_memory", "timelapse_frame_limits", "timelapse_remove_transient", "timelapse_mode", "timelapse_objects", "compartments"],
|
1289
|
+
"Plot": ["plot_filtration", "examples_to_plot", "normalize_plots", "normalize", "cmap", "figuresize", "plot"],
|
1290
|
+
"Object Image": ["save_png", "dialate_pngs", "dialate_png_ratios", "png_size", "png_dims", "save_arrays", "normalize_by", "dialate_png_ratios", "crop_mode", "dialate_pngs", "normalize", "use_bounding_box"],
|
1291
|
+
"Annotate Data": ["treatment_loc", "cells", "cell_loc", "pathogens", "pathogen_loc", "channel_of_interest", "measurement", "treatments", "representative_images", "um_per_pixel", "nr_imgs"],
|
1292
|
+
"Measurements": ["homogeneity", "homogeneity_distances", "radial_dist", "calculate_correlation", "manders_thresholds", "save_measurements"],
|
1293
|
+
"Advanced": ["preprocess", "remove_background", "normalize", "lower_percentile", "merge_pathogens", "batch_size", "filter", "save", "masks", "verbose", "randomize", "max_workers", "workers"],
|
1294
|
+
"Miscellaneous": ["all_to_mip", "pick_slice", "skip_mode", "upscale", "upscale_factor"],
|
1295
|
+
"Test": ["test_mode", "test_images", "random_test", "test_nr"]
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
def toggle_category(settings, var):
|
1299
|
+
for setting in settings:
|
1300
|
+
if setting in vars_dict:
|
1301
|
+
label, widget, _ = vars_dict[setting]
|
1302
|
+
if var.get() == 0:
|
1303
|
+
label.grid_remove()
|
1304
|
+
widget.grid_remove()
|
1305
|
+
else:
|
1306
|
+
label.grid()
|
1307
|
+
widget.grid()
|
1308
|
+
|
1309
|
+
row = 1
|
1310
|
+
col = 2 # Start from column 2 to avoid overlap with buttons
|
1311
|
+
category_idx = 0
|
1312
|
+
|
1313
|
+
for category, settings in categories.items():
|
1314
|
+
if any(setting in vars_dict for setting in settings):
|
1315
|
+
category_var = tk.IntVar(value=0)
|
1316
|
+
vars_dict[category] = (None, None, category_var)
|
1317
|
+
toggle = ttk.Checkbutton(
|
1318
|
+
button_scrollable_frame.scrollable_frame,
|
1319
|
+
text=category,
|
1320
|
+
variable=category_var,
|
1321
|
+
command=lambda cat=settings, var=category_var: toggle_category(cat, var),
|
1322
|
+
style='TCheckbutton'
|
1323
|
+
)
|
1324
|
+
toggle.grid(row=row, column=col, sticky="w", pady=2, padx=2)
|
1325
|
+
col += 1
|
1326
|
+
category_idx += 1
|
1327
|
+
|
1328
|
+
if category_idx % 4 == 0:
|
1329
|
+
row += 1
|
1330
|
+
col = 2 # Reset column to 2
|
1331
|
+
|
1332
|
+
for settings in categories.values():
|
1333
|
+
for setting in settings:
|
1334
|
+
if setting in vars_dict:
|
1335
|
+
label, widget, _ = vars_dict[setting]
|
1336
|
+
label.grid_remove()
|
1337
|
+
widget.grid_remove()
|
1338
|
+
|
1339
|
+
def initiate_abort():
|
1340
|
+
global thread_control
|
1341
|
+
if thread_control.get("stop_requested") is not None:
|
1342
|
+
thread_control["stop_requested"].value = 1
|
1343
|
+
|
1344
|
+
if thread_control.get("run_thread") is not None:
|
1345
|
+
thread_control["run_thread"].join(timeout=5)
|
1346
|
+
if thread_control["run_thread"].is_alive():
|
1347
|
+
thread_control["run_thread"].terminate()
|
1348
|
+
thread_control["run_thread"] = None
|
1349
|
+
|
1350
|
+
def run_mask_gui(settings, q, fig_queue, stop_requested):
|
1351
|
+
process_stdout_stderr(q)
|
1352
|
+
try:
|
1353
|
+
preprocess_generate_masks_wrapper(settings, q, fig_queue)
|
1354
|
+
except Exception as e:
|
1355
|
+
q.put(f"Error during processing: {e}")
|
1356
|
+
traceback.print_exc()
|
1357
|
+
finally:
|
1358
|
+
stop_requested.value = 1
|
1359
|
+
|
1360
|
+
def start_process(q, fig_queue, settings_type='mask'):
|
1361
|
+
global thread_control, vars_dict
|
1362
|
+
settings = check_settings(vars_dict)
|
1363
|
+
if thread_control.get("run_thread") is not None:
|
1364
|
+
initiate_abort()
|
1365
|
+
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
1366
|
+
thread_control["stop_requested"] = stop_requested
|
1367
|
+
if settings_type == 'mask':
|
1368
|
+
thread_control["run_thread"] = Process(target=run_mask_gui, args=(settings, q, fig_queue, stop_requested))
|
1369
|
+
elif settings_type == 'measure':
|
1370
|
+
thread_control["run_thread"] = Process(target=run_measure_gui, args=(settings, q, fig_queue, stop_requested))
|
1371
|
+
elif settings_type == 'classify':
|
1372
|
+
thread_control["run_thread"] = Process(target=run_classify_gui, args=(settings, q, fig_queue, stop_requested))
|
1373
|
+
thread_control["run_thread"].start()
|
1374
|
+
|
1375
|
+
def import_settings(settings_type='mask'):
|
1376
|
+
global vars_dict, scrollable_frame
|
1377
|
+
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
1378
|
+
csv_settings = read_settings_from_csv(csv_file_path)
|
1379
|
+
if settings_type == 'mask':
|
1380
|
+
settings = set_default_settings_preprocess_generate_masks(src='path', settings={})
|
1381
|
+
elif settings_type == 'measure':
|
1382
|
+
settings = get_measure_crop_settings(settings={})
|
1383
|
+
elif settings_type == 'classify':
|
1384
|
+
settings = set_default_train_test_model(settings={})
|
1385
|
+
else:
|
1386
|
+
raise ValueError(f"Invalid settings type: {settings_type}")
|
1387
|
+
|
1388
|
+
variables = convert_settings_dict_for_gui(settings)
|
1389
|
+
new_settings = update_settings_from_csv(variables, csv_settings)
|
1390
|
+
vars_dict = generate_fields(new_settings, scrollable_frame)
|
1391
|
+
|
1392
|
+
def process_fig_queue():
|
1393
|
+
global canvas, fig_queue, canvas_widget, parent_frame
|
1394
|
+
try:
|
1395
|
+
while not fig_queue.empty():
|
1396
|
+
clear_canvas(canvas)
|
1397
|
+
fig = fig_queue.get_nowait()
|
1398
|
+
for ax in fig.get_axes():
|
1399
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
1400
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
1401
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
1402
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
1403
|
+
fig.tight_layout()
|
1404
|
+
fig.set_facecolor('black')
|
1405
|
+
canvas.figure = fig
|
1406
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
1407
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
1408
|
+
canvas.draw_idle()
|
1409
|
+
except Exception as e:
|
1410
|
+
traceback.print_exc()
|
1411
|
+
finally:
|
1412
|
+
after_id = canvas_widget.after(100, process_fig_queue)
|
1413
|
+
parent_frame.after_tasks.append(after_id)
|
1414
|
+
|
1415
|
+
def process_console_queue():
|
1416
|
+
global q, console_output, parent_frame
|
1417
|
+
while not q.empty():
|
1418
|
+
message = q.get_nowait()
|
1419
|
+
console_output.insert(tk.END, message)
|
1420
|
+
console_output.see(tk.END)
|
1421
|
+
after_id = console_output.after(100, process_console_queue)
|
1422
|
+
parent_frame.after_tasks.append(after_id)
|
1423
|
+
|
1424
|
+
def setup_frame(parent_frame):
|
1425
|
+
style = ttk.Style(parent_frame)
|
1426
|
+
set_dark_style(style)
|
1427
|
+
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
1428
|
+
parent_frame.configure(bg='black')
|
1429
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
1430
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
1431
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, bg='black')
|
1432
|
+
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
1433
|
+
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.HORIZONTAL, bg='black')
|
1434
|
+
vertical_container.add(horizontal_container, stretch="always")
|
1435
|
+
horizontal_container.grid_columnconfigure(0, weight=1)
|
1436
|
+
horizontal_container.grid_columnconfigure(1, weight=1)
|
1437
|
+
settings_frame = tk.Frame(horizontal_container, bg='black')
|
1438
|
+
settings_frame.grid_rowconfigure(0, weight=0)
|
1439
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
1440
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
1441
|
+
horizontal_container.add(settings_frame, stretch="always", sticky="nsew")
|
1442
|
+
return parent_frame, vertical_container, horizontal_container
|
1443
|
+
|
1444
|
+
def run_measure_gui(settings, q, fig_queue, stop_requested):
|
1445
|
+
process_stdout_stderr(q)
|
1446
|
+
try:
|
1447
|
+
settings['input_folder'] = settings['src']
|
1448
|
+
measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
|
1449
|
+
except Exception as e:
|
1450
|
+
q.put(f"Error during processing: {e}")
|
1451
|
+
traceback.print_exc()
|
1452
|
+
finally:
|
1453
|
+
stop_requested.value = 1
|
1454
|
+
|
1455
|
+
def run_classify_gui(settings, q, fig_queue, stop_requested):
|
1456
|
+
process_stdout_stderr(q)
|
1457
|
+
try:
|
1458
|
+
train_test_model_wrapper(settings['src'], settings)
|
1459
|
+
except Exception as e:
|
1460
|
+
q.put(f"Error during processing: {e}")
|
1461
|
+
traceback.print_exc()
|
1462
|
+
finally:
|
1463
|
+
stop_requested.value = 1
|
1464
|
+
|
1465
|
+
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):
|
1466
|
+
global q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue
|
1467
|
+
q = q_var
|
1468
|
+
console_output = console_output_var
|
1469
|
+
parent_frame = parent_frame_var
|
1470
|
+
vars_dict = vars_dict_var
|
1471
|
+
canvas = canvas_var
|
1472
|
+
canvas_widget = canvas_widget_var
|
1473
|
+
scrollable_frame = scrollable_frame_var
|
1474
|
+
progress_label = progress_label_var
|
1475
|
+
fig_queue = fig_queue_var
|
1476
|
+
|
1477
|
+
def initiate_root(parent, settings_type='mask'):
|
1478
|
+
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, progress_label
|
1479
|
+
print("Initializing root with settings_type:", settings_type)
|
1480
|
+
parent_frame = parent
|
1481
|
+
|
1482
|
+
if not hasattr(parent_frame, 'after_tasks'):
|
1483
|
+
parent_frame.after_tasks = []
|
1484
|
+
|
1485
|
+
for widget in parent_frame.winfo_children():
|
1486
|
+
if widget.winfo_exists():
|
1487
|
+
try:
|
1488
|
+
widget.destroy()
|
1489
|
+
except tk.TclError as e:
|
1490
|
+
print(f"Error destroying widget: {e}")
|
1491
|
+
|
1492
|
+
q = Queue()
|
1493
|
+
fig_queue = Queue()
|
1494
|
+
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
1495
|
+
scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type) # Adjust height and width as needed
|
1496
|
+
progress_label = setup_button_section(horizontal_container, settings_type)
|
1497
|
+
canvas, canvas_widget = setup_plot_section(vertical_container)
|
1498
|
+
console_output = setup_console(vertical_container)
|
1499
|
+
set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue)
|
1500
|
+
process_console_queue()
|
1501
|
+
process_fig_queue()
|
1502
|
+
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
1503
|
+
parent_frame.after_tasks.append(after_id)
|
1504
|
+
print("Root initialization complete")
|
1505
|
+
return parent_frame, vars_dict
|
1506
|
+
|
1507
|
+
|
1508
|
+
def cancel_after_tasks(frame):
|
1509
|
+
if hasattr(frame, 'after_tasks'):
|
1510
|
+
for task in frame.after_tasks:
|
1511
|
+
frame.after_cancel(task)
|
1512
|
+
frame.after_tasks.clear()
|
1513
|
+
|
1514
|
+
def start_gui_app(settings_type='mask'):
|
1515
|
+
global q, fig_queue, parent_frame, scrollable_frame, vars_dict, canvas, canvas_widget, progress_label
|
1516
|
+
root = tk.Tk()
|
1517
|
+
width = root.winfo_screenwidth()
|
1518
|
+
height = root.winfo_screenheight()
|
1519
|
+
root.geometry(f"{width}x{height}")
|
1520
|
+
root.title(f"SpaCr: {settings_type.capitalize()}")
|
1521
|
+
root.content_frame = tk.Frame(root)
|
1522
|
+
print("Starting GUI app with settings_type:", settings_type)
|
1523
|
+
initiate_root(root.content_frame, settings_type)
|
1524
|
+
create_menu_bar(root)
|
1525
|
+
root.mainloop()
|