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