spacr 0.0.1__py3-none-any.whl → 0.0.6__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 +6 -2
- spacr/__main__.py +0 -2
- spacr/alpha.py +807 -0
- spacr/annotate_app.py +118 -120
- spacr/chris.py +50 -0
- spacr/cli.py +25 -187
- spacr/core.py +1611 -389
- spacr/deep_spacr.py +696 -0
- spacr/foldseek.py +779 -0
- spacr/get_alfafold_structures.py +72 -0
- spacr/graph_learning.py +320 -0
- spacr/graph_learning_lap.py +84 -0
- spacr/gui.py +145 -0
- spacr/gui_2.py +90 -0
- spacr/gui_classify_app.py +187 -0
- spacr/gui_mask_app.py +149 -174
- spacr/gui_measure_app.py +116 -109
- spacr/gui_sim_app.py +0 -0
- spacr/gui_utils.py +679 -139
- spacr/io.py +620 -469
- spacr/mask_app.py +116 -9
- spacr/measure.py +178 -84
- spacr/models/cp/toxo_pv_lumen.CP_model +0 -0
- spacr/old_code.py +255 -1
- spacr/plot.py +263 -100
- spacr/sequencing.py +1130 -0
- spacr/sim.py +634 -122
- spacr/timelapse.py +343 -53
- spacr/train.py +195 -22
- spacr/umap.py +0 -689
- spacr/utils.py +1530 -188
- spacr-0.0.6.dist-info/METADATA +118 -0
- spacr-0.0.6.dist-info/RECORD +39 -0
- {spacr-0.0.1.dist-info → spacr-0.0.6.dist-info}/WHEEL +1 -1
- spacr-0.0.6.dist-info/entry_points.txt +9 -0
- spacr-0.0.1.dist-info/METADATA +0 -64
- spacr-0.0.1.dist-info/RECORD +0 -26
- spacr-0.0.1.dist-info/entry_points.txt +0 -5
- {spacr-0.0.1.dist-info → spacr-0.0.6.dist-info}/LICENSE +0 -0
- {spacr-0.0.1.dist-info → spacr-0.0.6.dist-info}/top_level.txt +0 -0
spacr/gui_2.py
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
import customtkinter as ctk
|
2
|
+
from PIL import Image, ImageTk
|
3
|
+
import os
|
4
|
+
import requests
|
5
|
+
|
6
|
+
class MainApp(ctk.CTk):
|
7
|
+
def __init__(self):
|
8
|
+
super().__init__()
|
9
|
+
self.title("SpaCr GUI Collection")
|
10
|
+
self.geometry("1200x800")
|
11
|
+
ctk.set_appearance_mode("dark") # Modes: "System" (standard), "Dark", "Light"
|
12
|
+
ctk.set_default_color_theme("dark-blue") # Themes: "blue" (standard), "green", "dark-blue")
|
13
|
+
|
14
|
+
# Set scaling factor for high DPI displays; use a floating-point value.
|
15
|
+
self.tk.call('tk', 'scaling', 1.5)
|
16
|
+
|
17
|
+
self.create_widgets()
|
18
|
+
|
19
|
+
def create_widgets(self):
|
20
|
+
self.content_frame = ctk.CTkFrame(self)
|
21
|
+
self.content_frame.pack(fill="both", expand=True, padx=20, pady=20)
|
22
|
+
|
23
|
+
logo_frame = ctk.CTkFrame(self.content_frame)
|
24
|
+
logo_frame.pack(pady=20, expand=True)
|
25
|
+
|
26
|
+
if not self.load_logo(logo_frame):
|
27
|
+
ctk.CTkLabel(logo_frame, text="Logo not found", text_color="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
|
28
|
+
|
29
|
+
ctk.CTkLabel(logo_frame, text="SpaCr", text_color="#00BFFF", font=('Helvetica', 36, "bold")).pack(padx=10, pady=10)
|
30
|
+
|
31
|
+
button = ctk.CTkButton(
|
32
|
+
self.content_frame,
|
33
|
+
text="Mask",
|
34
|
+
command=self.load_mask_app,
|
35
|
+
width=250,
|
36
|
+
height=60,
|
37
|
+
corner_radius=20,
|
38
|
+
fg_color="#1E90FF",
|
39
|
+
hover_color="#4682B4",
|
40
|
+
text_color="white",
|
41
|
+
font=("Helvetica", 18, "bold")
|
42
|
+
)
|
43
|
+
button.pack(pady=20)
|
44
|
+
|
45
|
+
def load_logo(self, frame):
|
46
|
+
def download_image(url, save_path):
|
47
|
+
try:
|
48
|
+
response = requests.get(url, stream=True)
|
49
|
+
response.raise_for_status()
|
50
|
+
with open(save_path, 'wb') as f:
|
51
|
+
for chunk in response.iter_content(chunk_size=8192):
|
52
|
+
f.write(chunk)
|
53
|
+
return True
|
54
|
+
except requests.exceptions.RequestException as e:
|
55
|
+
print(f"Failed to download image from {url}: {e}")
|
56
|
+
return False
|
57
|
+
|
58
|
+
try:
|
59
|
+
img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
|
60
|
+
logo_image = Image.open(img_path)
|
61
|
+
except (FileNotFoundError, Image.UnidentifiedImageError):
|
62
|
+
if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
|
63
|
+
try:
|
64
|
+
logo_image = Image.open(img_path)
|
65
|
+
except Image.UnidentifiedImageError as e:
|
66
|
+
return False
|
67
|
+
else:
|
68
|
+
return False
|
69
|
+
except Exception as e:
|
70
|
+
return False
|
71
|
+
|
72
|
+
try:
|
73
|
+
logo_image = logo_image.resize((200, 200), Image.Resampling.LANCZOS)
|
74
|
+
logo_photo = ImageTk.PhotoImage(logo_image)
|
75
|
+
logo_label = ctk.CTkLabel(frame, image=logo_photo)
|
76
|
+
logo_label.image = logo_photo # Keep a reference to avoid garbage collection
|
77
|
+
logo_label.pack()
|
78
|
+
return True
|
79
|
+
except Exception as e:
|
80
|
+
return False
|
81
|
+
|
82
|
+
def load_mask_app(self):
|
83
|
+
print("Mask app loaded.") # Placeholder for mask app loading function
|
84
|
+
|
85
|
+
def gui_app():
|
86
|
+
app = MainApp()
|
87
|
+
app.mainloop()
|
88
|
+
|
89
|
+
if __name__ == "__main__":
|
90
|
+
gui_app()
|
@@ -0,0 +1,187 @@
|
|
1
|
+
import sys, ctypes, matplotlib
|
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
|
+
from matplotlib.figure import Figure
|
7
|
+
matplotlib.use('Agg')
|
8
|
+
from tkinter import filedialog
|
9
|
+
from multiprocessing import Process, Queue, Value
|
10
|
+
import traceback
|
11
|
+
|
12
|
+
try:
|
13
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
14
|
+
except AttributeError:
|
15
|
+
pass
|
16
|
+
|
17
|
+
from .logger import log_function_call
|
18
|
+
from .gui_utils import ScrollableFrame, StdoutRedirector, CustomButton, set_dark_style, set_default_font, generate_fields, process_stdout_stderr, clear_canvas, main_thread_update_function
|
19
|
+
from .gui_utils import classify_variables, check_classify_gui_settings, train_test_model_wrapper, read_settings_from_csv, update_settings_from_csv, style_text_boxes, create_menu_bar
|
20
|
+
|
21
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
22
|
+
|
23
|
+
#@log_function_call
|
24
|
+
def initiate_abort():
|
25
|
+
global thread_control
|
26
|
+
if thread_control.get("stop_requested") is not None:
|
27
|
+
thread_control["stop_requested"].value = 1
|
28
|
+
|
29
|
+
if thread_control.get("run_thread") is not None:
|
30
|
+
thread_control["run_thread"].join(timeout=5)
|
31
|
+
if thread_control["run_thread"].is_alive():
|
32
|
+
thread_control["run_thread"].terminate()
|
33
|
+
thread_control["run_thread"] = None
|
34
|
+
|
35
|
+
#@log_function_call
|
36
|
+
def run_classify_gui(q, fig_queue, stop_requested):
|
37
|
+
global vars_dict
|
38
|
+
process_stdout_stderr(q)
|
39
|
+
try:
|
40
|
+
settings = check_classify_gui_settings(vars_dict)
|
41
|
+
for key in settings:
|
42
|
+
value = settings[key]
|
43
|
+
print(key, value, type(value))
|
44
|
+
train_test_model_wrapper(settings['src'], settings)
|
45
|
+
except Exception as e:
|
46
|
+
q.put(f"Error during processing: {e}")
|
47
|
+
traceback.print_exc()
|
48
|
+
finally:
|
49
|
+
stop_requested.value = 1
|
50
|
+
|
51
|
+
#@log_function_call
|
52
|
+
def start_process(q, fig_queue):
|
53
|
+
global thread_control
|
54
|
+
if thread_control.get("run_thread") is not None:
|
55
|
+
initiate_abort()
|
56
|
+
|
57
|
+
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
58
|
+
thread_control["stop_requested"] = stop_requested
|
59
|
+
thread_control["run_thread"] = Process(target=run_classify_gui, args=(q, fig_queue, stop_requested))
|
60
|
+
thread_control["run_thread"].start()
|
61
|
+
|
62
|
+
def import_settings(scrollable_frame):
|
63
|
+
global vars_dict
|
64
|
+
|
65
|
+
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
66
|
+
csv_settings = read_settings_from_csv(csv_file_path)
|
67
|
+
variables = classify_variables()
|
68
|
+
new_settings = update_settings_from_csv(variables, csv_settings)
|
69
|
+
vars_dict = generate_fields(new_settings, scrollable_frame)
|
70
|
+
|
71
|
+
#@log_function_call
|
72
|
+
def initiate_classify_root(parent_frame):#, width, height):
|
73
|
+
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
|
74
|
+
|
75
|
+
style = ttk.Style(parent_frame)
|
76
|
+
set_dark_style(style)
|
77
|
+
style_text_boxes(style)
|
78
|
+
set_default_font(parent_frame, font_name="Arial", size=8)
|
79
|
+
|
80
|
+
parent_frame.configure(bg='#333333')
|
81
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
82
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
83
|
+
fig_queue = Queue()
|
84
|
+
|
85
|
+
def _process_fig_queue():
|
86
|
+
global canvas
|
87
|
+
try:
|
88
|
+
while not fig_queue.empty():
|
89
|
+
clear_canvas(canvas)
|
90
|
+
fig = fig_queue.get_nowait()
|
91
|
+
for ax in fig.get_axes():
|
92
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
93
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
94
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
95
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
96
|
+
fig.tight_layout()
|
97
|
+
fig.set_facecolor('#333333')
|
98
|
+
canvas.figure = fig
|
99
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
100
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
101
|
+
canvas.draw_idle()
|
102
|
+
except Exception as e:
|
103
|
+
traceback.print_exc()
|
104
|
+
finally:
|
105
|
+
canvas_widget.after(100, _process_fig_queue)
|
106
|
+
|
107
|
+
def _process_console_queue():
|
108
|
+
while not q.empty():
|
109
|
+
message = q.get_nowait()
|
110
|
+
console_output.insert(tk.END, message)
|
111
|
+
console_output.see(tk.END)
|
112
|
+
console_output.after(100, _process_console_queue)
|
113
|
+
|
114
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
115
|
+
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
116
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
117
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
118
|
+
|
119
|
+
# Settings Section
|
120
|
+
settings_frame = tk.Frame(vertical_container, bg='#333333')
|
121
|
+
vertical_container.add(settings_frame, stretch="always")
|
122
|
+
settings_label = ttk.Label(settings_frame, text="Settings", background="#333333", foreground="white")
|
123
|
+
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
124
|
+
scrollable_frame = ScrollableFrame(settings_frame, width=500)
|
125
|
+
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
126
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
127
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
128
|
+
|
129
|
+
# Setup for user input fields (variables)
|
130
|
+
variables = classify_variables()
|
131
|
+
vars_dict = generate_fields(variables, scrollable_frame)
|
132
|
+
|
133
|
+
# Button section
|
134
|
+
import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import Settings", command=lambda: import_settings(scrollable_frame))
|
135
|
+
import_btn.grid(row=47, column=0, pady=20, padx=20)
|
136
|
+
run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue))
|
137
|
+
run_button.grid(row=45, column=0, pady=20, padx=20)
|
138
|
+
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
139
|
+
abort_button.grid(row=45, column=1, pady=20, padx=20)
|
140
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
|
141
|
+
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
142
|
+
|
143
|
+
# Plot Canvas Section
|
144
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
|
145
|
+
vertical_container.add(plot_frame, stretch="always")
|
146
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='#333333')
|
147
|
+
plot = figure.add_subplot(111)
|
148
|
+
plot.plot([], [])
|
149
|
+
plot.axis('off')
|
150
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame)
|
151
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='#333333', highlightthickness=0)
|
152
|
+
canvas_widget = canvas.get_tk_widget()
|
153
|
+
plot_frame.add(canvas_widget, stretch="always")
|
154
|
+
canvas.draw()
|
155
|
+
canvas.figure = figure
|
156
|
+
|
157
|
+
# Console Section
|
158
|
+
console_frame = tk.Frame(vertical_container, bg='#333333')
|
159
|
+
vertical_container.add(console_frame, stretch="always")
|
160
|
+
console_label = ttk.Label(console_frame, text="Console", background="#333333", foreground="white")
|
161
|
+
console_label.grid(row=0, column=0, pady=10, padx=10)
|
162
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='#333333', fg='white', insertbackground='white')
|
163
|
+
console_output.grid(row=1, column=0, sticky="nsew")
|
164
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
165
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
166
|
+
|
167
|
+
q = Queue()
|
168
|
+
sys.stdout = StdoutRedirector(console_output)
|
169
|
+
sys.stderr = StdoutRedirector(console_output)
|
170
|
+
|
171
|
+
_process_console_queue()
|
172
|
+
_process_fig_queue()
|
173
|
+
|
174
|
+
parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
175
|
+
|
176
|
+
return parent_frame, vars_dict
|
177
|
+
|
178
|
+
def gui_classify():
|
179
|
+
root = tk.Tk()
|
180
|
+
root.geometry("1000x800")
|
181
|
+
root.title("SpaCer: generate masks")
|
182
|
+
initiate_classify_root(root, 1000, 800)
|
183
|
+
create_menu_bar(root)
|
184
|
+
root.mainloop()
|
185
|
+
|
186
|
+
if __name__ == "__main__":
|
187
|
+
gui_classify()
|
spacr/gui_mask_app.py
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
import
|
1
|
+
#import customtkinter as ctk
|
2
|
+
|
3
|
+
import sys, ctypes, matplotlib
|
2
4
|
import tkinter as tk
|
3
5
|
from tkinter import ttk, scrolledtext
|
4
|
-
from ttkthemes import ThemedTk
|
5
6
|
from matplotlib.figure import Figure
|
6
7
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
7
|
-
import matplotlib.pyplot as plt
|
8
|
-
from matplotlib.figure import Figure
|
9
|
-
from threading import Thread
|
10
8
|
matplotlib.use('Agg')
|
11
|
-
from threading import Thread
|
12
9
|
from tkinter import filedialog
|
10
|
+
from multiprocessing import Process, Queue, Value
|
11
|
+
import traceback
|
13
12
|
|
14
13
|
try:
|
15
14
|
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
@@ -17,230 +16,206 @@ except AttributeError:
|
|
17
16
|
pass
|
18
17
|
|
19
18
|
from .logger import log_function_call
|
20
|
-
from .gui_utils import ScrollableFrame, StdoutRedirector,
|
21
|
-
from .gui_utils import
|
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
22
|
|
23
23
|
thread_control = {"run_thread": None, "stop_requested": False}
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
25
|
+
def toggle_test_mode():
|
26
|
+
global vars_dict
|
27
|
+
current_state = vars_dict['test_mode'][2].get()
|
28
|
+
new_state = not current_state
|
29
|
+
vars_dict['test_mode'][2].set(new_state)
|
30
|
+
if new_state:
|
31
|
+
test_mode_button.config(bg="blue")
|
32
|
+
else:
|
33
|
+
test_mode_button.config(bg="gray")
|
34
|
+
|
35
|
+
def toggle_advanced_settings():
|
36
|
+
global vars_dict
|
37
|
+
|
38
|
+
timelapse_settings = ['timelapse', 'timelapse_memory', 'timelapse_remove_transient', 'timelapse_mode', 'timelapse_objects', 'timelapse_displacement', 'timelapse_frame_limits', 'fps']
|
39
|
+
misc_settings = ['examples_to_plot', 'all_to_mip', 'pick_slice', 'skip_mode']
|
40
|
+
opperational_settings = ['preprocess', 'masks', 'randomize', 'batch_size', 'custom_regex', 'merge', 'normalize_plots', 'workers', 'plot', 'remove_background', 'lower_quantile']
|
41
|
+
|
42
|
+
advanced_settings = timelapse_settings+misc_settings+opperational_settings
|
43
|
+
|
44
|
+
# Toggle visibility of advanced settings
|
45
|
+
for setting in advanced_settings:
|
46
|
+
label, widget, var = vars_dict[setting]
|
47
|
+
if advanced_var.get() is False:
|
48
|
+
label.grid_remove() # Hide the label
|
49
|
+
widget.grid_remove() # Hide the widget
|
50
|
+
else:
|
51
|
+
label.grid() # Show the label
|
52
|
+
widget.grid() # Show the widget
|
53
|
+
|
54
|
+
#@log_function_call
|
54
55
|
def initiate_abort():
|
55
|
-
global thread_control
|
56
|
-
thread_control
|
57
|
-
|
58
|
-
|
56
|
+
global thread_control
|
57
|
+
if thread_control.get("stop_requested") is not None:
|
58
|
+
thread_control["stop_requested"].value = 1
|
59
|
+
|
60
|
+
if thread_control.get("run_thread") is not None:
|
61
|
+
thread_control["run_thread"].join(timeout=5)
|
59
62
|
if thread_control["run_thread"].is_alive():
|
60
|
-
|
63
|
+
thread_control["run_thread"].terminate()
|
61
64
|
thread_control["run_thread"] = None
|
62
|
-
|
63
|
-
def preprocess_generate_masks_wrapper(*args, **kwargs):
|
64
|
-
global fig_queue
|
65
|
-
def my_show():
|
66
|
-
fig = plt.gcf()
|
67
|
-
fig_queue.put(fig) # Put the figure into the queue
|
68
|
-
plt.close(fig) # Close the figure to prevent it from being shown by plt.show()
|
69
65
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
spacr.core.preprocess_generate_masks(*args, **kwargs)
|
75
|
-
except Exception as e:
|
76
|
-
pass
|
77
|
-
finally:
|
78
|
-
plt.show = original_show
|
79
|
-
|
80
|
-
def run_mask_gui(q, fig_queue):
|
81
|
-
global vars_dict, thread_control
|
66
|
+
#@log_function_call
|
67
|
+
def run_mask_gui(q, fig_queue, stop_requested):
|
68
|
+
global vars_dict
|
69
|
+
process_stdout_stderr(q)
|
82
70
|
try:
|
83
|
-
|
84
|
-
|
85
|
-
settings = add_mask_gui_defaults(settings)
|
86
|
-
preprocess_generate_masks_wrapper(settings['src'], settings=settings, advanced_settings={})
|
87
|
-
thread_control["stop_requested"] = True
|
71
|
+
settings = check_mask_gui_settings(vars_dict)
|
72
|
+
preprocess_generate_masks_wrapper(settings, q, fig_queue)
|
88
73
|
except Exception as e:
|
89
|
-
|
90
|
-
|
74
|
+
q.put(f"Error during processing: {e}")
|
75
|
+
traceback.print_exc()
|
91
76
|
finally:
|
92
|
-
|
93
|
-
thread_control["run_thread"] = None
|
94
|
-
# Reset the stop_requested flag for future operations
|
95
|
-
thread_control["stop_requested"] = False
|
77
|
+
stop_requested.value = 1
|
96
78
|
|
97
|
-
|
79
|
+
#@log_function_call
|
80
|
+
def start_process(q, fig_queue):
|
98
81
|
global thread_control
|
99
|
-
thread_control
|
100
|
-
|
82
|
+
if thread_control.get("run_thread") is not None:
|
83
|
+
initiate_abort()
|
84
|
+
|
85
|
+
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
86
|
+
thread_control["stop_requested"] = stop_requested
|
87
|
+
thread_control["run_thread"] = Process(target=run_mask_gui, args=(q, fig_queue, stop_requested))
|
101
88
|
thread_control["run_thread"].start()
|
102
|
-
|
89
|
+
|
103
90
|
def import_settings(scrollable_frame):
|
104
|
-
global vars_dict
|
91
|
+
global vars_dict
|
105
92
|
|
106
93
|
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
94
|
+
csv_settings = read_settings_from_csv(csv_file_path)
|
95
|
+
variables = mask_variables()
|
96
|
+
new_settings = update_settings_from_csv(variables, csv_settings)
|
97
|
+
vars_dict = generate_fields(new_settings, scrollable_frame)
|
98
|
+
|
99
|
+
#@log_function_call
|
100
|
+
def initiate_mask_root(parent_frame, width, height):
|
101
|
+
global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, 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="Arial", size=8)
|
107
|
+
parent_frame.configure(bg='black')
|
108
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
109
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
107
110
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
imported_variables = {}
|
112
|
-
|
113
|
-
with open(csv_file_path, newline='') as csvfile:
|
114
|
-
reader = csv.DictReader(csvfile)
|
115
|
-
for row in reader:
|
116
|
-
key = row['Key']
|
117
|
-
value = row['Value']
|
118
|
-
# Evaluate the value safely using safe_literal_eval
|
119
|
-
imported_variables[key] = safe_literal_eval(value)
|
120
|
-
|
121
|
-
# Track changed variables and apply the imported ones, printing changes as we go
|
122
|
-
for key, var in vars_dict.items():
|
123
|
-
if key in imported_variables and var.get() != imported_variables[key]:
|
124
|
-
print(f"Updating '{key}' from '{var.get()}' to '{imported_variables[key]}'")
|
125
|
-
var.set(imported_variables[key])
|
126
|
-
|
127
|
-
@log_function_call
|
128
|
-
def initiate_mask_root(width, height):
|
129
|
-
global root, vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
|
130
|
-
|
131
|
-
theme = 'breeze'
|
132
|
-
|
133
|
-
if theme in ['clam']:
|
134
|
-
root = tk.Tk()
|
135
|
-
style = ttk.Style(root)
|
136
|
-
style.theme_use(theme) #plastik, clearlooks, elegance, default was clam #alt, breeze, arc
|
137
|
-
set_dark_style(style)
|
138
|
-
elif theme in ['breeze']:
|
139
|
-
root = ThemedTk(theme="breeze")
|
140
|
-
style = ttk.Style(root)
|
141
|
-
set_dark_style(style)
|
142
|
-
|
143
|
-
set_default_font(root, font_name="Arial", size=10)
|
144
|
-
#root.state('zoomed') # For Windows to maximize the window
|
145
|
-
root.attributes('-fullscreen', True)
|
146
|
-
root.geometry(f"{width}x{height}")
|
147
|
-
root.title("SpaCer: generate masks")
|
148
|
-
fig_queue = queue.Queue()
|
149
|
-
|
111
|
+
fig_queue = Queue()
|
112
|
+
|
150
113
|
def _process_fig_queue():
|
151
114
|
global canvas
|
152
115
|
try:
|
153
116
|
while not fig_queue.empty():
|
154
|
-
clear_canvas()
|
117
|
+
clear_canvas(canvas)
|
155
118
|
fig = fig_queue.get_nowait()
|
156
|
-
#set_fig_text_properties(fig, font_size=8)
|
157
119
|
for ax in fig.get_axes():
|
158
120
|
ax.set_xticks([]) # Remove x-axis ticks
|
159
121
|
ax.set_yticks([]) # Remove y-axis ticks
|
160
122
|
ax.xaxis.set_visible(False) # Hide the x-axis
|
161
123
|
ax.yaxis.set_visible(False) # Hide the y-axis
|
162
|
-
#ax.title.set_fontsize(14)
|
163
|
-
#disable_interactivity(fig)
|
164
124
|
fig.tight_layout()
|
165
|
-
fig.set_facecolor('
|
125
|
+
fig.set_facecolor('black')
|
166
126
|
canvas.figure = fig
|
167
127
|
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
168
128
|
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
169
|
-
canvas.draw_idle()
|
170
|
-
except queue.Empty:
|
171
|
-
pass
|
129
|
+
canvas.draw_idle()
|
172
130
|
except Exception as e:
|
173
|
-
|
131
|
+
traceback.print_exc()
|
174
132
|
finally:
|
175
133
|
canvas_widget.after(100, _process_fig_queue)
|
176
|
-
|
177
|
-
# Process queue for console output
|
134
|
+
|
178
135
|
def _process_console_queue():
|
179
136
|
while not q.empty():
|
180
137
|
message = q.get_nowait()
|
181
138
|
console_output.insert(tk.END, message)
|
182
139
|
console_output.see(tk.END)
|
183
140
|
console_output.after(100, _process_console_queue)
|
184
|
-
|
185
|
-
# Vertical container for settings and console
|
186
|
-
vertical_container = tk.PanedWindow(root, orient=tk.HORIZONTAL) #VERTICAL
|
187
|
-
vertical_container.pack(fill=tk.BOTH, expand=True)
|
188
|
-
|
189
|
-
# Scrollable Frame for user settings
|
190
|
-
scrollable_frame = ScrollableFrame(vertical_container, bg='#333333')
|
191
|
-
vertical_container.add(scrollable_frame, stretch="always")
|
192
141
|
|
193
|
-
|
142
|
+
vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
|
143
|
+
vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
|
144
|
+
parent_frame.grid_rowconfigure(0, weight=1)
|
145
|
+
parent_frame.grid_columnconfigure(0, weight=1)
|
146
|
+
|
147
|
+
# Settings Section
|
148
|
+
settings_frame = tk.Frame(vertical_container, bg='black')
|
149
|
+
vertical_container.add(settings_frame, stretch="always")
|
150
|
+
settings_label = ttk.Label(settings_frame, text="Settings", style="Custom.TLabel")
|
151
|
+
settings_label.grid(row=0, column=0, pady=10, padx=10)
|
152
|
+
scrollable_frame = ScrollableFrame(settings_frame, width=600)
|
153
|
+
scrollable_frame.grid(row=1, column=0, sticky="nsew")
|
154
|
+
settings_frame.grid_rowconfigure(1, weight=1)
|
155
|
+
settings_frame.grid_columnconfigure(0, weight=1)
|
156
|
+
|
157
|
+
# Create advanced settings checkbox
|
158
|
+
advanced_var = tk.BooleanVar(value=False)
|
159
|
+
advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
|
160
|
+
advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
|
194
161
|
variables = mask_variables()
|
195
162
|
vars_dict = generate_fields(variables, scrollable_frame)
|
163
|
+
toggle_advanced_settings()
|
164
|
+
vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
|
196
165
|
|
197
|
-
#
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
166
|
+
# Button section
|
167
|
+
test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode)
|
168
|
+
test_mode_button.grid(row=47, column=1, pady=10, padx=10)
|
169
|
+
import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame))
|
170
|
+
import_btn.grid(row=47, column=0, pady=10, padx=10)
|
171
|
+
run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue))
|
172
|
+
run_button.grid(row=45, column=0, pady=10, padx=10)
|
173
|
+
abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
174
|
+
abort_button.grid(row=45, column=1, pady=10, padx=10)
|
175
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
|
176
|
+
progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
|
177
|
+
|
178
|
+
# Plot Canvas Section
|
179
|
+
plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL) # Horizontal container for Matplotlib figure and the vertical pane (for settings and console)
|
180
|
+
vertical_container.add(plot_frame, stretch="always")
|
181
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='black') # Matplotlib figure setup
|
203
182
|
plot = figure.add_subplot(111)
|
204
183
|
plot.plot([], []) # This creates an empty plot.
|
205
184
|
plot.axis('off')
|
206
|
-
|
207
|
-
|
208
|
-
canvas = FigureCanvasTkAgg(figure, master=horizontal_container)
|
209
|
-
canvas.get_tk_widget().configure(cursor='arrow', background='#333333', highlightthickness=0)
|
210
|
-
#canvas.get_tk_widget().configure(cursor='arrow')
|
185
|
+
canvas = FigureCanvasTkAgg(figure, master=plot_frame) # Embedd Matplotlib figure in Tkinter window
|
186
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
|
211
187
|
canvas_widget = canvas.get_tk_widget()
|
212
|
-
|
188
|
+
plot_frame.add(canvas_widget, stretch="always")
|
213
189
|
canvas.draw()
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
190
|
+
canvas.figure = figure
|
191
|
+
|
192
|
+
# Console Section
|
193
|
+
console_frame = tk.Frame(vertical_container, bg='black')
|
194
|
+
vertical_container.add(console_frame, stretch="always")
|
195
|
+
console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
|
196
|
+
console_label.grid(row=0, column=0, pady=10, padx=10)
|
197
|
+
console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
|
198
|
+
console_output.grid(row=1, column=0, sticky="nsew")
|
199
|
+
console_frame.grid_rowconfigure(1, weight=1)
|
200
|
+
console_frame.grid_columnconfigure(0, weight=1)
|
201
|
+
|
202
|
+
q = Queue()
|
221
203
|
sys.stdout = StdoutRedirector(console_output)
|
222
204
|
sys.stderr = StdoutRedirector(console_output)
|
223
|
-
|
224
|
-
# This is your GUI setup where you create the Run button
|
225
|
-
run_button = ttk.Button(scrollable_frame.scrollable_frame, text="Run",command=lambda: start_thread(q, fig_queue))
|
226
|
-
run_button.grid(row=40, column=0, pady=10)
|
227
|
-
|
228
|
-
abort_button = ttk.Button(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
229
|
-
abort_button.grid(row=40, column=1, pady=10)
|
230
|
-
|
231
|
-
# Create the Import Settings button
|
232
|
-
import_btn = tk.Button(root, text="Import Settings", command=lambda: import_settings(scrollable_frame))
|
233
|
-
import_btn.pack(pady=20)
|
234
|
-
|
205
|
+
|
235
206
|
_process_console_queue()
|
236
207
|
_process_fig_queue()
|
237
|
-
create_dark_mode(root, style, console_output)
|
238
208
|
|
239
|
-
|
209
|
+
parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
|
210
|
+
|
211
|
+
return parent_frame, vars_dict
|
240
212
|
|
241
213
|
def gui_mask():
|
242
|
-
|
243
|
-
root
|
214
|
+
root = tk.Tk()
|
215
|
+
root.geometry("1000x800")
|
216
|
+
root.title("SpaCer: generate masks")
|
217
|
+
initiate_mask_root(root, 1000, 800)
|
218
|
+
create_menu_bar(root)
|
244
219
|
root.mainloop()
|
245
220
|
|
246
221
|
if __name__ == "__main__":
|