spacr 0.1.7__py3-none-any.whl → 0.1.8__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_utils.py CHANGED
@@ -1,9 +1,9 @@
1
- import io, sys, ast, ctypes, re, csv, ast
1
+ import os, io, sys, ast, ctypes, re, ast, sqlite3
2
2
  import tkinter as tk
3
3
  from tkinter import ttk
4
4
 
5
5
  from . gui_core import initiate_root
6
- from .gui_elements import spacrLabel, spacrCheckbutton, set_dark_style
6
+ from .gui_elements import spacrLabel, spacrCheckbutton, ImageApp
7
7
 
8
8
  try:
9
9
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -17,9 +17,7 @@ def set_default_font(root, font_name="Helvetica", size=12):
17
17
  root.option_add("*TLabel.Font", default_font)
18
18
  root.option_add("*TEntry.Font", default_font)
19
19
 
20
- def proceed_with_app(root, app_name, app_func):
21
- from .app_annotate import gui_annotate
22
- from .app_make_masks import gui_make_masks
20
+ def proceed_with_app_v1(root, app_name, app_func):
23
21
  from .gui import gui_app
24
22
 
25
23
  # Clear the current content frame
@@ -47,11 +45,23 @@ def proceed_with_app(root, app_name, app_func):
47
45
  elif app_name == "Umap":
48
46
  initiate_root(root.content_frame, 'umap')
49
47
  elif app_name == "Annotate":
50
- gui_annotate()
48
+ initiate_root(root.content_frame, 'annotate')
51
49
  elif app_name == "Make Masks":
52
- gui_make_masks()
50
+ initiate_root(root.content_frame, 'make_masks')
53
51
  else:
54
52
  raise ValueError(f"Invalid app name: {app_name}")
53
+
54
+ def proceed_with_app(root, app_name, app_func):
55
+ # Clear the current content frame
56
+ if hasattr(root, 'content_frame'):
57
+ for widget in root.content_frame.winfo_children():
58
+ try:
59
+ widget.destroy()
60
+ except tk.TclError as e:
61
+ print(f"Error destroying widget: {e}")
62
+
63
+ # Initialize the new app in the content frame
64
+ app_func(root.content_frame)
55
65
 
56
66
  def load_app(root, app_name, app_func):
57
67
  # Cancel all scheduled after tasks
@@ -60,8 +70,8 @@ def load_app(root, app_name, app_func):
60
70
  root.after_cancel(task)
61
71
  root.after_tasks = []
62
72
 
63
- # Exit functionality only for the annotation app
64
- if app_name != "Annotate" and hasattr(root, 'current_app_exit_func'):
73
+ # Exit functionality only for the annotation and make_masks apps
74
+ if app_name not in ["Annotate", "make_masks"] and hasattr(root, 'current_app_exit_func'):
65
75
  root.next_app_func = proceed_with_app
66
76
  root.next_app_args = (app_name, app_func) # Ensure correct arguments
67
77
  root.current_app_exit_func()
@@ -147,3 +157,170 @@ def cancel_after_tasks(frame):
147
157
  for task in frame.after_tasks:
148
158
  frame.after_cancel(task)
149
159
  frame.after_tasks.clear()
160
+
161
+ def main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label):
162
+ try:
163
+ ansi_escape_pattern = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
164
+ while not q.empty():
165
+ message = q.get_nowait()
166
+ clean_message = ansi_escape_pattern.sub('', message)
167
+ if clean_message.startswith("Progress"):
168
+ progress_label.config(text=clean_message)
169
+ if clean_message.startswith("\rProgress"):
170
+ progress_label.config(text=clean_message)
171
+ elif clean_message.startswith("Successfully"):
172
+ progress_label.config(text=clean_message)
173
+ elif clean_message.startswith("Processing"):
174
+ progress_label.config(text=clean_message)
175
+ elif clean_message.startswith("scale"):
176
+ pass
177
+ elif clean_message.startswith("plot_cropped_arrays"):
178
+ pass
179
+ elif clean_message == "" or clean_message == "\r" or clean_message.strip() == "":
180
+ pass
181
+ else:
182
+ print(clean_message)
183
+ except Exception as e:
184
+ print(f"Error updating GUI canvas: {e}")
185
+ finally:
186
+ root.after(100, lambda: main_thread_update_function(root, q, fig_queue, canvas_widget, progress_label))
187
+
188
+ def annotate(settings):
189
+ from .settings import set_annotate_default_settings
190
+ settings = set_annotate_default_settings(settings)
191
+ src = settings['src']
192
+
193
+ db = os.path.join(src, 'measurements/measurements.db')
194
+ conn = sqlite3.connect(db)
195
+ c = conn.cursor()
196
+ c.execute('PRAGMA table_info(png_list)')
197
+ cols = c.fetchall()
198
+ if settings['annotation_column'] not in [col[1] for col in cols]:
199
+ c.execute(f"ALTER TABLE png_list ADD COLUMN {settings['annotation_column']} integer")
200
+ conn.commit()
201
+ conn.close()
202
+
203
+ root = tk.Tk()
204
+ root.geometry(settings['geom'])
205
+ app = ImageApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
206
+ next_button = tk.Button(root, text="Next", command=app.next_page)
207
+ next_button.grid(row=app.grid_rows, column=app.grid_cols - 1)
208
+ back_button = tk.Button(root, text="Back", command=app.previous_page)
209
+ back_button.grid(row=app.grid_rows, column=app.grid_cols - 2)
210
+ exit_button = tk.Button(root, text="Exit", command=app.shutdown)
211
+ exit_button.grid(row=app.grid_rows, column=app.grid_cols - 3)
212
+
213
+ app.load_images()
214
+ root.mainloop()
215
+
216
+ def generate_annotate_fields(frame):
217
+ from .settings import set_annotate_default_settings
218
+ vars_dict = {}
219
+ settings = set_annotate_default_settings(settings={})
220
+
221
+ for setting in settings:
222
+ vars_dict[setting] = {
223
+ 'entry': ttk.Entry(frame),
224
+ 'value': settings[setting]
225
+ }
226
+
227
+ # Arrange input fields and labels
228
+ for row, (name, data) in enumerate(vars_dict.items()):
229
+ ttk.Label(frame, text=f"{name.replace('_', ' ').capitalize()}:",
230
+ background="black", foreground="white").grid(row=row, column=0)
231
+ if isinstance(data['value'], list):
232
+ # Convert lists to comma-separated strings
233
+ data['entry'].insert(0, ','.join(map(str, data['value'])))
234
+ else:
235
+ data['entry'].insert(0, data['value'])
236
+ data['entry'].grid(row=row, column=1)
237
+
238
+ return vars_dict
239
+
240
+ def run_annotate_app(vars_dict, parent_frame):
241
+ settings = {key: data['entry'].get() for key, data in vars_dict.items()}
242
+ settings['channels'] = settings['channels'].split(',')
243
+ settings['img_size'] = list(map(int, settings['img_size'].split(','))) # Convert string to list of integers
244
+ settings['percentiles'] = list(map(int, settings['percentiles'].split(','))) # Convert string to list of integers
245
+ settings['normalize'] = settings['normalize'].lower() == 'true'
246
+ settings['rows'] = int(settings['rows'])
247
+ settings['columns'] = int(settings['columns'])
248
+ settings['measurement'] = settings['measurement'].split(',')
249
+ settings['threshold'] = None if settings['threshold'].lower() == 'none' else int(settings['threshold'])
250
+
251
+ # Clear previous content instead of destroying the root
252
+ if hasattr(parent_frame, 'winfo_children'):
253
+ for widget in parent_frame.winfo_children():
254
+ widget.destroy()
255
+
256
+ # Start the annotate application in the same root window
257
+ annotate_app(parent_frame, settings)
258
+
259
+ # Global list to keep references to PhotoImage objects
260
+ global_image_refs = []
261
+
262
+ def annotate_app(parent_frame, settings):
263
+ global global_image_refs
264
+ global_image_refs.clear()
265
+ root = parent_frame.winfo_toplevel()
266
+ annotate_with_image_refs(settings, root, lambda: load_next_app(root))
267
+
268
+ def load_next_app(root):
269
+ # Get the next app function and arguments
270
+ next_app_func = root.next_app_func
271
+ next_app_args = root.next_app_args
272
+
273
+ if next_app_func:
274
+ try:
275
+ if not root.winfo_exists():
276
+ raise tk.TclError
277
+ next_app_func(root, *next_app_args)
278
+ except tk.TclError:
279
+ # Reinitialize root if it has been destroyed
280
+ new_root = tk.Tk()
281
+ width = new_root.winfo_screenwidth()
282
+ height = new_root.winfo_screenheight()
283
+ new_root.geometry(f"{width}x{height}")
284
+ new_root.title("SpaCr Application")
285
+ next_app_func(new_root, *next_app_args)
286
+
287
+ def annotate_with_image_refs(settings, root, shutdown_callback):
288
+ #from .gui_utils import proceed_with_app
289
+ from .gui import gui_app
290
+ from .settings import set_annotate_default_settings
291
+
292
+ settings = set_annotate_default_settings(settings)
293
+ src = settings['src']
294
+
295
+ db = os.path.join(src, 'measurements/measurements.db')
296
+ conn = sqlite3.connect(db)
297
+ c = conn.cursor()
298
+ c.execute('PRAGMA table_info(png_list)')
299
+ cols = c.fetchall()
300
+ if settings['annotation_column'] not in [col[1] for col in cols]:
301
+ c.execute(f"ALTER TABLE png_list ADD COLUMN {settings['annotation_column']} integer")
302
+ conn.commit()
303
+ conn.close()
304
+
305
+ app = ImageApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
306
+
307
+ # Set the canvas background to black
308
+ root.configure(bg='black')
309
+
310
+ next_button = tk.Button(root, text="Next", command=app.next_page, background='black', foreground='white')
311
+ next_button.grid(row=app.grid_rows, column=app.grid_cols - 1)
312
+ back_button = tk.Button(root, text="Back", command=app.previous_page, background='black', foreground='white')
313
+ back_button.grid(row=app.grid_rows, column=app.grid_cols - 2)
314
+ exit_button = tk.Button(root, text="Exit", command=lambda: [app.shutdown(), shutdown_callback()], background='black', foreground='white')
315
+ exit_button.grid(row=app.grid_rows, column=app.grid_cols - 3)
316
+
317
+ #app.load_images()
318
+
319
+ # Store the shutdown function and next app details in the root
320
+ root.current_app_exit_func = lambda: [app.shutdown(), shutdown_callback()]
321
+ root.next_app_func = proceed_with_app
322
+ root.next_app_args = ("Main App", gui_app)
323
+
324
+ # Call load_images after setting up the root window
325
+ app.load_images()
326
+
spacr/measure.py CHANGED
@@ -998,12 +998,12 @@ def measure_crop(settings):
998
998
  _save_settings_to_db(settings)
999
999
 
1000
1000
  files = [f for f in os.listdir(settings['input_folder']) if f.endswith('.npy')]
1001
- n_job = settings['n_job'] or mp.cpu_count()-4
1002
- print(f'using {n_job} cpu cores')
1001
+ n_jobs = settings['n_jobs'] or mp.cpu_count()-4
1002
+ print(f'using {n_jobs} cpu cores')
1003
1003
 
1004
1004
  with mp.Manager() as manager:
1005
1005
  time_ls = manager.list()
1006
- with mp.Pool(n_job) as pool:
1006
+ with mp.Pool(n_jobs) as pool:
1007
1007
  result = pool.starmap_async(_measure_crop_core, [(index, time_ls, file, settings) for index, file in enumerate(files)])
1008
1008
 
1009
1009
  # Track progress in the main process
@@ -1012,7 +1012,7 @@ def measure_crop(settings):
1012
1012
  files_processed = len(time_ls)
1013
1013
  files_to_process = len(files)
1014
1014
  average_time = np.mean(time_ls) if len(time_ls) > 0 else 0
1015
- time_left = (((files_to_process-files_processed)*average_time)/n_job)/60
1015
+ time_left = (((files_to_process-files_processed)*average_time)/n_jobs)/60
1016
1016
  print(f'Progress: {files_processed}/{files_to_process} Time/img {average_time:.3f}sec, Time Remaining {time_left:.3f} min.', end='\r', flush=True)
1017
1017
  result.get()
1018
1018