spacr 0.2.21__py3-none-any.whl → 0.2.32__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 CHANGED
@@ -3,7 +3,7 @@ import tkinter as tk
3
3
  from tkinter import ttk
4
4
  import tkinter.font as tkFont
5
5
  from queue import Queue
6
- from tkinter import Label
6
+ from tkinter import Label, Frame, Button
7
7
  import numpy as np
8
8
  from PIL import Image, ImageOps, ImageTk
9
9
  from concurrent.futures import ThreadPoolExecutor
@@ -15,14 +15,20 @@ 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
19
18
 
19
+ def set_default_font(root, font_name="Arial", size=12):
20
+ default_font = (font_name, size)
21
+ root.option_add("*Font", default_font)
22
+ root.option_add("*TButton.Font", default_font)
23
+ root.option_add("*TLabel.Font", default_font)
24
+ root.option_add("*TEntry.Font", default_font)
25
+
20
26
  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
27
 
22
28
  if active_color == 'teal':
23
29
  active_color = '#008080'
24
30
  if inactive_color == 'dark_gray':
25
- inactive_color = '#050505'
31
+ inactive_color = '#2B2B2B' # '#333333' #'#050505'
26
32
  if bg_color == 'black':
27
33
  bg_color = '#000000'
28
34
  if fg_color == 'white':
@@ -30,28 +36,39 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
30
36
  if active_color == 'blue':
31
37
  active_color = '#007BFF'
32
38
 
33
- style.theme_use('clam')
39
+ padding = '5 5 5 5'
34
40
  font_style = tkFont.Font(family=font_family, size=font_size)
35
- style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground=bg_color, foreground=fg_color, font=font_style)
36
- style.configure('TCombobox', fieldbackground=bg_color, background=bg_color, foreground=fg_color, selectbackground=bg_color, selectforeground=fg_color, font=font_style)
37
- style.map('TCombobox', fieldbackground=[('readonly', bg_color)], foreground=[('readonly', fg_color)], selectbackground=[('readonly', bg_color)], selectforeground=[('readonly', fg_color)])
38
- style.configure('Custom.TButton', background=bg_color, foreground=fg_color, bordercolor=fg_color, focusthickness=3, focuscolor=fg_color, font=(font_family, font_size))
39
- style.map('Custom.TButton', background=[('active', active_color), ('!active', bg_color)], foreground=[('active', fg_color), ('!active', fg_color)], bordercolor=[('active', fg_color), ('!active', fg_color)])
40
- style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background=bg_color, foreground=fg_color, font=font_style)
41
- style.configure('Spacr.TCheckbutton', background=bg_color, foreground=fg_color, indicatoron=False, relief='flat', font="15")
42
- style.map('Spacr.TCheckbutton', background=[('selected', bg_color), ('active', bg_color)], foreground=[('selected', fg_color), ('active', fg_color)])
43
- style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_style)
41
+
42
+ style.theme_use('clam')
43
+
44
+ style.configure('TEntry', padding=padding)
45
+ style.configure('TCombobox', padding=padding)
46
+ style.configure('Spacr.TEntry', padding=padding)
47
+ style.configure('TEntry', padding=padding)
48
+ style.configure('Spacr.TEntry', padding=padding)
49
+ style.configure('Custom.TLabel', padding=padding)
50
+ #style.configure('Spacr.TCheckbutton', padding=padding)
51
+ style.configure('TButton', padding=padding)
52
+
44
53
  style.configure('TFrame', background=bg_color)
45
54
  style.configure('TPanedwindow', background=bg_color)
46
- style.configure('TNotebook', background=bg_color, tabmargins=[2, 5, 2, 0])
47
- style.configure('TNotebook.Tab', background=bg_color, foreground=fg_color, padding=[5, 5], font=font_style)
48
- style.map('TNotebook.Tab', background=[('selected', active_color), ('active', active_color)], foreground=[('selected', fg_color), ('active', fg_color)])
49
- style.configure('TButton', background=bg_color, foreground=fg_color, padding='5 5 5 5', font=font_style)
50
- style.map('TButton', background=[('active', active_color), ('disabled', inactive_color)])
51
- style.configure('Vertical.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
52
- style.configure('Horizontal.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
53
- style.configure('Custom.TLabelFrame', font=(font_family, font_size, 'bold'), background=bg_color, foreground='white', relief='solid', borderwidth=1)
54
- style.configure('Custom.TLabelFrame.Label', background=bg_color, foreground='white', font=(font_family, font_size, 'bold'))
55
+ style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_style)
56
+
57
+
58
+ #style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background=bg_color, foreground=fg_color, font=font_style)
59
+ #style.configure('Spacr.TCheckbutton', background=bg_color, foreground=fg_color, indicatoron=False, relief='flat', font="15")
60
+ #style.map('Spacr.TCheckbutton', background=[('selected', bg_color), ('active', bg_color)], foreground=[('selected', fg_color), ('active', fg_color)])
61
+
62
+
63
+ #style.configure('TNotebook', background=bg_color, tabmargins=[2, 5, 2, 0])
64
+ #style.configure('TNotebook.Tab', background=bg_color, foreground=fg_color, padding=[5, 5], font=font_style)
65
+ #style.map('TNotebook.Tab', background=[('selected', active_color), ('active', active_color)], foreground=[('selected', fg_color), ('active', fg_color)])
66
+ #style.configure('TButton', background=bg_color, foreground=fg_color, padding='5 5 5 5', font=font_style)
67
+ #style.map('TButton', background=[('active', active_color), ('disabled', inactive_color)])
68
+ #style.configure('Vertical.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
69
+ #style.configure('Horizontal.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
70
+ #style.configure('Custom.TLabelFrame', font=(font_family, font_size, 'bold'), background=bg_color, foreground='white', relief='solid', borderwidth=1)
71
+ #style.configure('Custom.TLabelFrame.Label', background=bg_color, foreground='white', font=(font_family, font_size, 'bold'))
55
72
 
56
73
  if parent_frame:
57
74
  parent_frame.configure(bg=bg_color)
@@ -82,20 +99,245 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
82
99
 
83
100
  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}
84
101
 
85
- def set_default_font(root, font_name="Arial", size=12):
86
- default_font = (font_name, size)
87
- root.option_add("*Font", default_font)
88
- root.option_add("*TButton.Font", default_font)
89
- root.option_add("*TLabel.Font", default_font)
90
- root.option_add("*TEntry.Font", default_font)
102
+ class spacrEntry(tk.Frame):
103
+ def __init__(self, parent, textvariable=None, outline=False, *args, **kwargs):
104
+ super().__init__(parent, *args, **kwargs)
105
+
106
+ # Set dark style
107
+ style_out = set_dark_style(ttk.Style())
108
+ self.bg_color = style_out['inactive_color']
109
+ self.active_color = style_out['active_color']
110
+ self.fg_color = style_out['fg_color']
111
+ self.outline = outline
112
+ self.font_family = style_out['font_family']
113
+ self.font_size = style_out['font_size']
114
+
115
+ # Set the background color of the frame
116
+ self.configure(bg=style_out['bg_color'])
117
+
118
+ # Create a canvas for the rounded rectangle background
119
+ self.canvas_width = 220 # Adjusted for padding
120
+ self.canvas_height = 40 # Adjusted for padding
121
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=style_out['bg_color'])
122
+ self.canvas.pack()
123
+
124
+ self.entry = tk.Entry(self, textvariable=textvariable, bd=0, highlightthickness=0, fg=self.fg_color, font=(self.font_family, self.font_size), bg=self.bg_color)
125
+ self.entry.place(relx=0.5, rely=0.5, anchor=tk.CENTER, width=190, height=20) # Centered positioning
126
+
127
+ # Bind events to change the background color on focus
128
+ self.entry.bind("<FocusIn>", self.on_focus_in)
129
+ self.entry.bind("<FocusOut>", self.on_focus_out)
130
+
131
+ self.draw_rounded_rectangle(self.bg_color)
132
+
133
+ def draw_rounded_rectangle(self, color):
134
+ radius = 15 # Increased radius for more rounded corners
135
+ x0, y0 = 10, 5
136
+ x1, y1 = 210, 35
137
+ self.canvas.delete("all")
138
+ self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
139
+ self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
140
+ self.canvas.create_arc((x0, y1 - radius, x0 + radius, y1), start=180, extent=90, fill=color, outline=color)
141
+ self.canvas.create_arc((x1 - radius, y1 - radius, x1, y1), start=270, extent=90, fill=color, outline=color)
142
+ self.canvas.create_rectangle((x0 + radius / 2, y0, x1 - radius / 2, y1), fill=color, outline=color)
143
+ self.canvas.create_rectangle((x0, y0 + radius / 2, x1, y1 - radius / 2), fill=color, outline=color)
144
+
145
+ def on_focus_in(self, event):
146
+ self.draw_rounded_rectangle(self.active_color)
147
+ self.entry.config(bg=self.active_color)
148
+
149
+ def on_focus_out(self, event):
150
+ self.draw_rounded_rectangle(self.bg_color)
151
+ self.entry.config(bg=self.bg_color)
152
+
153
+ class spacrCheck(tk.Frame):
154
+ def __init__(self, parent, text="", variable=None, *args, **kwargs):
155
+ super().__init__(parent, *args, **kwargs)
156
+
157
+ style_out = set_dark_style(ttk.Style())
158
+ self.bg_color = style_out['bg_color']
159
+ self.active_color = style_out['active_color']
160
+ self.fg_color = style_out['fg_color']
161
+ self.inactive_color = style_out['inactive_color']
162
+ self.variable = variable
163
+
164
+ self.configure(bg=self.bg_color)
165
+
166
+ # Create a canvas for the rounded square background
167
+ self.canvas_width = 20
168
+ self.canvas_height = 20
169
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=self.bg_color)
170
+ self.canvas.pack()
171
+
172
+ # Draw the initial rounded square based on the variable's value
173
+ self.draw_rounded_square(self.active_color if self.variable.get() else self.inactive_color)
174
+
175
+ # Bind variable changes to update the checkbox
176
+ self.variable.trace_add('write', self.update_check)
177
+
178
+ # Bind click event to toggle the variable
179
+ self.canvas.bind("<Button-1>", self.toggle_variable)
180
+
181
+ def draw_rounded_square(self, color):
182
+ radius = 5 # Adjust the radius for more rounded corners
183
+ x0, y0 = 2, 2
184
+ x1, y1 = 18, 18
185
+ self.canvas.delete("all")
186
+ self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=self.fg_color)
187
+ self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=self.fg_color)
188
+ self.canvas.create_arc((x0, y1 - radius, x0 + radius, y1), start=180, extent=90, fill=color, outline=self.fg_color)
189
+ self.canvas.create_arc((x1 - radius, y1 - radius, x1, y1), start=270, extent=90, fill=color, outline=self.fg_color)
190
+ self.canvas.create_rectangle((x0 + radius / 2, y0, x1 - radius / 2, y1), fill=color, outline=color)
191
+ self.canvas.create_rectangle((x0, y0 + radius / 2, x1, y1 - radius / 2), fill=color, outline=color)
192
+ self.canvas.create_line(x0 + radius / 2, y0, x1 - radius / 2, y0, fill=self.fg_color)
193
+ self.canvas.create_line(x0 + radius / 2, y1, x1 - radius / 2, y1, fill=self.fg_color)
194
+ self.canvas.create_line(x0, y0 + radius / 2, x0, y1 - radius / 2, fill=self.fg_color)
195
+ self.canvas.create_line(x1, y0 + radius / 2, x1, y1 - radius / 2, fill=self.fg_color)
196
+
197
+ def update_check(self, *args):
198
+ self.draw_rounded_square(self.active_color if self.variable.get() else self.inactive_color)
199
+
200
+ def toggle_variable(self, event):
201
+ self.variable.set(not self.variable.get())
202
+
203
+ class spacrCombo(tk.Frame):
204
+ def __init__(self, parent, textvariable=None, values=None, *args, **kwargs):
205
+ super().__init__(parent, *args, **kwargs)
206
+
207
+ # Set dark style
208
+ style_out = set_dark_style(ttk.Style())
209
+ self.bg_color = style_out['bg_color']
210
+ self.active_color = style_out['active_color']
211
+ self.fg_color = style_out['fg_color']
212
+ self.inactive_color = style_out['inactive_color']
213
+ self.font_family = style_out['font_family']
214
+ self.font_size = style_out['font_size']
215
+
216
+ self.values = values or []
217
+
218
+ # Create a canvas for the rounded rectangle background
219
+ self.canvas_width = 220 # Adjusted for padding
220
+ self.canvas_height = 40 # Adjusted for padding
221
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=self.bg_color)
222
+ self.canvas.pack()
223
+
224
+ self.var = textvariable if textvariable else tk.StringVar()
225
+ self.selected_value = self.var.get()
226
+
227
+ # Create the label to display the selected value
228
+ self.label = tk.Label(self, text=self.selected_value, bg=self.inactive_color, fg=self.fg_color, font=(self.font_family, self.font_size))
229
+ self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
230
+
231
+ # Bind events to open the dropdown menu
232
+ self.canvas.bind("<Button-1>", self.on_click)
233
+ self.label.bind("<Button-1>", self.on_click)
234
+
235
+ self.draw_rounded_rectangle(self.inactive_color)
236
+
237
+ self.dropdown_menu = None
238
+
239
+ def draw_rounded_rectangle(self, color):
240
+ radius = 15 # Increased radius for more rounded corners
241
+ x0, y0 = 10, 5
242
+ x1, y1 = 210, 35
243
+ self.canvas.delete("all")
244
+ self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
245
+ self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
246
+ self.canvas.create_arc((x0, y1 - radius, x0 + radius, y1), start=180, extent=90, fill=color, outline=color)
247
+ self.canvas.create_arc((x1 - radius, y1 - radius, x1, y1), start=270, extent=90, fill=color, outline=color)
248
+ self.canvas.create_rectangle((x0 + radius / 2, y0, x1 - radius / 2, y1), fill=color, outline=color)
249
+ self.canvas.create_rectangle((x0, y0 + radius / 2, x1, y1 - radius / 2), fill=color, outline=color)
250
+ self.label.config(bg=color) # Update label background to match rectangle color
251
+
252
+ def on_click(self, event):
253
+ if self.dropdown_menu is None:
254
+ self.open_dropdown()
255
+ else:
256
+ self.close_dropdown()
257
+
258
+ def open_dropdown(self):
259
+ self.draw_rounded_rectangle(self.active_color)
260
+
261
+ self.dropdown_menu = tk.Toplevel(self)
262
+ self.dropdown_menu.wm_overrideredirect(True)
263
+
264
+ x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
265
+ self.dropdown_menu.geometry(f"{width}x{len(self.values) * 30}+{x}+{y + height}")
266
+
267
+ for index, value in enumerate(self.values):
268
+ display_text = value if value is not None else 'None'
269
+ item = tk.Label(self.dropdown_menu, text=display_text, bg=self.inactive_color, fg=self.fg_color, font=(self.font_family, self.font_size), anchor='w')
270
+ item.pack(fill='both')
271
+ item.bind("<Button-1>", lambda e, v=value: self.on_select(v))
272
+ item.bind("<Enter>", lambda e, w=item: w.config(bg=self.active_color))
273
+ item.bind("<Leave>", lambda e, w=item: w.config(bg=self.inactive_color))
274
+
275
+ def close_dropdown(self):
276
+ self.draw_rounded_rectangle(self.inactive_color)
277
+
278
+ if self.dropdown_menu:
279
+ self.dropdown_menu.destroy()
280
+ self.dropdown_menu = None
281
+
282
+ def on_select(self, value):
283
+ display_text = value if value is not None else 'None'
284
+ self.var.set(value)
285
+ self.label.config(text=display_text)
286
+ self.selected_value = value
287
+ self.close_dropdown()
288
+
289
+ def set(self, value):
290
+ display_text = value if value is not None else 'None'
291
+ self.var.set(value)
292
+ self.label.config(text=display_text)
293
+ self.selected_value = value
91
294
 
92
295
  class spacrDropdownMenu(tk.OptionMenu):
93
296
  def __init__(self, parent, variable, options, command=None, **kwargs):
94
297
  self.variable = variable
95
- self.variable.set("Select Category")
298
+ self.variable.set("Settings Category")
96
299
  super().__init__(parent, self.variable, *options, command=command, **kwargs)
97
300
  self.update_styles()
98
301
 
302
+ # Hide the original button
303
+ self.configure(highlightthickness=0, relief='flat', bg='#2B2B2B', fg='#2B2B2B')
304
+
305
+ # Create custom button
306
+ self.create_custom_button()
307
+
308
+ def create_custom_button(self):
309
+ self.canvas_width = self.winfo_reqwidth() # Use the required width of the widget
310
+ self.canvas_height = 40 # Adjust the height as needed
311
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg='#2B2B2B')
312
+ self.canvas.pack()
313
+ self.label = tk.Label(self.canvas, text="Settings Category", bg='#2B2B2B', fg='#ffffff', font=('Arial', 12))
314
+ self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
315
+ self.draw_rounded_rectangle('#2B2B2B')
316
+
317
+ # Bind the click event to open the dropdown menu
318
+ self.canvas.bind("<Button-1>", self.on_click)
319
+ self.label.bind("<Button-1>", self.on_click)
320
+
321
+ def draw_rounded_rectangle(self, color):
322
+ radius = 15
323
+ x0, y0 = 10, 5
324
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5 # Adjust based on canvas size
325
+ self.canvas.delete("all")
326
+ self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
327
+ self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
328
+ self.canvas.create_arc((x0, y1 - radius, x0 + radius, y1), start=180, extent=90, fill=color, outline=color)
329
+ self.canvas.create_arc((x1 - radius, y1 - radius, x1, y1), start=270, extent=90, fill=color, outline=color)
330
+ self.canvas.create_rectangle((x0 + radius / 2, y0, x1 - radius / 2, y1), fill=color, outline=color)
331
+ self.canvas.create_rectangle((x0, y0 + radius / 2, x1, y1 - radius / 2), fill=color, outline=color)
332
+ self.label.config(bg=color) # Update label background to match rectangle color
333
+
334
+ def on_click(self, event):
335
+ self.post_menu()
336
+
337
+ def post_menu(self):
338
+ x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
339
+ self.menu.post(x, y + height)
340
+
99
341
  def update_styles(self, active_categories=None):
100
342
  style = ttk.Style()
101
343
  style_out = set_dark_style(style, widgets=[self])
@@ -120,6 +362,56 @@ class spacrCheckbutton(ttk.Checkbutton):
120
362
  style = ttk.Style()
121
363
  _ = set_dark_style(style, widgets=[self])
122
364
 
365
+ class spacrProgressBar(ttk.Progressbar):
366
+ def __init__(self, parent, *args, **kwargs):
367
+ super().__init__(parent, *args, **kwargs)
368
+
369
+ # Get the style colors
370
+ style_out = set_dark_style(ttk.Style())
371
+
372
+ self.fg_color = style_out['fg_color']
373
+ self.bg_color = style_out['bg_color']
374
+ self.active_color = style_out['active_color']
375
+ self.inactive_color = style_out['inactive_color']
376
+
377
+ # Configure the style for the progress bar
378
+ self.style = ttk.Style()
379
+ self.style.configure(
380
+ "spacr.Horizontal.TProgressbar",
381
+ troughcolor=self.bg_color,
382
+ background=self.active_color,
383
+ thickness=20,
384
+ troughrelief='flat',
385
+ borderwidth=0
386
+ )
387
+ self.configure(style="spacr.Horizontal.TProgressbar")
388
+
389
+ # Set initial value to 0
390
+ self['value'] = 0
391
+
392
+ # Create the progress label
393
+ self.progress_label = tk.Label(parent, text="Processing: 0/0", anchor='w', justify='left', bg=self.inactive_color, fg=self.fg_color)
394
+ self.progress_label.grid(row=1, column=0, columnspan=2, pady=5, padx=5, sticky='ew')
395
+
396
+ # Initialize attributes for time and operation
397
+ self.operation_type = None
398
+ self.time_image = None
399
+ self.time_batch = None
400
+ self.time_left = None
401
+
402
+ def update_label(self):
403
+ # Update the progress label with current progress and additional info
404
+ label_text = f"Processing: {self['value']}/{self['maximum']}"
405
+ if self.operation_type:
406
+ label_text += f", {self.operation_type}"
407
+ if self.time_image:
408
+ label_text += f", Time/image: {self.time_image:.3f} sec"
409
+ if self.time_batch:
410
+ label_text += f", Time/batch: {self.time_batch:.3f} sec"
411
+ if self.time_left:
412
+ label_text += f", Time_left: {self.time_left:.3f} min"
413
+ self.progress_label.config(text=label_text)
414
+
123
415
  class spacrFrame(ttk.Frame):
124
416
  def __init__(self, container, width=None, *args, bg='black', **kwargs):
125
417
  super().__init__(container, *args, **kwargs)
@@ -192,7 +484,7 @@ class spacrLabel(tk.Frame):
192
484
  self.canvas.itemconfig(self.label_text, text=text)
193
485
 
194
486
  class spacrButton(tk.Frame):
195
- def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=True, *args, **kwargs):
487
+ def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=False, *args, **kwargs):
196
488
  super().__init__(parent, *args, **kwargs)
197
489
 
198
490
  self.text = text.capitalize() # Capitalize only the first letter of the text
@@ -216,10 +508,12 @@ class spacrButton(tk.Frame):
216
508
  # Apply dark style and get color settings
217
509
  color_settings = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
218
510
 
511
+ self.inactive_color = color_settings['inactive_color']
512
+
219
513
  if self.outline:
220
- 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'])
514
+ self.button_bg = self.create_rounded_rectangle(2, 2, self.button_width + 2, self.size + 2, radius=20, fill=self.inactive_color, outline=color_settings['fg_color'])
221
515
  else:
222
- 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'])
516
+ self.button_bg = self.create_rounded_rectangle(2, 2, self.button_width + 2, self.size + 2, radius=20, fill=self.inactive_color, outline=self.inactive_color)
223
517
 
224
518
  self.load_icon()
225
519
  self.font_style = font if font else ("Arial", 12) # Default font if not provided
@@ -234,7 +528,7 @@ class spacrButton(tk.Frame):
234
528
  self.canvas.bind("<Leave>", self.on_leave)
235
529
  self.canvas.bind("<Button-1>", self.on_click)
236
530
 
237
- self.bg_color = color_settings['bg_color']
531
+ self.bg_color = self.inactive_color
238
532
  self.active_color = color_settings['active_color']
239
533
  self.fg_color = color_settings['fg_color']
240
534
  self.is_zoomed_in = False # Track zoom state for smooth transitions
@@ -251,7 +545,7 @@ class spacrButton(tk.Frame):
251
545
  icon_image = Image.open(self.get_icon_path("default"))
252
546
  print(f'Icon not found: {icon_path}. Using default icon instead.')
253
547
 
254
- initial_size = int(self.size * 0.9) # Make the initial size slightly smaller
548
+ initial_size = int(self.size * 0.65) # 65% of button size initially
255
549
  self.original_icon_image = icon_image.resize((initial_size, initial_size), Image.Resampling.LANCZOS)
256
550
  self.icon_photo = ImageTk.PhotoImage(self.original_icon_image)
257
551
 
@@ -266,13 +560,13 @@ class spacrButton(tk.Frame):
266
560
  self.canvas.itemconfig(self.button_bg, fill=self.active_color)
267
561
  self.update_description(event)
268
562
  if not self.is_zoomed_in:
269
- self.animate_zoom(1.1) # Zoom in the icon by 10%
563
+ self.animate_zoom(0.85) # Zoom in the icon to 85% of button size
270
564
 
271
565
  def on_leave(self, event=None):
272
- self.canvas.itemconfig(self.button_bg, fill=self.bg_color)
566
+ self.canvas.itemconfig(self.button_bg, fill=self.inactive_color)
273
567
  self.clear_description(event)
274
568
  if self.is_zoomed_in:
275
- self.animate_zoom(1.0) # Reset the icon size
569
+ self.animate_zoom(0.65) # Reset the icon size to 65% of button size
276
570
 
277
571
  def on_click(self, event=None):
278
572
  if self.command:
@@ -317,7 +611,7 @@ class spacrButton(tk.Frame):
317
611
  parent = parent.master
318
612
 
319
613
  def animate_zoom(self, target_scale, steps=10, delay=10):
320
- current_scale = 1.1 if self.is_zoomed_in else 1.0
614
+ current_scale = 0.85 if self.is_zoomed_in else 0.65
321
615
  step_scale = (target_scale - current_scale) / steps
322
616
  self._animate_step(current_scale, step_scale, steps, delay)
323
617
 
@@ -331,13 +625,13 @@ class spacrButton(tk.Frame):
331
625
 
332
626
  def zoom_icon(self, scale_factor):
333
627
  # Resize the original icon image
334
- new_size = int(self.size * 0.9 * scale_factor)
628
+ new_size = int(self.size * scale_factor)
335
629
  resized_icon = self.original_icon_image.resize((new_size, new_size), Image.Resampling.LANCZOS)
336
630
  self.icon_photo = ImageTk.PhotoImage(resized_icon)
337
631
 
338
632
  # Update the icon on the canvas
339
633
  self.canvas.itemconfig(self.button_icon, image=self.icon_photo)
340
- self.canvas.image = self.icon_photo # Keep a reference to avoid garbage collection
634
+ self.canvas.image = self.icon_photo
341
635
 
342
636
  class spacrSwitch(ttk.Frame):
343
637
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
@@ -1292,14 +1586,19 @@ class ModifyMaskApp:
1292
1586
  self.update_display()
1293
1587
 
1294
1588
  class AnnotateApp:
1295
- 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):
1589
+ def __init__(self, root, db_path, src, image_type=None, channels=None, image_size=200, annotation_column='annotate', normalize=False, percentiles=(1, 99), measurement=None, threshold=None):
1296
1590
  self.root = root
1297
1591
  self.db_path = db_path
1298
1592
  self.src = src
1299
1593
  self.index = 0
1300
- self.grid_rows = grid_rows
1301
- self.grid_cols = grid_cols
1302
- self.image_size = image_size
1594
+
1595
+ if isinstance(image_size, list):
1596
+ self.image_size = (int(image_size[0]), int(image_size[0]))
1597
+ elif isinstance(image_size, int):
1598
+ self.image_size = (image_size, image_size)
1599
+ else:
1600
+ raise ValueError("Invalid image size")
1601
+
1303
1602
  self.annotation_column = annotation_column
1304
1603
  self.image_type = image_type
1305
1604
  self.channels = channels
@@ -1311,22 +1610,72 @@ class AnnotateApp:
1311
1610
  self.adjusted_to_original_paths = {}
1312
1611
  self.terminate = False
1313
1612
  self.update_queue = Queue()
1314
- self.status_label = Label(self.root, text="", font=("Arial", 12))
1315
- self.status_label.grid(row=self.grid_rows + 1, column=0, columnspan=self.grid_cols)
1316
1613
  self.measurement = measurement
1317
1614
  self.threshold = threshold
1318
1615
 
1616
+ style_out = set_dark_style(ttk.Style())
1617
+ self.root.configure(bg=style_out['inactive_color'])
1618
+
1319
1619
  self.filtered_paths_annotations = []
1320
1620
  self.prefilter_paths_annotations()
1321
1621
 
1322
1622
  self.db_update_thread = threading.Thread(target=self.update_database_worker)
1323
1623
  self.db_update_thread.start()
1324
1624
 
1325
- for i in range(grid_rows * grid_cols):
1326
- label = Label(root)
1327
- label.grid(row=i // grid_cols, column=i % grid_cols)
1625
+ # Set the initial window size and make it fit the screen size
1626
+ self.root.geometry(f"{self.root.winfo_screenwidth()}x{self.root.winfo_screenheight()}")
1627
+ self.root.update_idletasks()
1628
+
1629
+ # Create the status label
1630
+ self.status_label = Label(root, text="", font=("Arial", 12), bg=self.root.cget('bg'))
1631
+ self.status_label.grid(row=2, column=0, padx=10, pady=10, sticky="w")
1632
+
1633
+ # Place the buttons at the bottom right
1634
+ self.button_frame = Frame(root, bg=self.root.cget('bg'))
1635
+ self.button_frame.grid(row=2, column=1, padx=10, pady=10, sticky="se")
1636
+
1637
+ self.next_button = Button(self.button_frame, text="Next", command=self.next_page, bg='black', fg='white', highlightbackground='white', highlightcolor='white', highlightthickness=1)
1638
+ self.next_button.pack(side="right", padx=5)
1639
+
1640
+ self.previous_button = Button(self.button_frame, text="Back", command=self.previous_page, bg='black', fg='white', highlightbackground='white', highlightcolor='white', highlightthickness=1)
1641
+ self.previous_button.pack(side="right", padx=5)
1642
+
1643
+ self.exit_button = Button(self.button_frame, text="Exit", command=self.shutdown, bg='black', fg='white', highlightbackground='white', highlightcolor='white', highlightthickness=1)
1644
+ self.exit_button.pack(side="right", padx=5)
1645
+
1646
+ # Calculate grid rows and columns based on the root window size and image size
1647
+ self.calculate_grid_dimensions()
1648
+
1649
+ # Create a frame to hold the image grid
1650
+ self.grid_frame = Frame(root, bg=self.root.cget('bg'))
1651
+ self.grid_frame.grid(row=0, column=0, columnspan=2, padx=0, pady=0, sticky="nsew")
1652
+
1653
+ for i in range(self.grid_rows * self.grid_cols):
1654
+ label = Label(self.grid_frame, bg=self.root.cget('bg'))
1655
+ label.grid(row=i // self.grid_cols, column=i % self.grid_cols, padx=2, pady=2, sticky="nsew")
1328
1656
  self.labels.append(label)
1329
1657
 
1658
+ # Make the grid frame resize with the window
1659
+ self.root.grid_rowconfigure(0, weight=1)
1660
+ self.root.grid_columnconfigure(0, weight=1)
1661
+ self.root.grid_columnconfigure(1, weight=1)
1662
+
1663
+ for row in range(self.grid_rows):
1664
+ self.grid_frame.grid_rowconfigure(row, weight=1)
1665
+ for col in range(self.grid_cols):
1666
+ self.grid_frame.grid_columnconfigure(col, weight=1)
1667
+
1668
+ def calculate_grid_dimensions(self):
1669
+ window_width = self.root.winfo_width()
1670
+ window_height = self.root.winfo_height()
1671
+
1672
+ self.grid_cols = window_width // (self.image_size[0] + 4)
1673
+ self.grid_rows = (window_height - self.button_frame.winfo_height() - 4) // (self.image_size[1] + 4)
1674
+
1675
+ # Update to make sure grid_rows and grid_cols are at least 1
1676
+ self.grid_cols = max(1, self.grid_cols)
1677
+ self.grid_rows = max(1, self.grid_rows)
1678
+
1330
1679
  def prefilter_paths_annotations(self):
1331
1680
  from .io import _read_and_join_tables
1332
1681
  from .utils import is_list_of_lists
@@ -1580,6 +1929,7 @@ class AnnotateApp:
1580
1929
  self.update_queue.put(self.pending_updates.copy())
1581
1930
  self.pending_updates.clear()
1582
1931
  self.index += self.grid_rows * self.grid_cols
1932
+ self.prefilter_paths_annotations() # Re-fetch annotations from the database
1583
1933
  self.load_images()
1584
1934
 
1585
1935
  def previous_page(self):
@@ -1589,6 +1939,7 @@ class AnnotateApp:
1589
1939
  self.index -= self.grid_rows * self.grid_cols
1590
1940
  if self.index < 0:
1591
1941
  self.index = 0
1942
+ self.prefilter_paths_annotations() # Re-fetch annotations from the database
1592
1943
  self.load_images()
1593
1944
 
1594
1945
  def shutdown(self):
@@ -1603,20 +1954,20 @@ class AnnotateApp:
1603
1954
  def create_menu_bar(root):
1604
1955
  from .gui import initiate_root
1605
1956
  gui_apps = {
1606
- "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
1607
- "Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
1608
- "Annotate": (lambda frame: initiate_root(frame, 'annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
1609
- "Make Masks": (lambda frame: initiate_root(frame, 'make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
1610
- "Classify": (lambda frame: initiate_root(frame, 'classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
1611
- "Sequencing": (lambda frame: initiate_root(frame, 'sequencing'), "Analyze sequencing data."),
1612
- "Umap": (lambda frame: initiate_root(frame, 'umap'), "Generate UMAP embeddings with datapoints represented as images."),
1613
- "Train Cellpose": (lambda frame: initiate_root(frame, 'train_cellpose'), "Train custom Cellpose models."),
1614
- "ML Analyze": (lambda frame: initiate_root(frame, 'ml_analyze'), "Machine learning analysis of data."),
1615
- "Cellpose Masks": (lambda frame: initiate_root(frame, 'cellpose_masks'), "Generate Cellpose masks."),
1616
- "Cellpose All": (lambda frame: initiate_root(frame, 'cellpose_all'), "Run Cellpose on all images."),
1617
- "Map Barcodes": (lambda frame: initiate_root(frame, 'map_barcodes'), "Map barcodes to data."),
1618
- "Regression": (lambda frame: initiate_root(frame, 'regression'), "Perform regression analysis."),
1619
- "Recruitment": (lambda frame: initiate_root(frame, 'recruitment'), "Analyze recruitment data.")
1957
+ "Mask": (lambda frame: initiate_root(frame, settings_type='mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
1958
+ "Measure": (lambda frame: initiate_root(frame, settings_type='measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
1959
+ "Annotate": (lambda frame: initiate_root(frame, settings_type='annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
1960
+ "Make Masks": (lambda frame: initiate_root(frame, settings_type='make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
1961
+ "Classify": (lambda frame: initiate_root(frame, settings_type='classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
1962
+ "Sequencing": (lambda frame: initiate_root(frame, settings_type='sequencing'), "Analyze sequencing data."),
1963
+ "Umap": (lambda frame: initiate_root(frame, settings_type='umap'), "Generate UMAP embeddings with datapoints represented as images."),
1964
+ "Train Cellpose": (lambda frame: initiate_root(frame, settings_type='train_cellpose'), "Train custom Cellpose models."),
1965
+ "ML Analyze": (lambda frame: initiate_root(frame, settings_type='ml_analyze'), "Machine learning analysis of data."),
1966
+ "Cellpose Masks": (lambda frame: initiate_root(frame, settings_type='cellpose_masks'), "Generate Cellpose masks."),
1967
+ "Cellpose All": (lambda frame: initiate_root(frame, settings_type='cellpose_all'), "Run Cellpose on all images."),
1968
+ "Map Barcodes": (lambda frame: initiate_root(frame, settings_type='map_barcodes'), "Map barcodes to data."),
1969
+ "Regression": (lambda frame: initiate_root(frame, settings_type='regression'), "Perform regression analysis."),
1970
+ "Recruitment": (lambda frame: initiate_root(frame, settings_type='recruitment'), "Analyze recruitment data.")
1620
1971
  }
1621
1972
 
1622
1973
  def load_app_wrapper(app_name, app_func):