spacr 0.2.46__py3-none-any.whl → 0.2.53__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.
Files changed (58) hide show
  1. spacr/core.py +24 -11
  2. spacr/gui.py +0 -2
  3. spacr/gui_core.py +70 -55
  4. spacr/gui_elements.py +367 -152
  5. spacr/gui_utils.py +59 -68
  6. spacr/io.py +2 -3
  7. spacr/measure.py +196 -145
  8. spacr/plot.py +2 -42
  9. spacr/resources/font/open_sans/OFL.txt +93 -0
  10. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  11. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  12. spacr/resources/font/open_sans/README.txt +100 -0
  13. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  14. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  15. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  49. spacr/sequencing.py +107 -13
  50. spacr/settings.py +27 -84
  51. spacr/utils.py +9 -9
  52. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/METADATA +6 -4
  53. spacr-0.2.53.dist-info/RECORD +100 -0
  54. spacr-0.2.46.dist-info/RECORD +0 -60
  55. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/LICENSE +0 -0
  56. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/WHEEL +0 -0
  57. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/entry_points.txt +0 -0
  58. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import os, io, sys, ast, ctypes, ast, sqlite3, requests, time, traceback
2
- import traceback
3
2
  import tkinter as tk
4
3
  from tkinter import ttk
5
4
  import matplotlib
@@ -7,8 +6,7 @@ import matplotlib.pyplot as plt
7
6
  matplotlib.use('Agg')
8
7
  from huggingface_hub import list_repo_files
9
8
 
10
- from . gui_core import initiate_root
11
- from .gui_elements import AnnotateApp, spacrEntry, spacrCheck, spacrCombo, set_default_font
9
+ from .gui_elements import AnnotateApp, spacrEntry, spacrCheck, spacrCombo
12
10
 
13
11
  try:
14
12
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -28,6 +26,10 @@ def proceed_with_app(root, app_name, app_func):
28
26
  app_func(root.content_frame)
29
27
 
30
28
  def load_app(root, app_name, app_func):
29
+ # Clear the canvas if it exists
30
+ if root.canvas is not None:
31
+ root.clear_frame(root.canvas)
32
+
31
33
  # Cancel all scheduled after tasks
32
34
  if hasattr(root, 'after_tasks'):
33
35
  for task in root.after_tasks:
@@ -37,20 +39,10 @@ def load_app(root, app_name, app_func):
37
39
  # Exit functionality only for the annotation and make_masks apps
38
40
  if app_name not in ["Annotate", "make_masks"] and hasattr(root, 'current_app_exit_func'):
39
41
  root.next_app_func = proceed_with_app
40
- root.next_app_args = (app_name, app_func) # Ensure correct arguments
42
+ root.next_app_args = (app_name, app_func)
41
43
  root.current_app_exit_func()
42
44
  else:
43
45
  proceed_with_app(root, app_name, app_func)
44
-
45
- def parse_list_v1(value):
46
- try:
47
- parsed_value = ast.literal_eval(value)
48
- if isinstance(parsed_value, list):
49
- return parsed_value
50
- else:
51
- raise ValueError
52
- except (ValueError, SyntaxError):
53
- raise ValueError("Invalid format for list")
54
46
 
55
47
  def parse_list(value):
56
48
  try:
@@ -64,43 +56,57 @@ def parse_list(value):
64
56
 
65
57
  # Usage example in your create_input_field function
66
58
  def create_input_field(frame, label_text, row, var_type='entry', options=None, default_value=None):
67
- from .gui_elements import set_dark_style
59
+ from .gui_elements import set_dark_style, set_element_size
68
60
  label_column = 0
69
- widget_column = 1
61
+ widget_column = 0 # Both label and widget will be in the same column
70
62
 
71
63
  style_out = set_dark_style(ttk.Style())
64
+ font_loader = style_out['font_loader']
65
+ font_size = style_out['font_size']
66
+ size_dict = set_element_size()
67
+ size_dict['settings_width'] = size_dict['settings_width'] - int(size_dict['settings_width']*0.1)
72
68
 
73
69
  # Replace underscores with spaces and capitalize the first letter
74
70
  label_text = label_text.replace('_', ' ').capitalize()
75
71
 
76
72
  # Configure the column widths
77
- frame.grid_columnconfigure(label_column, weight=0) # Allow the label column to expand
78
- frame.grid_columnconfigure(widget_column, weight=1) # Allow the widget column to expand
73
+ frame.grid_columnconfigure(label_column, weight=1) # Allow the column to expand
74
+
75
+ # Create a custom frame with a translucent background and rounded edges
76
+ custom_frame = tk.Frame(frame, bg=style_out['bg_color'], bd=2, relief='solid', width=size_dict['settings_width'])
77
+ custom_frame.grid(column=label_column, row=row, sticky=tk.EW, padx=(5, 5), pady=5)
78
+
79
+ # Apply styles to custom frame
80
+ custom_frame.update_idletasks()
81
+ custom_frame.config(highlightbackground=style_out['bg_color'], highlightthickness=1, bd=2)
79
82
 
80
83
  # Create and configure the label
81
- label = ttk.Label(frame, text=label_text, background=style_out['bg_color'], foreground=style_out['fg_color'], font=(style_out['font_family'], style_out['font_size']), anchor='e', justify='right')
82
- label.grid(column=label_column, row=row, sticky=tk.E, padx=(5, 2), pady=5)
84
+ if font_loader:
85
+ label = ttk.Label(custom_frame, text=label_text, background=style_out['bg_color'], foreground=style_out['fg_color'], font=font_loader.get_font(size=font_size), anchor='e', justify='right')
86
+ label = ttk.Label(custom_frame, text=label_text, background=style_out['bg_color'], foreground=style_out['fg_color'], font=(style_out['font_family'], style_out['font_size']), anchor='e', justify='right')
87
+ label.grid(column=label_column, row=0, sticky=tk.W, padx=(5, 2), pady=5) # Place the label in the first row
83
88
 
89
+ # Create and configure the input widget based on var_type
84
90
  if var_type == 'entry':
85
91
  var = tk.StringVar(value=default_value)
86
- entry = spacrEntry(frame, textvariable=var, outline=False)
87
- entry.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
88
- return (label, entry, var) # Return both the label and the entry, and the variable
92
+ entry = spacrEntry(custom_frame, textvariable=var, outline=False, width=size_dict['settings_width'])
93
+ entry.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the entry in the second row
94
+ return (label, entry, var, custom_frame) # Return both the label and the entry, and the variable
89
95
  elif var_type == 'check':
90
96
  var = tk.BooleanVar(value=default_value) # Set default value (True/False)
91
- check = spacrCheck(frame, text="", variable=var)
92
- check.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
93
- return (label, check, var) # Return both the label and the checkbutton, and the variable
97
+ check = spacrCheck(custom_frame, text="", variable=var)
98
+ check.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the checkbutton in the second row
99
+ return (label, check, var, custom_frame) # Return both the label and the checkbutton, and the variable
94
100
  elif var_type == 'combo':
95
101
  var = tk.StringVar(value=default_value) # Set default value
96
- combo = spacrCombo(frame, textvariable=var, values=options) # Apply TCombobox style
97
- combo.grid(column=widget_column, row=row, sticky=tk.W, padx=(2, 5), pady=5) # Align widget to the left
102
+ combo = spacrCombo(custom_frame, textvariable=var, values=options, width=size_dict['settings_width']) # Apply TCombobox style
103
+ combo.grid(column=widget_column, row=1, sticky=tk.W, padx=(2, 5), pady=5) # Place the combobox in the second row
98
104
  if default_value:
99
105
  combo.set(default_value)
100
- return (label, combo, var) # Return both the label and the combobox, and the variable
106
+ return (label, combo, var, custom_frame) # Return both the label and the combobox, and the variable
101
107
  else:
102
108
  var = None # Placeholder in case of an undefined var_type
103
- return (label, None, var)
109
+ return (label, None, var, custom_frame)
104
110
 
105
111
  def process_stdout_stderr(q):
106
112
  """
@@ -116,10 +122,9 @@ class WriteToQueue(io.TextIOBase):
116
122
  """
117
123
  def __init__(self, q):
118
124
  self.q = q
119
-
120
125
  def write(self, msg):
121
- self.q.put(msg)
122
-
126
+ if msg.strip(): # Avoid empty messages
127
+ self.q.put(msg)
123
128
  def flush(self):
124
129
  pass
125
130
 
@@ -309,23 +314,6 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
309
314
  # Call load_images after setting up the root window
310
315
  app.load_images()
311
316
 
312
- def set_element_size(widget):
313
- screen_width = widget.winfo_screenwidth()
314
- screen_height = widget.winfo_screenheight()
315
- btn_size = screen_width // 40
316
- bar_size = screen_width // 50
317
- settings_width = screen_width // 5
318
- panel_height = screen_height // 12
319
- panel_width = settings_width
320
- size_dict = {
321
- 'btn_size': btn_size,
322
- 'bar_size': bar_size,
323
- 'settings_width': settings_width,
324
- 'panel_width': panel_width,
325
- 'panel_height': panel_height
326
- }
327
- return size_dict
328
-
329
317
  def convert_settings_dict_for_gui(settings):
330
318
  from torchvision import models as torch_models
331
319
  torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
@@ -383,6 +371,7 @@ def convert_settings_dict_for_gui(settings):
383
371
  variables[key] = ('entry', None, str(value))
384
372
  return variables
385
373
 
374
+
386
375
  def spacrFigShow(fig_queue=None):
387
376
  """
388
377
  Replacement for plt.show() that queues figures instead of displaying them.
@@ -464,7 +453,7 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
464
453
  imports = 1
465
454
  elif settings_type == 'map_barcodes':
466
455
  function = map_barcodes_folder
467
- imports = 2
456
+ imports = 1
468
457
  elif settings_type == 'regression':
469
458
  function = perform_regression
470
459
  imports = 2
@@ -481,6 +470,7 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
481
470
  finally:
482
471
  stop_requested.value = 1
483
472
 
473
+
484
474
  def hide_all_settings(vars_dict, categories):
485
475
  """
486
476
  Function to initially hide all settings in the GUI.
@@ -495,26 +485,27 @@ def hide_all_settings(vars_dict, categories):
495
485
 
496
486
  for category, settings in categories.items():
497
487
  if any(setting in vars_dict for setting in settings):
498
- vars_dict[category] = (None, None, tk.IntVar(value=0))
488
+ vars_dict[category] = (None, None, tk.IntVar(value=0), None)
499
489
 
500
490
  # Initially hide all settings
501
491
  for setting in settings:
502
492
  if setting in vars_dict:
503
- label, widget, _ = vars_dict[setting]
493
+ label, widget, _, frame = vars_dict[setting]
504
494
  label.grid_remove()
505
495
  widget.grid_remove()
496
+ frame.grid_remove()
506
497
  return vars_dict
507
498
 
508
499
  def setup_frame(parent_frame):
509
- from .gui_elements import set_dark_style, set_default_font
500
+ from .gui_elements import set_dark_style, set_element_size
510
501
 
511
502
  style = ttk.Style(parent_frame)
512
- size_dict = set_element_size(parent_frame)
503
+ size_dict = set_element_size()
513
504
  style_out = set_dark_style(style)
514
505
 
515
506
  settings_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['settings_width'], bg=style_out['bg_color'])
516
- vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, bg=style_out['bg_color'])
517
- horizontal_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], bg=style_out['bg_color'])
507
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['panel_width'], bg=style_out['bg_color'])
508
+ horizontal_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], width=size_dict['panel_width'], bg=style_out['bg_color'])
518
509
 
519
510
  parent_frame.grid_rowconfigure(0, weight=1)
520
511
  parent_frame.grid_rowconfigure(1, weight=0)
@@ -523,22 +514,18 @@ def setup_frame(parent_frame):
523
514
 
524
515
  settings_container.grid(row=0, column=0, rowspan=2, sticky="nsew")
525
516
  vertical_container.grid(row=0, column=1, sticky="nsew")
526
- horizontal_container.grid(row=1, column=1, sticky="nsew")
517
+ horizontal_container.grid(row=1, column=1, sticky="ew")
527
518
 
528
- # Lock the width of the horizontal_container
529
- horizontal_container.update_idletasks() # Ensure geometry manager calculates size
530
- fixed_width = horizontal_container.winfo_width()
531
- parent_frame.grid_columnconfigure(1, weight=0)
532
- horizontal_container.config(width=fixed_width)
519
+ # Ensure settings_container maintains its width
520
+ settings_container.grid_propagate(False)
521
+ settings_container.update_idletasks()
533
522
 
534
- tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).pack(fill=tk.BOTH, expand=True)
535
- tk.Label(vertical_container, text="Vertical Container", bg=style_out['bg_color']).pack(fill=tk.BOTH, expand=True)
523
+ tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).grid(row=0, column=0, sticky="ew")
536
524
 
537
525
  set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container])
538
526
 
539
- size = style_out['font_size'] - 2
540
-
541
- set_default_font(parent_frame, font_name=style_out['font_family'], size=size)
527
+ #size = style_out['font_size'] - 2
528
+ #set_default_font(parent_frame, font_name=style_out['font_family'], size=size)
542
529
 
543
530
  return parent_frame, vertical_container, horizontal_container, settings_container
544
531
 
@@ -622,3 +609,7 @@ def download_dataset(q, repo_id, subfolder, local_dir=None, retries=5, delay=5):
622
609
  time.sleep(delay)
623
610
 
624
611
  raise Exception("Failed to download files after multiple attempts.")
612
+
613
+ def ensure_after_tasks(frame):
614
+ if not hasattr(frame, 'after_tasks'):
615
+ frame.after_tasks = []
spacr/io.py CHANGED
@@ -597,7 +597,6 @@ def _rename_and_organize_image_files(src, regex, batch_size=100, pick_slice=Fals
597
597
  for idx in range(0, len(all_filenames), batch_size):
598
598
  start = time.time()
599
599
  batch_filenames = all_filenames[idx:idx+batch_size]
600
- files_processed = 0
601
600
  for filename in batch_filenames:
602
601
  images_by_key = _extract_filename_metadata(batch_filenames, src, images_by_key, regular_expression, metadata_type, pick_slice, skip_mode)
603
602
 
@@ -974,7 +973,7 @@ def _concatenate_channel(src, channels, randomize=True, timelapse=False, batch_s
974
973
  time_ls.append(duration)
975
974
  files_processed = i+1
976
975
  files_to_process = time_stack_path_lists
977
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
976
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type="Concatinating")
978
977
  stack = np.stack(stack_region)
979
978
  save_loc = os.path.join(channel_stack_loc, f'{name}.npz')
980
979
  np.savez(save_loc, data=stack, filenames=filenames_region)
@@ -1005,7 +1004,7 @@ def _concatenate_channel(src, channels, randomize=True, timelapse=False, batch_s
1005
1004
  time_ls.append(duration)
1006
1005
  files_processed = i+1
1007
1006
  files_to_process = nr_files
1008
- print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=None, operation_type="Concatinating")
1007
+ print_progress(files_processed, files_to_process, n_jobs=1, time_ls=time_ls, batch_size=batch_size, operation_type="Concatinating")
1009
1008
  if (i+1) % batch_size == 0 or i+1 == nr_files:
1010
1009
  unique_shapes = {arr.shape[:-1] for arr in stack_ls}
1011
1010
  if len(unique_shapes) > 1: