spacr 0.1.11__py3-none-any.whl → 0.1.12__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 +12 -14
- spacr/app_annotate.py +539 -0
- spacr/app_classify.py +201 -0
- spacr/app_make_masks.py +929 -0
- spacr/app_make_masks_v2.py +688 -0
- spacr/app_mask.py +251 -0
- spacr/app_measure.py +248 -0
- spacr/gui.py +9 -12
- spacr/gui_utils.py +55 -29
- spacr/make_masks_app.py +3 -1
- {spacr-0.1.11.dist-info → spacr-0.1.12.dist-info}/METADATA +1 -1
- {spacr-0.1.11.dist-info → spacr-0.1.12.dist-info}/RECORD +16 -10
- {spacr-0.1.11.dist-info → spacr-0.1.12.dist-info}/LICENSE +0 -0
- {spacr-0.1.11.dist-info → spacr-0.1.12.dist-info}/WHEEL +0 -0
- {spacr-0.1.11.dist-info → spacr-0.1.12.dist-info}/entry_points.txt +0 -0
- {spacr-0.1.11.dist-info → spacr-0.1.12.dist-info}/top_level.txt +0 -0
spacr/app_mask.py
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
#import customtkinter as ctk
|
2
|
+
|
3
|
+
import sys, ctypes, matplotlib
|
4
|
+
import tkinter as tk
|
5
|
+
from tkinter import ttk, scrolledtext
|
6
|
+
from matplotlib.figure import Figure
|
7
|
+
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
8
|
+
matplotlib.use('Agg')
|
9
|
+
from tkinter import filedialog
|
10
|
+
from multiprocessing import Process, Queue, Value
|
11
|
+
import traceback
|
12
|
+
|
13
|
+
try:
|
14
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
15
|
+
except AttributeError:
|
16
|
+
pass
|
17
|
+
|
18
|
+
from .logger import log_function_call
|
19
|
+
from .gui_utils import ScrollableFrame, StdoutRedirector, ToggleSwitch, CustomButton, ToolTip
|
20
|
+
from .gui_utils import clear_canvas, main_thread_update_function, set_dark_style, generate_fields, process_stdout_stderr, set_default_font, style_text_boxes
|
21
|
+
from .gui_utils import mask_variables, check_mask_gui_settings, preprocess_generate_masks_wrapper, read_settings_from_csv, update_settings_from_csv, create_menu_bar
|
22
|
+
|
23
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
24
|
+
|
25
|
+
test_mode_button = None
|
26
|
+
|
27
|
+
def toggle_test_mode():
|
28
|
+
global vars_dict
|
29
|
+
current_state = vars_dict['test_mode'][2].get()
|
30
|
+
new_state = not current_state
|
31
|
+
vars_dict['test_mode'][2].set(new_state)
|
32
|
+
if new_state:
|
33
|
+
test_mode_button.config(bg="blue")
|
34
|
+
else:
|
35
|
+
test_mode_button.config(bg="gray")
|
36
|
+
|
37
|
+
def toggle_advanced_settings():
|
38
|
+
global vars_dict
|
39
|
+
|
40
|
+
timelapse_settings = ['timelapse', 'timelapse_memory', 'timelapse_remove_transient', 'timelapse_mode', 'timelapse_objects', 'timelapse_displacement', 'timelapse_frame_limits', 'fps']
|
41
|
+
misc_settings = ['examples_to_plot', 'all_to_mip', 'pick_slice', 'skip_mode']
|
42
|
+
opperational_settings = ['preprocess', 'masks', 'randomize', 'batch_size', 'custom_regex', 'merge', 'normalize_plots', 'workers', 'plot', 'remove_background', 'lower_quantile']
|
43
|
+
|
44
|
+
advanced_settings = timelapse_settings+misc_settings+opperational_settings
|
45
|
+
|
46
|
+
# Toggle visibility of advanced settings
|
47
|
+
for setting in advanced_settings:
|
48
|
+
label, widget, var = vars_dict[setting]
|
49
|
+
if advanced_var.get() is False:
|
50
|
+
label.grid_remove() # Hide the label
|
51
|
+
widget.grid_remove() # Hide the widget
|
52
|
+
else:
|
53
|
+
label.grid() # Show the label
|
54
|
+
widget.grid() # Show the widget
|
55
|
+
|
56
|
+
#@log_function_call
|
57
|
+
def initiate_abort():
|
58
|
+
global thread_control
|
59
|
+
if thread_control.get("stop_requested") is not None:
|
60
|
+
thread_control["stop_requested"].value = 1
|
61
|
+
|
62
|
+
if thread_control.get("run_thread") is not None:
|
63
|
+
thread_control["run_thread"].join(timeout=5)
|
64
|
+
if thread_control["run_thread"].is_alive():
|
65
|
+
thread_control["run_thread"].terminate()
|
66
|
+
thread_control["run_thread"] = None
|
67
|
+
|
68
|
+
#@log_function_call
|
69
|
+
def run_mask_gui(q, fig_queue, stop_requested):
|
70
|
+
global vars_dict
|
71
|
+
process_stdout_stderr(q)
|
72
|
+
try:
|
73
|
+
settings = check_mask_gui_settings(vars_dict)
|
74
|
+
preprocess_generate_masks_wrapper(settings, q, fig_queue)
|
75
|
+
except Exception as e:
|
76
|
+
q.put(f"Error during processing: {e}")
|
77
|
+
traceback.print_exc()
|
78
|
+
finally:
|
79
|
+
stop_requested.value = 1
|
80
|
+
|
81
|
+
#@log_function_call
|
82
|
+
def start_process(q, fig_queue):
|
83
|
+
global thread_control
|
84
|
+
if thread_control.get("run_thread") is not None:
|
85
|
+
initiate_abort()
|
86
|
+
|
87
|
+
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
88
|
+
thread_control["stop_requested"] = stop_requested
|
89
|
+
thread_control["run_thread"] = Process(target=run_mask_gui, args=(q, fig_queue, stop_requested))
|
90
|
+
thread_control["run_thread"].start()
|
91
|
+
|
92
|
+
def import_settings(scrollable_frame):
|
93
|
+
global vars_dict
|
94
|
+
|
95
|
+
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
96
|
+
csv_settings = read_settings_from_csv(csv_file_path)
|
97
|
+
variables = mask_variables()
|
98
|
+
new_settings = update_settings_from_csv(variables, csv_settings)
|
99
|
+
vars_dict = generate_fields(new_settings, scrollable_frame)
|
100
|
+
|
101
|
+
#@log_function_call
|
102
|
+
def initiate_mask_root(parent_frame):
|
103
|
+
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, advanced_var, scrollable_frame
|
104
|
+
|
105
|
+
style = ttk.Style(parent_frame)
|
106
|
+
set_dark_style(style)
|
107
|
+
style_text_boxes(style)
|
108
|
+
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
109
|
+
parent_frame.configure(bg='black')
|
110
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
111
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
112
|
+
|
113
|
+
fig_queue = Queue()
|
114
|
+
|
115
|
+
# Initialize after_tasks if not already done
|
116
|
+
if not hasattr(parent_frame, 'after_tasks'):
|
117
|
+
parent_frame.after_tasks = []
|
118
|
+
|
119
|
+
def _process_fig_queue():
|
120
|
+
global canvas
|
121
|
+
try:
|
122
|
+
while not fig_queue.empty():
|
123
|
+
clear_canvas(canvas)
|
124
|
+
fig = fig_queue.get_nowait()
|
125
|
+
for ax in fig.get_axes():
|
126
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
127
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
128
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
129
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
130
|
+
fig.tight_layout()
|
131
|
+
fig.set_facecolor('black')
|
132
|
+
canvas.figure = fig
|
133
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
134
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
135
|
+
canvas.draw_idle()
|
136
|
+
except Exception as e:
|
137
|
+
traceback.print_exc()
|
138
|
+
finally:
|
139
|
+
after_id = canvas_widget.after(100, _process_fig_queue)
|
140
|
+
parent_frame.after_tasks.append(after_id)
|
141
|
+
|
142
|
+
def _process_console_queue():
|
143
|
+
while not q.empty():
|
144
|
+
message = q.get_nowait()
|
145
|
+
console_output.insert(tk.END, message)
|
146
|
+
console_output.see(tk.END)
|
147
|
+
after_id = console_output.after(100, _process_console_queue)
|
148
|
+
parent_frame.after_tasks.append(after_id)
|
149
|
+
|
150
|
+
# Clear previous content if any
|
151
|
+
for widget in parent_frame.winfo_children():
|
152
|
+
widget.destroy()
|
153
|
+
|
154
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
155
|
+
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
156
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
157
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
158
|
+
|
159
|
+
# Settings Section
|
160
|
+
settings_frame = tk.Frame(vertical_container, bg='black')
|
161
|
+
vertical_container.add(settings_frame, stretch="always")
|
162
|
+
settings_label = ttk.Label(settings_frame, text="Settings", style="Custom.TLabel")
|
163
|
+
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
164
|
+
scrollable_frame = ScrollableFrame(settings_frame, width=600)
|
165
|
+
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
166
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
167
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
168
|
+
|
169
|
+
# Create advanced settings checkbox
|
170
|
+
advanced_var = tk.BooleanVar(value=False)
|
171
|
+
advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
|
172
|
+
advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
|
173
|
+
variables = mask_variables()
|
174
|
+
vars_dict = generate_fields(variables, scrollable_frame)
|
175
|
+
toggle_advanced_settings()
|
176
|
+
vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
|
177
|
+
|
178
|
+
# Button section
|
179
|
+
test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode, font=('Helvetica', 10))
|
180
|
+
#CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name, app_func), font=('Helvetica', 12))
|
181
|
+
|
182
|
+
test_mode_button.grid(row=47, column=1, pady=10, padx=10)
|
183
|
+
import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
|
184
|
+
import_btn.grid(row=47, column=0, pady=10, padx=10)
|
185
|
+
run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue))
|
186
|
+
run_button.grid(row=45, column=0, pady=10, padx=10)
|
187
|
+
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
|
188
|
+
abort_button.grid(row=45, column=1, pady=10, padx=10)
|
189
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white")
|
190
|
+
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
191
|
+
|
192
|
+
# Plot Canvas Section
|
193
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
194
|
+
vertical_container.add(plot_frame, stretch="always")
|
195
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
196
|
+
plot = figure.add_subplot(111)
|
197
|
+
plot.plot([], []) # This creates an empty plot.
|
198
|
+
plot.axis('off')
|
199
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
200
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
201
|
+
canvas_widget = canvas.get_tk_widget()
|
202
|
+
plot_frame.add(canvas_widget, stretch="always")
|
203
|
+
canvas.draw()
|
204
|
+
canvas.figure = figure
|
205
|
+
|
206
|
+
# Console Section
|
207
|
+
console_frame = tk.Frame(vertical_container, bg='black')
|
208
|
+
vertical_container.add(console_frame, stretch="always")
|
209
|
+
console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
|
210
|
+
console_label.grid(row=0, column=0, pady=10, padx=10)
|
211
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
212
|
+
console_output.grid(row=1, column=0, sticky="nsew")
|
213
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
214
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
215
|
+
|
216
|
+
q = Queue()
|
217
|
+
sys.stdout = StdoutRedirector(console_output)
|
218
|
+
sys.stderr = StdoutRedirector(console_output)
|
219
|
+
|
220
|
+
_process_console_queue()
|
221
|
+
_process_fig_queue()
|
222
|
+
|
223
|
+
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
224
|
+
parent_frame.after_tasks.append(after_id)
|
225
|
+
|
226
|
+
return parent_frame, vars_dict
|
227
|
+
|
228
|
+
def gui_mask():
|
229
|
+
root = tk.Tk()
|
230
|
+
width = root.winfo_screenwidth()
|
231
|
+
height = root.winfo_screenheight()
|
232
|
+
root.geometry(f"{width}x{height}")
|
233
|
+
root.title("SpaCr: generate masks")
|
234
|
+
|
235
|
+
# Clear previous content if any
|
236
|
+
if hasattr(root, 'content_frame'):
|
237
|
+
for widget in root.content_frame.winfo_children():
|
238
|
+
widget.destroy()
|
239
|
+
root.content_frame.grid_forget()
|
240
|
+
else:
|
241
|
+
root.content_frame = tk.Frame(root)
|
242
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
243
|
+
root.grid_rowconfigure(1, weight=1)
|
244
|
+
root.grid_columnconfigure(0, weight=1)
|
245
|
+
|
246
|
+
initiate_mask_root(root.content_frame)
|
247
|
+
create_menu_bar(root)
|
248
|
+
root.mainloop()
|
249
|
+
|
250
|
+
if __name__ == "__main__":
|
251
|
+
gui_mask()
|
spacr/app_measure.py
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
import sys, traceback, matplotlib, ctypes
|
2
|
+
import tkinter as tk
|
3
|
+
from tkinter import ttk, scrolledtext
|
4
|
+
from matplotlib.figure import Figure
|
5
|
+
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
6
|
+
matplotlib.use('Agg') # Use the non-GUI Agg backend
|
7
|
+
from multiprocessing import Process, Queue, Value
|
8
|
+
from tkinter import filedialog
|
9
|
+
|
10
|
+
try:
|
11
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
12
|
+
except AttributeError:
|
13
|
+
pass
|
14
|
+
|
15
|
+
from .logger import log_function_call
|
16
|
+
from .gui_utils import ScrollableFrame, StdoutRedirector, CustomButton, ToggleSwitch, ToolTip
|
17
|
+
from .gui_utils import process_stdout_stderr, set_dark_style, set_default_font, generate_fields, main_thread_update_function, create_menu_bar
|
18
|
+
from .gui_utils import measure_variables, measure_crop_wrapper, clear_canvas, check_measure_gui_settings, read_settings_from_csv, update_settings_from_csv, style_text_boxes
|
19
|
+
|
20
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
21
|
+
|
22
|
+
def import_settings(scrollable_frame):
|
23
|
+
global vars_dict
|
24
|
+
|
25
|
+
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
26
|
+
csv_settings = read_settings_from_csv(csv_file_path)
|
27
|
+
variables = measure_variables()
|
28
|
+
new_settings = update_settings_from_csv(variables, csv_settings)
|
29
|
+
vars_dict = generate_fields(new_settings, scrollable_frame)
|
30
|
+
|
31
|
+
test_mode_button = None
|
32
|
+
|
33
|
+
def toggle_test_mode():
|
34
|
+
global vars_dict
|
35
|
+
current_state = vars_dict['test_mode'][2].get()
|
36
|
+
new_state = not current_state
|
37
|
+
vars_dict['test_mode'][2].set(new_state)
|
38
|
+
if new_state:
|
39
|
+
test_mode_button.config(bg="blue")
|
40
|
+
else:
|
41
|
+
test_mode_button.config(bg="gray")
|
42
|
+
|
43
|
+
def toggle_advanced_settings():
|
44
|
+
global vars_dict
|
45
|
+
|
46
|
+
timelapse_settings = ['timelapse', 'timelapse_objects']
|
47
|
+
misc_settings = ['representative_images', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'dialate_png_ratios']
|
48
|
+
opperational_settings = ['max_workers','experiment','cells','cell_loc','pathogens','pathogen_loc','treatments','treatment_loc','channel_of_interest','compartments','measurement','nr_imgs', 'um_per_pixel']
|
49
|
+
|
50
|
+
advanced_settings = timelapse_settings+misc_settings+opperational_settings
|
51
|
+
|
52
|
+
# Toggle visibility of advanced settings
|
53
|
+
for setting in advanced_settings:
|
54
|
+
label, widget, var = vars_dict[setting]
|
55
|
+
if advanced_var.get() is False:
|
56
|
+
label.grid_remove() # Hide the label
|
57
|
+
widget.grid_remove() # Hide the widget
|
58
|
+
else:
|
59
|
+
label.grid() # Show the label
|
60
|
+
widget.grid() # Show the widget
|
61
|
+
|
62
|
+
#@log_function_call
|
63
|
+
def run_measure_gui(q, fig_queue, stop_requested):
|
64
|
+
global vars_dict
|
65
|
+
process_stdout_stderr(q)
|
66
|
+
try:
|
67
|
+
print('hello')
|
68
|
+
settings = check_measure_gui_settings(vars_dict)
|
69
|
+
measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
|
70
|
+
except Exception as e:
|
71
|
+
q.put(f"Error during processing: {e}")
|
72
|
+
traceback.print_exc()
|
73
|
+
finally:
|
74
|
+
stop_requested.value = 1
|
75
|
+
|
76
|
+
#@log_function_call
|
77
|
+
def start_process(q, fig_queue):
|
78
|
+
global thread_control
|
79
|
+
if thread_control.get("run_thread") is not None:
|
80
|
+
initiate_abort()
|
81
|
+
|
82
|
+
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
83
|
+
thread_control["stop_requested"] = stop_requested
|
84
|
+
thread_control["run_thread"] = Process(target=run_measure_gui, args=(q, fig_queue, stop_requested))
|
85
|
+
thread_control["run_thread"].start()
|
86
|
+
|
87
|
+
#@log_function_call
|
88
|
+
def initiate_abort():
|
89
|
+
global thread_control
|
90
|
+
if thread_control.get("stop_requested") is not None:
|
91
|
+
thread_control["stop_requested"].value = 1
|
92
|
+
|
93
|
+
if thread_control.get("run_thread") is not None:
|
94
|
+
thread_control["run_thread"].join(timeout=5)
|
95
|
+
if thread_control["run_thread"].is_alive():
|
96
|
+
thread_control["run_thread"].terminate()
|
97
|
+
thread_control["run_thread"] = None
|
98
|
+
|
99
|
+
#@log_function_call
|
100
|
+
def initiate_measure_root(parent_frame):
|
101
|
+
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
|
102
|
+
|
103
|
+
style = ttk.Style(parent_frame)
|
104
|
+
set_dark_style(style)
|
105
|
+
style_text_boxes(style)
|
106
|
+
set_default_font(parent_frame, font_name="Helvetica", size=8)
|
107
|
+
|
108
|
+
parent_frame.configure(bg='black')
|
109
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
110
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
111
|
+
|
112
|
+
fig_queue = Queue()
|
113
|
+
|
114
|
+
# Initialize after_tasks if not already done
|
115
|
+
if not hasattr(parent_frame, 'after_tasks'):
|
116
|
+
parent_frame.after_tasks = []
|
117
|
+
|
118
|
+
def _process_fig_queue():
|
119
|
+
global canvas
|
120
|
+
try:
|
121
|
+
while not fig_queue.empty():
|
122
|
+
clear_canvas(canvas)
|
123
|
+
fig = fig_queue.get_nowait()
|
124
|
+
for ax in fig.get_axes():
|
125
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
126
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
127
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
128
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
129
|
+
fig.tight_layout()
|
130
|
+
fig.set_facecolor('black')
|
131
|
+
canvas.figure = fig
|
132
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
133
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
134
|
+
canvas.draw_idle()
|
135
|
+
except Exception as e:
|
136
|
+
traceback.print_exc()
|
137
|
+
finally:
|
138
|
+
after_id = canvas_widget.after(100, _process_fig_queue)
|
139
|
+
parent_frame.after_tasks.append(after_id)
|
140
|
+
|
141
|
+
def _process_console_queue():
|
142
|
+
while not q.empty():
|
143
|
+
message = q.get_nowait()
|
144
|
+
console_output.insert(tk.END, message)
|
145
|
+
console_output.see(tk.END)
|
146
|
+
after_id = console_output.after(100, _process_console_queue)
|
147
|
+
parent_frame.after_tasks.append(after_id)
|
148
|
+
|
149
|
+
# Clear previous content if any
|
150
|
+
for widget in parent_frame.winfo_children():
|
151
|
+
widget.destroy()
|
152
|
+
|
153
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
154
|
+
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
155
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
156
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
157
|
+
|
158
|
+
# Settings Section
|
159
|
+
settings_frame = tk.Frame(vertical_container, bg='black')
|
160
|
+
vertical_container.add(settings_frame, stretch="always")
|
161
|
+
settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
|
162
|
+
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
163
|
+
scrollable_frame = ScrollableFrame(settings_frame, width=500)
|
164
|
+
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
165
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
166
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
167
|
+
|
168
|
+
# Create advanced settings checkbox
|
169
|
+
advanced_var = tk.BooleanVar(value=False)
|
170
|
+
advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
|
171
|
+
advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
|
172
|
+
variables = measure_variables()
|
173
|
+
vars_dict = generate_fields(variables, scrollable_frame)
|
174
|
+
toggle_advanced_settings()
|
175
|
+
vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
|
176
|
+
|
177
|
+
# Button section
|
178
|
+
test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode)
|
179
|
+
test_mode_button.grid(row=47, column=1, pady=10, padx=10)
|
180
|
+
import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
|
181
|
+
import_btn.grid(row=47, column=0, pady=20, padx=20)
|
182
|
+
run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
|
183
|
+
run_button.grid(row=45, column=0, pady=20, padx=20)
|
184
|
+
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
|
185
|
+
abort_button.grid(row=45, column=1, pady=20, padx=20)
|
186
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
|
187
|
+
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
188
|
+
|
189
|
+
# Plot Canvas Section
|
190
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
191
|
+
vertical_container.add(plot_frame, stretch="always")
|
192
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
|
193
|
+
plot = figure.add_subplot(111)
|
194
|
+
plot.plot([], [])
|
195
|
+
plot.axis('off')
|
196
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
197
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
198
|
+
canvas_widget = canvas.get_tk_widget()
|
199
|
+
plot_frame.add(canvas_widget, stretch="always")
|
200
|
+
canvas.draw()
|
201
|
+
canvas.figure = figure
|
202
|
+
|
203
|
+
# Console Section
|
204
|
+
console_frame = tk.Frame(vertical_container, bg='black')
|
205
|
+
vertical_container.add(console_frame, stretch="always")
|
206
|
+
console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
|
207
|
+
console_label.grid(row=0, column=0, pady=10, padx=10)
|
208
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
209
|
+
console_output.grid(row=1, column=0, sticky="nsew")
|
210
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
211
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
212
|
+
|
213
|
+
q = Queue()
|
214
|
+
sys.stdout = StdoutRedirector(console_output)
|
215
|
+
sys.stderr = StdoutRedirector(console_output)
|
216
|
+
|
217
|
+
_process_console_queue()
|
218
|
+
_process_fig_queue()
|
219
|
+
|
220
|
+
after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
221
|
+
parent_frame.after_tasks.append(after_id)
|
222
|
+
|
223
|
+
return parent_frame, vars_dict
|
224
|
+
|
225
|
+
def gui_measure():
|
226
|
+
root = tk.Tk()
|
227
|
+
width = root.winfo_screenwidth()
|
228
|
+
height = root.winfo_screenheight()
|
229
|
+
root.geometry(f"{width}x{height}")
|
230
|
+
root.title("SpaCr: measure objects")
|
231
|
+
|
232
|
+
# Clear previous content if any
|
233
|
+
if hasattr(root, 'content_frame'):
|
234
|
+
for widget in root.content_frame.winfo_children():
|
235
|
+
widget.destroy()
|
236
|
+
root.content_frame.grid_forget()
|
237
|
+
else:
|
238
|
+
root.content_frame = tk.Frame(root)
|
239
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
240
|
+
root.grid_rowconfigure(1, weight=1)
|
241
|
+
root.grid_columnconfigure(0, weight=1)
|
242
|
+
|
243
|
+
initiate_measure_root(root.content_frame)
|
244
|
+
create_menu_bar(root)
|
245
|
+
root.mainloop()
|
246
|
+
|
247
|
+
if __name__ == "__main__":
|
248
|
+
gui_measure()
|
spacr/gui.py
CHANGED
@@ -6,21 +6,21 @@ import os
|
|
6
6
|
import requests
|
7
7
|
|
8
8
|
# Import your GUI apps
|
9
|
-
from .
|
10
|
-
from .
|
11
|
-
from .
|
12
|
-
from .
|
13
|
-
from .
|
9
|
+
from .app_mask import initiate_mask_root
|
10
|
+
from .app_measure import initiate_measure_root
|
11
|
+
from .app_annotate import initiate_annotation_app_root
|
12
|
+
from .app_make_masks import initiate_mask_app_root
|
13
|
+
from .app_classify import initiate_classify_root
|
14
14
|
from .gui_utils import CustomButton, style_text_boxes, create_menu_bar
|
15
15
|
|
16
16
|
class MainApp(tk.Tk):
|
17
17
|
def __init__(self):
|
18
18
|
super().__init__()
|
19
|
+
width = self.winfo_screenwidth()
|
20
|
+
height = self.winfo_screenheight()
|
21
|
+
self.geometry(f"{width}x{height}")
|
19
22
|
self.title("SpaCr GUI Collection")
|
20
|
-
self.geometry("1100x1500")
|
21
23
|
self.configure(bg="black")
|
22
|
-
#self.attributes('-fullscreen', True)
|
23
|
-
|
24
24
|
style = ttk.Style()
|
25
25
|
style_text_boxes(style)
|
26
26
|
|
@@ -37,7 +37,7 @@ class MainApp(tk.Tk):
|
|
37
37
|
|
38
38
|
def create_widgets(self):
|
39
39
|
# Create the menu bar
|
40
|
-
self
|
40
|
+
create_menu_bar(self)
|
41
41
|
|
42
42
|
# Create a canvas to hold the selected app and other elements
|
43
43
|
self.canvas = tk.Canvas(self, bg="black", highlightthickness=0)
|
@@ -52,9 +52,6 @@ class MainApp(tk.Tk):
|
|
52
52
|
# Create startup screen with buttons for each GUI app
|
53
53
|
self.create_startup_screen()
|
54
54
|
|
55
|
-
def gui_create_menu_bar(self):
|
56
|
-
create_menu_bar(self)
|
57
|
-
|
58
55
|
def create_startup_screen(self):
|
59
56
|
self.clear_frame(self.content_frame)
|
60
57
|
|
spacr/gui_utils.py
CHANGED
@@ -65,11 +65,11 @@ def load_app(root, app_name, app_func):
|
|
65
65
|
app_func(root.content_frame)
|
66
66
|
|
67
67
|
def create_menu_bar(root):
|
68
|
-
from .
|
69
|
-
from .
|
70
|
-
from .
|
71
|
-
from .
|
72
|
-
from .
|
68
|
+
from .app_mask import initiate_mask_root
|
69
|
+
from .app_measure import initiate_measure_root
|
70
|
+
from .app_annotate import initiate_annotation_app_root
|
71
|
+
from .app_make_masks import initiate_mask_app_root
|
72
|
+
from .app_classify import initiate_classify_root
|
73
73
|
|
74
74
|
gui_apps = {
|
75
75
|
"Mask": initiate_mask_root,
|
@@ -96,6 +96,42 @@ def create_menu_bar(root):
|
|
96
96
|
# Configure the menu for the root window
|
97
97
|
root.config(menu=menu_bar)
|
98
98
|
|
99
|
+
def proceed_with_app(root, app_name, app_func):
|
100
|
+
|
101
|
+
from .app_mask import gui_mask
|
102
|
+
from .app_measure import gui_measure
|
103
|
+
from .app_annotate import gui_annotate
|
104
|
+
from .app_make_masks import gui_make_masks
|
105
|
+
from .app_classify import gui_classify
|
106
|
+
from .gui import gui_app
|
107
|
+
|
108
|
+
# Clear the current content frame
|
109
|
+
if hasattr(root, 'content_frame'):
|
110
|
+
for widget in root.content_frame.winfo_children():
|
111
|
+
widget.destroy()
|
112
|
+
else:
|
113
|
+
root.content_frame = tk.Frame(root)
|
114
|
+
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
115
|
+
root.grid_rowconfigure(1, weight=1)
|
116
|
+
root.grid_columnconfigure(0, weight=1)
|
117
|
+
|
118
|
+
# Initialize the new app in the content frame
|
119
|
+
if app_name == "Main App":
|
120
|
+
root.destroy() # Close the current window
|
121
|
+
gui_app() # Open the main app window
|
122
|
+
elif app_name == "Mask":
|
123
|
+
gui_mask()
|
124
|
+
elif app_name == "Measure":
|
125
|
+
gui_measure()
|
126
|
+
elif app_name == "Annotate":
|
127
|
+
gui_annotate()
|
128
|
+
elif app_name == "Make Masks":
|
129
|
+
gui_make_masks()
|
130
|
+
elif app_name == "Classify":
|
131
|
+
gui_classify()
|
132
|
+
else:
|
133
|
+
raise ValueError(f"Invalid app name: {app_name}")
|
134
|
+
|
99
135
|
def load_app(root, app_name, app_func):
|
100
136
|
# Cancel all scheduled after tasks
|
101
137
|
if hasattr(root, 'after_tasks'):
|
@@ -103,35 +139,25 @@ def load_app(root, app_name, app_func):
|
|
103
139
|
root.after_cancel(task)
|
104
140
|
root.after_tasks = []
|
105
141
|
|
106
|
-
def proceed_with_app():
|
107
|
-
# Clear the current content frame
|
108
|
-
if hasattr(root, 'content_frame'):
|
109
|
-
for widget in root.content_frame.winfo_children():
|
110
|
-
widget.destroy()
|
111
|
-
else:
|
112
|
-
root.content_frame = tk.Frame(root)
|
113
|
-
root.content_frame.grid(row=1, column=0, sticky="nsew")
|
114
|
-
root.grid_rowconfigure(1, weight=1)
|
115
|
-
root.grid_columnconfigure(0, weight=1)
|
116
|
-
|
117
|
-
# Initialize the new app in the content frame
|
118
|
-
app_func(root.content_frame)
|
119
|
-
|
120
142
|
# Exit functionality only for the annotation app
|
121
143
|
if app_name != "Annotate" and hasattr(root, 'current_app_exit_func'):
|
122
144
|
root.next_app_func = proceed_with_app
|
145
|
+
root.next_app_args = (app_name, app_func) # Ensure correct arguments
|
123
146
|
root.current_app_exit_func()
|
124
147
|
else:
|
125
|
-
proceed_with_app()
|
148
|
+
proceed_with_app(root, app_name, app_func)
|
149
|
+
|
126
150
|
|
127
151
|
def create_menu_bar(root):
|
128
|
-
from .
|
129
|
-
from .
|
130
|
-
from .
|
131
|
-
from .
|
132
|
-
from .
|
152
|
+
from .app_mask import initiate_mask_root
|
153
|
+
from .app_measure import initiate_measure_root
|
154
|
+
from .app_annotate import initiate_annotation_app_root
|
155
|
+
from .app_make_masks import initiate_mask_app_root
|
156
|
+
from .app_classify import initiate_classify_root
|
157
|
+
from .gui import gui_app
|
133
158
|
|
134
159
|
gui_apps = {
|
160
|
+
"Main App": gui_app,
|
135
161
|
"Mask": initiate_mask_root,
|
136
162
|
"Measure": initiate_measure_root,
|
137
163
|
"Annotate": initiate_annotation_app_root,
|
@@ -381,19 +407,19 @@ def style_text_boxes_v1(style):
|
|
381
407
|
background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
|
382
408
|
foreground=[('active', '#ffffff'), ('disabled', '#888888')])
|
383
409
|
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
|
384
|
-
style.configure('TCheckbutton', background='
|
410
|
+
style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
|
385
411
|
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
386
412
|
|
387
413
|
def style_text_boxes(style):
|
388
414
|
font_style = tkFont.Font(family="Helvetica", size=10)
|
389
|
-
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='
|
390
|
-
style.configure('TCombobox', fieldbackground='
|
415
|
+
style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
|
416
|
+
style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
|
391
417
|
style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
|
392
418
|
style.map('Custom.TButton',
|
393
419
|
background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
|
394
420
|
foreground=[('active', '#ffffff'), ('disabled', '#888888')])
|
395
421
|
style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
|
396
|
-
style.configure('TCheckbutton', background='
|
422
|
+
style.configure('TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
|
397
423
|
style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
|
398
424
|
|
399
425
|
|