spacr 0.1.85__py3-none-any.whl → 0.2.0__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 (82) hide show
  1. spacr/__init__.py +6 -2
  2. spacr/app_annotate.py +6 -5
  3. spacr/app_make_masks.py +8 -15
  4. spacr/core.py +1 -1
  5. spacr/gui.py +62 -100
  6. spacr/gui_2.py +110 -113
  7. spacr/gui_core.py +43 -16
  8. spacr/gui_elements.py +109 -20
  9. spacr/gui_utils.py +3 -3
  10. spacr/icons/abort.png +0 -0
  11. spacr/icons/abort.svg +1 -0
  12. spacr/icons/download.png +0 -0
  13. spacr/icons/download.svg +1 -0
  14. spacr/icons/download_for_offline_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  15. spacr/icons/download_for_offline_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  16. spacr/icons/logo_spacr.png +0 -0
  17. spacr/icons/make_masks.png +0 -0
  18. spacr/icons/make_masks.svg +1 -0
  19. spacr/icons/map_barcodes.png +0 -0
  20. spacr/icons/map_barcodes.svg +1 -0
  21. spacr/icons/mask.png +0 -0
  22. spacr/icons/mask.svg +1 -0
  23. spacr/icons/measure.png +0 -0
  24. spacr/icons/measure.svg +1 -0
  25. spacr/icons/play_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  26. spacr/icons/play_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  27. spacr/icons/run.png +0 -0
  28. spacr/icons/run.svg +1 -0
  29. spacr/icons/sequencing.png +0 -0
  30. spacr/icons/sequencing.svg +1 -0
  31. spacr/icons/settings.png +0 -0
  32. spacr/icons/settings.svg +1 -0
  33. spacr/icons/settings_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  34. spacr/icons/settings_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  35. spacr/icons/stop_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  36. spacr/icons/stop_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  37. spacr/icons/theater_comedy_100dp_E8EAED_FILL0_wght100_GRAD200_opsz48.png +0 -0
  38. spacr/icons/theater_comedy_100dp_E8EAED_FILL0_wght100_GRAD200_opsz48.svg +1 -0
  39. spacr/resources/icons/abort.png +0 -0
  40. spacr/resources/icons/abort.svg +1 -0
  41. spacr/resources/icons/annotate.png +0 -0
  42. spacr/resources/icons/annotate.svg +1 -0
  43. spacr/resources/icons/cellpose.png +0 -0
  44. spacr/resources/icons/cellpose_masks.png +0 -0
  45. spacr/resources/icons/classify.png +0 -0
  46. spacr/resources/icons/classify.svg +1 -0
  47. spacr/resources/icons/default.png +0 -0
  48. spacr/resources/icons/download.png +0 -0
  49. spacr/resources/icons/download.svg +1 -0
  50. spacr/resources/icons/icon.psd +0 -0
  51. spacr/resources/icons/logo_spacr.png +0 -0
  52. spacr/resources/icons/make_masks.png +0 -0
  53. spacr/resources/icons/make_masks.svg +1 -0
  54. spacr/resources/icons/map_barcodes.png +0 -0
  55. spacr/resources/icons/map_barcodes.svg +1 -0
  56. spacr/resources/icons/mask.png +0 -0
  57. spacr/resources/icons/mask.svg +1 -0
  58. spacr/resources/icons/measure.png +0 -0
  59. spacr/resources/icons/measure.svg +1 -0
  60. spacr/resources/icons/regression.png +0 -0
  61. spacr/resources/icons/run.png +0 -0
  62. spacr/resources/icons/run.svg +1 -0
  63. spacr/resources/icons/run_2.png +0 -0
  64. spacr/resources/icons/run_2.svg +1 -0
  65. spacr/resources/icons/sequencing.png +0 -0
  66. spacr/resources/icons/sequencing.svg +1 -0
  67. spacr/resources/icons/settings.png +0 -0
  68. spacr/resources/icons/settings.svg +1 -0
  69. spacr/resources/icons/train_cellpose.png +0 -0
  70. spacr/resources/icons/train_cellpose.svg +1 -0
  71. spacr/resources/icons/umap.png +0 -0
  72. spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  73. spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +23 -0
  74. spacr/resources/models/cp/toxo_pv_lumen.CP_model +0 -0
  75. spacr/settings.py +12 -12
  76. {spacr-0.1.85.dist-info → spacr-0.2.0.dist-info}/METADATA +1 -1
  77. spacr-0.2.0.dist-info/RECORD +126 -0
  78. spacr-0.1.85.dist-info/RECORD +0 -61
  79. {spacr-0.1.85.dist-info → spacr-0.2.0.dist-info}/LICENSE +0 -0
  80. {spacr-0.1.85.dist-info → spacr-0.2.0.dist-info}/WHEEL +0 -0
  81. {spacr-0.1.85.dist-info → spacr-0.2.0.dist-info}/entry_points.txt +0 -0
  82. {spacr-0.1.85.dist-info → spacr-0.2.0.dist-info}/top_level.txt +0 -0
spacr/__init__.py CHANGED
@@ -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",
spacr/app_annotate.py CHANGED
@@ -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():
spacr/app_make_masks.py CHANGED
@@ -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():
spacr/core.py CHANGED
@@ -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']:
spacr/gui.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import tkinter as tk
2
2
  from tkinter import ttk
3
- from PIL import Image, ImageTk
4
- import os, requests
3
+ from PIL import Image, ImageTk, ImageDraw
4
+ import os
5
5
  from multiprocessing import set_start_method
6
6
  from .gui_elements import spacrButton, create_menu_bar, set_dark_style
7
7
  from .gui_core import initiate_root
@@ -13,10 +13,13 @@ class MainApp(tk.Tk):
13
13
  height = self.winfo_screenheight()
14
14
  self.geometry(f"{width}x{height}")
15
15
  self.title("SpaCr GUI Collection")
16
+ self.configure(bg='#333333') # Set window background to dark gray
16
17
 
17
18
  # Initialize style and apply dark style to the main window
18
19
  style = ttk.Style()
19
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
20
23
 
21
24
  self.main_gui_apps = {
22
25
  "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
@@ -58,7 +61,10 @@ class MainApp(tk.Tk):
58
61
 
59
62
  # Create a frame inside the canvas to hold the main content
60
63
  self.content_frame = tk.Frame(self.canvas)
61
- self.content_frame.pack(fill=tk.BOTH, expand=True)
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")
62
68
 
63
69
  # Apply dark style to canvas and content_frame
64
70
  set_dark_style(ttk.Style(), containers=[self.canvas, self.content_frame])
@@ -69,119 +75,75 @@ class MainApp(tk.Tk):
69
75
  def create_startup_screen(self):
70
76
  self.clear_frame(self.content_frame)
71
77
 
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])
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])
76
86
 
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])
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])
82
92
 
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])
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)
87
96
 
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])
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."
92
101
 
102
+ # Create icon buttons for the main apps
93
103
  for i, (app_name, app_data) in enumerate(self.main_gui_apps.items()):
94
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
95
108
 
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
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
169
136
 
170
137
  def load_app(self, app_name, app_func):
171
138
  # Clear the current content frame
172
- self.clear_frame(self.content_frame)
139
+ self.clear_frame(self.canvas)
173
140
 
174
141
  # Initialize the selected app
175
- app_frame = tk.Frame(self.content_frame)
142
+ app_frame = tk.Frame(self.canvas)
176
143
  app_frame.pack(fill=tk.BOTH, expand=True)
177
144
  set_dark_style(ttk.Style(), containers=[app_frame])
178
145
  app_func(app_frame)
179
146
 
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
147
  def clear_frame(self, frame):
186
148
  for widget in frame.winfo_children():
187
149
  widget.destroy()
spacr/gui_2.py CHANGED
@@ -1,160 +1,157 @@
1
1
  import tkinter as tk
2
2
  from tkinter import ttk
3
- from tkinter import font as tkFont
4
- from PIL import Image, ImageTk
3
+ from PIL import Image, ImageTk, ImageDraw
5
4
  import os
6
- import requests
7
-
8
- # Import your GUI apps
9
- from .gui_mask_app import initiate_mask_root
10
- from .gui_measure_app import initiate_measure_root
11
- from .annotate_app import initiate_annotation_app_root
12
- from .mask_app import initiate_mask_app_root
13
- from .gui_classify_app import initiate_classify_root
14
-
15
- from .gui_utils import CustomButton, style_text_boxes
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
16
8
 
17
9
  class MainApp(tk.Tk):
18
- def __init__(self):
10
+ def __init__(self, default_app=None):
19
11
  super().__init__()
12
+ width = self.winfo_screenwidth()
13
+ height = self.winfo_screenheight()
14
+ self.geometry(f"{width}x{height}")
20
15
  self.title("SpaCr GUI Collection")
21
- self.geometry("1100x1500")
22
- self.configure(bg="black")
23
- #self.attributes('-fullscreen', True)
16
+ self.configure(bg='#333333') # Set window background to dark gray
24
17
 
18
+ # Initialize style and apply dark style to the main window
25
19
  style = ttk.Style()
26
- style_text_boxes(style)
27
-
28
- self.gui_apps = {
29
- "Mask": (initiate_mask_root, "Generate cellpose masks for cells, nuclei and pathogen images."),
30
- "Measure": (initiate_measure_root, "Measure single object intensity and morphological feature. Crop and save single object image"),
31
- "Annotate": (initiate_annotation_app_root, "Annotation single object images on a grid. Annotations are saved to database."),
32
- "Make Masks": (initiate_mask_app_root, "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
33
- "Classify": (initiate_classify_root, "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images.")
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.")
34
42
  }
35
43
 
36
44
  self.selected_app = tk.StringVar()
37
45
  self.create_widgets()
38
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
+
39
52
  def create_widgets(self):
40
53
  # Create the menu bar
41
- #create_menu_bar(self)
54
+ create_menu_bar(self)
55
+
42
56
  # Create a canvas to hold the selected app and other elements
43
- self.canvas = tk.Canvas(self, bg="black", highlightthickness=0, width=4000, height=4000)
57
+ self.canvas = tk.Canvas(self, highlightthickness=0)
44
58
  self.canvas.grid(row=0, column=0, sticky="nsew")
45
59
  self.grid_rowconfigure(0, weight=1)
46
60
  self.grid_columnconfigure(0, weight=1)
61
+
47
62
  # Create a frame inside the canvas to hold the main content
48
- self.content_frame = tk.Frame(self.canvas, bg="black")
49
- self.content_frame.pack(fill=tk.BOTH, expand=True)
50
- # Create startup screen with buttons for each GUI app
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
51
73
  self.create_startup_screen()
52
74
 
53
75
  def create_startup_screen(self):
54
76
  self.clear_frame(self.content_frame)
55
77
 
56
- # Create a frame for the logo and description
57
- logo_frame = tk.Frame(self.content_frame, bg="black")
58
- logo_frame.pack(pady=20, expand=True)
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])
59
82
 
60
- # Load the logo image
61
- if not self.load_logo(logo_frame):
62
- tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
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])
63
86
 
64
- # Add SpaCr text below the logo with padding for sharper text
65
- tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24)).pack(padx=10, pady=10)
87
+ # Create a frame for the description below the icon grids
88
+ description_frame = tk.Frame(self.content_frame, height=50)
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])
66
92
 
67
- # Create a frame for the buttons and descriptions
68
- buttons_frame = tk.Frame(self.content_frame, bg="black")
69
- buttons_frame.pack(pady=10, expand=True, padx=10)
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)
70
96
 
71
- for i, (app_name, app_data) in enumerate(self.gui_apps.items()):
72
- app_func, app_desc = app_data
73
-
74
- # Create custom button with text
75
- button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name, app_func), font=('Helvetica', 12))
76
- button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
77
-
78
- description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 12))
79
- description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
80
-
81
- # Ensure buttons have a fixed width
82
- buttons_frame.grid_columnconfigure(0, minsize=150)
83
- # Ensure descriptions expand as needed
84
- buttons_frame.grid_columnconfigure(1, weight=1)
85
-
86
- def load_logo(self, frame):
87
- def download_image(url, save_path):
88
- try:
89
- response = requests.get(url, stream=True)
90
- response.raise_for_status() # Raise an HTTPError for bad responses
91
- with open(save_path, 'wb') as f:
92
- for chunk in response.iter_content(chunk_size=8192):
93
- f.write(chunk)
94
- return True
95
- except requests.exceptions.RequestException as e:
96
- print(f"Failed to download image from {url}: {e}")
97
- return False
98
-
99
- try:
100
- img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
101
- print(f"Trying to load logo from {img_path}")
102
- logo_image = Image.open(img_path)
103
- except (FileNotFoundError, Image.UnidentifiedImageError):
104
- print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
105
- if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
106
- try:
107
- print(f"Downloaded file size: {os.path.getsize(img_path)} bytes")
108
- logo_image = Image.open(img_path)
109
- except Image.UnidentifiedImageError as e:
110
- print(f"Downloaded file is not a valid image: {e}")
111
- return False
112
- else:
113
- return False
114
- except Exception as e:
115
- print(f"An error occurred while loading the logo: {e}")
116
- return False
117
- try:
118
- logo_image = logo_image.resize((800, 800), Image.Resampling.LANCZOS)
119
- logo_photo = ImageTk.PhotoImage(logo_image)
120
- logo_label = tk.Label(frame, image=logo_photo, bg="black")
121
- logo_label.image = logo_photo # Keep a reference to avoid garbage collection
122
- logo_label.pack()
123
- return True
124
- except Exception as e:
125
- print(f"An error occurred while processing the logo image: {e}")
126
- return False
127
-
128
- def load_app_v1(self, app_name):
129
- selected_app_func, _ = self.gui_apps[app_name]
130
- self.clear_frame(self.content_frame)
131
-
132
- app_frame = tk.Frame(self.content_frame, bg="black")
133
- app_frame.pack(fill=tk.BOTH, expand=True)
134
- selected_app_func(app_frame)
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."
135
101
 
136
- def load_app(root, app_name, app_func):
137
- if hasattr(root, 'current_app_id'):
138
- root.after_cancel(root.current_app_id)
139
- root.current_app_id = None
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
140
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)
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):
141
138
  # Clear the current content frame
142
- for widget in root.content_frame.winfo_children():
143
- widget.destroy()
139
+ self.clear_frame(self.canvas)
144
140
 
145
141
  # Initialize the selected app
146
- app_frame = tk.Frame(root.content_frame, bg="black")
142
+ app_frame = tk.Frame(self.canvas)
147
143
  app_frame.pack(fill=tk.BOTH, expand=True)
144
+ set_dark_style(ttk.Style(), containers=[app_frame])
148
145
  app_func(app_frame)
149
146
 
150
147
  def clear_frame(self, frame):
151
148
  for widget in frame.winfo_children():
152
149
  widget.destroy()
153
150
 
154
-
155
151
  def gui_app():
156
152
  app = MainApp()
157
153
  app.mainloop()
158
154
 
159
155
  if __name__ == "__main__":
160
- gui_app()
156
+ set_start_method('spawn', force=True)
157
+ gui_app()