spacr 0.1.0__py3-none-any.whl → 0.1.6__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/app_mask.py ADDED
@@ -0,0 +1,8 @@
1
+ from .gui import MainApp
2
+
3
+ def start_mask_app():
4
+ app = MainApp(default_app="Mask")
5
+ app.mainloop()
6
+
7
+ if __name__ == "__main__":
8
+ start_mask_app()
spacr/app_measure.py ADDED
@@ -0,0 +1,8 @@
1
+ from .gui import MainApp
2
+
3
+ def start_measure_app():
4
+ app = MainApp(default_app="Measure")
5
+ app.mainloop()
6
+
7
+ if __name__ == "__main__":
8
+ start_measure_app()
@@ -0,0 +1,8 @@
1
+ from .gui import MainApp
2
+
3
+ def start_seq_app():
4
+ app = MainApp(default_app="Sequencing")
5
+ app.mainloop()
6
+
7
+ if __name__ == "__main__":
8
+ start_seq_app()
spacr/app_umap.py ADDED
@@ -0,0 +1,8 @@
1
+ from .gui import MainApp
2
+
3
+ def start_umap_app():
4
+ app = MainApp(default_app="Umap")
5
+ app.mainloop()
6
+
7
+ if __name__ == "__main__":
8
+ start_umap_app()
spacr/classify_app.py ADDED
@@ -0,0 +1,201 @@
1
+ import sys, ctypes, matplotlib
2
+ import tkinter as tk
3
+ from tkinter import ttk, scrolledtext
4
+ from matplotlib.figure import Figure
5
+ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
6
+ from matplotlib.figure import Figure
7
+ matplotlib.use('Agg')
8
+ from tkinter import filedialog
9
+ from multiprocessing import Process, Queue, Value
10
+ import traceback
11
+
12
+ try:
13
+ ctypes.windll.shcore.SetProcessDpiAwareness(True)
14
+ except AttributeError:
15
+ pass
16
+
17
+ from .logger import log_function_call
18
+ from .gui_utils import ScrollableFrame, StdoutRedirector, CustomButton, set_dark_style, set_default_font, generate_fields, process_stdout_stderr, clear_canvas, main_thread_update_function
19
+ from .gui_utils import classify_variables, check_classify_gui_settings, train_test_model_wrapper, read_settings_from_csv, update_settings_from_csv, style_text_boxes, create_menu_bar
20
+
21
+ thread_control = {"run_thread": None, "stop_requested": False}
22
+
23
+ #@log_function_call
24
+ def initiate_abort():
25
+ global thread_control
26
+ if thread_control.get("stop_requested") is not None:
27
+ thread_control["stop_requested"].value = 1
28
+
29
+ if thread_control.get("run_thread") is not None:
30
+ thread_control["run_thread"].join(timeout=5)
31
+ if thread_control["run_thread"].is_alive():
32
+ thread_control["run_thread"].terminate()
33
+ thread_control["run_thread"] = None
34
+
35
+ #@log_function_call
36
+ def run_classify_gui(q, fig_queue, stop_requested):
37
+ global vars_dict
38
+ process_stdout_stderr(q)
39
+ try:
40
+ settings = check_classify_gui_settings(vars_dict)
41
+ for key in settings:
42
+ value = settings[key]
43
+ print(key, value, type(value))
44
+ train_test_model_wrapper(settings['src'], settings)
45
+ except Exception as e:
46
+ q.put(f"Error during processing: {e}")
47
+ traceback.print_exc()
48
+ finally:
49
+ stop_requested.value = 1
50
+
51
+ #@log_function_call
52
+ def start_process(q, fig_queue):
53
+ global thread_control
54
+ if thread_control.get("run_thread") is not None:
55
+ initiate_abort()
56
+
57
+ stop_requested = Value('i', 0) # multiprocessing shared value for inter-process communication
58
+ thread_control["stop_requested"] = stop_requested
59
+ thread_control["run_thread"] = Process(target=run_classify_gui, args=(q, fig_queue, stop_requested))
60
+ thread_control["run_thread"].start()
61
+
62
+ def import_settings(scrollable_frame):
63
+ global vars_dict
64
+
65
+ csv_file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv")])
66
+ csv_settings = read_settings_from_csv(csv_file_path)
67
+ variables = classify_variables()
68
+ new_settings = update_settings_from_csv(variables, csv_settings)
69
+ vars_dict = generate_fields(new_settings, scrollable_frame)
70
+
71
+ #@log_function_call
72
+ def initiate_classify_root(parent_frame):
73
+ global vars_dict, q, canvas, fig_queue, canvas_widget, thread_control
74
+
75
+ style = ttk.Style(parent_frame)
76
+ set_dark_style(style)
77
+ style_text_boxes(style)
78
+ set_default_font(parent_frame, font_name="Helvetica", size=8)
79
+
80
+ parent_frame.configure(bg='#333333')
81
+ parent_frame.grid_rowconfigure(0, weight=1)
82
+ parent_frame.grid_columnconfigure(0, weight=1)
83
+ fig_queue = Queue()
84
+
85
+ def _process_fig_queue():
86
+ global canvas
87
+ try:
88
+ while not fig_queue.empty():
89
+ clear_canvas(canvas)
90
+ fig = fig_queue.get_nowait()
91
+ for ax in fig.get_axes():
92
+ ax.set_xticks([]) # Remove x-axis ticks
93
+ ax.set_yticks([]) # Remove y-axis ticks
94
+ ax.xaxis.set_visible(False) # Hide the x-axis
95
+ ax.yaxis.set_visible(False) # Hide the y-axis
96
+ fig.tight_layout()
97
+ fig.set_facecolor('#333333')
98
+ canvas.figure = fig
99
+ fig_width, fig_height = canvas_widget.winfo_width(), canvas_widget.winfo_height()
100
+ fig.set_size_inches(fig_width / fig.dpi, fig_height / fig.dpi, forward=True)
101
+ canvas.draw_idle()
102
+ except Exception as e:
103
+ traceback.print_exc()
104
+ finally:
105
+ canvas_widget.after(100, _process_fig_queue)
106
+
107
+ def _process_console_queue():
108
+ while not q.empty():
109
+ message = q.get_nowait()
110
+ console_output.insert(tk.END, message)
111
+ console_output.see(tk.END)
112
+ console_output.after(100, _process_console_queue)
113
+
114
+ vertical_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL)
115
+ vertical_container.grid(row=0, column=0, sticky=tk.NSEW)
116
+ parent_frame.grid_rowconfigure(0, weight=1)
117
+ parent_frame.grid_columnconfigure(0, weight=1)
118
+
119
+ # Settings Section
120
+ settings_frame = tk.Frame(vertical_container, bg='#333333')
121
+ vertical_container.add(settings_frame, stretch="always")
122
+ settings_label = ttk.Label(settings_frame, text="Settings", background="#333333", foreground="white")
123
+ settings_label.grid(row=0, column=0, pady=10, padx=10)
124
+ scrollable_frame = ScrollableFrame(settings_frame, width=500)
125
+ scrollable_frame.grid(row=1, column=0, sticky="nsew")
126
+ settings_frame.grid_rowconfigure(1, weight=1)
127
+ settings_frame.grid_columnconfigure(0, weight=1)
128
+
129
+ # Setup for user input fields (variables)
130
+ variables = classify_variables()
131
+ vars_dict = generate_fields(variables, scrollable_frame)
132
+
133
+ # Button section
134
+ import_btn = CustomButton(scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(scrollable_frame), font=('Helvetica', 10))
135
+ import_btn.grid(row=47, column=0, pady=20, padx=20)
136
+ run_button = CustomButton(scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue), font=('Helvetica', 10))
137
+ run_button.grid(row=45, column=0, pady=20, padx=20)
138
+ abort_button = CustomButton(scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort, font=('Helvetica', 10))
139
+ abort_button.grid(row=45, column=1, pady=20, padx=20)
140
+ progress_label = ttk.Label(scrollable_frame.scrollable_frame, text="Processing: 0%", background="black", foreground="white") # Create progress field
141
+ progress_label.grid(row=50, column=0, columnspan=2, sticky="ew", pady=(5, 0), padx=10)
142
+
143
+ # Plot Canvas Section
144
+ plot_frame = tk.PanedWindow(vertical_container, orient=tk.VERTICAL)
145
+ vertical_container.add(plot_frame, stretch="always")
146
+ figure = Figure(figsize=(30, 4), dpi=100, facecolor='#333333')
147
+ plot = figure.add_subplot(111)
148
+ plot.plot([], [])
149
+ plot.axis('off')
150
+ canvas = FigureCanvasTkAgg(figure, master=plot_frame)
151
+ canvas.get_tk_widget().configure(cursor='arrow', background='#333333', highlightthickness=0)
152
+ canvas_widget = canvas.get_tk_widget()
153
+ plot_frame.add(canvas_widget, stretch="always")
154
+ canvas.draw()
155
+ canvas.figure = figure
156
+
157
+ # Console Section
158
+ console_frame = tk.Frame(vertical_container, bg='#333333')
159
+ vertical_container.add(console_frame, stretch="always")
160
+ console_label = ttk.Label(console_frame, text="Console", background="#333333", foreground="white")
161
+ console_label.grid(row=0, column=0, pady=10, padx=10)
162
+ console_output = scrolledtext.ScrolledText(console_frame, height=10, bg='#333333', fg='white', insertbackground='white')
163
+ console_output.grid(row=1, column=0, sticky="nsew")
164
+ console_frame.grid_rowconfigure(1, weight=1)
165
+ console_frame.grid_columnconfigure(0, weight=1)
166
+
167
+ q = Queue()
168
+ sys.stdout = StdoutRedirector(console_output)
169
+ sys.stderr = StdoutRedirector(console_output)
170
+
171
+ _process_console_queue()
172
+ _process_fig_queue()
173
+
174
+ parent_frame.after(100, lambda: main_thread_update_function(parent_frame, q, fig_queue, canvas_widget, progress_label))
175
+
176
+ return parent_frame, vars_dict
177
+
178
+ def gui_classify():
179
+ root = tk.Tk()
180
+ width = root.winfo_screenwidth()
181
+ height = root.winfo_screenheight()
182
+ root.geometry(f"{width}x{height}")
183
+ root.title("SpaCr: classify objects")
184
+
185
+ # Clear previous content if any
186
+ if hasattr(root, 'content_frame'):
187
+ for widget in root.content_frame.winfo_children():
188
+ widget.destroy()
189
+ root.content_frame.grid_forget()
190
+ else:
191
+ root.content_frame = tk.Frame(root)
192
+ root.content_frame.grid(row=1, column=0, sticky="nsew")
193
+ root.grid_rowconfigure(1, weight=1)
194
+ root.grid_columnconfigure(0, weight=1)
195
+
196
+ initiate_classify_root(root.content_frame)
197
+ create_menu_bar(root)
198
+ root.mainloop()
199
+
200
+ if __name__ == "__main__":
201
+ gui_classify()
spacr/core.py CHANGED
@@ -3021,9 +3021,9 @@ def generate_image_umap(settings={}):
3021
3021
  """
3022
3022
 
3023
3023
  from .io import _read_and_join_tables
3024
- from .utils import get_db_paths, preprocess_data, reduction_and_clustering, remove_noise, generate_colors, correct_paths, plot_embedding, plot_clusters_grid, cluster_feature_analysis, generate_umap_from_images
3025
- from .settings import get_umap_image_settings
3026
- settings = get_umap_image_settings(settings)
3024
+ from .utils import get_db_paths, preprocess_data, reduction_and_clustering, remove_noise, generate_colors, correct_paths, plot_embedding, plot_clusters_grid, cluster_feature_analysis #, generate_umap_from_images
3025
+ from .settings import set_default_umap_image_settings
3026
+ settings = set_default_umap_image_settings(settings)
3027
3027
 
3028
3028
  if isinstance(settings['src'], str):
3029
3029
  settings['src'] = [settings['src']]
@@ -3109,7 +3109,9 @@ def generate_image_umap(settings={}):
3109
3109
 
3110
3110
  else:
3111
3111
  if settings['resnet_features']:
3112
- numeric_data, embedding, labels = generate_umap_from_images(image_paths, settings['n_neighbors'], settings['min_dist'], settings['metric'], settings['clustering'], settings['eps'], settings['min_samples'], settings['n_jobs'], settings['verbose'])
3112
+ # placeholder for resnet features, not implemented yet
3113
+ pass
3114
+ #numeric_data, embedding, labels = generate_umap_from_images(image_paths, settings['n_neighbors'], settings['min_dist'], settings['metric'], settings['clustering'], settings['eps'], settings['min_samples'], settings['n_jobs'], settings['verbose'])
3113
3115
  else:
3114
3116
  # Apply the trained reducer to the entire dataset
3115
3117
  numeric_data = preprocess_data(all_df, settings['filter_by'], settings['remove_highly_correlated'], settings['log_data'], settings['exclude'])
@@ -3205,9 +3207,9 @@ def reducer_hyperparameter_search(settings={}, reduction_params=None, dbscan_par
3205
3207
 
3206
3208
  from .io import _read_and_join_tables
3207
3209
  from .utils import get_db_paths, preprocess_data, search_reduction_and_clustering, generate_colors
3208
- from .settings import get_umap_image_settings
3210
+ from .settings import set_default_umap_image_settings
3209
3211
 
3210
- settings = get_umap_image_settings(settings)
3212
+ settings = set_default_umap_image_settings(settings)
3211
3213
  pointsize = settings['dot_size']
3212
3214
  if isinstance(dbscan_params, dict):
3213
3215
  dbscan_params = [dbscan_params]
spacr/deep_spacr.py CHANGED
@@ -10,6 +10,9 @@ import matplotlib.pyplot as plt
10
10
  from PIL import Image
11
11
 
12
12
  from .logger import log_function_call
13
+ from .utils import close_multiprocessing_processes, reset_mp
14
+ #reset_mp()
15
+ #close_multiprocessing_processes()
13
16
 
14
17
  def evaluate_model_core(model, loader, loader_name, epoch, loss_type):
15
18
  """
@@ -42,7 +45,6 @@ def evaluate_model_core(model, loader, loader_name, epoch, loss_type):
42
45
  for batch_idx, (data, target, _) in enumerate(loader, start=1):
43
46
  start_time = time.time()
44
47
  data, target = data.to(device), target.to(device).float()
45
- #data, target = data.to(torch.float).to(device), target.to(device).float()
46
48
  output = model(data)
47
49
  loss += F.binary_cross_entropy_with_logits(output, target, reduction='sum').item()
48
50
  loss = calculate_loss(output, target, loss_type=loss_type)
spacr/gui.py CHANGED
@@ -1,52 +1,67 @@
1
1
  import tkinter as tk
2
2
  from tkinter import ttk
3
- from tkinter import font as tkFont
4
3
  from PIL import Image, ImageTk
5
- 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
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
+ from .app_annotate import initiate_annotation_app_root
9
+ from .app_make_masks import initiate_mask_app_root
16
10
 
17
11
  class MainApp(tk.Tk):
18
- def __init__(self):
12
+ def __init__(self, default_app=None):
19
13
  super().__init__()
14
+ width = self.winfo_screenwidth()
15
+ height = self.winfo_screenheight()
16
+ self.geometry(f"{width}x{height}")
20
17
  self.title("SpaCr GUI Collection")
21
- self.geometry("1100x1500")
22
18
  self.configure(bg="black")
23
- #self.attributes('-fullscreen', True)
24
-
25
19
  style = ttk.Style()
26
- style_text_boxes(style)
20
+ set_dark_style(style)
27
21
 
28
22
  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"),
23
+ "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
24
+ "Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
31
25
  "Annotate": (initiate_annotation_app_root, "Annotation single object images on a grid. Annotations are saved to database."),
32
26
  "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.")
27
+ "Classify": (lambda frame: initiate_root(frame, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
28
+ "Sequencing": (lambda frame: initiate_root(frame, 'sequencing'), "Analyze sequensing data."),
29
+ "Umap": (lambda frame: initiate_root(frame, 'umap'), "Generate UMAP embedings with datapoints represented as images.")
34
30
  }
35
31
 
36
32
  self.selected_app = tk.StringVar()
37
33
  self.create_widgets()
38
34
 
35
+
36
+ if default_app == "Mask":
37
+ self.load_app(default_app, self.gui_apps[default_app][0])
38
+ elif default_app == "Measure":
39
+ self.load_app(default_app, self.gui_apps[default_app][1])
40
+ elif default_app == "Annotate":
41
+ self.load_app(default_app, self.gui_apps[default_app][2])
42
+ elif default_app == "Make Masks":
43
+ self.load_app(default_app, self.gui_apps[default_app][3])
44
+ elif default_app == "Classify":
45
+ self.load_app(default_app, self.gui_apps[default_app][4])
46
+ elif default_app == "Sequencing":
47
+ self.load_app(default_app, self.gui_apps[default_app][5])
48
+ elif default_app == "Umap":
49
+ self.load_app(default_app, self.gui_apps[default_app][6])
50
+
39
51
  def create_widgets(self):
40
52
  # Create the menu bar
41
- #create_menu_bar(self)
53
+ create_menu_bar(self)
54
+
42
55
  # 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)
56
+ self.canvas = tk.Canvas(self, bg="black", highlightthickness=0)
44
57
  self.canvas.grid(row=0, column=0, sticky="nsew")
45
58
  self.grid_rowconfigure(0, weight=1)
46
59
  self.grid_columnconfigure(0, weight=1)
60
+
47
61
  # Create a frame inside the canvas to hold the main content
48
62
  self.content_frame = tk.Frame(self.canvas, bg="black")
49
63
  self.content_frame.pack(fill=tk.BOTH, expand=True)
64
+
50
65
  # Create startup screen with buttons for each GUI app
51
66
  self.create_startup_screen()
52
67
 
@@ -59,10 +74,10 @@ class MainApp(tk.Tk):
59
74
 
60
75
  # Load the logo image
61
76
  if not self.load_logo(logo_frame):
62
- tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Arial', 24, tkFont.NORMAL)).pack(padx=10, pady=10)
77
+ tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
63
78
 
64
79
  # Add SpaCr text below the logo with padding for sharper text
65
- tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Arial', 24, tkFont.NORMAL)).pack(padx=10, pady=10)
80
+ tk.Label(logo_frame, text="SpaCr", bg="black", fg="#008080", font=('Helvetica', 24)).pack(padx=10, pady=10)
66
81
 
67
82
  # Create a frame for the buttons and descriptions
68
83
  buttons_frame = tk.Frame(self.content_frame, bg="black")
@@ -72,10 +87,11 @@ class MainApp(tk.Tk):
72
87
  app_func, app_desc = app_data
73
88
 
74
89
  # Create custom button with text
75
- button = CustomButton(buttons_frame, text=app_name, command=lambda app_name=app_name: self.load_app(app_name))
90
+ 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))
91
+ #button = ttk.Button(buttons_frame, text=app_name, command=lambda app_name=app_name, app_func=app_func: self.load_app(app_name, app_func), style='Custom.TButton')
76
92
  button.grid(row=i, column=0, pady=10, padx=10, sticky="w")
77
93
 
78
- description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Arial', 10, tkFont.NORMAL))
94
+ description_label = tk.Label(buttons_frame, text=app_desc, bg="black", fg="white", wraplength=800, justify="left", font=('Helvetica', 12))
79
95
  description_label.grid(row=i, column=1, pady=10, padx=10, sticky="w")
80
96
 
81
97
  # Ensure buttons have a fixed width
@@ -98,7 +114,6 @@ class MainApp(tk.Tk):
98
114
 
99
115
  try:
100
116
  img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
101
- print(f"Trying to load logo from {img_path}")
102
117
  logo_image = Image.open(img_path)
103
118
  except (FileNotFoundError, Image.UnidentifiedImageError):
104
119
  print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
@@ -115,7 +130,9 @@ class MainApp(tk.Tk):
115
130
  print(f"An error occurred while loading the logo: {e}")
116
131
  return False
117
132
  try:
118
- logo_image = logo_image.resize((800, 800), Image.Resampling.LANCZOS)
133
+ screen_height = frame.winfo_screenheight()
134
+ new_height = int(screen_height // 4)
135
+ logo_image = logo_image.resize((new_height, new_height), Image.Resampling.LANCZOS)
119
136
  logo_photo = ImageTk.PhotoImage(logo_image)
120
137
  logo_label = tk.Label(frame, image=logo_photo, bg="black")
121
138
  logo_label.image = logo_photo # Keep a reference to avoid garbage collection
@@ -125,13 +142,14 @@ class MainApp(tk.Tk):
125
142
  print(f"An error occurred while processing the logo image: {e}")
126
143
  return False
127
144
 
128
- def load_app(self, app_name):
129
- selected_app_func, _ = self.gui_apps[app_name]
145
+ def load_app(self, app_name, app_func):
146
+ # Clear the current content frame
130
147
  self.clear_frame(self.content_frame)
131
148
 
149
+ # Initialize the selected app
132
150
  app_frame = tk.Frame(self.content_frame, bg="black")
133
151
  app_frame.pack(fill=tk.BOTH, expand=True)
134
- selected_app_func(app_frame, self.winfo_width(), self.winfo_height())
152
+ app_func(app_frame)
135
153
 
136
154
  def clear_frame(self, frame):
137
155
  for widget in frame.winfo_children():
@@ -142,4 +160,5 @@ def gui_app():
142
160
  app.mainloop()
143
161
 
144
162
  if __name__ == "__main__":
163
+ set_start_method('spawn', force=True)
145
164
  gui_app()
spacr/gui_2.py CHANGED
@@ -1,52 +1,93 @@
1
- import customtkinter as ctk
1
+ import tkinter as tk
2
+ from tkinter import ttk
3
+ from tkinter import font as tkFont
2
4
  from PIL import Image, ImageTk
3
5
  import os
4
6
  import requests
5
7
 
6
- class MainApp(ctk.CTk):
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
16
+
17
+ class MainApp(tk.Tk):
7
18
  def __init__(self):
8
19
  super().__init__()
9
20
  self.title("SpaCr GUI Collection")
10
- self.geometry("1200x800")
11
- ctk.set_appearance_mode("dark") # Modes: "System" (standard), "Dark", "Light"
12
- ctk.set_default_color_theme("dark-blue") # Themes: "blue" (standard), "green", "dark-blue")
13
-
14
- # Set scaling factor for high DPI displays; use a floating-point value.
15
- self.tk.call('tk', 'scaling', 1.5)
16
-
21
+ self.geometry("1100x1500")
22
+ self.configure(bg="black")
23
+ #self.attributes('-fullscreen', True)
24
+
25
+ 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.")
34
+ }
35
+
36
+ self.selected_app = tk.StringVar()
17
37
  self.create_widgets()
18
38
 
19
39
  def create_widgets(self):
20
- self.content_frame = ctk.CTkFrame(self)
21
- self.content_frame.pack(fill="both", expand=True, padx=20, pady=20)
40
+ # Create the menu bar
41
+ #create_menu_bar(self)
42
+ # 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)
44
+ self.canvas.grid(row=0, column=0, sticky="nsew")
45
+ self.grid_rowconfigure(0, weight=1)
46
+ self.grid_columnconfigure(0, weight=1)
47
+ # 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
51
+ self.create_startup_screen()
22
52
 
23
- logo_frame = ctk.CTkFrame(self.content_frame)
53
+ def create_startup_screen(self):
54
+ self.clear_frame(self.content_frame)
55
+
56
+ # Create a frame for the logo and description
57
+ logo_frame = tk.Frame(self.content_frame, bg="black")
24
58
  logo_frame.pack(pady=20, expand=True)
25
59
 
60
+ # Load the logo image
26
61
  if not self.load_logo(logo_frame):
27
- ctk.CTkLabel(logo_frame, text="Logo not found", text_color="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
28
-
29
- ctk.CTkLabel(logo_frame, text="SpaCr", text_color="#00BFFF", font=('Helvetica', 36, "bold")).pack(padx=10, pady=10)
30
-
31
- button = ctk.CTkButton(
32
- self.content_frame,
33
- text="Mask",
34
- command=self.load_mask_app,
35
- width=250,
36
- height=60,
37
- corner_radius=20,
38
- fg_color="#1E90FF",
39
- hover_color="#4682B4",
40
- text_color="white",
41
- font=("Helvetica", 18, "bold")
42
- )
43
- button.pack(pady=20)
62
+ tk.Label(logo_frame, text="Logo not found", bg="black", fg="white", font=('Helvetica', 24)).pack(padx=10, pady=10)
63
+
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)
66
+
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)
70
+
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)
44
85
 
45
86
  def load_logo(self, frame):
46
87
  def download_image(url, save_path):
47
88
  try:
48
89
  response = requests.get(url, stream=True)
49
- response.raise_for_status()
90
+ response.raise_for_status() # Raise an HTTPError for bad responses
50
91
  with open(save_path, 'wb') as f:
51
92
  for chunk in response.iter_content(chunk_size=8192):
52
93
  f.write(chunk)
@@ -57,34 +98,63 @@ class MainApp(ctk.CTk):
57
98
 
58
99
  try:
59
100
  img_path = os.path.join(os.path.dirname(__file__), 'logo_spacr.png')
101
+ print(f"Trying to load logo from {img_path}")
60
102
  logo_image = Image.open(img_path)
61
103
  except (FileNotFoundError, Image.UnidentifiedImageError):
104
+ print(f"File {img_path} not found or is not a valid image. Attempting to download from GitHub.")
62
105
  if download_image('https://raw.githubusercontent.com/EinarOlafsson/spacr/main/spacr/logo_spacr.png', img_path):
63
106
  try:
107
+ print(f"Downloaded file size: {os.path.getsize(img_path)} bytes")
64
108
  logo_image = Image.open(img_path)
65
109
  except Image.UnidentifiedImageError as e:
110
+ print(f"Downloaded file is not a valid image: {e}")
66
111
  return False
67
112
  else:
68
113
  return False
69
114
  except Exception as e:
115
+ print(f"An error occurred while loading the logo: {e}")
70
116
  return False
71
-
72
117
  try:
73
- logo_image = logo_image.resize((200, 200), Image.Resampling.LANCZOS)
118
+ logo_image = logo_image.resize((800, 800), Image.Resampling.LANCZOS)
74
119
  logo_photo = ImageTk.PhotoImage(logo_image)
75
- logo_label = ctk.CTkLabel(frame, image=logo_photo)
120
+ logo_label = tk.Label(frame, image=logo_photo, bg="black")
76
121
  logo_label.image = logo_photo # Keep a reference to avoid garbage collection
77
122
  logo_label.pack()
78
123
  return True
79
124
  except Exception as e:
125
+ print(f"An error occurred while processing the logo image: {e}")
80
126
  return False
81
127
 
82
- def load_mask_app(self):
83
- print("Mask app loaded.") # Placeholder for mask app loading function
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)
135
+
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
140
+
141
+ # Clear the current content frame
142
+ for widget in root.content_frame.winfo_children():
143
+ widget.destroy()
144
+
145
+ # Initialize the selected app
146
+ app_frame = tk.Frame(root.content_frame, bg="black")
147
+ app_frame.pack(fill=tk.BOTH, expand=True)
148
+ app_func(app_frame)
149
+
150
+ def clear_frame(self, frame):
151
+ for widget in frame.winfo_children():
152
+ widget.destroy()
153
+
84
154
 
85
155
  def gui_app():
86
156
  app = MainApp()
87
157
  app.mainloop()
88
158
 
89
159
  if __name__ == "__main__":
90
- gui_app()
160
+ gui_app()