spacr 0.2.46__py3-none-any.whl → 0.2.53__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.
Files changed (58) hide show
  1. spacr/core.py +24 -11
  2. spacr/gui.py +0 -2
  3. spacr/gui_core.py +70 -55
  4. spacr/gui_elements.py +367 -152
  5. spacr/gui_utils.py +59 -68
  6. spacr/io.py +2 -3
  7. spacr/measure.py +196 -145
  8. spacr/plot.py +2 -42
  9. spacr/resources/font/open_sans/OFL.txt +93 -0
  10. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  11. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  12. spacr/resources/font/open_sans/README.txt +100 -0
  13. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  14. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  15. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  49. spacr/sequencing.py +107 -13
  50. spacr/settings.py +27 -84
  51. spacr/utils.py +9 -9
  52. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/METADATA +6 -4
  53. spacr-0.2.53.dist-info/RECORD +100 -0
  54. spacr-0.2.46.dist-info/RECORD +0 -60
  55. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/LICENSE +0 -0
  56. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/WHEEL +0 -0
  57. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/entry_points.txt +0 -0
  58. {spacr-0.2.46.dist-info → spacr-0.2.53.dist-info}/top_level.txt +0 -0
spacr/gui_elements.py CHANGED
@@ -1,7 +1,8 @@
1
- import os, threading, time, sqlite3, webbrowser
1
+ import os, threading, time, sqlite3, webbrowser, pyautogui
2
2
  import tkinter as tk
3
3
  from tkinter import ttk
4
4
  import tkinter.font as tkFont
5
+ from tkinter import font
5
6
  from queue import Queue
6
7
  from tkinter import Label, Frame, Button
7
8
  import numpy as np
@@ -16,14 +17,28 @@ from skimage.transform import resize
16
17
  from scipy.ndimage import binary_fill_holes, label
17
18
  from tkinter import ttk, scrolledtext
18
19
 
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)
20
+ def set_element_size():
21
+
22
+ screen_width, screen_height = pyautogui.size()
23
+ screen_area = screen_width * screen_height
24
+
25
+ # Calculate sizes based on screen dimensions
26
+ btn_size = int((screen_area * 0.002) ** 0.5) # Button size as a fraction of screen area
27
+ bar_size = screen_height // 20 # Bar size based on screen height
28
+ settings_width = screen_width // 4 # Settings panel width as a fraction of screen width
29
+ panel_width = screen_width - settings_width # Panel width as a fraction of screen width
30
+ panel_height = screen_height // 6 # Panel height as a fraction of screen height
25
31
 
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'):
32
+ size_dict = {
33
+ 'btn_size': btn_size,
34
+ 'bar_size': bar_size,
35
+ 'settings_width': settings_width,
36
+ 'panel_width': panel_width,
37
+ 'panel_height': panel_height
38
+ }
39
+ return size_dict
40
+
41
+ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font_family="OpenSans", font_size=12, bg_color='black', fg_color='white', active_color='blue', inactive_color='dark_gray'):
27
42
 
28
43
  if active_color == 'teal':
29
44
  active_color = '#008080'
@@ -39,6 +54,11 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
39
54
  padding = '5 5 5 5'
40
55
  font_style = tkFont.Font(family=font_family, size=font_size)
41
56
 
57
+ if font_family == 'OpenSans':
58
+ font_loader = spacrFont(font_name='OpenSans', font_style='Regular', font_size=12)
59
+ else:
60
+ font_loader = None
61
+
42
62
  style.theme_use('clam')
43
63
 
44
64
  style.configure('TEntry', padding=padding)
@@ -50,7 +70,10 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
50
70
  style.configure('TButton', padding=padding)
51
71
  style.configure('TFrame', background=bg_color)
52
72
  style.configure('TPanedwindow', background=bg_color)
53
- style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_style)
73
+ if font_loader:
74
+ style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_loader.get_font(size=font_size))
75
+ else:
76
+ style.configure('TLabel', background=bg_color, foreground=fg_color, font=(font_family, font_size))
54
77
 
55
78
  if parent_frame:
56
79
  parent_frame.configure(bg=bg_color)
@@ -71,18 +94,193 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
71
94
  if isinstance(widget, (tk.Label, tk.Button, tk.Frame, ttk.LabelFrame, tk.Canvas)):
72
95
  widget.configure(bg=bg_color)
73
96
  if isinstance(widget, (tk.Label, tk.Button)):
74
- widget.configure(fg=fg_color, font=(font_family, font_size))
97
+ if font_loader:
98
+ widget.configure(fg=fg_color, font=font_loader.get_font(size=font_size))
99
+ else:
100
+ widget.configure(fg=fg_color, font=(font_family, font_size))
75
101
  if isinstance(widget, scrolledtext.ScrolledText):
76
102
  widget.configure(bg=bg_color, fg=fg_color, insertbackground=fg_color)
77
103
  if isinstance(widget, tk.OptionMenu):
78
- widget.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
104
+ if font_loader:
105
+ widget.configure(bg=bg_color, fg=fg_color, font=font_loader.get_font(size=font_size))
106
+ else:
107
+ widget.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
79
108
  menu = widget['menu']
80
- menu.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
109
+ if font_loader:
110
+ menu.configure(bg=bg_color, fg=fg_color, font=font_loader.get_font(size=font_size))
111
+ else:
112
+ menu.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
113
+
114
+ return {'font_loader':font_loader, 'font_family': font_family, 'font_size': font_size, 'bg_color': bg_color, 'fg_color': fg_color, 'active_color': active_color, 'inactive_color': inactive_color}
115
+
116
+ class spacrFont:
117
+ def __init__(self, font_name, font_style, font_size=12):
118
+ """
119
+ Initializes the FontLoader class.
120
+
121
+ Parameters:
122
+ - font_name: str, the name of the font (e.g., 'OpenSans').
123
+ - font_style: str, the style of the font (e.g., 'Regular', 'Bold').
124
+ - font_size: int, the size of the font (default: 12).
125
+ """
126
+ self.font_name = font_name
127
+ self.font_style = font_style
128
+ self.font_size = font_size
129
+
130
+ # Determine the path based on the font name and style
131
+ self.font_path = self.get_font_path(font_name, font_style)
132
+
133
+ # Register the font with Tkinter
134
+ self.load_font()
135
+
136
+ def get_font_path(self, font_name, font_style):
137
+ """
138
+ Returns the font path based on the font name and style.
139
+
140
+ Parameters:
141
+ - font_name: str, the name of the font.
142
+ - font_style: str, the style of the font.
143
+
144
+ Returns:
145
+ - str, the path to the font file.
146
+ """
147
+ base_dir = os.path.dirname(__file__)
148
+
149
+ if font_name == 'OpenSans':
150
+ if font_style == 'Regular':
151
+ return os.path.join(base_dir, 'resources/font/open_sans/static/OpenSans-Regular.ttf')
152
+ elif font_style == 'Bold':
153
+ return os.path.join(base_dir, 'resources/font/open_sans/static/OpenSans-Bold.ttf')
154
+ elif font_style == 'Italic':
155
+ return os.path.join(base_dir, 'resources/font/open_sans/static/OpenSans-Italic.ttf')
156
+ # Add more styles as needed
157
+ # Add more fonts as needed
158
+
159
+ raise ValueError(f"Font '{font_name}' with style '{font_style}' not found.")
160
+
161
+ def load_font(self):
162
+ """
163
+ Loads the font into Tkinter.
164
+ """
165
+ try:
166
+ font.Font(family=self.font_name, size=self.font_size)
167
+ except tk.TclError:
168
+ # Load the font manually if it's not already loaded
169
+ self.tk_font = font.Font(
170
+ name=self.font_name,
171
+ file=self.font_path,
172
+ size=self.font_size
173
+ )
174
+
175
+ def get_font(self, size=None):
176
+ """
177
+ Returns the font in the specified size.
178
+
179
+ Parameters:
180
+ - size: int, the size of the font (optional).
181
+
182
+ Returns:
183
+ - tkFont.Font object.
184
+ """
185
+ if size is None:
186
+ size = self.font_size
187
+ return font.Font(family=self.font_name, size=size)
188
+
189
+ class spacrContainer(tk.Frame):
190
+ def __init__(self, parent, orient=tk.VERTICAL, bg=None, *args, **kwargs):
191
+ super().__init__(parent, *args, **kwargs)
192
+ self.orient = orient
193
+ self.bg = bg if bg else 'lightgrey'
194
+ self.sash_thickness = 10
195
+
196
+ self.panes = []
197
+ self.sashes = []
198
+ self.bind("<Configure>", self.on_configure)
199
+
200
+ self.grid_rowconfigure(0, weight=1)
201
+ self.grid_columnconfigure(0, weight=1)
202
+
203
+ def add(self, widget, stretch='always'):
204
+ print(f"Adding widget: {widget} with stretch: {stretch}")
205
+ pane = tk.Frame(self, bg=self.bg)
206
+ pane.grid_propagate(False)
207
+ widget.grid(in_=pane, sticky="nsew") # Use grid for the widget within the pane
208
+ self.panes.append((pane, widget))
81
209
 
82
- 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}
210
+ if len(self.panes) > 1:
211
+ self.create_sash()
212
+
213
+ self.reposition_panes()
214
+
215
+ def create_sash(self):
216
+ sash = tk.Frame(self, bg=self.bg, cursor='sb_v_double_arrow' if self.orient == tk.VERTICAL else 'sb_h_double_arrow', height=self.sash_thickness, width=self.sash_thickness)
217
+ sash.bind("<Enter>", self.on_enter_sash)
218
+ sash.bind("<Leave>", self.on_leave_sash)
219
+ sash.bind("<ButtonPress-1>", self.start_resize)
220
+ self.sashes.append(sash)
221
+
222
+ def reposition_panes(self):
223
+ if not self.panes:
224
+ return
225
+
226
+ total_size = self.winfo_height() if self.orient == tk.VERTICAL else self.winfo_width()
227
+ pane_size = total_size // len(self.panes)
228
+
229
+ print(f"Total size: {total_size}, Pane size: {pane_size}, Number of panes: {len(self.panes)}")
230
+
231
+ for i, (pane, widget) in enumerate(self.panes):
232
+ if self.orient == tk.VERTICAL:
233
+ pane.grid(row=i * 2, column=0, sticky="nsew", pady=(0, self.sash_thickness if i < len(self.panes) - 1 else 0))
234
+ else:
235
+ pane.grid(row=0, column=i * 2, sticky="nsew", padx=(0, self.sash_thickness if i < len(self.panes) - 1 else 0))
236
+
237
+ for i, sash in enumerate(self.sashes):
238
+ if self.orient == tk.VERTICAL:
239
+ sash.grid(row=(i * 2) + 1, column=0, sticky="ew")
240
+ else:
241
+ sash.grid(row=0, column=(i * 2) + 1, sticky="ns")
242
+
243
+ def on_configure(self, event):
244
+ print(f"Configuring container: {self}")
245
+ self.reposition_panes()
246
+
247
+ def on_enter_sash(self, event):
248
+ event.widget.config(bg='blue')
249
+
250
+ def on_leave_sash(self, event):
251
+ event.widget.config(bg=self.bg)
252
+
253
+ def start_resize(self, event):
254
+ sash = event.widget
255
+ self.start_pos = event.y_root if self.orient == tk.VERTICAL else event.x_root
256
+ self.start_size = sash.winfo_y() if self.orient == tk.VERTICAL else sash.winfo_x()
257
+ sash.bind("<B1-Motion>", self.perform_resize)
258
+
259
+ def perform_resize(self, event):
260
+ sash = event.widget
261
+ delta = (event.y_root - self.start_pos) if self.orient == tk.VERTICAL else (event.x_root - self.start_pos)
262
+ new_size = self.start_size + delta
263
+
264
+ for i, (pane, widget) in enumerate(self.panes):
265
+ if self.orient == tk.VERTICAL:
266
+ new_row = max(0, new_size // self.sash_thickness)
267
+ if pane.winfo_y() >= new_size:
268
+ pane.grid_configure(row=new_row)
269
+ elif pane.winfo_y() < new_size and i > 0:
270
+ previous_row = max(0, (new_size - pane.winfo_height()) // self.sash_thickness)
271
+ self.panes[i - 1][0].grid_configure(row=previous_row)
272
+ else:
273
+ new_col = max(0, new_size // self.sash_thickness)
274
+ if pane.winfo_x() >= new_size:
275
+ pane.grid_configure(column=new_col)
276
+ elif pane.winfo_x() < new_size and i > 0:
277
+ previous_col = max(0, (new_size - pane.winfo_width()) // self.sash_thickness)
278
+ self.panes[i - 1][0].grid_configure(column=previous_col)
279
+
280
+ self.reposition_panes()
83
281
 
84
282
  class spacrEntry(tk.Frame):
85
- def __init__(self, parent, textvariable=None, outline=False, *args, **kwargs):
283
+ def __init__(self, parent, textvariable=None, outline=False, width=None, *args, **kwargs):
86
284
  super().__init__(parent, *args, **kwargs)
87
285
 
88
286
  # Set dark style
@@ -93,18 +291,26 @@ class spacrEntry(tk.Frame):
93
291
  self.outline = outline
94
292
  self.font_family = style_out['font_family']
95
293
  self.font_size = style_out['font_size']
294
+ self.font_loader = style_out['font_loader']
96
295
 
97
296
  # Set the background color of the frame
98
297
  self.configure(bg=style_out['bg_color'])
99
298
 
100
299
  # Create a canvas for the rounded rectangle background
101
- self.canvas_width = 220 # Adjusted for padding
300
+ if width is None:
301
+ self.canvas_width = 220 # Adjusted for padding
302
+ else:
303
+ self.canvas_width = width
102
304
  self.canvas_height = 40 # Adjusted for padding
103
305
  self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=style_out['bg_color'])
104
306
  self.canvas.pack()
105
307
 
106
- 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)
107
- self.entry.place(relx=0.5, rely=0.5, anchor=tk.CENTER, width=190, height=20) # Centered positioning
308
+ # Create the entry widget
309
+ if self.font_loader:
310
+ self.entry = tk.Entry(self, textvariable=textvariable, bd=0, highlightthickness=0, fg=self.fg_color, font=self.font_loader.get_font(size=self.font_size), bg=self.bg_color)
311
+ else:
312
+ 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)
313
+ self.entry.place(relx=0.5, rely=0.5, anchor=tk.CENTER, width=self.canvas_width - 30, height=20) # Centered positioning
108
314
 
109
315
  # Bind events to change the background color on focus
110
316
  self.entry.bind("<FocusIn>", self.on_focus_in)
@@ -115,7 +321,7 @@ class spacrEntry(tk.Frame):
115
321
  def draw_rounded_rectangle(self, color):
116
322
  radius = 15 # Increased radius for more rounded corners
117
323
  x0, y0 = 10, 5
118
- x1, y1 = 210, 35
324
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5
119
325
  self.canvas.delete("all")
120
326
  self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
121
327
  self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
@@ -183,7 +389,7 @@ class spacrCheck(tk.Frame):
183
389
  self.variable.set(not self.variable.get())
184
390
 
185
391
  class spacrCombo(tk.Frame):
186
- def __init__(self, parent, textvariable=None, values=None, *args, **kwargs):
392
+ def __init__(self, parent, textvariable=None, values=None, width=None, *args, **kwargs):
187
393
  super().__init__(parent, *args, **kwargs)
188
394
 
189
395
  # Set dark style
@@ -194,11 +400,12 @@ class spacrCombo(tk.Frame):
194
400
  self.inactive_color = style_out['inactive_color']
195
401
  self.font_family = style_out['font_family']
196
402
  self.font_size = style_out['font_size']
403
+ self.font_loader = style_out['font_loader']
197
404
 
198
405
  self.values = values or []
199
406
 
200
407
  # Create a canvas for the rounded rectangle background
201
- self.canvas_width = 220 # Adjusted for padding
408
+ self.canvas_width = width if width is not None else 220 # Adjusted for padding
202
409
  self.canvas_height = 40 # Adjusted for padding
203
410
  self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=self.bg_color)
204
411
  self.canvas.pack()
@@ -207,7 +414,10 @@ class spacrCombo(tk.Frame):
207
414
  self.selected_value = self.var.get()
208
415
 
209
416
  # Create the label to display the selected value
210
- self.label = tk.Label(self, text=self.selected_value, bg=self.inactive_color, fg=self.fg_color, font=(self.font_family, self.font_size))
417
+ if self.font_loader:
418
+ self.label = tk.Label(self, text=self.selected_value, bg=self.inactive_color, fg=self.fg_color, font=self.font_loader.get_font(size=self.font_size))
419
+ else:
420
+ self.label = tk.Label(self, text=self.selected_value, bg=self.inactive_color, fg=self.fg_color, font=(self.font_family, self.font_size))
211
421
  self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
212
422
 
213
423
  # Bind events to open the dropdown menu
@@ -221,7 +431,7 @@ class spacrCombo(tk.Frame):
221
431
  def draw_rounded_rectangle(self, color):
222
432
  radius = 15 # Increased radius for more rounded corners
223
433
  x0, y0 = 10, 5
224
- x1, y1 = 210, 35
434
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5
225
435
  self.canvas.delete("all")
226
436
  self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
227
437
  self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
@@ -248,7 +458,10 @@ class spacrCombo(tk.Frame):
248
458
 
249
459
  for index, value in enumerate(self.values):
250
460
  display_text = value if value is not None else 'None'
251
- 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')
461
+ if self.font_loader:
462
+ item = tk.Label(self.dropdown_menu, text=display_text, bg=self.inactive_color, fg=self.fg_color, font=self.font_loader.get_font(size=self.font_size), anchor='w')
463
+ else:
464
+ 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')
252
465
  item.pack(fill='both')
253
466
  item.bind("<Button-1>", lambda e, v=value: self.on_select(v))
254
467
  item.bind("<Enter>", lambda e, w=item: w.config(bg=self.active_color))
@@ -274,124 +487,101 @@ class spacrCombo(tk.Frame):
274
487
  self.label.config(text=display_text)
275
488
  self.selected_value = value
276
489
 
277
- class spacrDropdownMenu(tk.OptionMenu):
278
- def __init__(self, parent, variable, options, command=None, **kwargs):
279
- self.variable = variable
280
- self.variable.set("Settings Category")
281
- super().__init__(parent, self.variable, *options, command=command, **kwargs)
282
- self.update_styles()
283
-
284
- # Hide the original button
285
- self.configure(highlightthickness=0, relief='flat', bg='#2B2B2B', fg='#2B2B2B')
286
-
287
- # Create custom button
288
- self.create_custom_button()
289
-
290
- def create_custom_button(self):
291
- self.canvas_width = self.winfo_reqwidth() + 20 # Adjust width based on required size
292
- self.canvas_height = 40 # Adjust the height as needed
293
- self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg='#2B2B2B')
294
- self.canvas.pack()
295
- self.label = tk.Label(self.canvas, text="Settings Category", bg='#2B2B2B', fg='#ffffff', font=('Arial', 12))
296
- self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
297
- self.draw_rounded_rectangle('#2B2B2B')
298
-
299
- # Bind the click event to open the dropdown menu
300
- self.canvas.bind("<Button-1>", self.on_click)
301
- self.label.bind("<Button-1>", self.on_click)
302
-
303
- def draw_rounded_rectangle(self, color):
304
- radius = 15
305
- x0, y0 = 10, 5
306
- x1, y1 = self.canvas_width - 10, self.canvas_height - 5 # Adjust based on canvas size
307
-
308
- self.canvas.delete("all")
309
-
310
- # Create the rounded rectangle
311
- self.canvas.create_arc(x0, y0, x0 + 2 * radius, y0 + 2 * radius, start=90, extent=90, fill=color, outline=color)
312
- self.canvas.create_arc(x1 - 2 * radius, y0, x1, y0 + 2 * radius, start=0, extent=90, fill=color, outline=color)
313
- self.canvas.create_arc(x0, y1 - 2 * radius, x0 + 2 * radius, y1, start=180, extent=90, fill=color, outline=color)
314
- self.canvas.create_arc(x1 - 2 * radius, y1 - 2 * radius, x1, y1, start=270, extent=90, fill=color, outline=color)
490
+ class spacrDropdownMenu(tk.Frame):
315
491
 
316
- self.canvas.create_rectangle(x0 + radius, y0, x1 - radius, y1, fill=color, outline=color)
317
- self.canvas.create_rectangle(x0, y0 + radius, x1, y1 - radius, fill=color, outline=color)
318
-
319
- self.label.config(bg=color) # Update label background to match rectangle color
492
+ def __init__(self, parent, variable, options, command=None, font=None, size=50, **kwargs):
493
+ super().__init__(parent, **kwargs)
494
+ self.variable = variable
495
+ self.options = options
496
+ self.command = command
497
+ self.text = "Settings"
498
+ self.size = size
320
499
 
321
- def on_click(self, event):
322
- self.post_menu()
500
+ # Apply dark style and get color settings
501
+ style_out = set_dark_style(ttk.Style())
502
+ self.font_size = style_out['font_size']
503
+ self.font_loader = style_out['font_loader']
323
504
 
324
- def post_menu(self):
325
- x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
326
- self.menu.post(x, y + height)
505
+ # Button size configuration
506
+ self.button_width = int(size * 3)
507
+ self.canvas_width = self.button_width + 4
508
+ self.canvas_height = self.size + 4
327
509
 
328
- def update_styles(self, active_categories=None):
329
- style = ttk.Style()
330
- style_out = set_dark_style(style, widgets=[self])
331
- self.menu = self['menu']
332
- style_out = set_dark_style(style, widgets=[self.menu])
510
+ # Create the canvas and rounded button
511
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, highlightthickness=0, bg=style_out['bg_color'])
512
+ self.canvas.grid(row=0, column=0)
333
513
 
334
- if active_categories is not None:
335
- for idx in range(self.menu.index("end") + 1):
336
- option = self.menu.entrycget(idx, "label")
337
- if option in active_categories:
338
- self.menu.entryconfig(idx, background=style_out['active_color'], foreground=style_out['fg_color'])
339
- else:
340
- self.menu.entryconfig(idx, background=style_out['bg_color'], foreground=style_out['fg_color'])
514
+ # Apply dark style and get color settings
515
+ color_settings = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
516
+ self.inactive_color = color_settings['inactive_color']
517
+ self.active_color = color_settings['active_color']
518
+ self.fg_color = color_settings['fg_color']
341
519
 
520
+ # Create the button with rounded edges
521
+ 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)
342
522
 
523
+ # Create and place the label on the button
524
+ if self.font_loader:
525
+ self.font_style = self.font_loader.get_font(size=self.font_size)
526
+ else:
527
+ self.font_style = font if font else ("Arial", 12)
343
528
 
344
- class spacrDropdownMenu_v1(tk.OptionMenu):
345
- def __init__(self, parent, variable, options, command=None, **kwargs):
346
- self.variable = variable
347
- self.variable.set("Settings Category")
348
- super().__init__(parent, self.variable, *options, command=command, **kwargs)
349
- self.update_styles()
529
+ self.button_text = self.canvas.create_text(self.button_width // 2, self.size // 2 + 2, text=self.text, fill=self.fg_color, font=self.font_style, anchor="center")
350
530
 
351
- # Hide the original button
352
- self.configure(highlightthickness=0, relief='flat', bg='#2B2B2B', fg='#2B2B2B')
531
+ # Bind events for button behavior
532
+ self.bind("<Enter>", self.on_enter)
533
+ self.bind("<Leave>", self.on_leave)
534
+ self.bind("<Button-1>", self.on_click)
535
+ self.canvas.bind("<Enter>", self.on_enter)
536
+ self.canvas.bind("<Leave>", self.on_leave)
537
+ self.canvas.bind("<Button-1>", self.on_click)
353
538
 
354
- # Create custom button
355
- self.create_custom_button()
539
+ # Create a popup menu
540
+ self.menu = tk.Menu(self, tearoff=0)
541
+ for option in self.options:
542
+ self.menu.add_command(label=option, command=lambda opt=option: self.on_select(opt))
356
543
 
357
- def create_custom_button(self):
358
- self.canvas_width = self.winfo_reqwidth() # Use the required width of the widget
359
- self.canvas_height = 40 # Adjust the height as needed
360
- self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg='#2B2B2B')
361
- self.canvas.pack()
362
- self.label = tk.Label(self.canvas, text="Settings Category", bg='#2B2B2B', fg='#ffffff', font=('Arial', 12))
363
- self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
364
- self.draw_rounded_rectangle('#2B2B2B')
544
+ def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
545
+ points = [
546
+ x1 + radius, y1,
547
+ x2 - radius, y1,
548
+ x2 - radius, y1,
549
+ x2, y1,
550
+ x2, y1 + radius,
551
+ x2, y2 - radius,
552
+ x2, y2 - radius,
553
+ x2, y2,
554
+ x2 - radius, y2,
555
+ x1 + radius, y2,
556
+ x1 + radius, y2,
557
+ x1, y2,
558
+ x1, y2 - radius,
559
+ x1, y2 - radius,
560
+ x1, y1 + radius,
561
+ x1, y1 + radius,
562
+ x1, y1
563
+ ]
564
+ return self.canvas.create_polygon(points, **kwargs, smooth=True)
365
565
 
366
- # Bind the click event to open the dropdown menu
367
- self.canvas.bind("<Button-1>", self.on_click)
368
- self.label.bind("<Button-1>", self.on_click)
566
+ def on_enter(self, event=None):
567
+ self.canvas.itemconfig(self.button_bg, fill=self.active_color)
369
568
 
370
- def draw_rounded_rectangle(self, color):
371
- radius = 15
372
- x0, y0 = 10, 5
373
- x1, y1 = self.canvas_width - 10, self.canvas_height - 5 # Adjust based on canvas size
374
- self.canvas.delete("all")
375
- self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
376
- self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
377
- self.canvas.create_arc((x0, y1 - radius, x0 + radius, y1), start=180, extent=90, fill=color, outline=color)
378
- self.canvas.create_arc((x1 - radius, y1 - radius, x1, y1), start=270, extent=90, fill=color, outline=color)
379
- self.canvas.create_rectangle((x0 + radius / 2, y0, x1 - radius / 2, y1), fill=color, outline=color)
380
- self.canvas.create_rectangle((x0, y0 + radius / 2, x1, y1 - radius / 2), fill=color, outline=color)
381
- self.label.config(bg=color) # Update label background to match rectangle color
569
+ def on_leave(self, event=None):
570
+ self.canvas.itemconfig(self.button_bg, fill=self.inactive_color)
382
571
 
383
- def on_click(self, event):
572
+ def on_click(self, event=None):
384
573
  self.post_menu()
385
574
 
386
575
  def post_menu(self):
387
576
  x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
388
577
  self.menu.post(x, y + height)
389
578
 
579
+ def on_select(self, option):
580
+ if self.command:
581
+ self.command(option)
582
+
390
583
  def update_styles(self, active_categories=None):
391
- style = ttk.Style()
392
- style_out = set_dark_style(style, widgets=[self])
393
- self.menu = self['menu']
394
- style_out = set_dark_style(style, widgets=[self.menu])
584
+ style_out = set_dark_style(ttk.Style(), widgets=[self.menu])
395
585
 
396
586
  if active_categories is not None:
397
587
  for idx in range(self.menu.index("end") + 1):
@@ -401,6 +591,7 @@ class spacrDropdownMenu_v1(tk.OptionMenu):
401
591
  else:
402
592
  self.menu.entryconfig(idx, background=style_out['bg_color'], foreground=style_out['fg_color'])
403
593
 
594
+
404
595
  class spacrCheckbutton(ttk.Checkbutton):
405
596
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
406
597
  super().__init__(parent, *args, **kwargs)
@@ -587,12 +778,16 @@ class spacrLabel(tk.Frame):
587
778
  label_height = height
588
779
  label_width = label_height * 10
589
780
 
590
- style_out = set_dark_style(ttk.Style())
781
+ self.style_out = set_dark_style(ttk.Style())
782
+ self.font_style = self.style_out['font_family']
783
+ self.font_size = self.style_out['font_size']
784
+ self.font_family = self.style_out['font_family']
785
+ self.font_loader = self.style_out['font_loader']
591
786
 
592
- self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=style_out['bg_color'])
787
+ self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=self.style_out['bg_color'])
593
788
  self.canvas.grid(row=0, column=0, sticky="ew")
594
-
595
- self.font_style = font if font else tkFont.Font(family=style_out['font_family'], size=style_out['font_size'], weight=tkFont.NORMAL)
789
+ if self.style_out['font_family'] != 'OpenSans':
790
+ self.font_style = font if font else tkFont.Font(family=self.style_out['font_family'], size=self.style_out['font_size'], weight=tkFont.NORMAL)
596
791
  self.style = style
597
792
 
598
793
  if self.align == "center":
@@ -604,13 +799,21 @@ class spacrLabel(tk.Frame):
604
799
 
605
800
  if self.style:
606
801
  ttk_style = ttk.Style()
607
- ttk_style.configure(self.style, font=self.font_style, background=style_out['bg_color'], foreground=style_out['fg_color'])
802
+ if self.font_loader:
803
+ ttk_style.configure(self.style, font=self.font_loader.get_font(size=self.font_size), background=self.style_out['bg_color'], foreground=self.style_out['fg_color'])
804
+ else:
805
+ ttk_style.configure(self.style, font=self.font_style, background=self.style_out['bg_color'], foreground=self.style_out['fg_color'])
608
806
  self.label_text = ttk.Label(self.canvas, text=self.text, style=self.style, anchor=text_anchor)
609
807
  self.label_text.pack(fill=tk.BOTH, expand=True)
610
808
  else:
611
- self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
612
- label_height // 2, text=self.text, fill=style_out['fg_color'],
613
- font=self.font_style, anchor=anchor_value, justify=tk.RIGHT)
809
+ if self.font_loader:
810
+ self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
811
+ label_height // 2, text=self.text, fill=self.style_out['fg_color'],
812
+ font=self.font_loader.get_font(size=self.font_size), anchor=anchor_value, justify=tk.RIGHT)
813
+ else:
814
+ self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
815
+ label_height // 2, text=self.text, fill=self.style_out['fg_color'],
816
+ font=self.font_style, anchor=anchor_value, justify=tk.RIGHT)
614
817
 
615
818
  _ = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
616
819
 
@@ -633,6 +836,8 @@ class spacrButton(tk.Frame):
633
836
  self.animation = animation # Add animation attribute
634
837
 
635
838
  style_out = set_dark_style(ttk.Style())
839
+ self.font_size = style_out['font_size']
840
+ self.font_loader = style_out['font_loader']
636
841
 
637
842
  if self.show_text:
638
843
  self.button_width = int(size * 3)
@@ -654,7 +859,10 @@ class spacrButton(tk.Frame):
654
859
  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)
655
860
 
656
861
  self.load_icon()
657
- self.font_style = font if font else ("Arial", 12) # Default font if not provided
862
+ if self.font_loader:
863
+ self.font_style = self.font_loader.get_font(size=self.font_size)
864
+ else:
865
+ self.font_style = font if font else ("Arial", 12)
658
866
 
659
867
  if self.show_text:
660
868
  self.button_text = self.canvas.create_text(self.size + 10, self.size // 2 + 2, text=self.text, fill=color_settings['fg_color'], font=self.font_style, anchor="w") # Align text to the left of the specified point
@@ -1752,6 +1960,14 @@ class AnnotateApp:
1752
1960
  self.threshold = threshold
1753
1961
 
1754
1962
  style_out = set_dark_style(ttk.Style())
1963
+ self.font_loader = style_out['font_loader']
1964
+ self.font_size = style_out['font_size']
1965
+
1966
+ if self.font_loader:
1967
+ self.font_style = self.font_loader.get_font(size=self.font_size)
1968
+ else:
1969
+ self.font_style = ("Arial", 12)
1970
+
1755
1971
  self.root.configure(bg=style_out['inactive_color'])
1756
1972
 
1757
1973
  self.filtered_paths_annotations = []
@@ -1765,7 +1981,7 @@ class AnnotateApp:
1765
1981
  self.root.update_idletasks()
1766
1982
 
1767
1983
  # Create the status label
1768
- self.status_label = Label(root, text="", font=("Arial", 12), bg=self.root.cget('bg'))
1984
+ self.status_label = Label(root, text="", font=self.font_style, bg=self.root.cget('bg'))
1769
1985
  self.status_label.grid(row=2, column=0, padx=10, pady=10, sticky="w")
1770
1986
 
1771
1987
  # Place the buttons at the bottom right
@@ -2095,25 +2311,22 @@ class AnnotateApp:
2095
2311
  def create_menu_bar(root):
2096
2312
  from .gui import initiate_root
2097
2313
  gui_apps = {
2098
- "Mask": (lambda frame: initiate_root(frame, settings_type='mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
2099
- "Measure": (lambda frame: initiate_root(frame, settings_type='measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
2100
- "Annotate": (lambda frame: initiate_root(frame, settings_type='annotate'), "Annotation single object images on a grid. Annotations are saved to database."),
2101
- "Make Masks": (lambda frame: initiate_root(frame, settings_type='make_masks'), "Adjust pre-existing Cellpose models to your specific dataset for improved performance"),
2102
- "Classify": (lambda frame: initiate_root(frame, settings_type='classify'), "Train Torch Convolutional Neural Networks (CNNs) or Transformers to classify single object images."),
2103
- "Sequencing": (lambda frame: initiate_root(frame, settings_type='sequencing'), "Analyze sequencing data."),
2104
- "Umap": (lambda frame: initiate_root(frame, settings_type='umap'), "Generate UMAP embeddings with datapoints represented as images."),
2105
- "Train Cellpose": (lambda frame: initiate_root(frame, settings_type='train_cellpose'), "Train custom Cellpose models."),
2106
- "ML Analyze": (lambda frame: initiate_root(frame, settings_type='ml_analyze'), "Machine learning analysis of data."),
2107
- "Cellpose Masks": (lambda frame: initiate_root(frame, settings_type='cellpose_masks'), "Generate Cellpose masks."),
2108
- "Cellpose All": (lambda frame: initiate_root(frame, settings_type='cellpose_all'), "Run Cellpose on all images."),
2109
- "Map Barcodes": (lambda frame: initiate_root(frame, settings_type='map_barcodes'), "Map barcodes to data."),
2110
- "Regression": (lambda frame: initiate_root(frame, settings_type='regression'), "Perform regression analysis."),
2111
- "Recruitment": (lambda frame: initiate_root(frame, settings_type='recruitment'), "Analyze recruitment data.")
2314
+ "Mask": lambda: initiate_root(root, settings_type='mask'),
2315
+ "Measure": lambda: initiate_root(root, settings_type='measure'),
2316
+ "Annotate": lambda: initiate_root(root, settings_type='annotate'),
2317
+ "Make Masks": lambda: initiate_root(root, settings_type='make_masks'),
2318
+ "Classify": lambda: initiate_root(root, settings_type='classify'),
2319
+ "Sequencing": lambda: initiate_root(root, settings_type='sequencing'),
2320
+ "Umap": lambda: initiate_root(root, settings_type='umap'),
2321
+ "Train Cellpose": lambda: initiate_root(root, settings_type='train_cellpose'),
2322
+ "ML Analyze": lambda: initiate_root(root, settings_type='ml_analyze'),
2323
+ "Cellpose Masks": lambda: initiate_root(root, settings_type='cellpose_masks'),
2324
+ "Cellpose All": lambda: initiate_root(root, settings_type='cellpose_all'),
2325
+ "Map Barcodes": lambda: initiate_root(root, settings_type='map_barcodes'),
2326
+ "Regression": lambda: initiate_root(root, settings_type='regression'),
2327
+ "Recruitment": lambda: initiate_root(root, settings_type='recruitment')
2112
2328
  }
2113
2329
 
2114
- def load_app_wrapper(app_name, app_func):
2115
- root.load_app(app_name, app_func)
2116
-
2117
2330
  # Create the menu bar
2118
2331
  menu_bar = tk.Menu(root, bg="#008080", fg="white")
2119
2332
 
@@ -2122,14 +2335,16 @@ def create_menu_bar(root):
2122
2335
  menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
2123
2336
 
2124
2337
  # Add options to the "SpaCr Applications" menu
2125
- for app_name, app_data in gui_apps.items():
2126
- app_func, app_desc = app_data
2127
- app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
2338
+ for app_name, app_func in gui_apps.items():
2339
+ app_menu.add_command(
2340
+ label=app_name,
2341
+ command=app_func
2342
+ )
2128
2343
 
2129
2344
  # Add a separator and an exit option
2130
2345
  app_menu.add_separator()
2131
- app_menu.add_command(label="Help", command=lambda: webbrowser.open("https://readthedocs.org/projects/spacr/badge/?version=latest"))
2346
+ app_menu.add_command(label="Help", command=lambda: webbrowser.open("https://spacr.readthedocs.io/en/latest/?badge=latest"))
2132
2347
  app_menu.add_command(label="Exit", command=root.quit)
2133
2348
 
2134
2349
  # Configure the menu for the root window
2135
- root.config(menu=menu_bar)
2350
+ root.config(menu=menu_bar)