spacr 0.1.0__py3-none-any.whl → 0.1.11__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/measure_app.py ADDED
@@ -0,0 +1,246 @@
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
+ def toggle_test_mode():
32
+ global vars_dict
33
+ current_state = vars_dict['test_mode'][2].get()
34
+ new_state = not current_state
35
+ vars_dict['test_mode'][2].set(new_state)
36
+ if new_state:
37
+ test_mode_button.config(bg="blue")
38
+ else:
39
+ test_mode_button.config(bg="gray")
40
+
41
+ def toggle_advanced_settings():
42
+ global vars_dict
43
+
44
+ timelapse_settings = ['timelapse', 'timelapse_objects']
45
+ misc_settings = ['representative_images', 'plot', 'plot_filtration', 'include_uninfected', 'dialate_pngs', 'dialate_png_ratios']
46
+ opperational_settings = ['max_workers','experiment','cells','cell_loc','pathogens','pathogen_loc','treatments','treatment_loc','channel_of_interest','compartments','measurement','nr_imgs', 'um_per_pixel']
47
+
48
+ advanced_settings = timelapse_settings+misc_settings+opperational_settings
49
+
50
+ # Toggle visibility of advanced settings
51
+ for setting in advanced_settings:
52
+ label, widget, var = vars_dict[setting]
53
+ if advanced_var.get() is False:
54
+ label.grid_remove() # Hide the label
55
+ widget.grid_remove() # Hide the widget
56
+ else:
57
+ label.grid() # Show the label
58
+ widget.grid() # Show the widget
59
+
60
+ #@log_function_call
61
+ def run_measure_gui(q, fig_queue, stop_requested):
62
+ global vars_dict
63
+ process_stdout_stderr(q)
64
+ try:
65
+ print('hello')
66
+ settings = check_measure_gui_settings(vars_dict)
67
+ measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
68
+ except Exception as e:
69
+ q.put(f"Error during processing: {e}")
70
+ traceback.print_exc()
71
+ finally:
72
+ stop_requested.value = 1
73
+
74
+ #@log_function_call
75
+ def start_process(q, fig_queue):
76
+ global thread_control
77
+ if thread_control.get("run_thread") is not None:
78
+ initiate_abort()
79
+
80
+ stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
81
+ thread_control["stop_requested"] = stop_requested
82
+ thread_control["run_thread"] = Process(target=run_measure_gui, args=(q, fig_queue, stop_requested))
83
+ thread_control["run_thread"].start()
84
+
85
+ #@log_function_call
86
+ def initiate_abort():
87
+ global thread_control
88
+ if thread_control.get("stop_requested") is not None:
89
+ thread_control["stop_requested"].value = 1
90
+
91
+ if thread_control.get("run_thread") is not None:
92
+ thread_control["run_thread"].join(timeout=5)
93
+ if thread_control["run_thread"].is_alive():
94
+ thread_control["run_thread"].terminate()
95
+ thread_control["run_thread"] = None
96
+
97
+ #@log_function_call
98
+ def initiate_measure_root(parent_frame):
99
+ global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
100
+
101
+ style = ttk.Style(parent_frame)
102
+ set_dark_style(style)
103
+ style_text_boxes(style)
104
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
105
+
106
+ parent_frame.configure(bg='black')
107
+ parent_frame.grid_rowconfigure(0, weight=1)
108
+ parent_frame.grid_columnconfigure(0, weight=1)
109
+
110
+ fig_queue = Queue()
111
+
112
+ # Initialize after_tasks if not already done
113
+ if not hasattr(parent_frame, 'after_tasks'):
114
+ parent_frame.after_tasks = []
115
+
116
+ def _process_fig_queue():
117
+ global canvas
118
+ try:
119
+ while not fig_queue.empty():
120
+ clear_canvas(canvas)
121
+ fig = fig_queue.get_nowait()
122
+ for ax in fig.get_axes():
123
+ ax.set_xticks([]) # Remove x-axis ticks
124
+ ax.set_yticks([]) # Remove y-axis ticks
125
+ ax.xaxis.set_visible(False) # Hide the x-axis
126
+ ax.yaxis.set_visible(False) # Hide the y-axis
127
+ fig.tight_layout()
128
+ fig.set_facecolor('black')
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
+ finally:
136
+ after_id = canvas_widget.after(100, _process_fig_queue)
137
+ parent_frame.after_tasks.append(after_id)
138
+
139
+ def _process_console_queue():
140
+ while not q.empty():
141
+ message = q.get_nowait()
142
+ console_output.insert(tk.END, message)
143
+ console_output.see(tk.END)
144
+ after_id = console_output.after(100, _process_console_queue)
145
+ parent_frame.after_tasks.append(after_id)
146
+
147
+ # Clear previous content if any
148
+ for widget in parent_frame.winfo_children():
149
+ widget.destroy()
150
+
151
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
152
+ vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
153
+ parent_frame.grid_rowconfigure(0, weight=1)
154
+ parent_frame.grid_columnconfigure(0, weight=1)
155
+
156
+ # Settings Section
157
+ settings_frame = tk.Frame(vertical_container, bg='black')
158
+ vertical_container.add(settings_frame, stretch="always")
159
+ settings_label = ttk.Label(settings_frame, text="Settings", background="black", foreground="white")
160
+ settings_label.grid(row=0, column=0, pady=10, padx=10)
161
+ scrollable_frame = ScrollableFrame(settings_frame, width=500)
162
+ scrollable_frame.grid(row=1, column=0, sticky="nsew")
163
+ settings_frame.grid_rowconfigure(1, weight=1)
164
+ settings_frame.grid_columnconfigure(0, weight=1)
165
+
166
+ # Create advanced settings checkbox
167
+ advanced_var = tk.BooleanVar(value=False)
168
+ advanced_Toggle = ToggleSwitch(scrollable_frame.scrollable_frame, text="Advanced Settings", variable=advanced_var, command=toggle_advanced_settings)
169
+ advanced_Toggle.grid(row=48, column=0, pady=10, padx=10)
170
+ variables = measure_variables()
171
+ vars_dict = generate_fields(variables, scrollable_frame)
172
+ toggle_advanced_settings()
173
+ vars_dict['Test mode'] = (None, None, tk.BooleanVar(value=False))
174
+
175
+ # Button section
176
+ test_mode_button = CustomButton(scrollable_frame.scrollable_frame, text="Test Mode", command=toggle_test_mode)
177
+ test_mode_button.grid(row=47, column=1, pady=10, padx=10)
178
+ import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
179
+ import_btn.grid(row=47, column=0, pady=20, padx=20)
180
+ run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
181
+ run_button.grid(row=45, column=0, pady=20, padx=20)
182
+ abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
183
+ abort_button.grid(row=45, column=1, pady=20, padx=20)
184
+ progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
185
+ progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
186
+
187
+ # Plot Canvas Section
188
+ plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
189
+ vertical_container.add(plot_frame, stretch="always")
190
+ figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
191
+ plot = figure.add_subplot(111)
192
+ plot.plot([], [])
193
+ plot.axis('off')
194
+ canvas = FigureCanvasTkAgg(figure, master=plot_frame)
195
+ canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
196
+ canvas_widget = canvas.get_tk_widget()
197
+ plot_frame.add(canvas_widget, stretch="always")
198
+ canvas.draw()
199
+ canvas.figure = figure
200
+
201
+ # Console Section
202
+ console_frame = tk.Frame(vertical_container, bg='black')
203
+ vertical_container.add(console_frame, stretch="always")
204
+ console_label = ttk.Label(console_frame, text="Console", background="black", foreground="white")
205
+ console_label.grid(row=0, column=0, pady=10, padx=10)
206
+ console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='black', fg='white', insertbackground='white')
207
+ console_output.grid(row=1, column=0, sticky="nsew")
208
+ console_frame.grid_rowconfigure(1, weight=1)
209
+ console_frame.grid_columnconfigure(0, weight=1)
210
+
211
+ q = Queue()
212
+ sys.stdout = StdoutRedirector(console_output)
213
+ sys.stderr = StdoutRedirector(console_output)
214
+
215
+ _process_console_queue()
216
+ _process_fig_queue()
217
+
218
+ after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
219
+ parent_frame.after_tasks.append(after_id)
220
+
221
+ return parent_frame, vars_dict
222
+
223
+ def gui_measure():
224
+ root = tk.Tk()
225
+ width = root.winfo_screenwidth()
226
+ height = root.winfo_screenheight()
227
+ root.geometry(f"{width}x{height}")
228
+ root.title("SpaCr: measure objects")
229
+
230
+ # Clear previous content if any
231
+ if hasattr(root, 'content_frame'):
232
+ for widget in root.content_frame.winfo_children():
233
+ widget.destroy()
234
+ root.content_frame.grid_forget()
235
+ else:
236
+ root.content_frame = tk.Frame(root)
237
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
238
+ root.grid_rowconfigure(1, weight=1)
239
+ root.grid_columnconfigure(0, weight=1)
240
+
241
+ initiate_measure_root(root.content_frame)
242
+ create_menu_bar(root)
243
+ root.mainloop()
244
+
245
+ if __name__ == "__main__":
246
+ gui_measure()
spacr/sim_app.py ADDED
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.0
3
+ Version: 0.1.11
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -40,6 +40,10 @@ Requires-Dist: ttf-opensans >=2020.10.30
40
40
  Requires-Dist: customtkinter <6.0,>=5.2.2
41
41
  Requires-Dist: biopython <2.0,>=1.80
42
42
  Requires-Dist: lxml <6.0,>=5.1.0
43
+ Requires-Dist: qtpy <2.5,>=2.4.1
44
+ Requires-Dist: superqt <0.7,>=0.6.7
45
+ Requires-Dist: pyqt6 <6.8,>=6.7.1
46
+ Requires-Dist: pyqtgraph <0.14,>=0.13.7
43
47
  Provides-Extra: dev
44
48
  Requires-Dist: pytest >=3.9 ; extra == 'dev'
45
49
  Provides-Extra: full
@@ -1,40 +1,48 @@
1
- spacr/__init__.py,sha256=rnb_oYH6HmC1KvJmc7ymrdtHvmMW5t7bn8tJa03cxcA,1286
1
+ spacr/__init__.py,sha256=M16gIwyDInyfifwm0w9F0RKEbIoG0O4TMO_bdFYtfBc,1373
2
2
  spacr/__main__.py,sha256=bkAJJD2kjIqOP-u1kLvct9jQQCeUXzlEjdgitwi1Lm8,75
3
3
  spacr/alpha.py,sha256=Y95sLEfpK2OSYKRn3M8eUOU33JJeXfV8zhrC4KnwSTY,35244
4
- spacr/annotate_app.py,sha256=2X_xnXFN_w19RG99awsTPLzQfQZyQdwbaT-lcRxyV-w,20670
5
- spacr/annotate_app_v2.py,sha256=kvikj_QbN4EHdyYwB0kjEepEuq2uVwfAF-VJ531qO3Q,22647
4
+ spacr/annotate_app.py,sha256=imQ7ZEXDyM6ce1dxZ1xUS1-KequuF_NCI4xCaPLjvco,29275
5
+ spacr/annotate_app_v2.py,sha256=imQ7ZEXDyM6ce1dxZ1xUS1-KequuF_NCI4xCaPLjvco,29275
6
6
  spacr/chris.py,sha256=YlBjSgeZaY8HPy6jkrT_ISAnCMAKVfvCxF0I9eAZLFM,2418
7
+ spacr/classify_app.py,sha256=Zi15ryc1ocYitRF4kyxlC27XxGyzfSPdvj2d6ZrSh7E,8446
7
8
  spacr/cli.py,sha256=507jfOOEV8BoL4eeUcblvH-iiDHdBrEVJLu1ghAAPSc,1800
8
9
  spacr/core.py,sha256=m9fsk-qDPow4AzOYpTIsd4jT7PF_L_4y5xillR5eRdk,160253
9
10
  spacr/deep_spacr.py,sha256=N0o7ILD2p1FTfU4DFxnpjs00xjLhwib-ev0XGqA6muU,37035
10
11
  spacr/foldseek.py,sha256=YIP1d4Ci6CeA9jSyiv-HTDbNmAmcSM9Y_DaOs7wYzLY,33546
11
12
  spacr/get_alfafold_structures.py,sha256=ehx_MQgb12k3hFecP6cYVlm5TLO8iWjgevy8ESyS3cw,3544
12
13
  spacr/graph_learning.py,sha256=1tR-ZxvXE3dBz1Saw7BeVFcrsUFu9OlUZeZVifih9eo,13070
13
- spacr/gui.py,sha256=zu-i8ezLJ03jNRACK7CRgNhkM8g8-pJFwZ-OSDFzsPg,6498
14
- spacr/gui_2.py,sha256=FPlmvGm1VIood_YBnG44IafgjjaVfagybTnjVEOs5Ig,3299
15
- spacr/gui_classify_app.py,sha256=LY33wott1mR7AFYwBI9ZQZYY16lBB-wuaY4pL_poaQ0,7884
16
- spacr/gui_mask_app.py,sha256=WKkAH0jv-SnfaZdJ8MkC7mkUIVSSrNE8lUfH3QBvUak,9747
17
- spacr/gui_measure_app.py,sha256=5vjjds5NFaOcE8XeuWDug9k-NI4jbTrwp54sJ7DNaNI,9625
14
+ spacr/gui.py,sha256=5kKGKa-iZ5DxAOquW2IkNCP8C87nVKNOA3eONOa7iHo,6559
15
+ spacr/gui_2.py,sha256=ZAI5quQYbhQJ40vK0NCqU_UMSPLkpfeQpomBWUSM0fc,6946
16
+ spacr/gui_annotate.py,sha256=ugBksLGOHdtOLlEuRyyc59TrkYKu3rDf8JxEgiBSVao,6536
17
+ spacr/gui_classify_app.py,sha256=Zi15ryc1ocYitRF4kyxlC27XxGyzfSPdvj2d6ZrSh7E,8446
18
+ spacr/gui_make_masks_app.py,sha256=tl4M4Q2WQgrrwjRBJVevxJxpNowqzPhWkdCOm2UfRbw,45053
19
+ spacr/gui_make_masks_app_v2.py,sha256=X3izTBXdCZDlkVe-fbG-jmCQtcAbmK0OIivjyWaLhug,30576
20
+ spacr/gui_mask_app.py,sha256=mhTl_XzXLFl8Tx3WYEMpdYB_qw9u5JJa0EdkvlcIzAE,10706
21
+ spacr/gui_measure_app.py,sha256=_C1-XFL5HSquUEEbM_NcxdvHx-socPFCx85MBG4d6xo,10598
18
22
  spacr/gui_sim_app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- spacr/gui_utils.py,sha256=JRWwmGEEVSPgs0UtZRukdNwIUJepbP675_Fvs5qocPk,49718
23
+ spacr/gui_utils.py,sha256=elvnIAd-bqVmwzDihg-g_RqWayvkrVpZJGDMskdn-HE,55663
20
24
  spacr/io.py,sha256=IoERqSwoxJrInYl-E0WfwFOEDZXFdJofk5DmpbyLGWM,112077
21
25
  spacr/logger.py,sha256=7Zqr3TuuOQLWT32gYr2q1qvv7x0a2JhLANmZcnBXAW8,670
22
- spacr/mask_app.py,sha256=jlKmj_evveIkkyH3PYEcAshcLXN0DOPWB1oc4hAwq9E,44201
26
+ spacr/make_masks_app.py,sha256=tl4M4Q2WQgrrwjRBJVevxJxpNowqzPhWkdCOm2UfRbw,45053
27
+ spacr/make_masks_app_v2.py,sha256=X3izTBXdCZDlkVe-fbG-jmCQtcAbmK0OIivjyWaLhug,30576
28
+ spacr/mask_app.py,sha256=mhTl_XzXLFl8Tx3WYEMpdYB_qw9u5JJa0EdkvlcIzAE,10706
23
29
  spacr/measure.py,sha256=0FRsHF5ftar4JZ0B_6Nq-NlyP5t6aiO0IrskyikIBEE,55000
30
+ spacr/measure_app.py,sha256=_C1-XFL5HSquUEEbM_NcxdvHx-socPFCx85MBG4d6xo,10598
24
31
  spacr/old_code.py,sha256=jw67DAGoLBd7mWofVzRJSEmCI1Qrff26zIo65SEkV00,13817
25
32
  spacr/plot.py,sha256=lrwU51OTWfby1wx73XGyjYmTjLVia7WOmGH5LZZ-4jM,67145
26
33
  spacr/sequencing.py,sha256=U_TBJGNfOBfokGegUe950W_KPfm51VOgpfibXoZ8RMQ,83974
27
34
  spacr/settings.py,sha256=Tr2fo2I75FGfmEVQOONOpGwqXMzFCrYMz4NAxav3ckg,21183
28
35
  spacr/sim.py,sha256=FveaVgBi3eypO2oVB5Dx-v0CC1Ny7UPfXkJiiRRodAk,71212
36
+ spacr/sim_app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
37
  spacr/timelapse.py,sha256=KMYCgHzf9LTZe-lWl5mvH2EjbKRE6OhpwdY13wEumGc,39504
30
38
  spacr/utils.py,sha256=O7dpCF3bU95d2v0UuPFeJtzXYrkh0r-6aLxaqkKkFwY,184619
31
39
  spacr/version.py,sha256=axH5tnGwtgSnJHb5IDhiu4Zjk5GhLyAEDRe-rnaoFOA,409
32
40
  spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model,sha256=z8BbHWZPRnE9D_BHO0fBREE85c1vkltDs-incs2ytXQ,26566572
33
41
  spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv,sha256=fBAGuL_B8ERVdVizO3BHozTDSbZUh1yFzsYK3wkQN68,420
34
42
  spacr/models/cp/toxo_pv_lumen.CP_model,sha256=2y_CindYhmTvVwBH39SNILF3rI3x9SsRn6qrMxHy3l0,26562451
35
- spacr-0.1.0.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
36
- spacr-0.1.0.dist-info/METADATA,sha256=AkkTVCUxEtm5QUQS4c58ZC0eaCx3ctpiFXchwjyRV9o,5157
37
- spacr-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
38
- spacr-0.1.0.dist-info/entry_points.txt,sha256=xncHsqD9MI5wj0_p4mgZlrB8dHm_g_qF0Ggo1c78LqY,315
39
- spacr-0.1.0.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
40
- spacr-0.1.0.dist-info/RECORD,,
43
+ spacr-0.1.11.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
44
+ spacr-0.1.11.dist-info/METADATA,sha256=FykpqeYP5uOY3XnsUaqwe2tSctPILiguTqqPNOJZ4Vg,5301
45
+ spacr-0.1.11.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
46
+ spacr-0.1.11.dist-info/entry_points.txt,sha256=PxmdWbth1nQzQfE0s2M3XST_of47tBtlBPH20KJrvkE,354
47
+ spacr-0.1.11.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
48
+ spacr-0.1.11.dist-info/RECORD,,
@@ -1,9 +1,9 @@
1
1
  [console_scripts]
2
- annotate = spacr.annotate_app:gui_annotation
2
+ annotate = spacr.annotate_app_v2:gui_annotate
3
3
  classify = spacr.gui_classify_app:gui_classify
4
4
  gui = spacr.gui:gui_app
5
- gui2 = spacr.gui_2:gui_app
6
- make_masks = spacr.mask_app:gui_make_masks
5
+ make_masks = spacr.gui_make_mask_app:gui_make_masks
6
+ make_masks2 = spacr.gui_make_mask_app_v2:gui_make_masks
7
7
  mask = spacr.gui_mask_app:gui_mask
8
8
  measure = spacr.gui_measure_app:gui_measure
9
9
  sim = spacr.gui_sim_app:gui_sim
File without changes