spacr 0.0.1__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 +37 -0
- spacr/__main__.py +15 -0
- spacr/annotate_app.py +495 -0
- spacr/cli.py +203 -0
- spacr/core.py +2250 -0
- spacr/gui_mask_app.py +247 -0
- spacr/gui_measure_app.py +214 -0
- spacr/gui_utils.py +488 -0
- spacr/io.py +2271 -0
- spacr/logger.py +20 -0
- spacr/mask_app.py +818 -0
- spacr/measure.py +1014 -0
- spacr/old_code.py +104 -0
- spacr/plot.py +1273 -0
- spacr/sim.py +1187 -0
- spacr/timelapse.py +576 -0
- spacr/train.py +494 -0
- spacr/umap.py +689 -0
- spacr/utils.py +2726 -0
- spacr/version.py +19 -0
- spacr-0.0.1.dist-info/LICENSE +21 -0
- spacr-0.0.1.dist-info/METADATA +64 -0
- spacr-0.0.1.dist-info/RECORD +26 -0
- spacr-0.0.1.dist-info/WHEEL +5 -0
- spacr-0.0.1.dist-info/entry_points.txt +5 -0
- spacr-0.0.1.dist-info/top_level.txt +1 -0
spacr/gui_mask_app.py
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
import spacr, sys, queue, ctypes, csv, matplotlib
|
2
|
+
import tkinter as tk
|
3
|
+
from tkinter import ttk, scrolledtext
|
4
|
+
from ttkthemes import ThemedTk
|
5
|
+
from matplotlib.figure import Figure
|
6
|
+
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
|
+
matplotlib.use('Agg')
|
11
|
+
from threading import Thread
|
12
|
+
from tkinter import filedialog
|
13
|
+
|
14
|
+
try:
|
15
|
+
ctypes.windll.shcore.SetProcessDpiAwareness(True)
|
16
|
+
except AttributeError:
|
17
|
+
pass
|
18
|
+
|
19
|
+
from .logger import log_function_call
|
20
|
+
from .gui_utils import ScrollableFrame, StdoutRedirector, create_dark_mode, set_dark_style, set_default_font, mask_variables, generate_fields, check_mask_gui_settings, add_mask_gui_defaults
|
21
|
+
from .gui_utils import safe_literal_eval
|
22
|
+
|
23
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
24
|
+
|
25
|
+
class ScrollableFrame(ttk.Frame):
|
26
|
+
def __init__(self, container, *args, bg='#333333', **kwargs):
|
27
|
+
super().__init__(container, *args, **kwargs)
|
28
|
+
self.configure(style='TFrame') # Ensure this uses the styled frame from dark mode
|
29
|
+
|
30
|
+
canvas = tk.Canvas(self, bg=bg) # Set canvas background to match dark mode
|
31
|
+
scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
|
32
|
+
|
33
|
+
self.scrollable_frame = ttk.Frame(canvas, style='TFrame') # Ensure it uses the styled frame
|
34
|
+
self.scrollable_frame.bind(
|
35
|
+
"<Configure>",
|
36
|
+
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
37
|
+
)
|
38
|
+
|
39
|
+
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
40
|
+
canvas.configure(yscrollcommand=scrollbar.set)
|
41
|
+
|
42
|
+
canvas.pack(side="left", fill="both", expand=True)
|
43
|
+
scrollbar.pack(side="right", fill="y")
|
44
|
+
|
45
|
+
def clear_canvas():
|
46
|
+
global canvas
|
47
|
+
# Clear each plot (axes) in the figure
|
48
|
+
for ax in canvas.figure.get_axes():
|
49
|
+
ax.clear() # Clears the axes, but keeps them visible for new plots
|
50
|
+
|
51
|
+
# Redraw the now empty canvas without changing its size
|
52
|
+
canvas.draw_idle() # Using draw_idle for efficiency in redrawing
|
53
|
+
|
54
|
+
def initiate_abort():
|
55
|
+
global thread_control, q
|
56
|
+
thread_control["stop_requested"] = True
|
57
|
+
if thread_control["run_thread"] is not None:
|
58
|
+
thread_control["run_thread"].join(timeout=1) # Timeout after 1 second
|
59
|
+
if thread_control["run_thread"].is_alive():
|
60
|
+
q.put("Thread didn't terminate within timeout.")
|
61
|
+
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
|
+
|
70
|
+
original_show = plt.show
|
71
|
+
plt.show = my_show
|
72
|
+
|
73
|
+
try:
|
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
|
82
|
+
try:
|
83
|
+
while not thread_control["stop_requested"]:
|
84
|
+
settings = check_mask_gui_settings(vars_dict)
|
85
|
+
settings = add_mask_gui_defaults(settings)
|
86
|
+
preprocess_generate_masks_wrapper(settings['src'], settings=settings, advanced_settings={})
|
87
|
+
thread_control["stop_requested"] = True
|
88
|
+
except Exception as e:
|
89
|
+
pass
|
90
|
+
#q.put(f"Error during processing: {e}")
|
91
|
+
finally:
|
92
|
+
# Ensure the thread is marked as not running anymore
|
93
|
+
thread_control["run_thread"] = None
|
94
|
+
# Reset the stop_requested flag for future operations
|
95
|
+
thread_control["stop_requested"] = False
|
96
|
+
|
97
|
+
def start_thread(q, fig_queue):
|
98
|
+
global thread_control
|
99
|
+
thread_control["stop_requested"] = False # Reset the stop signal
|
100
|
+
thread_control["run_thread"] = Thread(target=run_mask_gui, args=(q, fig_queue))
|
101
|
+
thread_control["run_thread"].start()
|
102
|
+
|
103
|
+
def import_settings(scrollable_frame):
|
104
|
+
global vars_dict, original_variables_structure
|
105
|
+
|
106
|
+
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
107
|
+
|
108
|
+
if not csv_file_path:
|
109
|
+
return
|
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
|
+
|
150
|
+
def _process_fig_queue():
|
151
|
+
global canvas
|
152
|
+
try:
|
153
|
+
while not fig_queue.empty():
|
154
|
+
clear_canvas()
|
155
|
+
fig = fig_queue.get_nowait()
|
156
|
+
#set_fig_text_properties(fig, font_size=8)
|
157
|
+
for ax in fig.get_axes():
|
158
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
159
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
160
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
161
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
162
|
+
#ax.title.set_fontsize(14)
|
163
|
+
#disable_interactivity(fig)
|
164
|
+
fig.tight_layout()
|
165
|
+
fig.set_facecolor('#333333')
|
166
|
+
canvas.figure = fig
|
167
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
168
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
169
|
+
canvas.draw_idle()
|
170
|
+
except queue.Empty:
|
171
|
+
pass
|
172
|
+
except Exception as e:
|
173
|
+
pass
|
174
|
+
finally:
|
175
|
+
canvas_widget.after(100, _process_fig_queue)
|
176
|
+
|
177
|
+
# Process queue for console output
|
178
|
+
def _process_console_queue():
|
179
|
+
while not q.empty():
|
180
|
+
message = q.get_nowait()
|
181
|
+
console_output.insert(tk.END, message)
|
182
|
+
console_output.see(tk.END)
|
183
|
+
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
|
+
|
193
|
+
# Setup for user input fields (variables)
|
194
|
+
variables = mask_variables()
|
195
|
+
vars_dict = generate_fields(variables, scrollable_frame)
|
196
|
+
|
197
|
+
# Horizontal container for Matplotlib figure and the vertical pane (for settings and console)
|
198
|
+
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.VERTICAL) #HORIZONTAL
|
199
|
+
vertical_container.add(horizontal_container, stretch="always")
|
200
|
+
|
201
|
+
# Matplotlib figure setup
|
202
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='#333333')
|
203
|
+
plot = figure.add_subplot(111)
|
204
|
+
plot.plot([], []) # This creates an empty plot.
|
205
|
+
plot.axis('off')
|
206
|
+
|
207
|
+
# Embedding the Matplotlib figure in the Tkinter window
|
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')
|
211
|
+
canvas_widget = canvas.get_tk_widget()
|
212
|
+
horizontal_container.add(canvas_widget, stretch="always")
|
213
|
+
canvas.draw()
|
214
|
+
|
215
|
+
# Console output setup below the settings
|
216
|
+
console_output = scrolledtext.ScrolledText(vertical_container, height=10)
|
217
|
+
vertical_container.add(console_output, stretch="always")
|
218
|
+
|
219
|
+
# Queue and redirection setup for updating console output safely
|
220
|
+
q = queue.Queue()
|
221
|
+
sys.stdout = StdoutRedirector(console_output)
|
222
|
+
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
|
+
|
235
|
+
_process_console_queue()
|
236
|
+
_process_fig_queue()
|
237
|
+
create_dark_mode(root, style, console_output)
|
238
|
+
|
239
|
+
return root, vars_dict
|
240
|
+
|
241
|
+
def gui_mask():
|
242
|
+
global vars_dict, root
|
243
|
+
root, vars_dict = initiate_mask_root(1000, 1500)
|
244
|
+
root.mainloop()
|
245
|
+
|
246
|
+
if __name__ == "__main__":
|
247
|
+
gui_mask()
|
spacr/gui_measure_app.py
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
import sys, traceback, matplotlib, ctypes, csv
|
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
|
+
import matplotlib.pyplot as plt
|
7
|
+
matplotlib.use('Agg') # Use the non-GUI Agg backend
|
8
|
+
from multiprocessing import Process, Queue, Value
|
9
|
+
from ttkthemes import ThemedTk
|
10
|
+
from tkinter import filedialog
|
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, set_dark_style, set_default_font, measure_variables, generate_fields, create_dark_mode, check_measure_gui_settings, add_measure_gui_defaults, main_thread_update_function
|
19
|
+
from .gui_utils import process_stdout_stderr, measure_crop_wrapper, clear_canvas, safe_literal_eval
|
20
|
+
|
21
|
+
thread_control = {"run_thread": None, "stop_requested": False}
|
22
|
+
|
23
|
+
|
24
|
+
@log_function_call
|
25
|
+
def run_measure_gui(q, fig_queue, stop_requested):
|
26
|
+
global vars_dict
|
27
|
+
process_stdout_stderr(q)
|
28
|
+
try:
|
29
|
+
settings = check_measure_gui_settings(vars_dict)
|
30
|
+
settings = add_measure_gui_defaults(settings)
|
31
|
+
#for key in settings:
|
32
|
+
# value = settings[key]
|
33
|
+
# print(key, value, type(value))
|
34
|
+
measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
|
35
|
+
except Exception as e:
|
36
|
+
q.put(f"Error during processing: {e}")
|
37
|
+
traceback.print_exc()
|
38
|
+
finally:
|
39
|
+
stop_requested.value = 1
|
40
|
+
|
41
|
+
@log_function_call
|
42
|
+
def start_process(q, fig_queue):
|
43
|
+
global thread_control
|
44
|
+
if thread_control.get("run_thread") is not None:
|
45
|
+
initiate_abort()
|
46
|
+
|
47
|
+
stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
|
48
|
+
thread_control["stop_requested"] = stop_requested
|
49
|
+
thread_control["run_thread"] = Process(target=run_measure_gui, args=(q, fig_queue, stop_requested))
|
50
|
+
thread_control["run_thread"].start()
|
51
|
+
|
52
|
+
@log_function_call
|
53
|
+
def initiate_abort():
|
54
|
+
global thread_control
|
55
|
+
if thread_control.get("stop_requested") is not None:
|
56
|
+
thread_control["stop_requested"].value = 1
|
57
|
+
|
58
|
+
if thread_control.get("run_thread") is not None:
|
59
|
+
thread_control["run_thread"].join(timeout=5)
|
60
|
+
if thread_control["run_thread"].is_alive():
|
61
|
+
thread_control["run_thread"].terminate()
|
62
|
+
thread_control["run_thread"] = None
|
63
|
+
|
64
|
+
def import_settings(scrollable_frame):
|
65
|
+
global vars_dict, original_variables_structure
|
66
|
+
|
67
|
+
csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
|
68
|
+
|
69
|
+
if not csv_file_path:
|
70
|
+
return
|
71
|
+
|
72
|
+
imported_variables = {}
|
73
|
+
|
74
|
+
with open(csv_file_path, newline='') as csvfile:
|
75
|
+
reader = csv.DictReader(csvfile)
|
76
|
+
for row in reader:
|
77
|
+
key = row['Key']
|
78
|
+
value = row['Value']
|
79
|
+
# Evaluate the value safely using safe_literal_eval
|
80
|
+
imported_variables[key] = safe_literal_eval(value)
|
81
|
+
|
82
|
+
# Track changed variables and apply the imported ones, printing changes as we go
|
83
|
+
for key, var in vars_dict.items():
|
84
|
+
if key in imported_variables and var.get() != imported_variables[key]:
|
85
|
+
print(f"Updating '{key}' from '{var.get()}' to '{imported_variables[key]}'")
|
86
|
+
var.set(imported_variables[key])
|
87
|
+
|
88
|
+
@log_function_call
|
89
|
+
def initiate_measure_root(width, height):
|
90
|
+
global root, vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables
|
91
|
+
|
92
|
+
theme = 'breeze'
|
93
|
+
|
94
|
+
if theme in ['clam']:
|
95
|
+
root = tk.Tk()
|
96
|
+
style = ttk.Style(root)
|
97
|
+
style.theme_use(theme) #plastik, clearlooks, elegance, default was clam #alt, breeze, arc
|
98
|
+
set_dark_style(style)
|
99
|
+
|
100
|
+
elif theme in ['breeze']:
|
101
|
+
root = ThemedTk(theme="breeze")
|
102
|
+
style = ttk.Style(root)
|
103
|
+
set_dark_style(style)
|
104
|
+
|
105
|
+
set_default_font(root, font_name="Arial", size=10)
|
106
|
+
#root.state('zoomed') # For Windows to maximize the window
|
107
|
+
root.attributes('-fullscreen', True)
|
108
|
+
root.geometry(f"{width}x{height}")
|
109
|
+
root.configure(bg='#333333')
|
110
|
+
root.title("SpaCer: generate masks")
|
111
|
+
fig_queue = Queue()
|
112
|
+
|
113
|
+
def _process_fig_queue():
|
114
|
+
global canvas
|
115
|
+
try:
|
116
|
+
while not fig_queue.empty():
|
117
|
+
clear_canvas(canvas)
|
118
|
+
fig = fig_queue.get_nowait()
|
119
|
+
#set_fig_text_properties(fig, font_size=8)
|
120
|
+
for ax in fig.get_axes():
|
121
|
+
ax.set_xticks([]) # Remove x-axis ticks
|
122
|
+
ax.set_yticks([]) # Remove y-axis ticks
|
123
|
+
ax.xaxis.set_visible(False) # Hide the x-axis
|
124
|
+
ax.yaxis.set_visible(False) # Hide the y-axis
|
125
|
+
#ax.title.set_fontsize(14)
|
126
|
+
#disable_interactivity(fig)
|
127
|
+
fig.tight_layout()
|
128
|
+
fig.set_facecolor('#333333')
|
129
|
+
canvas.figure = fig
|
130
|
+
fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
|
131
|
+
fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
|
132
|
+
canvas.draw_idle()
|
133
|
+
except Exception as e:
|
134
|
+
traceback.print_exc()
|
135
|
+
#pass
|
136
|
+
finally:
|
137
|
+
canvas_widget.after(100, _process_fig_queue)
|
138
|
+
|
139
|
+
# Process queue for console output
|
140
|
+
def _process_console_queue():
|
141
|
+
while not q.empty():
|
142
|
+
message = q.get_nowait()
|
143
|
+
console_output.insert(tk.END, message)
|
144
|
+
console_output.see(tk.END)
|
145
|
+
console_output.after(100, _process_console_queue)
|
146
|
+
|
147
|
+
# Vertical container for settings and console
|
148
|
+
vertical_container = tk.PanedWindow(root, orient=tk.HORIZONTAL) #VERTICAL
|
149
|
+
vertical_container.pack(fill=tk.BOTH, expand=True)
|
150
|
+
|
151
|
+
# Scrollable Frame for user settings
|
152
|
+
scrollable_frame = ScrollableFrame(vertical_container)
|
153
|
+
vertical_container.add(scrollable_frame, stretch="always")
|
154
|
+
|
155
|
+
# Setup for user input fields (variables)
|
156
|
+
variables = measure_variables()
|
157
|
+
vars_dict = generate_fields(variables, scrollable_frame)
|
158
|
+
|
159
|
+
# Horizontal container for Matplotlib figure and the vertical pane (for settings and console)
|
160
|
+
horizontal_container = tk.PanedWindow(vertical_container, orient=tk.VERTICAL) #HORIZONTAL
|
161
|
+
vertical_container.add(horizontal_container, stretch="always")
|
162
|
+
|
163
|
+
# Matplotlib figure setup
|
164
|
+
figure = Figure(figsize=(30, 4), dpi=100, facecolor='#333333')
|
165
|
+
plot = figure.add_subplot(111)
|
166
|
+
plot.plot([], []) # This creates an empty plot.
|
167
|
+
plot.axis('off')
|
168
|
+
|
169
|
+
# Embedding the Matplotlib figure in the Tkinter window
|
170
|
+
canvas = FigureCanvasTkAgg(figure, master=horizontal_container)
|
171
|
+
canvas.get_tk_widget().configure(cursor='arrow', background='#333333', highlightthickness=0)
|
172
|
+
#canvas.get_tk_widget().configure(cursor='arrow')
|
173
|
+
canvas_widget = canvas.get_tk_widget()
|
174
|
+
horizontal_container.add(canvas_widget, stretch="always")
|
175
|
+
canvas.draw()
|
176
|
+
|
177
|
+
# Console output setup below the settings
|
178
|
+
console_output = scrolledtext.ScrolledText(vertical_container, height=10)
|
179
|
+
vertical_container.add(console_output, stretch="always")
|
180
|
+
|
181
|
+
# Queue and redirection setup for updating console output safely
|
182
|
+
q = Queue()
|
183
|
+
sys.stdout = StdoutRedirector(console_output)
|
184
|
+
sys.stderr = StdoutRedirector(console_output)
|
185
|
+
|
186
|
+
# This is your GUI setup where you create the Run button
|
187
|
+
run_button = ttk.Button(scrollable_frame.scrollable_frame, text="Run",command=lambda: start_process(q, fig_queue))
|
188
|
+
run_button.grid(row=40, column=0, pady=10)
|
189
|
+
|
190
|
+
abort_button = ttk.Button(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
|
191
|
+
abort_button.grid(row=40, column=1, pady=10)
|
192
|
+
|
193
|
+
progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Progress: 0%", background="#333333", foreground="white")
|
194
|
+
progress_label.grid(row=41, column=0, columnspan=2, sticky="ew", pady=(5, 0))
|
195
|
+
|
196
|
+
# Create the Import Settings button
|
197
|
+
import_btn = tk.Button(root, text="Import Settings", command=lambda: import_settings(scrollable_frame))
|
198
|
+
import_btn.pack(pady=20)
|
199
|
+
|
200
|
+
_process_console_queue()
|
201
|
+
_process_fig_queue()
|
202
|
+
create_dark_mode(root, style, console_output)
|
203
|
+
|
204
|
+
root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
|
205
|
+
|
206
|
+
return root, vars_dict
|
207
|
+
|
208
|
+
def gui_measure():
|
209
|
+
global vars_dict, root
|
210
|
+
root, vars_dict = initiate_measure_root(1000, 1500)
|
211
|
+
root.mainloop()
|
212
|
+
|
213
|
+
if __name__ == "__main__":
|
214
|
+
gui_measure()
|