spacr 0.1.55__py3-none-any.whl → 0.1.62__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,324 @@
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 = 50 #screen_height // 50
106
+ button_width = 140 #button_height * 3
107
+
108
+ #print(button_height, button_width)
109
+
110
+ # Increase the canvas size to accommodate the button and the rim
111
+ self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg="black")
112
+ self.canvas.grid(row=0, column=0)
113
+
114
+ self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
115
+
116
+ self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
117
+ self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill="white", font=self.font_style)
118
+
119
+ self.bind("<Enter>", self.on_enter)
120
+ self.bind("<Leave>", self.on_leave)
121
+ self.bind("<Button-1>", self.on_click)
122
+ self.canvas.bind("<Enter>", self.on_enter)
123
+ self.canvas.bind("<Leave>", self.on_leave)
124
+ self.canvas.bind("<Button-1>", self.on_click)
125
+
126
+ def on_enter(self, event=None):
127
+ self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
128
+
129
+ def on_leave(self, event=None):
130
+ self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
131
+
132
+ def on_click(self, event=None):
133
+ if self.command:
134
+ self.command()
135
+
136
+ def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
137
+ points = [
138
+ x1 + radius, y1,
139
+ x1 + radius, y1,
140
+ x2 - radius, y1,
141
+ x2 - radius, y1,
142
+ x2, y1,
143
+ x2, y1 + radius,
144
+ x2, y1 + radius,
145
+ x2, y2 - radius,
146
+ x2, y2 - radius,
147
+ x2, y2,
148
+ x2 - radius, y2,
149
+ x2 - radius, y2,
150
+ x1 + radius, y2,
151
+ x1 + radius, y2,
152
+ x1, y2,
153
+ x1, y2 - radius,
154
+ x1, y2 - radius,
155
+ x1, y1 + radius,
156
+ x1, y1 + radius,
157
+ x1, y1
158
+ ]
159
+ return self.canvas.create_polygon(points, **kwargs, smooth=True)
160
+
161
+ class spacrSwitch(ttk.Frame):
162
+ def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
163
+ super().__init__(parent, *args, **kwargs)
164
+ self.text = text
165
+ self.variable = variable if variable else tk.BooleanVar()
166
+ self.command = command
167
+ self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
168
+ self.canvas.grid(row=0, column=1, padx=(10, 0))
169
+ self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
170
+ self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
171
+ self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
172
+ self.label.grid(row=0, column=0, padx=(0, 10))
173
+ self.bind("<Button-1>", self.toggle)
174
+ self.canvas.bind("<Button-1>", self.toggle)
175
+ self.label.bind("<Button-1>", self.toggle)
176
+ self.update_switch()
177
+
178
+ def toggle(self, event=None):
179
+ self.variable.set(not self.variable.get())
180
+ self.animate_switch()
181
+ if self.command:
182
+ self.command()
183
+
184
+ def update_switch(self):
185
+ if self.variable.get():
186
+ self.canvas.itemconfig(self.switch, fill="#008080") # Teal
187
+ self.canvas.coords(self.switch, 24, 4, 36, 16) # Move switch to the right
188
+ else:
189
+ self.canvas.itemconfig(self.switch, fill="#800080") # Purple
190
+ self.canvas.coords(self.switch, 4, 4, 16, 16) # Move switch to the left
191
+
192
+ def animate_switch(self):
193
+ if self.variable.get():
194
+ start_x, end_x = 4, 24
195
+ final_color = "#008080" # Teal
196
+ else:
197
+ start_x, end_x = 24, 4
198
+ final_color = "#800080" # Purple
199
+
200
+ self.animate_movement(start_x, end_x, final_color)
201
+
202
+ def animate_movement(self, start_x, end_x, final_color):
203
+ step = 1 if start_x < end_x else -1
204
+ for i in range(start_x, end_x, step):
205
+ self.canvas.coords(self.switch, i, 4, i + 12, 16)
206
+ self.canvas.update()
207
+ self.after(10) # Small delay for smooth animation
208
+ self.canvas.itemconfig(self.switch, fill=final_color)
209
+
210
+ def get(self):
211
+ return self.variable.get()
212
+
213
+ def set(self, value):
214
+ self.variable.set(value)
215
+ self.update_switch()
216
+
217
+ def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs): # Smaller radius for smaller switch
218
+ points = [x1 + radius, y1,
219
+ x1 + radius, y1,
220
+ x2 - radius, y1,
221
+ x2 - radius, y1,
222
+ x2, y1,
223
+ x2, y1 + radius,
224
+ x2, y1 + radius,
225
+ x2, y2 - radius,
226
+ x2, y2 - radius,
227
+ x2, y2,
228
+ x2 - radius, y2,
229
+ x2 - radius, y2,
230
+ x1 + radius, y2,
231
+ x1 + radius, y2,
232
+ x1, y2,
233
+ x1, y2 - radius,
234
+ x1, y2 - radius,
235
+ x1, y1 + radius,
236
+ x1, y1 + radius,
237
+ x1, y1]
238
+
239
+ return self.canvas.create_polygon(points, **kwargs, smooth=True)
240
+
241
+ class spacrToolTip:
242
+ def __init__(self, widget, text):
243
+ self.widget = widget
244
+ self.text = text
245
+ self.tooltip_window = None
246
+ widget.bind("<Enter>", self.show_tooltip)
247
+ widget.bind("<Leave>", self.hide_tooltip)
248
+
249
+ def show_tooltip(self, event):
250
+ x = event.x_root + 20
251
+ y = event.y_root + 10
252
+ self.tooltip_window = tk.Toplevel(self.widget)
253
+ self.tooltip_window.wm_overrideredirect(True)
254
+ self.tooltip_window.wm_geometry(f"+{x}+{y}")
255
+ self.tooltip_window.configure(bg='black')
256
+ label = tk.Label(self.tooltip_window, text=self.text, background="#333333", foreground="white", relief='flat', borderwidth=0)
257
+ label.grid(row=0, column=0, padx=5, pady=5)
258
+
259
+ def hide_tooltip(self, event):
260
+ if self.tooltip_window:
261
+ self.tooltip_window.destroy()
262
+ self.tooltip_window = None
263
+
264
+ def create_menu_bar(root):
265
+ from .app_annotate import initiate_annotation_app_root
266
+ from .app_make_masks import initiate_mask_app_root
267
+ from .gui_utils import load_app
268
+
269
+ gui_apps = {
270
+ "Mask": 'mask',
271
+ "Measure": 'measure',
272
+ "Annotate": initiate_annotation_app_root,
273
+ "Make Masks": initiate_mask_app_root,
274
+ "Classify": 'classify',
275
+ "Sequencing": 'sequencing',
276
+ "Umap": 'umap'
277
+ }
278
+
279
+ def load_app_wrapper(app_name, app_func):
280
+ load_app(root, app_name, app_func)
281
+
282
+ # Create the menu bar
283
+ menu_bar = tk.Menu(root, bg="#008080", fg="white")
284
+ # Create a "SpaCr Applications" menu
285
+ app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
286
+ menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
287
+ # Add options to the "SpaCr Applications" menu
288
+ for app_name, app_func in gui_apps.items():
289
+ app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
290
+ # Add a separator and an exit option
291
+ app_menu.add_separator()
292
+ app_menu.add_command(label="Exit", command=root.quit)
293
+ # Configure the menu for the root window
294
+ root.config(menu=menu_bar)
295
+
296
+ def set_dark_style(style):
297
+ font_style = tkFont.Font(family="Helvetica", size=24)
298
+ style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
299
+ style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', font=font_style)
300
+ style.map('TCombobox', fieldbackground=[('readonly', 'black')], foreground=[('readonly', '#ffffff')])
301
+ style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
302
+ style.map('Custom.TButton', background=[('active', 'teal'), ('!active', 'black')], foreground=[('active', 'white'), ('!active', 'white')], bordercolor=[('active', 'white'), ('!active', 'white')])
303
+ style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style)
304
+ style.configure('Spacr.TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font="15")
305
+ style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
306
+ style.configure('TLabel', background='black', foreground='#ffffff', font=font_style)
307
+ style.configure('TFrame', background='black')
308
+ style.configure('TPanedwindow', background='black')
309
+ style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0])
310
+ style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
311
+ style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
312
+ style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style)
313
+ style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
314
+ style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black')
315
+ style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black')
316
+ style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
317
+ style.configure('Custom.TLabelFrame.Label', background='black', foreground='white', font=('Helvetica', 10, 'bold'))
318
+
319
+ def set_default_font(root, font_name="Helvetica", size=12):
320
+ default_font = (font_name, size)
321
+ root.option_add("*Font", default_font)
322
+ root.option_add("*TButton.Font", default_font)
323
+ root.option_add("*TLabel.Font", default_font)
324
+ 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