spacr 0.1.85__tar.gz → 0.2.0__tar.gz

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 (77) hide show
  1. {spacr-0.1.85/spacr.egg-info → spacr-0.2.0}/PKG-INFO +1 -1
  2. {spacr-0.1.85 → spacr-0.2.0}/setup.py +2 -2
  3. {spacr-0.1.85 → spacr-0.2.0}/spacr/__init__.py +6 -2
  4. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_annotate.py +6 -5
  5. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_make_masks.py +8 -15
  6. {spacr-0.1.85 → spacr-0.2.0}/spacr/core.py +1 -1
  7. spacr-0.2.0/spacr/gui.py +157 -0
  8. {spacr-0.1.85 → spacr-0.2.0}/spacr/gui_core.py +43 -16
  9. {spacr-0.1.85 → spacr-0.2.0}/spacr/gui_elements.py +109 -20
  10. {spacr-0.1.85 → spacr-0.2.0}/spacr/gui_utils.py +3 -3
  11. spacr-0.2.0/spacr/resources/icons/abort.png +0 -0
  12. spacr-0.2.0/spacr/resources/icons/annotate.png +0 -0
  13. spacr-0.2.0/spacr/resources/icons/cellpose_masks.png +0 -0
  14. spacr-0.2.0/spacr/resources/icons/classify.png +0 -0
  15. spacr-0.2.0/spacr/resources/icons/default.png +0 -0
  16. spacr-0.2.0/spacr/resources/icons/download.png +0 -0
  17. spacr-0.2.0/spacr/resources/icons/logo_spacr.png +0 -0
  18. spacr-0.2.0/spacr/resources/icons/make_masks.png +0 -0
  19. spacr-0.2.0/spacr/resources/icons/map_barcodes.png +0 -0
  20. spacr-0.2.0/spacr/resources/icons/mask.png +0 -0
  21. spacr-0.2.0/spacr/resources/icons/measure.png +0 -0
  22. spacr-0.2.0/spacr/resources/icons/regression.png +0 -0
  23. spacr-0.2.0/spacr/resources/icons/run.png +0 -0
  24. spacr-0.2.0/spacr/resources/icons/sequencing.png +0 -0
  25. spacr-0.2.0/spacr/resources/icons/settings.png +0 -0
  26. spacr-0.2.0/spacr/resources/icons/train_cellpose.png +0 -0
  27. spacr-0.2.0/spacr/resources/icons/umap.png +0 -0
  28. {spacr-0.1.85 → spacr-0.2.0}/spacr/settings.py +12 -12
  29. {spacr-0.1.85 → spacr-0.2.0/spacr.egg-info}/PKG-INFO +1 -1
  30. {spacr-0.1.85 → spacr-0.2.0}/spacr.egg-info/SOURCES.txt +20 -3
  31. spacr-0.1.85/spacr/gui.py +0 -195
  32. {spacr-0.1.85 → spacr-0.2.0}/LICENSE +0 -0
  33. {spacr-0.1.85 → spacr-0.2.0}/MANIFEST.in +0 -0
  34. {spacr-0.1.85 → spacr-0.2.0}/README.rst +0 -0
  35. {spacr-0.1.85 → spacr-0.2.0}/setup.cfg +0 -0
  36. {spacr-0.1.85 → spacr-0.2.0}/spacr/__main__.py +0 -0
  37. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_classify.py +0 -0
  38. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_mask.py +0 -0
  39. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_measure.py +0 -0
  40. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_sequencing.py +0 -0
  41. {spacr-0.1.85 → spacr-0.2.0}/spacr/app_umap.py +0 -0
  42. {spacr-0.1.85 → spacr-0.2.0}/spacr/chris.py +0 -0
  43. {spacr-0.1.85 → spacr-0.2.0}/spacr/deep_spacr.py +0 -0
  44. {spacr-0.1.85 → spacr-0.2.0}/spacr/graph_learning.py +0 -0
  45. {spacr-0.1.85 → spacr-0.2.0}/spacr/io.py +0 -0
  46. {spacr-0.1.85 → spacr-0.2.0}/spacr/logger.py +0 -0
  47. {spacr-0.1.85 → spacr-0.2.0}/spacr/measure.py +0 -0
  48. {spacr-0.1.85 → spacr-0.2.0}/spacr/plot.py +0 -0
  49. {spacr-0.1.85/spacr → spacr-0.2.0/spacr/resources}/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  50. {spacr-0.1.85/spacr → spacr-0.2.0/spacr/resources}/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
  51. {spacr-0.1.85/spacr → spacr-0.2.0/spacr/resources}/models/cp/toxo_pv_lumen.CP_model +0 -0
  52. {spacr-0.1.85 → spacr-0.2.0}/spacr/sequencing.py +0 -0
  53. {spacr-0.1.85 → spacr-0.2.0}/spacr/sim.py +0 -0
  54. {spacr-0.1.85 → spacr-0.2.0}/spacr/sim_app.py +0 -0
  55. {spacr-0.1.85 → spacr-0.2.0}/spacr/timelapse.py +0 -0
  56. {spacr-0.1.85 → spacr-0.2.0}/spacr/utils.py +0 -0
  57. {spacr-0.1.85 → spacr-0.2.0}/spacr/version.py +0 -0
  58. {spacr-0.1.85 → spacr-0.2.0}/spacr.egg-info/dependency_links.txt +0 -0
  59. {spacr-0.1.85 → spacr-0.2.0}/spacr.egg-info/entry_points.txt +0 -0
  60. {spacr-0.1.85 → spacr-0.2.0}/spacr.egg-info/requires.txt +0 -0
  61. {spacr-0.1.85 → spacr-0.2.0}/spacr.egg-info/top_level.txt +0 -0
  62. {spacr-0.1.85 → spacr-0.2.0}/tests/test_annotate_app.py +0 -0
  63. {spacr-0.1.85 → spacr-0.2.0}/tests/test_core.py +0 -0
  64. {spacr-0.1.85 → spacr-0.2.0}/tests/test_gui_classify_app.py +0 -0
  65. {spacr-0.1.85 → spacr-0.2.0}/tests/test_gui_mask_app.py +0 -0
  66. {spacr-0.1.85 → spacr-0.2.0}/tests/test_gui_measure_app.py +0 -0
  67. {spacr-0.1.85 → spacr-0.2.0}/tests/test_gui_sim_app.py +0 -0
  68. {spacr-0.1.85 → spacr-0.2.0}/tests/test_gui_utils.py +0 -0
  69. {spacr-0.1.85 → spacr-0.2.0}/tests/test_io.py +0 -0
  70. {spacr-0.1.85 → spacr-0.2.0}/tests/test_mask_app.py +0 -0
  71. {spacr-0.1.85 → spacr-0.2.0}/tests/test_measure.py +0 -0
  72. {spacr-0.1.85 → spacr-0.2.0}/tests/test_plot.py +0 -0
  73. {spacr-0.1.85 → spacr-0.2.0}/tests/test_sim.py +0 -0
  74. {spacr-0.1.85 → spacr-0.2.0}/tests/test_timelapse.py +0 -0
  75. {spacr-0.1.85 → spacr-0.2.0}/tests/test_train.py +0 -0
  76. {spacr-0.1.85 → spacr-0.2.0}/tests/test_umap.py +0 -0
  77. {spacr-0.1.85 → spacr-0.2.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.85
3
+ Version: 0.2.0
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
@@ -50,7 +50,7 @@ dependencies = [
50
50
 
51
51
  setup(
52
52
  name="spacr",
53
- version="0.1.85",
53
+ version="0.2.0",
54
54
  author="Einar Birnir Olafsson",
55
55
  author_email="olafsson@med.umich.com",
56
56
  description="Spatial phenotype analysis of crisp screens (SpaCr)",
@@ -58,7 +58,7 @@ setup(
58
58
  url="https://github.com/EinarOlafsson/spacr",
59
59
  packages=find_packages(exclude=["tests.*", "tests"]),
60
60
  include_package_data=True,
61
- package_data={'spacr': ['models/cp/*'],},
61
+ package_data={'spacr': ['resources/models/cp/*', 'resources/icons/*'],},
62
62
  install_requires=dependencies,
63
63
  entry_points={
64
64
  'console_scripts': [
@@ -16,8 +16,8 @@ from . import app_annotate
16
16
  from . import gui_utils
17
17
  from . import gui_elements
18
18
  from . import gui_core
19
- from . import gui_run
20
- from . import gui_wrappers
19
+ from . import gui
20
+ from . import gui
21
21
  from . import app_make_masks
22
22
  from . import app_mask
23
23
  from . import app_measure
@@ -40,6 +40,10 @@ __all__ = [
40
40
  "deep_spacr",
41
41
  "app_annotate",
42
42
  "gui_utils",
43
+ "gui_elements",
44
+ "gui_core",
45
+ "gui",
46
+ "gui",
43
47
  "app_make_masks",
44
48
  "app_mask",
45
49
  "app_measure",
@@ -1,15 +1,16 @@
1
1
  import tkinter as tk
2
+ from tkinter import ttk
2
3
  from .gui import MainApp
4
+ from .gui_elements import set_dark_style
3
5
 
4
6
  def initiate_annotation_app(parent_frame):
5
7
  from .gui_utils import generate_annotate_fields, annotate_app
6
8
  # Set up the settings window
7
9
  settings_window = tk.Toplevel(parent_frame)
8
10
  settings_window.title("Annotation Settings")
9
- settings_window.configure(bg='black') # Set the background color to black
10
-
11
- # Use the existing function to create the settings UI
12
- settings_frame = tk.Frame(settings_window, bg='black') # Set the background color to black
11
+ style_out = set_dark_style(ttk.Style())
12
+ settings_window.configure(bg=style_out['bg_color'])
13
+ settings_frame = tk.Frame(settings_window, bg=style_out['bg_color'])
13
14
  settings_frame.pack(fill=tk.BOTH, expand=True)
14
15
  vars_dict = generate_annotate_fields(settings_frame)
15
16
 
@@ -41,7 +42,7 @@ def initiate_annotation_app(parent_frame):
41
42
  settings_window.destroy()
42
43
  annotate_app(parent_frame, settings)
43
44
 
44
- start_button = tk.Button(settings_window, text="Start Annotation", command=start_annotation_app, bg='black', fg='white')
45
+ start_button = tk.Button(settings_window, text="Start Annotation", command=start_annotation_app, bg=style_out['bg_color'], fg=style_out['bg_color'])
45
46
  start_button.pack(pady=10)
46
47
 
47
48
  def start_annotate_app():
@@ -3,26 +3,22 @@ from tkinter import ttk
3
3
  from .gui import MainApp
4
4
 
5
5
  def initiate_make_mask_app(parent_frame):
6
- from .gui_elements import modify_masks
7
- # Set up the settings window
6
+ from .gui_elements import ModifyMaskApp, set_dark_style
8
7
  settings_window = tk.Toplevel(parent_frame)
9
8
  settings_window.title("Make Masks Settings")
10
- settings_window.configure(bg='black') # Set the background color to black
11
-
12
- # Use the existing function to create the settings UI
13
- settings_frame = tk.Frame(settings_window, bg='black') # Set the background color to black
9
+ style_out = set_dark_style(ttk.Style())
10
+ settings_window.configure(bg=style_out['bg_color'])
11
+ settings_frame = tk.Frame(settings_window, bg=style_out['bg_color'])
14
12
  settings_frame.pack(fill=tk.BOTH, expand=True)
15
13
 
16
14
  vars_dict = {
17
15
  'folder_path': ttk.Entry(settings_frame),
18
16
  'scale_factor': ttk.Entry(settings_frame)
19
17
  }
20
-
21
- # Arrange input fields and labels
22
18
  row = 0
23
19
  for name, entry in vars_dict.items():
24
20
  ttk.Label(settings_frame, text=f"{name.replace('_', ' ').capitalize()}:",
25
- background="black", foreground="white").grid(row=row, column=0)
21
+ background=style_out['bg_color'], foreground=style_out['fg_color']).grid(row=row, column=0)
26
22
  entry.grid(row=row, column=1)
27
23
  row += 1
28
24
 
@@ -32,15 +28,12 @@ def initiate_make_mask_app(parent_frame):
32
28
  try:
33
29
  scale_factor = float(vars_dict['scale_factor'].get())
34
30
  except ValueError:
35
- scale_factor = None # Handle invalid input gracefully
36
-
37
- # Convert empty strings to None
31
+ scale_factor = None
38
32
  folder_path = folder_path if folder_path != '' else None
39
-
40
33
  settings_window.destroy()
41
- modify_masks(parent_frame, folder_path, scale_factor)
34
+ ModifyMaskApp(parent_frame, folder_path, scale_factor)
42
35
 
43
- run_button = tk.Button(settings_window, text="Start Make Masks", command=start_make_mask_app, bg='black', fg='white')
36
+ run_button = tk.Button(settings_window, text="Start Make Masks", command=start_make_mask_app, bg=style_out['bg_color'], fg=style_out['fg_color'])
44
37
  run_button.pack(pady=10)
45
38
 
46
39
  def start_make_mask_app():
@@ -1738,7 +1738,7 @@ def preprocess_generate_masks(src, settings={}):
1738
1738
  if os.path.exists(os.path.join(src,'measurements')):
1739
1739
  _pivot_counts_table(db_path=os.path.join(src,'measurements', 'measurements.db'))
1740
1740
 
1741
- #Concatinate stack with masks
1741
+ #Concatenate stack with masks
1742
1742
  _load_and_concatenate_arrays(src, settings['channels'], settings['cell_channel'], settings['nucleus_channel'], settings['pathogen_channel'])
1743
1743
 
1744
1744
  if settings['plot']:
@@ -0,0 +1,157 @@
1
+ import tkinter as tk
2
+ from tkinter import ttk
3
+ from PIL import Image, ImageTk, ImageDraw
4
+ import os
5
+ from multiprocessing import set_start_method
6
+ from .gui_elements import spacrButton, create_menu_bar, set_dark_style
7
+ from .gui_core import initiate_root
8
+
9
+ class MainApp(tk.Tk):
10
+ def __init__(self, default_app=None):
11
+ super().__init__()
12
+ width = self.winfo_screenwidth()
13
+ height = self.winfo_screenheight()
14
+ self.geometry(f"{width}x{height}")
15
+ self.title("SpaCr GUI Collection")
16
+ self.configure(bg='#333333') # Set window background to dark gray
17
+
18
+ # Initialize style and apply dark style to the main window
19
+ style = ttk.Style()
20
+ self.color_settings = set_dark_style(style, parent_frame=self)
21
+ self.main_buttons = {} # Initialize main_buttons dictionary here
22
+ self.additional_buttons = {} # Initialize additional_buttons dictionary here
23
+
24
+ self.main_gui_apps = {
25
+ "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
26
+ "Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
27
+ "Annotate": (lambda frame: initiate_root(frame, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
28
+ "Make Masks": (lambda frame: initiate_root(frame, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
29
+ "Classify": (lambda frame: initiate_root(frame, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
30
+ }
31
+
32
+ self.additional_gui_apps = {
33
+ "Sequencing": (lambda frame: initiate_root(frame, 'sequencing'), "Analyze sequencing data."),
34
+ "Umap": (lambda frame: initiate_root(frame, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
35
+ "Train Cellpose": (lambda frame: initiate_root(frame, 'train_cellpose'), "Train custom Cellpose models."),
36
+ "ML Analyze": (lambda frame: initiate_root(frame, 'ml_analyze'), "Machine learning analysis of data."),
37
+ "Cellpose Masks": (lambda frame: initiate_root(frame, 'cellpose_masks'), "Generate Cellpose masks."),
38
+ "Cellpose All": (lambda frame: initiate_root(frame, 'cellpose_all'), "Run Cellpose on all images."),
39
+ "Map Barcodes": (lambda frame: initiate_root(frame, 'map_barcodes'), "Map barcodes to data."),
40
+ "Regression": (lambda frame: initiate_root(frame, 'regression'), "Perform regression analysis."),
41
+ "Recruitment": (lambda frame: initiate_root(frame, 'recruitment'), "Analyze recruitment data.")
42
+ }
43
+
44
+ self.selected_app = tk.StringVar()
45
+ self.create_widgets()
46
+
47
+ if default_app in self.main_gui_apps:
48
+ self.load_app(default_app, self.main_gui_apps[default_app][0])
49
+ elif default_app in self.additional_gui_apps:
50
+ self.load_app(default_app, self.additional_gui_apps[default_app][0])
51
+
52
+ def create_widgets(self):
53
+ # Create the menu bar
54
+ create_menu_bar(self)
55
+
56
+ # Create a canvas to hold the selected app and other elements
57
+ self.canvas = tk.Canvas(self, highlightthickness=0)
58
+ self.canvas.grid(row=0, column=0, sticky="nsew")
59
+ self.grid_rowconfigure(0, weight=1)
60
+ self.grid_columnconfigure(0, weight=1)
61
+
62
+ # Create a frame inside the canvas to hold the main content
63
+ self.content_frame = tk.Frame(self.canvas)
64
+ self.content_frame.grid(row=0, column=0, sticky="nsew")
65
+
66
+ # Center the content frame within the canvas
67
+ self.canvas.create_window((self.winfo_screenwidth() // 2, self.winfo_screenheight() // 2), window=self.content_frame, anchor="center")
68
+
69
+ # Apply dark style to canvas and content_frame
70
+ set_dark_style(ttk.Style(), containers=[self.canvas, self.content_frame])
71
+
72
+ # Create startup screen with buttons for each main GUI app and drop-down for additional apps
73
+ self.create_startup_screen()
74
+
75
+ def create_startup_screen(self):
76
+ self.clear_frame(self.content_frame)
77
+
78
+ # Create frames for the grids
79
+ main_buttons_frame = tk.Frame(self.content_frame)
80
+ main_buttons_frame.pack(pady=10)
81
+ set_dark_style(ttk.Style(), containers=[main_buttons_frame])
82
+
83
+ additional_buttons_frame = tk.Frame(self.content_frame)
84
+ additional_buttons_frame.pack(pady=10)
85
+ set_dark_style(ttk.Style(), containers=[additional_buttons_frame])
86
+
87
+ # Create a frame for the description below the icon grids
88
+ description_frame = tk.Frame(self.content_frame, height=70) # Increased height to 70
89
+ description_frame.pack(fill=tk.X, pady=10)
90
+ description_frame.pack_propagate(False) # Prevent the frame from resizing based on its content
91
+ set_dark_style(ttk.Style(), containers=[description_frame])
92
+
93
+ # Use a Label widget to display descriptions
94
+ self.description_label = tk.Label(description_frame, text="", wraplength=800, justify="center", font=('Helvetica', 12), fg=self.color_settings['fg_color'], bg=self.color_settings['bg_color'])
95
+ self.description_label.pack(fill=tk.BOTH, pady=10)
96
+
97
+ # Load the logo image and place it in the main apps row
98
+ logo_button = spacrButton(main_buttons_frame, text="SpaCr", command=lambda: self.load_app("logo_spacr", initiate_root), icon_name="logo_spacr", size=100, show_text=False)
99
+ logo_button.grid(row=0, column=0, padx=5, pady=5)
100
+ self.main_buttons[logo_button] = "SpaCr: An advanced application suite for cellpose masks, measurements, annotations, and more."
101
+
102
+ # Create icon buttons for the main apps
103
+ for i, (app_name, app_data) in enumerate(self.main_gui_apps.items()):
104
+ app_func, app_desc = app_data
105
+ button = spacrButton(main_buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), icon_name=app_name.lower(), size=100, show_text=False)
106
+ button.grid(row=0, column=i + 1, padx=5, pady=5)
107
+ self.main_buttons[button] = app_desc
108
+
109
+ # Create icon buttons for the additional apps
110
+ for i, (app_name, app_data) in enumerate(self.additional_gui_apps.items()):
111
+ app_func, app_desc = app_data
112
+ button = spacrButton(additional_buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), icon_name=app_name.lower(), size=75, show_text=False)
113
+ button.grid(row=0, column=i, padx=5, pady=5) # Ensure all icons are in a single row
114
+ self.additional_buttons[button] = app_desc
115
+
116
+ # Update description initially
117
+ self.update_description()
118
+
119
+ def update_description(self):
120
+ # Check all buttons and update description if any has the active color
121
+ for button, desc in {**self.main_buttons, **self.additional_buttons}.items():
122
+ if button.canvas.itemcget(button.button_bg, "fill") == self.color_settings['active_color']:
123
+ self.show_description(desc)
124
+ return
125
+ self.clear_description()
126
+
127
+ def show_description(self, description):
128
+ if self.description_label.winfo_exists():
129
+ self.description_label.config(text=description)
130
+ self.description_label.update_idletasks() # Ensure the label updates immediately
131
+
132
+ def clear_description(self):
133
+ if self.description_label.winfo_exists():
134
+ self.description_label.config(text="")
135
+ self.description_label.update_idletasks() # Ensure the label updates immediately
136
+
137
+ def load_app(self, app_name, app_func):
138
+ # Clear the current content frame
139
+ self.clear_frame(self.canvas)
140
+
141
+ # Initialize the selected app
142
+ app_frame = tk.Frame(self.canvas)
143
+ app_frame.pack(fill=tk.BOTH, expand=True)
144
+ set_dark_style(ttk.Style(), containers=[app_frame])
145
+ app_func(app_frame)
146
+
147
+ def clear_frame(self, frame):
148
+ for widget in frame.winfo_children():
149
+ widget.destroy()
150
+
151
+ def gui_app():
152
+ app = MainApp()
153
+ app.mainloop()
154
+
155
+ if __name__ == "__main__":
156
+ set_start_method('spawn', force=True)
157
+ gui_app()
@@ -231,25 +231,43 @@ def import_settings(settings_type='mask'):
231
231
  vars_dict = hide_all_settings(vars_dict, categories=None)
232
232
 
233
233
  def convert_settings_dict_for_gui(settings):
234
+ from torchvision import models as torch_models
235
+ torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
236
+ chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
237
+ chans_v2 = [0, 1, 2, 3, None]
234
238
  variables = {}
235
239
  special_cases = {
236
240
  'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
237
241
  'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
238
- 'cell_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
239
- 'nucleus_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
240
- 'pathogen_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
241
- #'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
242
+ 'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
243
+ 'cell_mask_dim': ('combo', chans, None),
244
+ 'cell_chann_dim': ('combo', chans, None),
245
+ 'nucleus_mask_dim': ('combo', chans, None),
246
+ 'nucleus_chann_dim': ('combo', chans, None),
247
+ 'pathogen_mask_dim': ('combo', chans, None),
248
+ 'pathogen_chann_dim': ('combo', chans, None),
249
+ 'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
242
250
  'magnification': ('combo', [20, 40, 60], 20),
243
- 'nucleus_channel': ('combo', [0, 1, 2, 3, None], None),
244
- 'cell_channel': ('combo', [0, 1, 2, 3, None], None),
245
- 'pathogen_channel': ('combo', [0, 1, 2, 3, None], None),
251
+ 'nucleus_channel': ('combo', chans_v2, None),
252
+ 'cell_channel': ('combo', chans_v2, None),
253
+ 'channel_of_interest': ('combo', chans_v2, None),
254
+ 'pathogen_channel': ('combo', chans_v2, None),
246
255
  'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
256
+ 'train_mode': ('combo', ['erm', 'irm'], 'erm'),
257
+ 'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
258
+ 'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
259
+ 'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
260
+ 'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
247
261
  'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
248
- 'model_type': ('combo', ['resnet50', 'other_model'], 'resnet50'),
262
+ 'model_type': ('combo', torchvision_models, 'resnet50'),
249
263
  'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
250
264
  'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
251
265
  'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
252
266
  'normalize_by': ('combo', ['fov', 'png'], 'png'),
267
+ 'agg_type': ('combo', ['mean', 'median'], 'mean'),
268
+ 'grouping': ('combo', ['mean', 'median'], 'mean'),
269
+ 'min_max': ('combo', ['allq', 'all'], 'allq'),
270
+ 'transform': ('combo', ['log', 'sqrt', 'square', None], None)
253
271
  }
254
272
 
255
273
  for key, value in settings.items():
@@ -338,9 +356,10 @@ def setup_plot_section(vertical_container):
338
356
  plot_frame.add(canvas_widget, stretch="always")
339
357
  canvas.draw()
340
358
  canvas.figure = figure
341
- # Set the figure and axes background to black
342
- figure.patch.set_facecolor('black')
343
- plot.set_facecolor('black')
359
+ style_out = set_dark_style(ttk.Style())
360
+
361
+ figure.patch.set_facecolor(style_out['bg_color'])
362
+ plot.set_facecolor(style_out['bg_color'])
344
363
  containers = [plot_frame]
345
364
  widgets = [canvas_widget]
346
365
  style = ttk.Style(vertical_container)
@@ -488,25 +507,25 @@ def setup_button_section(horizontal_container, settings_type='mask', window_dime
488
507
  btn_row = 1
489
508
 
490
509
  if run:
491
- run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue, settings_type))
510
+ run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type))
492
511
  run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
493
512
  widgets.append(run_button)
494
513
  btn_row += 1
495
514
 
496
515
  if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
497
- abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
516
+ abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=initiate_abort)
498
517
  abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
499
518
  widgets.append(abort_button)
500
519
  btn_row += 1
501
520
 
502
521
  if download and settings_type in ['mask']:
503
- download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Download", command=download_hug_dataset)
522
+ download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset)
504
523
  download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
505
524
  widgets.append(download_dataset_button)
506
525
  btn_row += 1
507
526
 
508
527
  if import_btn:
509
- import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(settings_type))
528
+ import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type))
510
529
  import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
511
530
  widgets.append(import_button)
512
531
 
@@ -516,11 +535,19 @@ def setup_button_section(horizontal_container, settings_type='mask', window_dime
516
535
  description_frame = tk.Frame(horizontal_container)
517
536
  horizontal_container.add(description_frame, stretch="always", sticky="nsew")
518
537
  description_frame.grid_columnconfigure(0, weight=1)
538
+ description_frame.grid_rowconfigure(0, weight=1) # Add this line to make the row expandable
539
+
519
540
  description_label = tk.Label(description_frame, text="Module Description", anchor='nw', justify='left', wraplength=width - 50)
520
541
  description_label.grid(row=0, column=0, pady=50, padx=20, sticky='nsew')
521
542
  description_text = descriptions.get(settings_type, "No description available for this module.")
522
543
  description_label.config(text=description_text)
523
544
 
545
+ def update_wraplength(event):
546
+ new_width = event.width - 40 # Adjust as needed
547
+ description_label.config(wraplength=new_width)
548
+
549
+ description_label.bind('<Configure>', update_wraplength)
550
+
524
551
  containers = [button_frame, description_frame]
525
552
  widgets.extend([description_label])
526
553
 
@@ -588,7 +615,7 @@ def toggle_settings(button_scrollable_frame):
588
615
  category_var = tk.StringVar()
589
616
  non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
590
617
  category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
591
- category_dropdown.grid(row=1, column=3, sticky="ew", pady=2, padx=2)
618
+ category_dropdown.grid(row=5, column=0, sticky="ew", pady=2, padx=2)
592
619
  vars_dict = hide_all_settings(vars_dict, categories)
593
620
 
594
621
  def process_fig_queue():
@@ -15,17 +15,25 @@ from skimage.draw import polygon, line
15
15
  from skimage.transform import resize
16
16
  from scipy.ndimage import binary_fill_holes, label
17
17
  from tkinter import ttk, scrolledtext
18
+ import platform
18
19
 
19
- def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font_family="Arial", font_size=12, bg_color='black', fg_color='white', active_color='teal', inactive_color='gray'):
20
+ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font_family="Arial", font_size=12, bg_color='black', fg_color='white', active_color='blue', inactive_color='dark_gray'):
21
+
22
+ if platform.system() == 'Darwin':
23
+ bg_color = '#313131'
24
+ else:
25
+ bg_color = '#000000'
20
26
 
21
27
  if active_color == 'teal':
22
28
  active_color = '#008080'
23
- if inactive_color == 'gray':
24
- inactive_color = '#555555'
29
+ if inactive_color == 'dark_gray':
30
+ inactive_color = '#050505'
25
31
  if bg_color == 'black':
26
32
  bg_color = '#000000'
27
33
  if fg_color == 'white':
28
34
  fg_color = '#ffffff'
35
+ if active_color == 'blue':
36
+ active_color = '#007BFF'
29
37
 
30
38
  font_style = tkFont.Font(family=font_family, size=font_size)
31
39
  style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground=bg_color, foreground=fg_color, font=font_style)
@@ -76,7 +84,7 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
76
84
  menu = widget['menu']
77
85
  menu.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
78
86
 
79
- return {'font_family':font_family, 'font_size':font_size, 'bg_color':bg_color, 'fg_color':fg_color, 'active_color':active_color, 'inactive_color':inactive_color}
87
+ return {'font_family': font_family, 'font_size': font_size, 'bg_color': bg_color, 'fg_color': fg_color, 'active_color': active_color, 'inactive_color': inactive_color}
80
88
 
81
89
  def set_default_font(root, font_name="Arial", size=12):
82
90
  default_font = (font_name, size)
@@ -116,7 +124,6 @@ class spacrCheckbutton(ttk.Checkbutton):
116
124
  style = ttk.Style()
117
125
  _ = set_dark_style(style, widgets=[self])
118
126
 
119
-
120
127
  class spacrFrame(ttk.Frame):
121
128
  def __init__(self, container, width=None, *args, bg='black', **kwargs):
122
129
  super().__init__(container, *args, **kwargs)
@@ -189,25 +196,40 @@ class spacrLabel(tk.Frame):
189
196
  self.canvas.itemconfig(self.label_text, text=text)
190
197
 
191
198
  class spacrButton(tk.Frame):
192
- def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
199
+ def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=True, *args, **kwargs):
193
200
  super().__init__(parent, *args, **kwargs)
194
201
 
195
- self.text = text
202
+ self.text = text.capitalize() # Capitalize only the first letter of the text
196
203
  self.command = command
197
- button_height = 50
198
- button_width = 140
204
+ self.icon_name = icon_name if icon_name else text.lower()
205
+ self.size = size
206
+ self.show_text = show_text
207
+ self.outline = outline
208
+
199
209
  style_out = set_dark_style(ttk.Style())
200
210
 
211
+ if self.show_text:
212
+ self.button_width = int(size * 3)
213
+ else:
214
+ self.button_width = self.size # Make the button width equal to the size if show_text is False
215
+
201
216
  # Create the canvas first
202
- self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg=style_out['bg_color'])
217
+ self.canvas = tk.Canvas(self, width=self.button_width + 4, height=self.size + 4, highlightthickness=0, bg=style_out['bg_color'])
203
218
  self.canvas.grid(row=0, column=0)
204
219
 
205
220
  # Apply dark style and get color settings
206
221
  color_settings = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
207
222
 
208
- self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill=color_settings['bg_color'], outline=color_settings['fg_color'])
209
- self.font_style = font if font else tkFont.Font(family=color_settings['font_family'], size=color_settings['font_size'], weight=tkFont.NORMAL)
210
- self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill=color_settings['fg_color'], font=self.font_style)
223
+ if self.outline:
224
+ self.button_bg = self.create_rounded_rectangle(2, 2, self.button_width + 2, self.size + 2, radius=20, fill=color_settings['bg_color'], outline=color_settings['fg_color'])
225
+ else:
226
+ self.button_bg = self.create_rounded_rectangle(2, 2, self.button_width + 2, self.size + 2, radius=20, fill=color_settings['bg_color'], outline=color_settings['bg_color'])
227
+
228
+ self.load_icon()
229
+ self.font_style = font if font else ("Arial", 12) # Default font if not provided
230
+
231
+ if self.show_text:
232
+ self.button_text = self.canvas.create_text(self.size + 10, self.size // 2 + 2, text=self.text, fill=color_settings['fg_color'], font=self.font_style, anchor="w") # Align text to the left of the specified point
211
233
 
212
234
  self.bind("<Enter>", self.on_enter)
213
235
  self.bind("<Leave>", self.on_leave)
@@ -219,12 +241,42 @@ class spacrButton(tk.Frame):
219
241
  self.bg_color = color_settings['bg_color']
220
242
  self.active_color = color_settings['active_color']
221
243
  self.fg_color = color_settings['fg_color']
244
+ self.is_zoomed_in = False # Track zoom state for smooth transitions
245
+
246
+ def load_icon(self):
247
+ icon_path = self.get_icon_path(self.icon_name)
248
+ try:
249
+ icon_image = Image.open(icon_path)
250
+ except (FileNotFoundError, Image.UnidentifiedImageError):
251
+ try:
252
+ icon_path = icon_path.replace(' ', '_')
253
+ icon_image = Image.open(icon_path)
254
+ except (FileNotFoundError, Image.UnidentifiedImageError):
255
+ icon_image = Image.open(self.get_icon_path("default"))
256
+ print(f'Icon not found: {icon_path}. Using default icon instead.')
257
+
258
+ initial_size = int(self.size * 0.9) # Make the initial size slightly smaller
259
+ self.original_icon_image = icon_image.resize((initial_size, initial_size), Image.Resampling.LANCZOS)
260
+ self.icon_photo = ImageTk.PhotoImage(self.original_icon_image)
261
+
262
+ self.button_icon = self.canvas.create_image(self.size // 2 + 2, self.size // 2 + 2, image=self.icon_photo)
263
+ self.canvas.image = self.icon_photo # Keep a reference to avoid garbage collection
264
+
265
+ def get_icon_path(self, icon_name):
266
+ icon_dir = os.path.join(os.path.dirname(__file__), 'resources', 'icons')
267
+ return os.path.join(icon_dir, f"{icon_name}.png")
222
268
 
223
269
  def on_enter(self, event=None):
224
270
  self.canvas.itemconfig(self.button_bg, fill=self.active_color)
271
+ self.update_description(event)
272
+ if not self.is_zoomed_in:
273
+ self.animate_zoom(1.1) # Zoom in the icon by 10%
225
274
 
226
275
  def on_leave(self, event=None):
227
276
  self.canvas.itemconfig(self.button_bg, fill=self.bg_color)
277
+ self.clear_description(event)
278
+ if self.is_zoomed_in:
279
+ self.animate_zoom(1.0) # Reset the icon size
228
280
 
229
281
  def on_click(self, event=None):
230
282
  if self.command:
@@ -232,18 +284,15 @@ class spacrButton(tk.Frame):
232
284
 
233
285
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
234
286
  points = [
235
- x1 + radius, y1,
236
287
  x1 + radius, y1,
237
288
  x2 - radius, y1,
238
289
  x2 - radius, y1,
239
290
  x2, y1,
240
291
  x2, y1 + radius,
241
- x2, y1 + radius,
242
292
  x2, y2 - radius,
243
293
  x2, y2 - radius,
244
294
  x2, y2,
245
295
  x2 - radius, y2,
246
- x2 - radius, y2,
247
296
  x1 + radius, y2,
248
297
  x1 + radius, y2,
249
298
  x1, y2,
@@ -254,6 +303,46 @@ class spacrButton(tk.Frame):
254
303
  x1, y1
255
304
  ]
256
305
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
306
+
307
+ def update_description(self, event):
308
+ parent = self.master
309
+ while parent:
310
+ if hasattr(parent, 'show_description'):
311
+ parent.show_description(parent.main_buttons.get(self, "No description available."))
312
+ return
313
+ parent = parent.master
314
+
315
+ def clear_description(self, event):
316
+ parent = self.master
317
+ while parent:
318
+ if hasattr(parent, 'clear_description'):
319
+ parent.clear_description()
320
+ return
321
+ parent = parent.master
322
+
323
+ def animate_zoom(self, target_scale, steps=10, delay=10):
324
+ current_scale = 1.1 if self.is_zoomed_in else 1.0
325
+ step_scale = (target_scale - current_scale) / steps
326
+ self._animate_step(current_scale, step_scale, steps, delay)
327
+
328
+ def _animate_step(self, current_scale, step_scale, steps, delay):
329
+ if steps > 0:
330
+ new_scale = current_scale + step_scale
331
+ self.zoom_icon(new_scale)
332
+ self.after(delay, self._animate_step, new_scale, step_scale, steps - 1, delay)
333
+ else:
334
+ self.is_zoomed_in = not self.is_zoomed_in
335
+
336
+ def zoom_icon(self, scale_factor):
337
+ # Resize the original icon image
338
+ new_size = int(self.size * 0.9 * scale_factor)
339
+ resized_icon = self.original_icon_image.resize((new_size, new_size), Image.Resampling.LANCZOS)
340
+ self.icon_photo = ImageTk.PhotoImage(resized_icon)
341
+
342
+ # Update the icon on the canvas
343
+ self.canvas.itemconfig(self.button_icon, image=self.icon_photo)
344
+ self.canvas.image = self.icon_photo # Keep a reference to avoid garbage collection
345
+
257
346
 
258
347
  class spacrSwitch(ttk.Frame):
259
348
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
@@ -363,7 +452,7 @@ class spacrToolTip:
363
452
  self.tooltip_window.destroy()
364
453
  self.tooltip_window = None
365
454
 
366
- class modify_masks:
455
+ class ModifyMaskApp:
367
456
  def __init__(self, root, folder_path, scale_factor):
368
457
  self.root = root
369
458
  self.folder_path = folder_path
@@ -1207,7 +1296,7 @@ class modify_masks:
1207
1296
  self.mask[labeled_mask == i] = 0 # Remove small objects
1208
1297
  self.update_display()
1209
1298
 
1210
- class ImageApp:
1299
+ class AnnotateApp:
1211
1300
  def __init__(self, root, db_path, src, image_type=None, channels=None, grid_rows=None, grid_cols=None, image_size=(200, 200), annotation_column='annotate', normalize=False, percentiles=(1,99), measurement=None, threshold=None):
1212
1301
  self.root = root
1213
1302
  self.db_path = db_path
@@ -1471,7 +1560,7 @@ class ImageApp:
1471
1560
  break
1472
1561
 
1473
1562
  if not self.update_queue.empty():
1474
- ImageApp.update_html("Do not exit, Updating database...")
1563
+ AnnotateApp.update_html("Do not exit, Updating database...")
1475
1564
  self.status_label.config(text='Do not exit, Updating database...')
1476
1565
 
1477
1566
  pending_updates = self.update_queue.get()
@@ -1482,7 +1571,7 @@ class ImageApp:
1482
1571
  c.execute(f'UPDATE png_list SET {self.annotation_column} = ? WHERE png_path = ?', (new_annotation, path))
1483
1572
  conn.commit()
1484
1573
 
1485
- ImageApp.update_html('')
1574
+ AnnotateApp.update_html('')
1486
1575
  self.status_label.config(text='')
1487
1576
  self.root.update()
1488
1577
  time.sleep(0.1)
@@ -3,7 +3,7 @@ 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, ImageApp
6
+ from .gui_elements import spacrLabel, spacrCheckbutton, AnnotateApp
7
7
 
8
8
  try:
9
9
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -202,7 +202,7 @@ def annotate(settings):
202
202
 
203
203
  root = tk.Tk()
204
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'])
205
+ app = AnnotateApp(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
206
  next_button = tk.Button(root, text="Next", command=app.next_page)
207
207
  next_button.grid(row=app.grid_rows, column=app.grid_cols - 1)
208
208
  back_button = tk.Button(root, text="Back", command=app.previous_page)
@@ -302,7 +302,7 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
302
302
  conn.commit()
303
303
  conn.close()
304
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'])
305
+ app = AnnotateApp(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
306
 
307
307
  # Set the canvas background to black
308
308
  root.configure(bg='black')
@@ -1114,29 +1114,29 @@ categories = {
1114
1114
  }
1115
1115
 
1116
1116
  descriptions = {
1117
- 'mask': "Generate Cellpose masks for Cells, Nuclei, and Pathogens. This module uses: preprocess_generate_masks from spacr.core.\n\nKey Features:\n- Automated Mask Generation: Automatically generate accurate masks for various cellular components using Cellpose, a robust deep learning model for cell segmentation.\n- Versatility: Capable of handling different types of biological samples, including cells, nuclei, and pathogens.\n- Integration: Directly integrates with other modules, providing the foundational masks required for subsequent analysis.",
1117
+ 'mask': "Generate Cellpose masks for Cells, Nuclei, and Pathogens. Function: preprocess_generate_masks from spacr.core.\n\nKey Features:\n- Automated Mask Generation: Automatically generate accurate masks for various cellular components using Cellpose, a robust deep learning model for cell segmentation.\n- Versatility: Capable of handling different types of biological samples, including cells, nuclei, and pathogens.\n- Integration: Directly integrates with other modules, providing the foundational masks required for subsequent analysis.",
1118
1118
 
1119
- 'measure': "Capture Measurements from Cells, Nuclei, Pathogens, and Cytoplasm objects. Generate single object PNG images for one or several objects. (Requires masks from the Mask module). This module uses: measure_crop from spacr.measure.\n\nKey Features:\n- Comprehensive Measurement Capture: Obtain detailed measurements for various cellular components, including area, perimeter, intensity, and more.\n- Image Generation: Create high-resolution PNG images of individual objects, facilitating further analysis and visualization.\n- Mask Dependency: Requires accurate masks generated by the Mask module to ensure precise measurements.",
1119
+ 'measure': "Capture Measurements from Cells, Nuclei, Pathogens, and Cytoplasm objects. Generate single object PNG images for one or several objects. (Requires masks from the Mask module). Function: measure_crop from spacr.measure.\n\nKey Features:\n- Comprehensive Measurement Capture: Obtain detailed measurements for various cellular components, including area, perimeter, intensity, and more.\n- Image Generation: Create high-resolution PNG images of individual objects, facilitating further analysis and visualization.\n- Mask Dependency: Requires accurate masks generated by the Mask module to ensure precise measurements.",
1120
1120
 
1121
- 'classify': "Train and Test any Torch Computer vision model. (Requires PNG images from the Measure module). This module uses: train_test_model from spacr.deep_spacr.\n\nKey Features:\n- Deep Learning Integration: Train and evaluate state-of-the-art Torch models for various classification tasks.\n- Flexible Training: Supports a wide range of Torch models, allowing customization based on specific research needs.\n- Data Requirement: Requires PNG images generated by the Measure module for training and testing.",
1121
+ 'classify': "Train and Test any Torch Computer vision model. (Requires PNG images from the Measure module). Function: train_test_model from spacr.deep_spacr.\n\nKey Features:\n- Deep Learning Integration: Train and evaluate state-of-the-art Torch models for various classification tasks.\n- Flexible Training: Supports a wide range of Torch models, allowing customization based on specific research needs.\n- Data Requirement: Requires PNG images generated by the Measure module for training and testing.",
1122
1122
 
1123
- 'sequencing': "Find Barcodes and gRNA sequences in FASTQ files. (Requires paired-end FASTQ files, R1 and R2). This module uses: analyze_reads from spacr.sequencing.\n\nKey Features:\n- Barcode and gRNA Identification: Efficiently detect and extract barcode and gRNA sequences from raw sequencing data.\n- Paired-End Support: Specifically designed to handle paired-end FASTQ files, ensuring accurate sequence alignment and analysis.\n- High Throughput: Capable of processing large sequencing datasets quickly and accurately.",
1123
+ 'sequencing': "Find Barcodes and gRNA sequences in FASTQ files. (Requires paired-end FASTQ files, R1 and R2). Function: analyze_reads from spacr.sequencing.\n\nKey Features:\n- Barcode and gRNA Identification: Efficiently detect and extract barcode and gRNA sequences from raw sequencing data.\n- Paired-End Support: Specifically designed to handle paired-end FASTQ files, ensuring accurate sequence alignment and analysis.\n- High Throughput: Capable of processing large sequencing datasets quickly and accurately.",
1124
1124
 
1125
- 'umap': "Generate UMAP or tSNE embeddings and represent points as single cell images. (Requires measurements.db and PNG images from the Measure module). This module uses: generate_image_umap from spacr.core.\n\nKey Features:\n- Dimensionality Reduction: Employ UMAP or tSNE algorithms to reduce high-dimensional data into two dimensions for visualization.\n- Single Cell Representation: Visualize embedding points as single cell images, providing an intuitive understanding of data clusters.\n- Data Integration: Requires measurements and images generated by the Measure module, ensuring comprehensive data representation.",
1125
+ 'umap': "Generate UMAP or tSNE embeddings and represent points as single cell images. (Requires measurements.db and PNG images from the Measure module). Function: generate_image_umap from spacr.core.\n\nKey Features:\n- Dimensionality Reduction: Employ UMAP or tSNE algorithms to reduce high-dimensional data into two dimensions for visualization.\n- Single Cell Representation: Visualize embedding points as single cell images, providing an intuitive understanding of data clusters.\n- Data Integration: Requires measurements and images generated by the Measure module, ensuring comprehensive data representation.",
1126
1126
 
1127
- 'train_cellpose': "Train custom Cellpose models for your specific dataset. This module uses: train_cellpose_model from spacr.core.\n\nKey Features:\n- Custom Model Training: Train Cellpose models on your dataset to improve segmentation accuracy.\n- Data Adaptation: Tailor the model to handle specific types of biological samples more effectively.\n- Advanced Training Options: Supports various training parameters and configurations for optimized performance.",
1127
+ 'train_cellpose': "Train custom Cellpose models for your specific dataset. Function: train_cellpose_model from spacr.core.\n\nKey Features:\n- Custom Model Training: Train Cellpose models on your dataset to improve segmentation accuracy.\n- Data Adaptation: Tailor the model to handle specific types of biological samples more effectively.\n- Advanced Training Options: Supports various training parameters and configurations for optimized performance.",
1128
1128
 
1129
- 'ml_analyze': "Perform machine learning analysis on your data. This module uses: ml_analysis_tools from spacr.ml.\n\nKey Features:\n- Comprehensive Analysis: Utilize a suite of machine learning tools for data analysis.\n- Customizable Workflows: Configure and run different ML algorithms based on your research requirements.\n- Integration: Works seamlessly with other modules to analyze data produced from various steps.",
1129
+ 'ml_analyze': "Perform machine learning analysis on your data. Function: ml_analysis_tools from spacr.ml.\n\nKey Features:\n- Comprehensive Analysis: Utilize a suite of machine learning tools for data analysis.\n- Customizable Workflows: Configure and run different ML algorithms based on your research requirements.\n- Integration: Works seamlessly with other modules to analyze data produced from various steps.",
1130
1130
 
1131
- 'cellpose_masks': "Generate masks using Cellpose for all images in your dataset. This module uses: generate_masks from spacr.cellpose.\n\nKey Features:\n- Batch Processing: Generate masks for large sets of images efficiently.\n- Robust Segmentation: Leverage Cellpose's capabilities for accurate segmentation across diverse samples.\n- Automation: Automate the mask generation process for streamlined workflows.",
1131
+ 'cellpose_masks': "Generate masks using Cellpose for all images in your dataset. Function: generate_masks from spacr.cellpose.\n\nKey Features:\n- Batch Processing: Generate masks for large sets of images efficiently.\n- Robust Segmentation: Leverage Cellpose's capabilities for accurate segmentation across diverse samples.\n- Automation: Automate the mask generation process for streamlined workflows.",
1132
1132
 
1133
- 'cellpose_all': "Run Cellpose on all images in your dataset and obtain masks and measurements. This module uses: cellpose_analysis from spacr.cellpose.\n\nKey Features:\n- End-to-End Analysis: Perform both segmentation and measurement extraction in a single step.\n- Efficiency: Process entire datasets with minimal manual intervention.\n- Comprehensive Output: Obtain detailed masks and corresponding measurements for further analysis.",
1133
+ 'cellpose_all': "Run Cellpose on all images in your dataset and obtain masks and measurements. Function: cellpose_analysis from spacr.cellpose.\n\nKey Features:\n- End-to-End Analysis: Perform both segmentation and measurement extraction in a single step.\n- Efficiency: Process entire datasets with minimal manual intervention.\n- Comprehensive Output: Obtain detailed masks and corresponding measurements for further analysis.",
1134
1134
 
1135
- 'map_barcodes': "Map barcodes to your data for identification and tracking. This module uses: barcode_mapping_tools from spacr.sequencing.\n\nKey Features:\n- Barcode Integration: Efficiently map and integrate barcode information into your dataset.\n- Tracking: Enable tracking and identification of samples using barcodes.\n- Compatibility: Works with sequencing data to ensure accurate mapping and analysis.",
1135
+ 'map_barcodes': "Map barcodes to your data for identification and tracking. Function: barcode_mapping_tools from spacr.sequencing.\n\nKey Features:\n- Barcode Integration: Efficiently map and integrate barcode information into your dataset.\n- Tracking: Enable tracking and identification of samples using barcodes.\n- Compatibility: Works with sequencing data to ensure accurate mapping and analysis.",
1136
1136
 
1137
- 'regression': "Perform regression analysis on your data. This module uses: regression_tools from spacr.analysis.\n\nKey Features:\n- Statistical Analysis: Conduct various types of regression analysis to identify relationships within your data.\n- Flexible Options: Supports multiple regression models and configurations.\n- Data Insight: Gain deeper insights into your dataset through advanced regression techniques.",
1137
+ 'regression': "Perform regression analysis on your data. Function: regression_tools from spacr.analysis.\n\nKey Features:\n- Statistical Analysis: Conduct various types of regression analysis to identify relationships within your data.\n- Flexible Options: Supports multiple regression models and configurations.\n- Data Insight: Gain deeper insights into your dataset through advanced regression techniques.",
1138
1138
 
1139
- 'recruitment': "Analyze recruitment data to understand sample recruitment dynamics. This module uses: recruitment_analysis_tools from spacr.analysis.\n\nKey Features:\n- Recruitment Analysis: Investigate and analyze the recruitment of samples over time or conditions.\n- Visualization: Generate visualizations to represent recruitment trends and patterns.\n- Integration: Utilize data from various sources for a comprehensive recruitment analysis."
1139
+ 'recruitment': "Analyze recruitment data to understand sample recruitment dynamics. Function: recruitment_analysis_tools from spacr.analysis.\n\nKey Features:\n- Recruitment Analysis: Investigate and analyze the recruitment of samples over time or conditions.\n- Visualization: Generate visualizations to represent recruitment trends and patterns.\n- Integration: Utilize data from various sources for a comprehensive recruitment analysis."
1140
1140
  }
1141
1141
 
1142
1142
  def set_annotate_default_settings(settings):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.1.85
3
+ Version: 0.2.0
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
@@ -36,9 +36,26 @@ spacr.egg-info/dependency_links.txt
36
36
  spacr.egg-info/entry_points.txt
37
37
  spacr.egg-info/requires.txt
38
38
  spacr.egg-info/top_level.txt
39
- spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model
40
- spacr/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv
41
- spacr/models/cp/toxo_pv_lumen.CP_model
39
+ spacr/resources/icons/abort.png
40
+ spacr/resources/icons/annotate.png
41
+ spacr/resources/icons/cellpose_masks.png
42
+ spacr/resources/icons/classify.png
43
+ spacr/resources/icons/default.png
44
+ spacr/resources/icons/download.png
45
+ spacr/resources/icons/logo_spacr.png
46
+ spacr/resources/icons/make_masks.png
47
+ spacr/resources/icons/map_barcodes.png
48
+ spacr/resources/icons/mask.png
49
+ spacr/resources/icons/measure.png
50
+ spacr/resources/icons/regression.png
51
+ spacr/resources/icons/run.png
52
+ spacr/resources/icons/sequencing.png
53
+ spacr/resources/icons/settings.png
54
+ spacr/resources/icons/train_cellpose.png
55
+ spacr/resources/icons/umap.png
56
+ spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model
57
+ spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv
58
+ spacr/resources/models/cp/toxo_pv_lumen.CP_model
42
59
  tests/test_annotate_app.py
43
60
  tests/test_core.py
44
61
  tests/test_gui_classify_app.py
spacr-0.1.85/spacr/gui.py DELETED
@@ -1,195 +0,0 @@
1
- import tkinter as tk
2
- from tkinter import ttk
3
- from PIL import Image, ImageTk
4
- import os, requests
5
- from multiprocessing import set_start_method
6
- from .gui_elements import spacrButton, create_menu_bar, set_dark_style
7
- from .gui_core import initiate_root
8
-
9
- class MainApp(tk.Tk):
10
- def __init__(self, default_app=None):
11
- super().__init__()
12
- width = self.winfo_screenwidth()
13
- height = self.winfo_screenheight()
14
- self.geometry(f"{width}x{height}")
15
- self.title("SpaCr GUI Collection")
16
-
17
- # Initialize style and apply dark style to the main window
18
- style = ttk.Style()
19
- self.color_settings = set_dark_style(style, parent_frame=self)
20
-
21
- self.main_gui_apps = {
22
- "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
23
- "Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
24
- "Annotate": (lambda frame: initiate_root(frame, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
25
- "Make Masks": (lambda frame: initiate_root(frame, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
26
- "Classify": (lambda frame: initiate_root(frame, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
27
- }
28
-
29
- self.additional_gui_apps = {
30
- "Sequencing": (lambda frame: initiate_root(frame, 'sequencing'), "Analyze sequencing data."),
31
- "Umap": (lambda frame: initiate_root(frame, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
32
- "Train Cellpose": (lambda frame: initiate_root(frame, 'train_cellpose'), "Train custom Cellpose models."),
33
- "ML Analyze": (lambda frame: initiate_root(frame, 'ml_analyze'), "Machine learning analysis of data."),
34
- "Cellpose Masks": (lambda frame: initiate_root(frame, 'cellpose_masks'), "Generate Cellpose masks."),
35
- "Cellpose All": (lambda frame: initiate_root(frame, 'cellpose_all'), "Run Cellpose on all images."),
36
- "Map Barcodes": (lambda frame: initiate_root(frame, 'map_barcodes'), "Map barcodes to data."),
37
- "Regression": (lambda frame: initiate_root(frame, 'regression'), "Perform regression analysis."),
38
- "Recruitment": (lambda frame: initiate_root(frame, 'recruitment'), "Analyze recruitment data.")
39
- }
40
-
41
- self.selected_app = tk.StringVar()
42
- self.create_widgets()
43
-
44
- if default_app in self.main_gui_apps:
45
- self.load_app(default_app, self.main_gui_apps[default_app][0])
46
- elif default_app in self.additional_gui_apps:
47
- self.load_app(default_app, self.additional_gui_apps[default_app][0])
48
-
49
- def create_widgets(self):
50
- # Create the menu bar
51
- create_menu_bar(self)
52
-
53
- # Create a canvas to hold the selected app and other elements
54
- self.canvas = tk.Canvas(self, highlightthickness=0)
55
- self.canvas.grid(row=0, column=0, sticky="nsew")
56
- self.grid_rowconfigure(0, weight=1)
57
- self.grid_columnconfigure(0, weight=1)
58
-
59
- # Create a frame inside the canvas to hold the main content
60
- self.content_frame = tk.Frame(self.canvas)
61
- self.content_frame.pack(fill=tk.BOTH, expand=True)
62
-
63
- # Apply dark style to canvas and content_frame
64
- set_dark_style(ttk.Style(), containers=[self.canvas, self.content_frame])
65
-
66
- # Create startup screen with buttons for each main GUI app and drop-down for additional apps
67
- self.create_startup_screen()
68
-
69
- def create_startup_screen(self):
70
- self.clear_frame(self.content_frame)
71
-
72
- # Create a frame for the logo and description
73
- logo_frame = tk.Frame(self.content_frame)
74
- logo_frame.pack(pady=20, expand=True)
75
- set_dark_style(ttk.Style(), containers=[logo_frame])
76
-
77
- # Load the logo image
78
- if not self.load_logo(logo_frame):
79
- logo_not_found = tk.Label(logo_frame, text="Logo not found", font=('Helvetica', 24))
80
- logo_not_found.pack(padx=10, pady=10)
81
- set_dark_style(ttk.Style(), widgets=[logo_not_found])
82
-
83
- # Add SpaCr text below the logo with padding for sharper text
84
- spacr_label = tk.Label(logo_frame, text="SpaCr", fg=self.color_settings['active_color'], font=('Helvetica', 24))
85
- spacr_label.pack(padx=10, pady=10)
86
- set_dark_style(ttk.Style(), widgets=[spacr_label])
87
-
88
- # Create a frame for the buttons and descriptions
89
- buttons_frame = tk.Frame(self.content_frame)
90
- buttons_frame.pack(pady=10, expand=True, padx=10)
91
- set_dark_style(ttk.Style(), containers=[buttons_frame])
92
-
93
- for i, (app_name, app_data) in enumerate(self.main_gui_apps.items()):
94
- app_func, app_desc = app_data
95
-
96
- # Create custom button with text
97
- button = spacrButton(buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), font=('Helvetica', 12))
98
- button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
99
-
100
- description_label = tk.Label(buttons_frame, text=app_desc, wraplength=800, justify="left", font=('Helvetica', 12))
101
- description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
102
- set_dark_style(ttk.Style(), widgets=[description_label])
103
-
104
- # Add drop-down menu for additional apps
105
- dropdown_frame = tk.Frame(buttons_frame)
106
- dropdown_frame.grid(row=len(self.main_gui_apps), column=0, columnspan=2, pady=20)
107
- set_dark_style(ttk.Style(), containers=[dropdown_frame])
108
-
109
- additional_apps_label = tk.Label(dropdown_frame, text="Additional Apps", font=('Helvetica', 12))
110
- additional_apps_label.pack(side=tk.LEFT, padx=5)
111
- set_dark_style(ttk.Style(), widgets=[additional_apps_label])
112
-
113
- self.additional_apps_var = tk.StringVar(value="Select an app")
114
- dropdown = ttk.Combobox(dropdown_frame, textvariable=self.additional_apps_var, values=list(self.additional_gui_apps.keys()))
115
- dropdown.pack(side=tk.LEFT, padx=5)
116
- set_dark_style(ttk.Style(), widgets=[dropdown])
117
-
118
- load_button = spacrButton(dropdown_frame, text="Load", command=self.load_additional_app, font=('Helvetica', 12))
119
- load_button.pack(side=tk.LEFT, padx=5)
120
-
121
- # Ensure buttons have a fixed width
122
- buttons_frame.grid_columnconfigure(0, minsize=150)
123
- # Ensure descriptions expand as needed
124
- buttons_frame.grid_columnconfigure(1, weight=1)
125
-
126
- def load_logo(self, frame):
127
- def download_image(url, save_path):
128
- try:
129
- response = requests.get(url, stream=True)
130
- response.raise_for_status() # Raise an HTTPError for bad responses
131
- with open(save_path, 'wb') as f:
132
- for chunk in response.iter_content(chunk_size=8192):
133
- f.write(chunk)
134
- return True
135
- except requests.exceptions.RequestException as e:
136
- print(f"Failed to download image from {url}: {e}")
137
- return False
138
-
139
- try:
140
- img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
141
- logo_image = Image.open(img_path)
142
- except (FileNotFoundError, Image.UnidentifiedImageError):
143
- print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
144
- if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
145
- try:
146
- print(f"Downloaded file size: {os.path.getsize(img_path)} bytes")
147
- logo_image = Image.open(img_path)
148
- except Image.UnidentifiedImageError as e:
149
- print(f"Downloaded file is not a valid image: {e}")
150
- return False
151
- else:
152
- return False
153
- except Exception as e:
154
- print(f"An error occurred while loading the logo: {e}")
155
- return False
156
- try:
157
- screen_height = frame.winfo_screenheight()
158
- new_height = int(screen_height // 4)
159
- logo_image = logo_image.resize((new_height, new_height), Image.Resampling.LANCZOS)
160
- logo_photo = ImageTk.PhotoImage(logo_image)
161
- logo_label = tk.Label(frame, image=logo_photo)
162
- logo_label.image = logo_photo # Keep a reference to avoid garbage collection
163
- logo_label.pack()
164
- set_dark_style(ttk.Style(), widgets=[logo_label])
165
- return True
166
- except Exception as e:
167
- print(f"An error occurred while processing the logo image: {e}")
168
- return False
169
-
170
- def load_app(self, app_name, app_func):
171
- # Clear the current content frame
172
- self.clear_frame(self.content_frame)
173
-
174
- # Initialize the selected app
175
- app_frame = tk.Frame(self.content_frame)
176
- app_frame.pack(fill=tk.BOTH, expand=True)
177
- set_dark_style(ttk.Style(), containers=[app_frame])
178
- app_func(app_frame)
179
-
180
- def load_additional_app(self):
181
- selected_app = self.additional_apps_var.get()
182
- if selected_app in self.additional_gui_apps:
183
- self.load_app(selected_app, self.additional_gui_apps[selected_app][0])
184
-
185
- def clear_frame(self, frame):
186
- for widget in frame.winfo_children():
187
- widget.destroy()
188
-
189
- def gui_app():
190
- app = MainApp()
191
- app.mainloop()
192
-
193
- if __name__ == "__main__":
194
- set_start_method('spawn', force=True)
195
- gui_app()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes