spacr 0.1.50__py3-none-any.whl → 0.1.55__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 +3 -3
- spacr/app_make_masks.py +3 -3
- spacr/app_make_masks_v2.py +4 -4
- spacr/app_sequencing.py +8 -0
- spacr/app_umap.py +8 -0
- spacr/core.py +8 -6
- spacr/gui.py +10 -4
- spacr/gui_utils.py +388 -701
- spacr/sequencing.py +1 -17
- spacr/settings.py +427 -4
- {spacr-0.1.50.dist-info → spacr-0.1.55.dist-info}/METADATA +1 -1
- {spacr-0.1.50.dist-info → spacr-0.1.55.dist-info}/RECORD +17 -15
- {spacr-0.1.50.dist-info → spacr-0.1.55.dist-info}/LICENSE +0 -0
- {spacr-0.1.50.dist-info → spacr-0.1.55.dist-info}/WHEEL +0 -0
- {spacr-0.1.50.dist-info → spacr-0.1.55.dist-info}/entry_points.txt +0 -0
- {spacr-0.1.50.dist-info → spacr-0.1.55.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py
CHANGED
@@ -1,28 +1,22 @@
|
|
1
|
-
import os, spacr,
|
1
|
+
import os, spacr, traceback, io, sys, ast, ctypes, matplotlib, re, csv, requests, ast
|
2
2
|
import matplotlib.pyplot as plt
|
3
3
|
matplotlib.use('Agg')
|
4
|
-
import numpy as np
|
5
4
|
import tkinter as tk
|
6
|
-
from tkinter import ttk
|
5
|
+
from tkinter import ttk
|
7
6
|
import tkinter.font as tkFont
|
8
7
|
from tkinter import filedialog
|
9
8
|
from tkinter import Checkbutton
|
10
9
|
from tkinter import font as tkFont
|
11
|
-
from torchvision import models
|
12
|
-
|
13
10
|
from multiprocessing import Process, Value, Queue, Manager, set_start_method
|
14
|
-
import multiprocessing as mp
|
15
|
-
|
16
11
|
from tkinter import ttk, scrolledtext
|
17
12
|
from matplotlib.figure import Figure
|
18
13
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
19
14
|
import time
|
20
15
|
import requests
|
21
|
-
from requests.exceptions import HTTPError, Timeout
|
22
16
|
from huggingface_hub import list_repo_files, hf_hub_download
|
23
17
|
|
24
18
|
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
|
19
|
+
from .settings import set_default_train_test_model, get_measure_crop_settings, set_default_settings_preprocess_generate_masks, get_analyze_reads_default_settings, set_default_umap_image_settings
|
26
20
|
|
27
21
|
try:
|
28
22
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
@@ -42,42 +36,15 @@ fig_queue = None
|
|
42
36
|
|
43
37
|
thread_control = {"run_thread": None, "stop_requested": False}
|
44
38
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
67
|
-
|
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'))
|
72
|
-
|
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)
|
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')
|
79
46
|
|
80
|
-
class
|
47
|
+
class spacrFrame(ttk.Frame):
|
81
48
|
def __init__(self, container, width=None, *args, bg='black', **kwargs):
|
82
49
|
super().__init__(container, *args, **kwargs)
|
83
50
|
self.configure(style='TFrame')
|
@@ -105,40 +72,64 @@ class ScrollableFrame(ttk.Frame):
|
|
105
72
|
for child in self.scrollable_frame.winfo_children():
|
106
73
|
child.configure(bg='black')
|
107
74
|
|
108
|
-
class
|
109
|
-
def __init__(self,
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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)
|
119
109
|
|
120
|
-
def
|
121
|
-
|
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)
|
122
115
|
|
123
|
-
class
|
116
|
+
class spacrButton(tk.Frame):
|
124
117
|
def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
|
125
118
|
super().__init__(parent, *args, **kwargs)
|
126
119
|
self.text = text
|
127
120
|
self.command = command
|
128
|
-
|
129
|
-
# Detect screen height and calculate button dimensions
|
130
121
|
screen_height = self.winfo_screenheight()
|
131
122
|
button_height = screen_height // 50
|
132
123
|
button_width = button_height * 3
|
133
124
|
|
134
|
-
|
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")
|
135
127
|
self.canvas.grid(row=0, column=0)
|
136
128
|
|
137
|
-
self.button_bg = self.create_rounded_rectangle(
|
129
|
+
self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
|
138
130
|
|
139
|
-
# Use the passed font or default to Helvetica if not provided
|
140
131
|
self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
|
141
|
-
self.button_text = self.canvas.create_text(button_width // 2, button_height // 2, text=self.text, fill="white", font=self.font_style)
|
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)
|
142
133
|
|
143
134
|
self.bind("<Enter>", self.on_enter)
|
144
135
|
self.bind("<Leave>", self.on_leave)
|
@@ -148,62 +139,56 @@ class CustomButton(tk.Frame):
|
|
148
139
|
self.canvas.bind("<Button-1>", self.on_click)
|
149
140
|
|
150
141
|
def on_enter(self, event=None):
|
151
|
-
self.canvas.itemconfig(self.button_bg, fill="#
|
142
|
+
self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
|
152
143
|
|
153
144
|
def on_leave(self, event=None):
|
154
|
-
self.canvas.itemconfig(self.button_bg, fill="#
|
145
|
+
self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
|
155
146
|
|
156
147
|
def on_click(self, event=None):
|
157
148
|
if self.command:
|
158
149
|
self.command()
|
159
150
|
|
160
151
|
def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
|
161
|
-
points = [
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
+
]
|
182
174
|
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
183
175
|
|
184
|
-
|
176
|
+
|
177
|
+
class spacrSwitch(ttk.Frame):
|
185
178
|
def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
|
186
179
|
super().__init__(parent, *args, **kwargs)
|
187
180
|
self.text = text
|
188
181
|
self.variable = variable if variable else tk.BooleanVar()
|
189
182
|
self.command = command
|
190
|
-
|
191
183
|
self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
|
192
184
|
self.canvas.grid(row=0, column=1, padx=(10, 0))
|
193
|
-
|
194
|
-
# Background rounded rectangle with smaller dimensions and no outline
|
195
185
|
self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
|
196
|
-
|
197
|
-
# Switch ball with no outline
|
198
186
|
self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
|
199
|
-
|
200
|
-
self.label = ttk.Label(self, text=self.text, background="black", foreground="white")
|
187
|
+
self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
|
201
188
|
self.label.grid(row=0, column=0, padx=(0, 10))
|
202
|
-
|
203
189
|
self.bind("<Button-1>", self.toggle)
|
204
190
|
self.canvas.bind("<Button-1>", self.toggle)
|
205
191
|
self.label.bind("<Button-1>", self.toggle)
|
206
|
-
|
207
192
|
self.update_switch()
|
208
193
|
|
209
194
|
def toggle(self, event=None):
|
@@ -269,7 +254,7 @@ class ToggleSwitch(ttk.Frame):
|
|
269
254
|
|
270
255
|
return self.canvas.create_polygon(points, **kwargs, smooth=True)
|
271
256
|
|
272
|
-
class
|
257
|
+
class spacrToolTip:
|
273
258
|
def __init__(self, widget, text):
|
274
259
|
self.widget = widget
|
275
260
|
self.text = text
|
@@ -283,7 +268,8 @@ class ToolTip:
|
|
283
268
|
self.tooltip_window = tk.Toplevel(self.widget)
|
284
269
|
self.tooltip_window.wm_overrideredirect(True)
|
285
270
|
self.tooltip_window.wm_geometry(f"+{x}+{y}")
|
286
|
-
|
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)
|
287
273
|
label.grid(row=0, column=0, padx=5, pady=5)
|
288
274
|
|
289
275
|
def hide_tooltip(self, event):
|
@@ -291,6 +277,114 @@ class ToolTip:
|
|
291
277
|
self.tooltip_window.destroy()
|
292
278
|
self.tooltip_window = None
|
293
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
|
+
def set_default_font(root, font_name="Helvetica", size=12):
|
382
|
+
default_font = (font_name, size)
|
383
|
+
root.option_add("*Font", default_font)
|
384
|
+
root.option_add("*TButton.Font", default_font)
|
385
|
+
root.option_add("*TLabel.Font", default_font)
|
386
|
+
root.option_add("*TEntry.Font", default_font)
|
387
|
+
|
294
388
|
def create_menu_bar(root):
|
295
389
|
from .app_annotate import initiate_annotation_app_root
|
296
390
|
from .app_make_masks import initiate_mask_app_root
|
@@ -300,7 +394,9 @@ def create_menu_bar(root):
|
|
300
394
|
"Measure": 'measure',
|
301
395
|
"Annotate": initiate_annotation_app_root,
|
302
396
|
"Make Masks": initiate_mask_app_root,
|
303
|
-
"Classify": 'classify'
|
397
|
+
"Classify": 'classify',
|
398
|
+
"Sequencing": 'sequencing',
|
399
|
+
"Umap": 'umap'
|
304
400
|
}
|
305
401
|
|
306
402
|
def load_app_wrapper(app_name, app_func):
|
@@ -345,6 +441,10 @@ def proceed_with_app(root, app_name, app_func):
|
|
345
441
|
initiate_root(root.content_frame, 'measure')
|
346
442
|
elif app_name == "Classify":
|
347
443
|
initiate_root(root.content_frame, 'classify')
|
444
|
+
elif app_name == "Sequencing":
|
445
|
+
initiate_root(root.content_frame, 'sequencing')
|
446
|
+
elif app_name == "Umap":
|
447
|
+
initiate_root(root.content_frame, 'umap')
|
348
448
|
elif app_name == "Annotate":
|
349
449
|
gui_annotate()
|
350
450
|
elif app_name == "Make Masks":
|
@@ -397,464 +497,39 @@ def parse_list(value):
|
|
397
497
|
except (ValueError, SyntaxError):
|
398
498
|
raise ValueError("Invalid format for list")
|
399
499
|
|
500
|
+
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
501
|
+
label_column = 0
|
502
|
+
widget_column = 1
|
400
503
|
|
401
|
-
|
402
|
-
|
403
|
-
|
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
|
-
}
|
654
|
-
|
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"]:
|
658
|
-
|
659
|
-
q.put(f"Key {key} not found in expected types.")
|
660
|
-
continue
|
661
|
-
|
662
|
-
value = var.get()
|
663
|
-
expected_type = expected_types.get(key, str)
|
664
|
-
|
665
|
-
try:
|
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")
|
673
|
-
else:
|
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
|
694
|
-
else:
|
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
|
504
|
+
# Configure the column widths
|
505
|
+
frame.grid_columnconfigure(label_column, weight=0) # Allow the label column to expand
|
506
|
+
frame.grid_columnconfigure(widget_column, weight=1) # Allow the widget column to expand
|
702
507
|
|
703
|
-
|
508
|
+
# Right-align the label text and the label itself
|
509
|
+
label = spacrLabel(frame, text=label_text, background="black", foreground="white", anchor='e', justify='right')
|
510
|
+
label.grid(column=label_column, row=row, sticky=tk.E, padx=(5, 2), pady=5) # Align label to the right
|
704
511
|
|
705
|
-
def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
|
706
|
-
label = ttk.Label(frame, text=label_text, style='Custom.TLabel') # Apply Custom.TLabel style for labels
|
707
|
-
label.grid(column=0, row=row, sticky=tk.W, padx=5, pady=5)
|
708
|
-
|
709
512
|
if var_type == 'entry':
|
710
513
|
var = tk.StringVar(value=default_value) # Set default value
|
711
514
|
entry = ttk.Entry(frame, textvariable=var, style='TEntry') # Apply TEntry style for entries
|
712
|
-
entry.grid(column=
|
515
|
+
entry.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
|
713
516
|
return (label, entry, var) # Return both the label and the entry, and the variable
|
714
517
|
elif var_type == 'check':
|
715
518
|
var = tk.BooleanVar(value=default_value) # Set default value (True/False)
|
716
|
-
check =
|
717
|
-
check.grid(column=
|
519
|
+
check = spacrCheckbutton(frame, text="", variable=var, style='TCheckbutton')
|
520
|
+
check.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
|
718
521
|
return (label, check, var) # Return both the label and the checkbutton, and the variable
|
719
522
|
elif var_type == 'combo':
|
720
523
|
var = tk.StringVar(value=default_value) # Set default value
|
721
524
|
combo = ttk.Combobox(frame, textvariable=var, values=options, style='TCombobox') # Apply TCombobox style
|
722
|
-
combo.grid(column=
|
525
|
+
combo.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
|
723
526
|
if default_value:
|
724
527
|
combo.set(default_value)
|
725
528
|
return (label, combo, var) # Return both the label and the combobox, and the variable
|
726
529
|
else:
|
727
530
|
var = None # Placeholder in case of an undefined var_type
|
728
531
|
return (label, None, var)
|
729
|
-
|
730
|
-
def generate_fields(variables, scrollable_frame):
|
731
|
-
row = 1
|
732
|
-
vars_dict = {}
|
733
|
-
tooltips = {
|
734
|
-
"src": "Path to the folder containing the images.",
|
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.",
|
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'.",
|
737
|
-
"experiment": "Name of the experiment. This will be used to name the output files.",
|
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.",
|
739
|
-
"magnification": "At what magnification the images were taken. This will be used to determine the size of the objects in the images.",
|
740
|
-
"nucleus_channel": "The channel to use for the nucleus. If None, the nucleus will not be segmented.",
|
741
|
-
"nucleus_background": "The background intensity for the nucleus channel. This will be used to remove background noise.",
|
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.",
|
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.",
|
745
|
-
"cell_channel": "The channel to use for the cell. If None, the cell will not be segmented.",
|
746
|
-
"cell_background": "The background intensity for the cell channel. This will be used to remove background noise.",
|
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.",
|
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.",
|
750
|
-
"pathogen_channel": "The channel to use for the pathogen. If None, the pathogen will not be segmented.",
|
751
|
-
"pathogen_background": "The background intensity for the pathogen channel. This will be used to remove background noise.",
|
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.",
|
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.",
|
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.",
|
756
|
-
"masks": "Whether to generate masks for the segmented objects. If True, masks will be generated for the nucleus, cell, and pathogen.",
|
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.",
|
758
|
-
"randomize": "Whether to randomize the order of the images before processing. Recommended to avoid bias in the segmentation.",
|
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.",
|
760
|
-
"timelapse": "Whether to process the images as a timelapse.",
|
761
|
-
"timelapse_displacement": "The displacement between frames in the timelapse. This will be used to align the frames before processing.",
|
762
|
-
"timelapse_memory": "The number of frames to in tandem objects must be present in to be considered the same object in the timelapse.",
|
763
|
-
"timelapse_frame_limits": "The frame limits to use for the timelapse. This will determine which frames are processed. For example, [5,20] will process frames 5 to 20.",
|
764
|
-
"timelapse_remove_transient": "Whether to remove transient objects in the timelapse. Transient objects are present in fewer than all frames.",
|
765
|
-
"timelapse_mode": "The mode to use for processing the timelapse. 'trackpy' uses the trackpy library for tracking objects, while 'btrack' uses the btrack library.",
|
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.",
|
767
|
-
"fps": "Frames per second of the automatically generated timelapse movies.",
|
768
|
-
"remove_background": "Whether to remove background noise from the images. This will help improve the quality of the segmentation.",
|
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.",
|
771
|
-
"normalize_plots": "Whether to normalize the plots.",
|
772
|
-
"all_to_mip": "Whether to convert all images to maximum intensity projections before processing.",
|
773
|
-
"pick_slice": "Whether to pick a single slice from the z-stack images. If False, the maximum intensity projection will be used.",
|
774
|
-
"skip_mode": "The mode to use for skipping images. This will determine how to handle images that cannot be processed.",
|
775
|
-
"save": "Whether to save the results to disk.",
|
776
|
-
"merge_edge_pathogen_cells": "Whether to merge cells that share pathogen objects.",
|
777
|
-
"plot": "Whether to plot the results.",
|
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.",
|
779
|
-
"verbose": "Whether to print verbose output during processing.",
|
780
|
-
"input_folder": "Path to the folder containing the images.",
|
781
|
-
"cell_mask_dim": "The dimension of the array the cell mask is saved in.",
|
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.",
|
785
|
-
"nucleus_mask_dim": "The dimension of the array the nucleus mask is saved in.",
|
786
|
-
"nucleus_min_size": "The minimum size of nucleus objects in pixels^2.",
|
787
|
-
"pathogen_mask_dim": "The dimension of the array the pathogen mask is saved in.",
|
788
|
-
"pathogen_min_size": "The minimum size of pathogen objects in pixels^2.",
|
789
|
-
"save_png": "Whether to save the segmented objects as PNG images.",
|
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).",
|
791
|
-
"use_bounding_box": "Whether to use the bounding box of the objects for cropping. If False, only the object itself will be cropped.",
|
792
|
-
"png_size": "The size of the PNG images to save. This will determine the size of the saved images.",
|
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.",
|
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].",
|
795
|
-
"normalize_by": "Whether to normalize the images by field of view (fov) or by PNG image (png).",
|
796
|
-
"save_measurements": "Whether to save the measurements to disk.",
|
797
|
-
"representative_images": "Whether to save representative images of the segmented objects (Not working yet).",
|
798
|
-
"plot_filtration": "Whether to plot the filtration steps.",
|
799
|
-
"include_uninfected": "Whether to include uninfected cells in the analysis.",
|
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.",
|
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.",
|
803
|
-
"cells": "The cell types to include in the analysis.",
|
804
|
-
"cell_loc": "The locations of the cell types in the images.",
|
805
|
-
"pathogens": "The pathogen types to include in the analysis.",
|
806
|
-
"pathogen_loc": "The locations of the pathogen types in the images.",
|
807
|
-
"treatments": "The treatments to include in the analysis.",
|
808
|
-
"treatment_loc": "The locations of the treatments in the images.",
|
809
|
-
"channel_of_interest": "The channel of interest to use for the analysis.",
|
810
|
-
"compartments": "The compartments to measure in the images.",
|
811
|
-
"measurement": "The measurement to use for the analysis.",
|
812
|
-
"nr_imgs": "The number of images to plot.",
|
813
|
-
"um_per_pixel": "The micrometers per pixel for the images."
|
814
|
-
}
|
815
|
-
|
816
|
-
for key, (var_type, options, default_value) in variables.items():
|
817
|
-
label, widget, var = create_input_field(scrollable_frame.scrollable_frame, key, row, var_type, options, default_value)
|
818
|
-
vars_dict[key] = (label, widget, var) # Store the label, widget, and variable
|
819
|
-
|
820
|
-
# Add tooltip to the label if it exists in the tooltips dictionary
|
821
|
-
if key in tooltips:
|
822
|
-
ToolTip(label, tooltips[key])
|
823
|
-
row += 1
|
824
|
-
return vars_dict
|
825
|
-
|
826
|
-
class TextRedirector(object):
|
827
|
-
def __init__(self, widget, queue):
|
828
|
-
self.widget = widget
|
829
|
-
self.queue = queue
|
830
532
|
|
831
|
-
def write(self, str):
|
832
|
-
self.queue.put(str)
|
833
|
-
|
834
|
-
def flush(self):
|
835
|
-
pass
|
836
|
-
|
837
|
-
def create_dark_mode(root, style, console_output):
|
838
|
-
dark_bg = 'black'
|
839
|
-
light_text = 'white'
|
840
|
-
dark_text = 'black'
|
841
|
-
input_bg = '#555555' # Slightly lighter background for input fields
|
842
|
-
|
843
|
-
# Configure ttkcompartments('TFrame', background=dark_bg)
|
844
|
-
style.configure('TLabel', background=dark_bg, foreground=light_text)
|
845
|
-
style.configure('TEntry', fieldbackground=input_bg, foreground=dark_text, background=dark_bg)
|
846
|
-
style.configure('TButton', background=dark_bg, foreground=dark_text)
|
847
|
-
style.map('TButton', background=[('active', dark_bg)], foreground=[('active', dark_text)])
|
848
|
-
style.configure('Dark.TCheckbutton', background=dark_bg, foreground=dark_text)
|
849
|
-
style.map('Dark.TCheckbutton', background=[('active', dark_bg)], foreground=[('active', dark_text)])
|
850
|
-
style.configure('TCombobox', fieldbackground=input_bg, foreground=dark_text, background=dark_bg, selectbackground=input_bg, selectforeground=dark_text)
|
851
|
-
style.map('TCombobox', fieldbackground=[('readonly', input_bg)], selectbackground=[('readonly', input_bg)], foreground=[('readonly', dark_text)])
|
852
|
-
|
853
|
-
if console_output != None:
|
854
|
-
console_output.config(bg=dark_bg, fg=light_text, insertbackground=light_text) #, font=("Helvetica", 12)
|
855
|
-
root.configure(bg=dark_bg)
|
856
|
-
|
857
|
-
##@log_function_call
|
858
533
|
def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label):
|
859
534
|
try:
|
860
535
|
ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
|
@@ -910,6 +585,14 @@ def clear_canvas(canvas):
|
|
910
585
|
|
911
586
|
# Redraw the now empty canvas without changing its size
|
912
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)
|
913
596
|
|
914
597
|
def measure_crop_wrapper(settings, q, fig_queue):
|
915
598
|
"""
|
@@ -921,14 +604,6 @@ def measure_crop_wrapper(settings, q, fig_queue):
|
|
921
604
|
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
922
605
|
"""
|
923
606
|
|
924
|
-
def my_show():
|
925
|
-
"""
|
926
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
927
|
-
"""
|
928
|
-
fig = plt.gcf()
|
929
|
-
fig_queue.put(fig) # Queue the figure for GUI display
|
930
|
-
plt.close(fig) # Prevent the figure from being shown by plt.show()
|
931
|
-
|
932
607
|
# Temporarily override plt.show
|
933
608
|
original_show = plt.show
|
934
609
|
plt.show = my_show
|
@@ -938,12 +613,11 @@ def measure_crop_wrapper(settings, q, fig_queue):
|
|
938
613
|
spacr.measure.measure_crop(settings=settings)
|
939
614
|
except Exception as e:
|
940
615
|
errorMessage = f"Error during processing: {e}"
|
941
|
-
q.put(errorMessage)
|
616
|
+
q.put(errorMessage)
|
942
617
|
traceback.print_exc()
|
943
618
|
finally:
|
944
|
-
plt.show = original_show
|
619
|
+
plt.show = original_show
|
945
620
|
|
946
|
-
#@log_function_call
|
947
621
|
def preprocess_generate_masks_wrapper(settings, q, fig_queue):
|
948
622
|
"""
|
949
623
|
Wraps the measure_crop function to integrate with GUI processes.
|
@@ -954,14 +628,6 @@ def preprocess_generate_masks_wrapper(settings, q, fig_queue):
|
|
954
628
|
- fig_queue: multiprocessing.Queue, Queue for sending figures to the GUI.
|
955
629
|
"""
|
956
630
|
|
957
|
-
def my_show():
|
958
|
-
"""
|
959
|
-
Replacement for plt.show() that queues figures instead of displaying them.
|
960
|
-
"""
|
961
|
-
fig = plt.gcf()
|
962
|
-
fig_queue.put(fig) # Queue the figure for GUI display
|
963
|
-
plt.close(fig) # Prevent the figure from being shown by plt.show()
|
964
|
-
|
965
631
|
# Temporarily override plt.show
|
966
632
|
original_show = plt.show
|
967
633
|
plt.show = my_show
|
@@ -970,10 +636,40 @@ def preprocess_generate_masks_wrapper(settings, q, fig_queue):
|
|
970
636
|
spacr.core.preprocess_generate_masks(src=settings['src'], settings=settings)
|
971
637
|
except Exception as e:
|
972
638
|
errorMessage = f"Error during processing: {e}"
|
973
|
-
q.put(errorMessage)
|
639
|
+
q.put(errorMessage)
|
974
640
|
traceback.print_exc()
|
975
641
|
finally:
|
976
|
-
plt.show = original_show
|
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
|
977
673
|
|
978
674
|
def train_test_model_wrapper(settings, q, fig_queue):
|
979
675
|
"""
|
@@ -1077,9 +773,29 @@ def convert_settings_dict_for_gui(settings):
|
|
1077
773
|
variables[key] = ('entry', None, str(value))
|
1078
774
|
return variables
|
1079
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
|
+
|
1080
796
|
def setup_settings_panel(vertical_container, settings_type='mask', frame_height=500, frame_width=1000):
|
1081
797
|
global vars_dict, scrollable_frame
|
1082
|
-
from .settings import set_default_settings_preprocess_generate_masks, get_measure_crop_settings, set_default_train_test_model
|
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
|
1083
799
|
|
1084
800
|
print("Setting up settings panel")
|
1085
801
|
|
@@ -1088,11 +804,11 @@ def setup_settings_panel(vertical_container, settings_type='mask', frame_height=
|
|
1088
804
|
vertical_container.add(settings_frame, stretch="always")
|
1089
805
|
|
1090
806
|
# Add settings label
|
1091
|
-
settings_label =
|
807
|
+
settings_label = spacrLabel(settings_frame, text="Settings", background="black", foreground="white", anchor='center', justify='center', align="center")
|
1092
808
|
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
1093
809
|
|
1094
|
-
# Create a
|
1095
|
-
scrollable_frame =
|
810
|
+
# Create a spacrFrame inside the settings_frame
|
811
|
+
scrollable_frame = spacrFrame(settings_frame, bg='black', width=frame_width)
|
1096
812
|
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
1097
813
|
|
1098
814
|
# Configure the weights for resizing
|
@@ -1106,17 +822,19 @@ def setup_settings_panel(vertical_container, settings_type='mask', frame_height=
|
|
1106
822
|
settings = get_measure_crop_settings(settings={})
|
1107
823
|
elif settings_type == 'classify':
|
1108
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={})
|
1109
829
|
else:
|
1110
830
|
raise ValueError(f"Invalid settings type: {settings_type}")
|
1111
831
|
|
1112
832
|
# Generate fields for settings
|
1113
833
|
variables = convert_settings_dict_for_gui(settings)
|
1114
834
|
vars_dict = generate_fields(variables, scrollable_frame)
|
1115
|
-
|
1116
835
|
print("Settings panel setup complete")
|
1117
836
|
return scrollable_frame, vars_dict
|
1118
837
|
|
1119
|
-
|
1120
838
|
def setup_plot_section(vertical_container):
|
1121
839
|
global canvas, canvas_widget
|
1122
840
|
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
@@ -1133,6 +851,35 @@ def setup_plot_section(vertical_container):
|
|
1133
851
|
canvas.figure = figure
|
1134
852
|
return canvas, canvas_widget
|
1135
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
|
+
|
1136
883
|
def download_hug_dataset():
|
1137
884
|
global vars_dict, q
|
1138
885
|
repo_id = "einarolafsson/toxo_mito"
|
@@ -1206,8 +953,8 @@ def download_dataset(repo_id, subfolder, local_dir=None, retries=5, delay=5):
|
|
1206
953
|
|
1207
954
|
raise Exception("Failed to download dataset after multiple attempts.")
|
1208
955
|
|
1209
|
-
def setup_button_section(horizontal_container, settings_type='mask',
|
1210
|
-
global button_frame, run_button, abort_button, download_dataset_button, import_button,
|
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
|
1211
958
|
|
1212
959
|
button_frame = tk.Frame(horizontal_container, bg='black')
|
1213
960
|
horizontal_container.add(button_frame, stretch="always", sticky="nsew")
|
@@ -1215,54 +962,38 @@ def setup_button_section(horizontal_container, settings_type='mask', btn_row=1,
|
|
1215
962
|
button_frame.grid_rowconfigure(1, weight=1)
|
1216
963
|
button_frame.grid_columnconfigure(0, weight=1)
|
1217
964
|
|
1218
|
-
categories_label =
|
965
|
+
categories_label = spacrLabel(button_frame, text="Categories", background="black", foreground="white", font=('Helvetica', 12), anchor='center', justify='center', align="center") # Increase font size
|
1219
966
|
categories_label.grid(row=0, column=0, pady=10, padx=10)
|
1220
967
|
|
1221
|
-
button_scrollable_frame =
|
968
|
+
button_scrollable_frame = spacrFrame(button_frame, bg='black')
|
1222
969
|
button_scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
1223
970
|
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
971
|
+
btn_col = 0
|
972
|
+
btn_row = 1
|
973
|
+
|
1228
974
|
if run:
|
1229
|
-
run_button =
|
1230
|
-
run_button.grid(row=btn_row, column=
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
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
|
+
|
1238
989
|
if import_btn:
|
1239
|
-
import_button =
|
1240
|
-
import_button.grid(row=btn_row, column=
|
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)
|
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')
|
1245
992
|
|
1246
993
|
# Call toggle_settings after vars_dict is initialized
|
1247
994
|
if vars_dict is not None:
|
1248
995
|
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
|
996
|
+
return button_scrollable_frame
|
1266
997
|
|
1267
998
|
def toggle_test_mode():
|
1268
999
|
global vars_dict, test_mode_button
|
@@ -1276,24 +1007,10 @@ def toggle_test_mode():
|
|
1276
1007
|
|
1277
1008
|
def toggle_settings(button_scrollable_frame):
|
1278
1009
|
global vars_dict
|
1010
|
+
from .settings import categories
|
1279
1011
|
|
1280
1012
|
if vars_dict is None:
|
1281
1013
|
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
1014
|
|
1298
1015
|
def toggle_category(settings, var):
|
1299
1016
|
for setting in settings:
|
@@ -1307,27 +1024,32 @@ def toggle_settings(button_scrollable_frame):
|
|
1307
1024
|
widget.grid()
|
1308
1025
|
|
1309
1026
|
row = 1
|
1310
|
-
col = 2
|
1027
|
+
col = 2
|
1311
1028
|
category_idx = 0
|
1312
1029
|
|
1313
1030
|
for category, settings in categories.items():
|
1314
1031
|
if any(setting in vars_dict for setting in settings):
|
1315
1032
|
category_var = tk.IntVar(value=0)
|
1316
1033
|
vars_dict[category] = (None, None, category_var)
|
1317
|
-
toggle =
|
1034
|
+
toggle = spacrCheckbutton(
|
1318
1035
|
button_scrollable_frame.scrollable_frame,
|
1319
1036
|
text=category,
|
1320
1037
|
variable=category_var,
|
1321
|
-
command=lambda cat=settings, var=category_var: toggle_category(cat, var)
|
1322
|
-
style='TCheckbutton'
|
1038
|
+
command=lambda cat=settings, var=category_var: toggle_category(cat, var)
|
1323
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')
|
1324
1046
|
toggle.grid(row=row, column=col, sticky="w", pady=2, padx=2)
|
1325
1047
|
col += 1
|
1326
1048
|
category_idx += 1
|
1327
1049
|
|
1328
1050
|
if category_idx % 4 == 0:
|
1329
1051
|
row += 1
|
1330
|
-
col = 2
|
1052
|
+
col = 2
|
1331
1053
|
|
1332
1054
|
for settings in categories.values():
|
1333
1055
|
for setting in settings:
|
@@ -1336,59 +1058,6 @@ def toggle_settings(button_scrollable_frame):
|
|
1336
1058
|
label.grid_remove()
|
1337
1059
|
widget.grid_remove()
|
1338
1060
|
|
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
1061
|
def process_fig_queue():
|
1393
1062
|
global canvas, fig_queue, canvas_widget, parent_frame
|
1394
1063
|
try:
|
@@ -1421,25 +1090,35 @@ def process_console_queue():
|
|
1421
1090
|
after_id = console_output.after(100, process_console_queue)
|
1422
1091
|
parent_frame.after_tasks.append(after_id)
|
1423
1092
|
|
1424
|
-
def
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
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
|
1443
1122
|
|
1444
1123
|
def run_measure_gui(settings, q, fig_queue, stop_requested):
|
1445
1124
|
process_stdout_stderr(q)
|
@@ -1475,7 +1154,7 @@ def set_globals(q_var, console_output_var, parent_frame_var, vars_dict_var, canv
|
|
1475
1154
|
fig_queue = fig_queue_var
|
1476
1155
|
|
1477
1156
|
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
|
1157
|
+
global q, fig_queue, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, progress_label, button_scrollable_frame
|
1479
1158
|
print("Initializing root with settings_type:", settings_type)
|
1480
1159
|
parent_frame = parent
|
1481
1160
|
|
@@ -1493,9 +1172,15 @@ def initiate_root(parent, settings_type='mask'):
|
|
1493
1172
|
fig_queue = Queue()
|
1494
1173
|
parent_frame, vertical_container, horizontal_container = setup_frame(parent_frame)
|
1495
1174
|
scrollable_frame, vars_dict = setup_settings_panel(horizontal_container, settings_type) # Adjust height and width as needed
|
1496
|
-
|
1175
|
+
button_scrollable_frame = setup_button_section(horizontal_container, settings_type)
|
1497
1176
|
canvas, canvas_widget = setup_plot_section(vertical_container)
|
1498
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
|
+
|
1499
1184
|
set_globals(q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, progress_label, fig_queue)
|
1500
1185
|
process_console_queue()
|
1501
1186
|
process_fig_queue()
|
@@ -1504,7 +1189,6 @@ def initiate_root(parent, settings_type='mask'):
|
|
1504
1189
|
print("Root initialization complete")
|
1505
1190
|
return parent_frame, vars_dict
|
1506
1191
|
|
1507
|
-
|
1508
1192
|
def cancel_after_tasks(frame):
|
1509
1193
|
if hasattr(frame, 'after_tasks'):
|
1510
1194
|
for task in frame.after_tasks:
|
@@ -1522,4 +1206,7 @@ def start_gui_app(settings_type='mask'):
|
|
1522
1206
|
print("Starting GUI app with settings_type:", settings_type)
|
1523
1207
|
initiate_root(root.content_frame, settings_type)
|
1524
1208
|
create_menu_bar(root)
|
1525
|
-
root.mainloop()
|
1209
|
+
root.mainloop()
|
1210
|
+
|
1211
|
+
|
1212
|
+
|