spacr 0.1.50__py3-none-any.whl → 0.1.61__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/gui_elements.py ADDED
@@ -0,0 +1,322 @@
1
+ import tkinter as tk
2
+ from tkinter import ttk
3
+ import tkinter.font as tkFont
4
+
5
+ class spacrDropdownMenu(tk.OptionMenu):
6
+ def __init__(self, parent, variable, options, command=None, **kwargs):
7
+ self.variable = variable
8
+ self.variable.set("Select Category")
9
+ super().__init__(parent, self.variable, *options, command=command, **kwargs)
10
+ self.config(bg='black', fg='white', font=('Helvetica', 12), indicatoron=0)
11
+ self.menu = self['menu']
12
+ self.menu.config(bg='black', fg='white', font=('Helvetica', 12))
13
+
14
+ def update_styles(self, active_categories):
15
+ menu = self['menu']
16
+ for idx, option in enumerate(self['menu'].entrycget(idx, "label") for idx in range(self['menu'].index("end")+1)):
17
+ if option in active_categories:
18
+ menu.entryconfig(idx, background='teal', foreground='white')
19
+ else:
20
+ menu.entryconfig(idx, background='black', foreground='white')
21
+
22
+ class spacrCheckbutton(ttk.Checkbutton):
23
+ def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
24
+ super().__init__(parent, *args, **kwargs)
25
+ self.text = text
26
+ self.variable = variable if variable else tk.BooleanVar()
27
+ self.command = command
28
+ self.configure(text=self.text, variable=self.variable, command=self.command, style='Spacr.TCheckbutton')
29
+
30
+ class spacrFrame(ttk.Frame):
31
+ def __init__(self, container, width=None, *args, bg='black', **kwargs):
32
+ super().__init__(container, *args, **kwargs)
33
+ self.configure(style='TFrame')
34
+ if width is None:
35
+ screen_width = self.winfo_screenwidth()
36
+ width = screen_width // 4
37
+ canvas = tk.Canvas(self, bg=bg, width=width)
38
+ scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
39
+
40
+ self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
41
+ self.scrollable_frame.bind(
42
+ "<Configure>",
43
+ lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
44
+ )
45
+ canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
46
+ canvas.configure(yscrollcommand=scrollbar.set)
47
+
48
+ canvas.grid(row=0, column=0, sticky="nsew")
49
+ scrollbar.grid(row=0, column=1, sticky="ns")
50
+
51
+ self.grid_rowconfigure(0, weight=1)
52
+ self.grid_columnconfigure(0, weight=1)
53
+ self.grid_columnconfigure(1, weight=0)
54
+
55
+ for child in self.scrollable_frame.winfo_children():
56
+ child.configure(bg='black')
57
+
58
+ class spacrLabel(tk.Frame):
59
+ def __init__(self, parent, text="", font=None, style=None, align="right", **kwargs):
60
+ label_kwargs = {k: v for k, v in kwargs.items() if k in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
61
+ for key in label_kwargs.keys():
62
+ kwargs.pop(key)
63
+ super().__init__(parent, **kwargs)
64
+ self.text = text
65
+ self.kwargs = label_kwargs
66
+ self.align = align
67
+ screen_height = self.winfo_screenheight()
68
+ label_height = screen_height // 50
69
+ label_width = label_height * 10
70
+ self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=self.kwargs.get("background", "black"))
71
+ self.canvas.grid(row=0, column=0, sticky="ew")
72
+
73
+ self.font_style = font if font else tkFont.Font(family=self.kwargs.get("font_family", "Helvetica"), size=self.kwargs.get("font_size", 12), weight=tkFont.NORMAL)
74
+ self.style = style
75
+
76
+ if self.align == "center":
77
+ anchor_value = tk.CENTER
78
+ text_anchor = 'center'
79
+ else: # default to right alignment
80
+ anchor_value = tk.E
81
+ text_anchor = 'e'
82
+
83
+ if self.style:
84
+ ttk_style = ttk.Style()
85
+ ttk_style.configure(self.style, **label_kwargs)
86
+ self.label_text = ttk.Label(self.canvas, text=self.text, style=self.style, anchor=text_anchor, justify=text_anchor)
87
+ self.label_text.pack(fill=tk.BOTH, expand=True)
88
+ else:
89
+ self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
90
+ label_height // 2, text=self.text, fill=self.kwargs.get("foreground", "white"),
91
+ font=self.font_style, anchor=anchor_value, justify=tk.RIGHT)
92
+
93
+ def set_text(self, text):
94
+ if self.style:
95
+ self.label_text.config(text=text)
96
+ else:
97
+ self.canvas.itemconfig(self.label_text, text=text)
98
+
99
+ class spacrButton(tk.Frame):
100
+ def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
101
+ super().__init__(parent, *args, **kwargs)
102
+ self.text = text
103
+ self.command = command
104
+ screen_height = self.winfo_screenheight()
105
+ button_height = screen_height // 50
106
+ button_width = button_height * 3
107
+
108
+ # Increase the canvas size to accommodate the button and the rim
109
+ self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg="black")
110
+ self.canvas.grid(row=0, column=0)
111
+
112
+ self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
113
+
114
+ self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
115
+ self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill="white", font=self.font_style)
116
+
117
+ self.bind("<Enter>", self.on_enter)
118
+ self.bind("<Leave>", self.on_leave)
119
+ self.bind("<Button-1>", self.on_click)
120
+ self.canvas.bind("<Enter>", self.on_enter)
121
+ self.canvas.bind("<Leave>", self.on_leave)
122
+ self.canvas.bind("<Button-1>", self.on_click)
123
+
124
+ def on_enter(self, event=None):
125
+ self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
126
+
127
+ def on_leave(self, event=None):
128
+ self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
129
+
130
+ def on_click(self, event=None):
131
+ if self.command:
132
+ self.command()
133
+
134
+ def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
135
+ points = [
136
+ x1 + radius, y1,
137
+ x1 + radius, y1,
138
+ x2 - radius, y1,
139
+ x2 - radius, y1,
140
+ x2, y1,
141
+ x2, y1 + radius,
142
+ x2, y1 + radius,
143
+ x2, y2 - radius,
144
+ x2, y2 - radius,
145
+ x2, y2,
146
+ x2 - radius, y2,
147
+ x2 - radius, y2,
148
+ x1 + radius, y2,
149
+ x1 + radius, y2,
150
+ x1, y2,
151
+ x1, y2 - radius,
152
+ x1, y2 - radius,
153
+ x1, y1 + radius,
154
+ x1, y1 + radius,
155
+ x1, y1
156
+ ]
157
+ return self.canvas.create_polygon(points, **kwargs, smooth=True)
158
+
159
+ class spacrSwitch(ttk.Frame):
160
+ def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
161
+ super().__init__(parent, *args, **kwargs)
162
+ self.text = text
163
+ self.variable = variable if variable else tk.BooleanVar()
164
+ self.command = command
165
+ self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
166
+ self.canvas.grid(row=0, column=1, padx=(10, 0))
167
+ self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
168
+ self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
169
+ self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
170
+ self.label.grid(row=0, column=0, padx=(0, 10))
171
+ self.bind("<Button-1>", self.toggle)
172
+ self.canvas.bind("<Button-1>", self.toggle)
173
+ self.label.bind("<Button-1>", self.toggle)
174
+ self.update_switch()
175
+
176
+ def toggle(self, event=None):
177
+ self.variable.set(not self.variable.get())
178
+ self.animate_switch()
179
+ if self.command:
180
+ self.command()
181
+
182
+ def update_switch(self):
183
+ if self.variable.get():
184
+ self.canvas.itemconfig(self.switch, fill="#008080") # Teal
185
+ self.canvas.coords(self.switch, 24, 4, 36, 16) # Move switch to the right
186
+ else:
187
+ self.canvas.itemconfig(self.switch, fill="#800080") # Purple
188
+ self.canvas.coords(self.switch, 4, 4, 16, 16) # Move switch to the left
189
+
190
+ def animate_switch(self):
191
+ if self.variable.get():
192
+ start_x, end_x = 4, 24
193
+ final_color = "#008080" # Teal
194
+ else:
195
+ start_x, end_x = 24, 4
196
+ final_color = "#800080" # Purple
197
+
198
+ self.animate_movement(start_x, end_x, final_color)
199
+
200
+ def animate_movement(self, start_x, end_x, final_color):
201
+ step = 1 if start_x < end_x else -1
202
+ for i in range(start_x, end_x, step):
203
+ self.canvas.coords(self.switch, i, 4, i + 12, 16)
204
+ self.canvas.update()
205
+ self.after(10) # Small delay for smooth animation
206
+ self.canvas.itemconfig(self.switch, fill=final_color)
207
+
208
+ def get(self):
209
+ return self.variable.get()
210
+
211
+ def set(self, value):
212
+ self.variable.set(value)
213
+ self.update_switch()
214
+
215
+ def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs): # Smaller radius for smaller switch
216
+ points = [x1 + radius, y1,
217
+ x1 + radius, y1,
218
+ x2 - radius, y1,
219
+ x2 - radius, y1,
220
+ x2, y1,
221
+ x2, y1 + radius,
222
+ x2, y1 + radius,
223
+ x2, y2 - radius,
224
+ x2, y2 - radius,
225
+ x2, y2,
226
+ x2 - radius, y2,
227
+ x2 - radius, y2,
228
+ x1 + radius, y2,
229
+ x1 + radius, y2,
230
+ x1, y2,
231
+ x1, y2 - radius,
232
+ x1, y2 - radius,
233
+ x1, y1 + radius,
234
+ x1, y1 + radius,
235
+ x1, y1]
236
+
237
+ return self.canvas.create_polygon(points, **kwargs, smooth=True)
238
+
239
+ class spacrToolTip:
240
+ def __init__(self, widget, text):
241
+ self.widget = widget
242
+ self.text = text
243
+ self.tooltip_window = None
244
+ widget.bind("<Enter>", self.show_tooltip)
245
+ widget.bind("<Leave>", self.hide_tooltip)
246
+
247
+ def show_tooltip(self, event):
248
+ x = event.x_root + 20
249
+ y = event.y_root + 10
250
+ self.tooltip_window = tk.Toplevel(self.widget)
251
+ self.tooltip_window.wm_overrideredirect(True)
252
+ self.tooltip_window.wm_geometry(f"+{x}+{y}")
253
+ self.tooltip_window.configure(bg='black')
254
+ label = tk.Label(self.tooltip_window, text=self.text, background="#333333", foreground="white", relief='flat', borderwidth=0)
255
+ label.grid(row=0, column=0, padx=5, pady=5)
256
+
257
+ def hide_tooltip(self, event):
258
+ if self.tooltip_window:
259
+ self.tooltip_window.destroy()
260
+ self.tooltip_window = None
261
+
262
+ def create_menu_bar(root):
263
+ from .app_annotate import initiate_annotation_app_root
264
+ from .app_make_masks import initiate_mask_app_root
265
+ from .gui_utils import load_app
266
+
267
+ gui_apps = {
268
+ "Mask": 'mask',
269
+ "Measure": 'measure',
270
+ "Annotate": initiate_annotation_app_root,
271
+ "Make Masks": initiate_mask_app_root,
272
+ "Classify": 'classify',
273
+ "Sequencing": 'sequencing',
274
+ "Umap": 'umap'
275
+ }
276
+
277
+ def load_app_wrapper(app_name, app_func):
278
+ load_app(root, app_name, app_func)
279
+
280
+ # Create the menu bar
281
+ menu_bar = tk.Menu(root, bg="#008080", fg="white")
282
+ # Create a "SpaCr Applications" menu
283
+ app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
284
+ menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
285
+ # Add options to the "SpaCr Applications" menu
286
+ for app_name, app_func in gui_apps.items():
287
+ app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
288
+ # Add a separator and an exit option
289
+ app_menu.add_separator()
290
+ app_menu.add_command(label="Exit", command=root.quit)
291
+ # Configure the menu for the root window
292
+ root.config(menu=menu_bar)
293
+
294
+ def set_dark_style(style):
295
+ font_style = tkFont.Font(family="Helvetica", size=24)
296
+ style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
297
+ style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
298
+ style.map('TCombobox', fieldbackground=[('readonly', 'black')], foreground=[('readonly', '#ffffff')])
299
+ style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
300
+ style.map('Custom.TButton', background=[('active', 'teal'), ('!active', 'black')], foreground=[('active', 'white'), ('!active', 'white')], bordercolor=[('active', 'white'), ('!active', 'white')])
301
+ style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style)
302
+ style.configure('Spacr.TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font="15")
303
+ style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
304
+ style.configure('TLabel', background='black', foreground='#ffffff', font=font_style)
305
+ style.configure('TFrame', background='black')
306
+ style.configure('TPanedwindow', background='black')
307
+ style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0])
308
+ style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
309
+ style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
310
+ style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style)
311
+ style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
312
+ style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black')
313
+ style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black')
314
+ style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
315
+ style.configure('Custom.TLabelFrame.Label', background='black', foreground='white', font=('Helvetica', 10, 'bold'))
316
+
317
+ def set_default_font(root, font_name="Helvetica", size=12):
318
+ default_font = (font_name, size)
319
+ root.option_add("*Font", default_font)
320
+ root.option_add("*TButton.Font", default_font)
321
+ root.option_add("*TLabel.Font", default_font)
322
+ root.option_add("*TEntry.Font", default_font)
spacr/gui_run.py ADDED
@@ -0,0 +1,58 @@
1
+ import traceback
2
+ from .gui_wrappers import measure_crop_wrapper, preprocess_generate_masks_wrapper, train_test_model_wrapper, umap_wrapper, sequencing_wrapper
3
+
4
+ def run_mask_gui(settings, q, fig_queue, stop_requested):
5
+ from .gui_utils import process_stdout_stderr
6
+ process_stdout_stderr(q)
7
+ try:
8
+ preprocess_generate_masks_wrapper(settings, q, fig_queue)
9
+ except Exception as e:
10
+ q.put(f"Error during processing: {e}")
11
+ traceback.print_exc()
12
+ finally:
13
+ stop_requested.value = 1
14
+
15
+ def run_sequencing_gui(settings, q, fig_queue, stop_requested):
16
+ from .gui_utils import process_stdout_stderr
17
+ process_stdout_stderr(q)
18
+ try:
19
+ sequencing_wrapper(settings, q, fig_queue)
20
+ except Exception as e:
21
+ q.put(f"Error during processing: {e}")
22
+ traceback.print_exc()
23
+ finally:
24
+ stop_requested.value = 1
25
+
26
+ def run_umap_gui(settings, q, fig_queue, stop_requested):
27
+ from .gui_utils import process_stdout_stderr
28
+ process_stdout_stderr(q)
29
+ try:
30
+ umap_wrapper(settings, q, fig_queue)
31
+ except Exception as e:
32
+ q.put(f"Error during processing: {e}")
33
+ traceback.print_exc()
34
+ finally:
35
+ stop_requested.value = 1
36
+
37
+ def run_measure_gui(settings, q, fig_queue, stop_requested):
38
+ from .gui_utils import process_stdout_stderr
39
+ process_stdout_stderr(q)
40
+ try:
41
+ settings['input_folder'] = settings['src']
42
+ measure_crop_wrapper(settings=settings, q=q, fig_queue=fig_queue)
43
+ except Exception as e:
44
+ q.put(f"Error during processing: {e}")
45
+ traceback.print_exc()
46
+ finally:
47
+ stop_requested.value = 1
48
+
49
+ def run_classify_gui(settings, q, fig_queue, stop_requested):
50
+ from .gui_utils import process_stdout_stderr
51
+ process_stdout_stderr(q)
52
+ try:
53
+ train_test_model_wrapper(settings['src'], settings)
54
+ except Exception as e:
55
+ q.put(f"Error during processing: {e}")
56
+ traceback.print_exc()
57
+ finally:
58
+ stop_requested.value = 1