spacr 0.0.82__py3-none-any.whl → 0.1.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/gui_mask_app.py CHANGED
@@ -97,19 +97,23 @@ def import_settings(scrollable_frame):
97
97
  vars_dict = generate_fields(new_settings, scrollable_frame)
98
98
 
99
99
  #@log_function_call
100
- def initiate_mask_root(parent_frame, width, height):
100
+ def initiate_mask_root(parent_frame):
101
101
  global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, advanced_var, scrollable_frame
102
102
 
103
103
  style = ttk.Style(parent_frame)
104
104
  set_dark_style(style)
105
105
  style_text_boxes(style)
106
- set_default_font(parent_frame, font_name="Arial", size=8)
106
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
107
107
  parent_frame.configure(bg='black')
108
108
  parent_frame.grid_rowconfigure(0, weight=1)
109
109
  parent_frame.grid_columnconfigure(0, weight=1)
110
110
 
111
111
  fig_queue = Queue()
112
112
 
113
+ # Initialize after_tasks if not already done
114
+ if not hasattr(parent_frame, 'after_tasks'):
115
+ parent_frame.after_tasks = []
116
+
113
117
  def _process_fig_queue():
114
118
  global canvas
115
119
  try:
@@ -130,14 +134,20 @@ def initiate_mask_root(parent_frame, width, height):
130
134
  except Exception as e:
131
135
  traceback.print_exc()
132
136
  finally:
133
- canvas_widget.after(100, _process_fig_queue)
137
+ after_id = canvas_widget.after(100, _process_fig_queue)
138
+ parent_frame.after_tasks.append(after_id)
134
139
 
135
140
  def _process_console_queue():
136
141
  while not q.empty():
137
142
  message = q.get_nowait()
138
143
  console_output.insert(tk.END, message)
139
144
  console_output.see(tk.END)
140
- console_output.after(100, _process_console_queue)
145
+ after_id = console_output.after(100, _process_console_queue)
146
+ parent_frame.after_tasks.append(after_id)
147
+
148
+ # Clear previous content if any
149
+ for widget in parent_frame.winfo_children():
150
+ widget.destroy()
141
151
 
142
152
  vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
143
153
  vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
@@ -172,17 +182,17 @@ def initiate_mask_root(parent_frame, width, height):
172
182
  run_button.grid(row=45, column=0, pady=10, padx=10)
173
183
  abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
174
184
  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
185
+ progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white")
176
186
  progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
177
187
 
178
188
  # 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)
189
+ plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
180
190
  vertical_container.add(plot_frame, stretch="always")
181
- figure = Figure(figsize=(30, 4), dpi=100, facecolor='black') # Matplotlib figure setup
191
+ figure = Figure(figsize=(30, 4), dpi=100, facecolor='black')
182
192
  plot = figure.add_subplot(111)
183
193
  plot.plot([], []) # This creates an empty plot.
184
194
  plot.axis('off')
185
- canvas = FigureCanvasTkAgg(figure, master=plot_frame) # Embedd Matplotlib figure in Tkinter window
195
+ canvas = FigureCanvasTkAgg(figure, master=plot_frame)
186
196
  canvas.get_tk_widget().configure(cursor='arrow', background='black', highlightthickness=0)
187
197
  canvas_widget = canvas.get_tk_widget()
188
198
  plot_frame.add(canvas_widget, stretch="always")
@@ -206,15 +216,28 @@ def initiate_mask_root(parent_frame, width, height):
206
216
  _process_console_queue()
207
217
  _process_fig_queue()
208
218
 
209
- parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
219
+ after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
220
+ parent_frame.after_tasks.append(after_id)
210
221
 
211
222
  return parent_frame, vars_dict
212
223
 
213
224
  def gui_mask():
214
225
  root = tk.Tk()
215
226
  root.geometry("1000x800")
216
- root.title("SpaCer: generate masks")
217
- initiate_mask_root(root, 1000, 800)
227
+ root.title("SpaCr: generate masks")
228
+
229
+ # Clear previous content if any
230
+ if hasattr(root, 'content_frame'):
231
+ for widget in root.content_frame.winfo_children():
232
+ widget.destroy()
233
+ root.content_frame.grid_forget()
234
+ else:
235
+ root.content_frame = tk.Frame(root)
236
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
237
+ root.grid_rowconfigure(1, weight=1)
238
+ root.grid_columnconfigure(0, weight=1)
239
+
240
+ initiate_mask_root(root.content_frame)
218
241
  create_menu_bar(root)
219
242
  root.mainloop()
220
243
 
spacr/gui_measure_app.py CHANGED
@@ -86,29 +86,24 @@ def initiate_abort():
86
86
  thread_control["run_thread"] = None
87
87
 
88
88
  #@log_function_call
89
- def import_settings(scrollable_frame):
90
- global vars_dict
91
-
92
- csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
93
- csv_settings = read_settings_from_csv(csv_file_path)
94
- variables = measure_variables()
95
- new_settings = update_settings_from_csv(variables, csv_settings)
96
- vars_dict = generate_fields(new_settings, scrollable_frame)
97
-
98
- #@log_function_call
99
- def initiate_measure_root(parent_frame):#, width, height):
89
+ def initiate_measure_root(parent_frame):
100
90
  global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control, variables, advanced_var, scrollable_frame
101
91
 
102
92
  style = ttk.Style(parent_frame)
103
93
  set_dark_style(style)
104
94
  style_text_boxes(style)
105
- set_default_font(parent_frame, font_name="Arial", size=8)
95
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
106
96
 
107
97
  parent_frame.configure(bg='black')
108
98
  parent_frame.grid_rowconfigure(0, weight=1)
109
99
  parent_frame.grid_columnconfigure(0, weight=1)
100
+
110
101
  fig_queue = Queue()
111
102
 
103
+ # Initialize after_tasks if not already done
104
+ if not hasattr(parent_frame, 'after_tasks'):
105
+ parent_frame.after_tasks = []
106
+
112
107
  def _process_fig_queue():
113
108
  global canvas
114
109
  try:
@@ -129,14 +124,20 @@ def initiate_measure_root(parent_frame):#, width, height):
129
124
  except Exception as e:
130
125
  traceback.print_exc()
131
126
  finally:
132
- canvas_widget.after(100, _process_fig_queue)
127
+ after_id = canvas_widget.after(100, _process_fig_queue)
128
+ parent_frame.after_tasks.append(after_id)
133
129
 
134
130
  def _process_console_queue():
135
131
  while not q.empty():
136
132
  message = q.get_nowait()
137
133
  console_output.insert(tk.END, message)
138
134
  console_output.see(tk.END)
139
- console_output.after(100, _process_console_queue)
135
+ after_id = console_output.after(100, _process_console_queue)
136
+ parent_frame.after_tasks.append(after_id)
137
+
138
+ # Clear previous content if any
139
+ for widget in parent_frame.winfo_children():
140
+ widget.destroy()
140
141
 
141
142
  vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
142
143
  vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
@@ -205,15 +206,29 @@ def initiate_measure_root(parent_frame):#, width, height):
205
206
  _process_console_queue()
206
207
  _process_fig_queue()
207
208
 
208
- parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
209
+ after_id = parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
210
+ parent_frame.after_tasks.append(after_id)
209
211
 
210
212
  return parent_frame, vars_dict
211
213
 
214
+
212
215
  def gui_measure():
213
216
  root = tk.Tk()
214
217
  root.geometry("1000x800")
215
- root.title("SpaCer: generate masks")
216
- initiate_measure_root(root, 1000, 800)
218
+ root.title("SpaCr: measure objects")
219
+
220
+ # Clear previous content if any
221
+ if hasattr(root, 'content_frame'):
222
+ for widget in root.content_frame.winfo_children():
223
+ widget.destroy()
224
+ root.content_frame.grid_forget()
225
+ else:
226
+ root.content_frame = tk.Frame(root)
227
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
228
+ root.grid_rowconfigure(1, weight=1)
229
+ root.grid_columnconfigure(0, weight=1)
230
+
231
+ initiate_measure_root(root.content_frame)
217
232
  create_menu_bar(root)
218
233
  root.mainloop()
219
234
 
spacr/gui_utils.py CHANGED
@@ -40,21 +40,27 @@ class ToolTip:
40
40
  self.tooltip_window.destroy()
41
41
  self.tooltip_window = None
42
42
 
43
-
44
43
  def load_app(root, app_name, app_func):
45
- # Destroy the current window
46
- root.destroy()
47
- # Create a new window for the app
48
- app_window = tk.Tk()
49
- app_window.title(f"SpaCr - {app_name}")
50
- app_window.geometry("1200x800")
51
- #app_window.attributes('-fullscreen', True)
52
- app_window.configure(bg="black")
53
- create_menu_bar(app_window) # Add menu to the new window
54
- app_func(app_window, app_window.winfo_width(), app_window.winfo_height())
44
+ # Cancel all scheduled after tasks
45
+ if hasattr(root, 'after_tasks'):
46
+ for task in root.after_tasks:
47
+ root.after_cancel(task)
48
+ root.after_tasks = []
49
+
50
+ # Clear the current content frame
51
+ if hasattr(root, 'content_frame'):
52
+ for widget in root.content_frame.winfo_children():
53
+ widget.destroy()
54
+ else:
55
+ root.content_frame = tk.Frame(root)
56
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
57
+ root.grid_rowconfigure(1, weight=1)
58
+ root.grid_columnconfigure(0, weight=1)
55
59
 
56
- def create_menu_bar(root):
60
+ # Initialize the new app in the content frame
61
+ app_func(root.content_frame)
57
62
 
63
+ def create_menu_bar(root):
58
64
  from .gui_mask_app import initiate_mask_root
59
65
  from .gui_measure_app import initiate_measure_root
60
66
  from .annotate_app import initiate_annotation_app_root
@@ -68,6 +74,10 @@ def create_menu_bar(root):
68
74
  "Make Masks": initiate_mask_app_root,
69
75
  "Classify": initiate_classify_root
70
76
  }
77
+
78
+ def load_app_wrapper(app_name, app_func):
79
+ load_app(root, app_name, app_func)
80
+
71
81
  # Create the menu bar
72
82
  menu_bar = tk.Menu(root, bg="#008080", fg="white")
73
83
  # Create a "SpaCr Applications" menu
@@ -75,7 +85,7 @@ def create_menu_bar(root):
75
85
  menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
76
86
  # Add options to the "SpaCr Applications" menu
77
87
  for app_name, app_func in gui_apps.items():
78
- app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app(root, app_name, app_func))
88
+ app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
79
89
  # Add a separator and an exit option
80
90
  app_menu.add_separator()
81
91
  app_menu.add_command(label="Exit", command=root.quit)
@@ -83,7 +93,7 @@ def create_menu_bar(root):
83
93
  root.config(menu=menu_bar)
84
94
 
85
95
  class CustomButton(tk.Frame):
86
- def __init__(self, parent, text="", command=None, *args, **kwargs):
96
+ def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
87
97
  super().__init__(parent, *args, **kwargs)
88
98
  self.text = text
89
99
  self.command = command
@@ -93,8 +103,8 @@ class CustomButton(tk.Frame):
93
103
 
94
104
  self.button_bg = self.create_rounded_rectangle(0, 0, 150, 50, radius=20, fill="#800080")
95
105
 
96
- # Create a Tkinter font object using OpenSans
97
- self.font_style = tkFont.Font(family="Arial", size=12, weight=tkFont.NORMAL)
106
+ # Use the passed font or default to Helvetica if not provided
107
+ self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
98
108
  self.button_text = self.canvas.create_text(75, 25, text=self.text, fill="white", font=self.font_style)
99
109
 
100
110
  self.bind("<Enter>", self.on_enter)
@@ -137,7 +147,7 @@ class CustomButton(tk.Frame):
137
147
  x1, y1]
138
148
 
139
149
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
140
-
150
+
141
151
  class ToggleSwitch(ttk.Frame):
142
152
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
143
153
  super().__init__(parent, *args, **kwargs)
@@ -226,15 +236,60 @@ class ToggleSwitch(ttk.Frame):
226
236
 
227
237
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
228
238
 
229
- def set_default_font(root, font_name="Arial", size=12):
239
+ def set_default_font(root, font_name="Helvetica", size=12):
230
240
  default_font = (font_name, size)
231
241
  root.option_add("*Font", default_font)
232
242
  root.option_add("*TButton.Font", default_font)
233
243
  root.option_add("*TLabel.Font", default_font)
234
244
  root.option_add("*TEntry.Font", default_font)
235
245
 
246
+ def check_and_download_font_v1():
247
+ font_name = "Helvetica"
248
+ font_dir = "fonts"
249
+ font_path = os.path.join(font_dir, "OpenSans-Regular.ttf")
250
+
251
+ # Check if the font is already available
252
+ available_fonts = list(tkFont.families())
253
+ if font_name not in available_fonts:
254
+ print(f"Font '{font_name}' not found. Downloading...")
255
+ if not os.path.exists(font_dir):
256
+ os.makedirs(font_dir)
257
+
258
+ if not os.path.exists(font_path):
259
+ url = "https://github.com/google/fonts/blob/main/apache/opensans/OpenSans-Regular.ttf?raw=true"
260
+ response = requests.get(url)
261
+ with open(font_path, "wb") as f:
262
+ f.write(response.content)
263
+
264
+ # Load the font
265
+ try:
266
+ tkFont.nametofont("TkDefaultFont").configure(family=font_name, size=10)
267
+ tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
268
+ tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
269
+ except tk.TclError:
270
+ tkFont.nametofont("TkDefaultFont").configure(family="Helvetica", size=10)
271
+ tkFont.nametofont("TkTextFont").configure(family="Helvetica", size=10)
272
+ tkFont.nametofont("TkHeadingFont").configure(family="Helvetica", size=12)
273
+ else:
274
+ tkFont.nametofont("TkDefaultFont").configure(family=font_name, size=10)
275
+ tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
276
+ tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
277
+
278
+ def style_text_boxes_v1(style):
279
+ check_and_download_font()
280
+ font_style = tkFont.Font(family="Helvetica", size=10) # Define the Helvetica font
281
+ style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#000000', foreground='#ffffff', font=font_style)
282
+ style.configure('TCombobox', fieldbackground='#000000', background='#000000', foreground='#ffffff', font=font_style)
283
+ style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
284
+ style.map('Custom.TButton',
285
+ background=[('active', '#66b2b2'), ('disabled', '#004d4d'), ('!disabled', '#008080')],
286
+ foreground=[('active', '#ffffff'), ('disabled', '#888888')])
287
+ style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='#000000', foreground='#ffffff', font=font_style)
288
+ style.configure('TCheckbutton', background='#333333', foreground='#ffffff', indicatoron=False, relief='flat', font=font_style)
289
+ style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
290
+
236
291
  def check_and_download_font():
237
- font_name = "Arial"
292
+ font_name = "Helvetica"
238
293
  font_dir = "fonts"
239
294
  font_path = os.path.join(font_dir, "OpenSans-Regular.ttf")
240
295
 
@@ -257,9 +312,9 @@ def check_and_download_font():
257
312
  tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
258
313
  tkFont.nametofont("TkHeadingFont").configure(family=font_name, size=12)
259
314
  except tk.TclError:
260
- tkFont.nametofont("TkDefaultFont").configure(family="Arial", size=10)
261
- tkFont.nametofont("TkTextFont").configure(family="Arial", size=10)
262
- tkFont.nametofont("TkHeadingFont").configure(family="Arial", size=12)
315
+ tkFont.nametofont("TkDefaultFont").configure(family="Helvetica", size=10)
316
+ tkFont.nametofont("TkTextFont").configure(family="Helvetica", size=10)
317
+ tkFont.nametofont("TkHeadingFont").configure(family="Helvetica", size=12)
263
318
  else:
264
319
  tkFont.nametofont("TkDefaultFont").configure(family=font_name, size=10)
265
320
  tkFont.nametofont("TkTextFont").configure(family=font_name, size=10)
@@ -267,7 +322,7 @@ def check_and_download_font():
267
322
 
268
323
  def style_text_boxes(style):
269
324
  check_and_download_font()
270
- font_style = tkFont.Font(family="Arial", size=10) # Define the Arial font
325
+ font_style = tkFont.Font(family="Helvetica", size=10) # Define the Helvetica font
271
326
  style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='#000000', foreground='#ffffff', font=font_style)
272
327
  style.configure('TCombobox', fieldbackground='#000000', background='#000000', foreground='#ffffff', font=font_style)
273
328
  style.configure('Custom.TButton', padding='10 10 10 10', borderwidth=1, relief='solid', background='#008080', foreground='#ffffff', font=font_style)
@@ -279,7 +334,6 @@ def style_text_boxes(style):
279
334
  style.map('TCheckbutton', background=[('selected', '#555555'), ('active', '#555555')])
280
335
 
281
336
 
282
-
283
337
  def read_settings_from_csv(csv_file_path):
284
338
  settings = {}
285
339
  with open(csv_file_path, newline='') as csvfile:
@@ -337,7 +391,7 @@ class ScrollableFrame(ttk.Frame):
337
391
  canvas.pack(side="left", fill="both", expand=True)
338
392
  scrollbar.pack(side="right", fill="y")
339
393
 
340
- class StdoutRedirector(object):
394
+ class StdoutRedirector_v1(object):
341
395
  def __init__(self, text_widget):
342
396
  self.text_widget = text_widget
343
397
 
@@ -348,6 +402,21 @@ class StdoutRedirector(object):
348
402
  def flush(self):
349
403
  pass
350
404
 
405
+ class StdoutRedirector:
406
+ def __init__(self, text_widget):
407
+ self.text_widget = text_widget
408
+
409
+ def write(self, string):
410
+ try:
411
+ if self.text_widget.winfo_exists():
412
+ self.text_widget.insert(tk.END, string)
413
+ self.text_widget.see(tk.END)
414
+ except tk.TclError:
415
+ pass # Handle or log the error as needed
416
+
417
+ def flush(self):
418
+ pass
419
+
351
420
  def check_mask_gui_settings(vars_dict):
352
421
  settings = {}
353
422
  for key, var in vars_dict.items():
@@ -804,8 +873,6 @@ def generate_fields(variables, scrollable_frame):
804
873
  row += 1
805
874
  return vars_dict
806
875
 
807
-
808
-
809
876
  class TextRedirector(object):
810
877
  def __init__(self, widget, queue):
811
878
  self.widget = widget
@@ -834,7 +901,7 @@ def create_dark_mode(root, style, console_output):
834
901
  style.map('TCombobox', fieldbackground=[('readonly', input_bg)], selectbackground=[('readonly', input_bg)], foreground=[('readonly', dark_text)])
835
902
 
836
903
  if console_output != None:
837
- console_output.config(bg=dark_bg, fg=light_text, insertbackground=light_text) #, font=("Arial", 12)
904
+ console_output.config(bg=dark_bg, fg=light_text, insertbackground=light_text) #, font=("Helvetica", 12)
838
905
  root.configure(bg=dark_bg)
839
906
 
840
907
  def set_dark_style(style):
@@ -1025,4 +1092,4 @@ def run_multiple_simulations_wrapper(settings, q, fig_queue):
1025
1092
  q.put(errorMessage) # Send the error message to the GUI via the queue
1026
1093
  traceback.print_exc()
1027
1094
  finally:
1028
- plt.show = original_show # Restore the original plt.show function
1095
+ plt.show = original_show # Restore the original plt.show function