spacr 0.1.81__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 (83) 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 +72 -91
  6. spacr/gui_2.py +110 -113
  7. spacr/gui_core.py +113 -67
  8. spacr/gui_elements.py +233 -87
  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/test_gui.py +0 -0
  77. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/METADATA +1 -1
  78. spacr-0.2.0.dist-info/RECORD +126 -0
  79. spacr-0.1.81.dist-info/RECORD +0 -60
  80. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/LICENSE +0 -0
  81. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/WHEEL +0 -0
  82. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/entry_points.txt +0 -0
  83. {spacr-0.1.81.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
@@ -11,11 +11,15 @@ class MainApp(tk.Tk):
11
11
  super().__init__()
12
12
  width = self.winfo_screenwidth()
13
13
  height = self.winfo_screenheight()
14
- self.geometry(f"{width}x{height}")
14
+ self.geometry(f"{width}x{height}")
15
15
  self.title("SpaCr GUI Collection")
16
- self.configure(bg="black")
16
+ self.configure(bg='#333333') # Set window background to dark gray
17
+
18
+ # Initialize style and apply dark style to the main window
17
19
  style = ttk.Style()
18
- set_dark_style(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
19
23
 
20
24
  self.main_gui_apps = {
21
25
  "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
@@ -50,14 +54,20 @@ class MainApp(tk.Tk):
50
54
  create_menu_bar(self)
51
55
 
52
56
  # Create a canvas to hold the selected app and other elements
53
- self.canvas = tk.Canvas(self, bg="black", highlightthickness=0)
57
+ self.canvas = tk.Canvas(self, highlightthickness=0)
54
58
  self.canvas.grid(row=0, column=0, sticky="nsew")
55
59
  self.grid_rowconfigure(0, weight=1)
56
60
  self.grid_columnconfigure(0, weight=1)
57
61
 
58
62
  # Create a frame inside the canvas to hold the main content
59
- self.content_frame = tk.Frame(self.canvas, bg="black")
60
- self.content_frame.pack(fill=tk.BOTH, expand=True)
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])
61
71
 
62
72
  # Create startup screen with buttons for each main GUI app and drop-down for additional apps
63
73
  self.create_startup_screen()
@@ -65,104 +75,75 @@ class MainApp(tk.Tk):
65
75
  def create_startup_screen(self):
66
76
  self.clear_frame(self.content_frame)
67
77
 
68
- # Create a frame for the logo and description
69
- logo_frame = tk.Frame(self.content_frame, bg="black")
70
- 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])
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])
71
86
 
72
- # Load the logo image
73
- if not self.load_logo(logo_frame):
74
- tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", 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=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])
75
92
 
76
- # Add SpaCr text below the logo with padding for sharper text
77
- tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24)).pack(padx=10, pady=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)
78
96
 
79
- # Create a frame for the buttons and descriptions
80
- buttons_frame = tk.Frame(self.content_frame, bg="black")
81
- buttons_frame.pack(pady=10, expand=True, padx=10)
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."
82
101
 
102
+ # Create icon buttons for the main apps
83
103
  for i, (app_name, app_data) in enumerate(self.main_gui_apps.items()):
84
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
85
108
 
86
- # Create custom button with text
87
- 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))
88
- button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
89
-
90
- description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 12))
91
- description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
92
-
93
- # Add drop-down menu for additional apps
94
- dropdown_frame = tk.Frame(buttons_frame, bg="black")
95
- dropdown_frame.grid(row=len(self.main_gui_apps), column=0, columnspan=2, pady=20)
96
-
97
- tk.Label(dropdown_frame, text="Additional Apps", bg="black", fg="white", font=('Helvetica', 12)).pack(side=tk.LEFT, padx=5)
98
- self.additional_apps_var = tk.StringVar(value="Select an app")
99
- dropdown = ttk.Combobox(dropdown_frame, textvariable=self.additional_apps_var, values=list(self.additional_gui_apps.keys()))
100
- dropdown.pack(side=tk.LEFT, padx=5)
101
- load_button = spacrButton(dropdown_frame, text="Load", command=self.load_additional_app, font=('Helvetica', 12))
102
- load_button.pack(side=tk.LEFT, padx=5)
103
-
104
- # Ensure buttons have a fixed width
105
- buttons_frame.grid_columnconfigure(0, minsize=150)
106
- # Ensure descriptions expand as needed
107
- buttons_frame.grid_columnconfigure(1, weight=1)
108
-
109
- def load_logo(self, frame):
110
- def download_image(url, save_path):
111
- try:
112
- response = requests.get(url, stream=True)
113
- response.raise_for_status() # Raise an HTTPError for bad responses
114
- with open(save_path, 'wb') as f:
115
- for chunk in response.iter_content(chunk_size=8192):
116
- f.write(chunk)
117
- return True
118
- except requests.exceptions.RequestException as e:
119
- print(f"Failed to download image from {url}: {e}")
120
- return False
121
-
122
- try:
123
- img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
124
- logo_image = Image.open(img_path)
125
- except (FileNotFoundError, Image.UnidentifiedImageError):
126
- print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
127
- if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
128
- try:
129
- print(f"Downloaded file size: {os.path.getsize(img_path)} bytes")
130
- logo_image = Image.open(img_path)
131
- except Image.UnidentifiedImageError as e:
132
- print(f"Downloaded file is not a valid image: {e}")
133
- return False
134
- else:
135
- return False
136
- except Exception as e:
137
- print(f"An error occurred while loading the logo: {e}")
138
- return False
139
- try:
140
- screen_height = frame.winfo_screenheight()
141
- new_height = int(screen_height // 4)
142
- logo_image = logo_image.resize((new_height, new_height), Image.Resampling.LANCZOS)
143
- logo_photo = ImageTk.PhotoImage(logo_image)
144
- logo_label = tk.Label(frame, image=logo_photo, bg="black")
145
- logo_label.image = logo_photo # Keep a reference to avoid garbage collection
146
- logo_label.pack()
147
- return True
148
- except Exception as e:
149
- print(f"An error occurred while processing the logo image: {e}")
150
- 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
151
136
 
152
137
  def load_app(self, app_name, app_func):
153
138
  # Clear the current content frame
154
- self.clear_frame(self.content_frame)
139
+ self.clear_frame(self.canvas)
155
140
 
156
141
  # Initialize the selected app
157
- app_frame = tk.Frame(self.content_frame, bg="black")
142
+ app_frame = tk.Frame(self.canvas)
158
143
  app_frame.pack(fill=tk.BOTH, expand=True)
144
+ set_dark_style(ttk.Style(), containers=[app_frame])
159
145
  app_func(app_frame)
160
146
 
161
- def load_additional_app(self):
162
- selected_app = self.additional_apps_var.get()
163
- if selected_app in self.additional_gui_apps:
164
- self.load_app(selected_app, self.additional_gui_apps[selected_app][0])
165
-
166
147
  def clear_frame(self, frame):
167
148
  for widget in frame.winfo_children():
168
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()