spacr 0.1.85__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spacr/__init__.py +6 -2
- spacr/app_annotate.py +6 -5
- spacr/app_make_masks.py +8 -15
- spacr/core.py +1 -1
- spacr/gui.py +62 -100
- spacr/gui_2.py +110 -113
- spacr/gui_core.py +43 -16
- spacr/gui_elements.py +116 -25
- spacr/gui_utils.py +3 -3
- spacr/icons/abort.png +0 -0
- spacr/icons/abort.svg +1 -0
- spacr/icons/download.png +0 -0
- spacr/icons/download.svg +1 -0
- spacr/icons/download_for_offline_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
- spacr/icons/download_for_offline_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
- spacr/icons/logo_spacr.png +0 -0
- spacr/icons/make_masks.png +0 -0
- spacr/icons/make_masks.svg +1 -0
- spacr/icons/map_barcodes.png +0 -0
- spacr/icons/map_barcodes.svg +1 -0
- spacr/icons/mask.png +0 -0
- spacr/icons/mask.svg +1 -0
- spacr/icons/measure.png +0 -0
- spacr/icons/measure.svg +1 -0
- spacr/icons/play_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
- spacr/icons/play_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
- spacr/icons/run.png +0 -0
- spacr/icons/run.svg +1 -0
- spacr/icons/sequencing.png +0 -0
- spacr/icons/sequencing.svg +1 -0
- spacr/icons/settings.png +0 -0
- spacr/icons/settings.svg +1 -0
- spacr/icons/settings_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
- spacr/icons/settings_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
- spacr/icons/stop_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
- spacr/icons/stop_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
- spacr/icons/theater_comedy_100dp_E8EAED_FILL0_wght100_GRAD200_opsz48.png +0 -0
- spacr/icons/theater_comedy_100dp_E8EAED_FILL0_wght100_GRAD200_opsz48.svg +1 -0
- spacr/resources/icons/abort.png +0 -0
- spacr/resources/icons/abort.svg +1 -0
- spacr/resources/icons/annotate.png +0 -0
- spacr/resources/icons/annotate.svg +1 -0
- spacr/resources/icons/cellpose.png +0 -0
- spacr/resources/icons/cellpose_masks.png +0 -0
- spacr/resources/icons/classify.png +0 -0
- spacr/resources/icons/classify.svg +1 -0
- spacr/resources/icons/default.png +0 -0
- spacr/resources/icons/download.png +0 -0
- spacr/resources/icons/download.svg +1 -0
- spacr/resources/icons/icon.psd +0 -0
- spacr/resources/icons/logo_spacr.png +0 -0
- spacr/resources/icons/make_masks.png +0 -0
- spacr/resources/icons/make_masks.svg +1 -0
- spacr/resources/icons/map_barcodes.png +0 -0
- spacr/resources/icons/map_barcodes.svg +1 -0
- spacr/resources/icons/mask.png +0 -0
- spacr/resources/icons/mask.svg +1 -0
- spacr/resources/icons/measure.png +0 -0
- spacr/resources/icons/measure.svg +1 -0
- spacr/resources/icons/regression.png +0 -0
- spacr/resources/icons/run.png +0 -0
- spacr/resources/icons/run.svg +1 -0
- spacr/resources/icons/run_2.png +0 -0
- spacr/resources/icons/run_2.svg +1 -0
- spacr/resources/icons/sequencing.png +0 -0
- spacr/resources/icons/sequencing.svg +1 -0
- spacr/resources/icons/settings.png +0 -0
- spacr/resources/icons/settings.svg +1 -0
- spacr/resources/icons/train_cellpose.png +0 -0
- spacr/resources/icons/train_cellpose.svg +1 -0
- spacr/resources/icons/umap.png +0 -0
- spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
- spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +23 -0
- spacr/resources/models/cp/toxo_pv_lumen.CP_model +0 -0
- spacr/settings.py +12 -12
- {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/METADATA +1 -1
- spacr-0.2.1.dist-info/RECORD +126 -0
- spacr-0.1.85.dist-info/RECORD +0 -61
- {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/LICENSE +0 -0
- {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/WHEEL +0 -0
- {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/entry_points.txt +0 -0
- {spacr-0.1.85.dist-info → spacr-0.2.1.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
|
20
|
-
from . import
|
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
|
-
|
10
|
-
|
11
|
-
|
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='
|
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
|
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
|
-
|
11
|
-
|
12
|
-
|
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=
|
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
|
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
|
-
|
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='
|
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
|
-
#
|
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
|
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.
|
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
|
73
|
-
|
74
|
-
|
75
|
-
set_dark_style(ttk.Style(), containers=[
|
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
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
87
|
+
# Create a frame for the description below the icon grids
|
88
|
+
description_frame = tk.Frame(self.content_frame, height=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
|
-
#
|
84
|
-
|
85
|
-
|
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
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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)
|
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.
|
139
|
+
self.clear_frame(self.canvas)
|
173
140
|
|
174
141
|
# Initialize the selected app
|
175
|
-
app_frame = tk.Frame(self.
|
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
|
4
|
-
from PIL import Image, ImageTk
|
3
|
+
from PIL import Image, ImageTk, ImageDraw
|
5
4
|
import os
|
6
|
-
import
|
7
|
-
|
8
|
-
|
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.
|
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
|
-
|
27
|
-
|
28
|
-
self.
|
29
|
-
|
30
|
-
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
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
|
-
|
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,
|
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
|
49
|
-
self.content_frame.
|
50
|
-
|
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
|
57
|
-
|
58
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
#
|
65
|
-
|
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
|
-
#
|
68
|
-
|
69
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
143
|
-
widget.destroy()
|
139
|
+
self.clear_frame(self.canvas)
|
144
140
|
|
145
141
|
# Initialize the selected app
|
146
|
-
app_frame = tk.Frame(
|
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
|
-
|
156
|
+
set_start_method('spawn', force=True)
|
157
|
+
gui_app()
|