spacr 0.1.81__py3-none-any.whl → 0.2.0__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 (83) hide show
  1. spacr/__init__.py +6 -2
  2. spacr/app_annotate.py +6 -5
  3. spacr/app_make_masks.py +8 -15
  4. spacr/core.py +1 -1
  5. spacr/gui.py +72 -91
  6. spacr/gui_2.py +110 -113
  7. spacr/gui_core.py +113 -67
  8. spacr/gui_elements.py +233 -87
  9. spacr/gui_utils.py +3 -3
  10. spacr/icons/abort.png +0 -0
  11. spacr/icons/abort.svg +1 -0
  12. spacr/icons/download.png +0 -0
  13. spacr/icons/download.svg +1 -0
  14. spacr/icons/download_for_offline_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  15. spacr/icons/download_for_offline_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  16. spacr/icons/logo_spacr.png +0 -0
  17. spacr/icons/make_masks.png +0 -0
  18. spacr/icons/make_masks.svg +1 -0
  19. spacr/icons/map_barcodes.png +0 -0
  20. spacr/icons/map_barcodes.svg +1 -0
  21. spacr/icons/mask.png +0 -0
  22. spacr/icons/mask.svg +1 -0
  23. spacr/icons/measure.png +0 -0
  24. spacr/icons/measure.svg +1 -0
  25. spacr/icons/play_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  26. spacr/icons/play_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  27. spacr/icons/run.png +0 -0
  28. spacr/icons/run.svg +1 -0
  29. spacr/icons/sequencing.png +0 -0
  30. spacr/icons/sequencing.svg +1 -0
  31. spacr/icons/settings.png +0 -0
  32. spacr/icons/settings.svg +1 -0
  33. spacr/icons/settings_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  34. spacr/icons/settings_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  35. spacr/icons/stop_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.png +0 -0
  36. spacr/icons/stop_circle_100dp_E8EAED_FILL0_wght100_GRAD-25_opsz48.svg +1 -0
  37. spacr/icons/theater_comedy_100dp_E8EAED_FILL0_wght100_GRAD200_opsz48.png +0 -0
  38. spacr/icons/theater_comedy_100dp_E8EAED_FILL0_wght100_GRAD200_opsz48.svg +1 -0
  39. spacr/resources/icons/abort.png +0 -0
  40. spacr/resources/icons/abort.svg +1 -0
  41. spacr/resources/icons/annotate.png +0 -0
  42. spacr/resources/icons/annotate.svg +1 -0
  43. spacr/resources/icons/cellpose.png +0 -0
  44. spacr/resources/icons/cellpose_masks.png +0 -0
  45. spacr/resources/icons/classify.png +0 -0
  46. spacr/resources/icons/classify.svg +1 -0
  47. spacr/resources/icons/default.png +0 -0
  48. spacr/resources/icons/download.png +0 -0
  49. spacr/resources/icons/download.svg +1 -0
  50. spacr/resources/icons/icon.psd +0 -0
  51. spacr/resources/icons/logo_spacr.png +0 -0
  52. spacr/resources/icons/make_masks.png +0 -0
  53. spacr/resources/icons/make_masks.svg +1 -0
  54. spacr/resources/icons/map_barcodes.png +0 -0
  55. spacr/resources/icons/map_barcodes.svg +1 -0
  56. spacr/resources/icons/mask.png +0 -0
  57. spacr/resources/icons/mask.svg +1 -0
  58. spacr/resources/icons/measure.png +0 -0
  59. spacr/resources/icons/measure.svg +1 -0
  60. spacr/resources/icons/regression.png +0 -0
  61. spacr/resources/icons/run.png +0 -0
  62. spacr/resources/icons/run.svg +1 -0
  63. spacr/resources/icons/run_2.png +0 -0
  64. spacr/resources/icons/run_2.svg +1 -0
  65. spacr/resources/icons/sequencing.png +0 -0
  66. spacr/resources/icons/sequencing.svg +1 -0
  67. spacr/resources/icons/settings.png +0 -0
  68. spacr/resources/icons/settings.svg +1 -0
  69. spacr/resources/icons/train_cellpose.png +0 -0
  70. spacr/resources/icons/train_cellpose.svg +1 -0
  71. spacr/resources/icons/umap.png +0 -0
  72. spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  73. spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +23 -0
  74. spacr/resources/models/cp/toxo_pv_lumen.CP_model +0 -0
  75. spacr/settings.py +12 -12
  76. spacr/test_gui.py +0 -0
  77. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/METADATA +1 -1
  78. spacr-0.2.0.dist-info/RECORD +126 -0
  79. spacr-0.1.81.dist-info/RECORD +0 -60
  80. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/LICENSE +0 -0
  81. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/WHEEL +0 -0
  82. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/entry_points.txt +0 -0
  83. {spacr-0.1.81.dist-info → spacr-0.2.0.dist-info}/top_level.txt +0 -0
spacr/gui_elements.py CHANGED
@@ -14,23 +14,105 @@ from collections import deque
14
14
  from skimage.draw import polygon, line
15
15
  from skimage.transform import resize
16
16
  from scipy.ndimage import binary_fill_holes, label
17
+ from tkinter import ttk, scrolledtext
18
+ import platform
19
+
20
+ 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
+
22
+ if platform.system() == 'Darwin':
23
+ bg_color = '#313131'
24
+ else:
25
+ bg_color = '#000000'
26
+
27
+ if active_color == 'teal':
28
+ active_color = '#008080'
29
+ if inactive_color == 'dark_gray':
30
+ inactive_color = '#050505'
31
+ if bg_color == 'black':
32
+ bg_color = '#000000'
33
+ if fg_color == 'white':
34
+ fg_color = '#ffffff'
35
+ if active_color == 'blue':
36
+ active_color = '#007BFF'
37
+
38
+ font_style = tkFont.Font(family=font_family, size=font_size)
39
+ style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground=bg_color, foreground=fg_color, font=font_style)
40
+ style.configure('TCombobox', fieldbackground=bg_color, background=bg_color, foreground=fg_color, selectbackground=bg_color, selectforeground=fg_color, font=font_style)
41
+ style.map('TCombobox', fieldbackground=[('readonly', bg_color)], foreground=[('readonly', fg_color)], selectbackground=[('readonly', bg_color)], selectforeground=[('readonly', fg_color)])
42
+ style.configure('Custom.TButton', background=bg_color, foreground=fg_color, bordercolor=fg_color, focusthickness=3, focuscolor=fg_color, font=(font_family, font_size))
43
+ 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)])
44
+ style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background=bg_color, foreground=fg_color, font=font_style)
45
+ style.configure('Spacr.TCheckbutton', background=bg_color, foreground=fg_color, indicatoron=False, relief='flat', font="15")
46
+ style.map('Spacr.TCheckbutton', background=[('selected', bg_color), ('active', bg_color)], foreground=[('selected', fg_color), ('active', fg_color)])
47
+ style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_style)
48
+ style.configure('TFrame', background=bg_color)
49
+ style.configure('TPanedwindow', background=bg_color)
50
+ style.configure('TNotebook', background=bg_color, tabmargins=[2, 5, 2, 0])
51
+ style.configure('TNotebook.Tab', background=bg_color, foreground=fg_color, padding=[5, 5], font=font_style)
52
+ style.map('TNotebook.Tab', background=[('selected', active_color), ('active', active_color)], foreground=[('selected', fg_color), ('active', fg_color)])
53
+ style.configure('TButton', background=bg_color, foreground=fg_color, padding='5 5 5 5', font=font_style)
54
+ style.map('TButton', background=[('active', active_color), ('disabled', inactive_color)])
55
+ style.configure('Vertical.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
56
+ style.configure('Horizontal.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
57
+ style.configure('Custom.TLabelFrame', font=(font_family, font_size, 'bold'), background=bg_color, foreground='white', relief='solid', borderwidth=1)
58
+ style.configure('Custom.TLabelFrame.Label', background=bg_color, foreground='white', font=(font_family, font_size, 'bold'))
59
+
60
+ if parent_frame:
61
+ parent_frame.configure(bg=bg_color)
62
+ parent_frame.grid_rowconfigure(0, weight=1)
63
+ parent_frame.grid_columnconfigure(0, weight=1)
64
+
65
+ if containers:
66
+ for container in containers:
67
+ if isinstance(container, ttk.Frame):
68
+ container_style = ttk.Style()
69
+ container_style.configure(f'{container.winfo_class()}.TFrame', background=bg_color)
70
+ container.configure(style=f'{container.winfo_class()}.TFrame')
71
+ else:
72
+ container.configure(bg=bg_color)
73
+
74
+ if widgets:
75
+ for widget in widgets:
76
+ if isinstance(widget, (tk.Label, tk.Button, tk.Frame, ttk.LabelFrame, tk.Canvas)):
77
+ widget.configure(bg=bg_color)
78
+ if isinstance(widget, (tk.Label, tk.Button)):
79
+ widget.configure(fg=fg_color, font=(font_family, font_size))
80
+ if isinstance(widget, scrolledtext.ScrolledText):
81
+ widget.configure(bg=bg_color, fg=fg_color, insertbackground=fg_color)
82
+ if isinstance(widget, tk.OptionMenu):
83
+ widget.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
84
+ menu = widget['menu']
85
+ menu.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
86
+
87
+ 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}
88
+
89
+ def set_default_font(root, font_name="Arial", size=12):
90
+ default_font = (font_name, size)
91
+ root.option_add("*Font", default_font)
92
+ root.option_add("*TButton.Font", default_font)
93
+ root.option_add("*TLabel.Font", default_font)
94
+ root.option_add("*TEntry.Font", default_font)
17
95
 
18
96
  class spacrDropdownMenu(tk.OptionMenu):
19
97
  def __init__(self, parent, variable, options, command=None, **kwargs):
20
98
  self.variable = variable
21
99
  self.variable.set("Select Category")
22
100
  super().__init__(parent, self.variable, *options, command=command, **kwargs)
23
- self.config(bg='black', fg='white', font=('Helvetica', 12), indicatoron=0)
101
+ self.update_styles()
102
+
103
+ def update_styles(self, active_categories=None):
104
+ style = ttk.Style()
105
+ style_out = set_dark_style(style, widgets=[self])
24
106
  self.menu = self['menu']
25
- self.menu.config(bg='black', fg='white', font=('Helvetica', 12))
107
+ style_out = set_dark_style(style, widgets=[self.menu])
26
108
 
27
- def update_styles(self, active_categories):
28
- menu = self['menu']
29
- for idx, option in enumerate(self['menu'].entrycget(idx, "label") for idx in range(self['menu'].index("end")+1)):
30
- if option in active_categories:
31
- menu.entryconfig(idx, background='teal', foreground='white')
32
- else:
33
- menu.entryconfig(idx, background='black', foreground='white')
109
+ if active_categories is not None:
110
+ for idx in range(self.menu.index("end") + 1):
111
+ option = self.menu.entrycget(idx, "label")
112
+ if option in active_categories:
113
+ self.menu.entryconfig(idx, background=style_out['active_color'], foreground=style_out['fg_color'])
114
+ else:
115
+ self.menu.entryconfig(idx, background=style_out['bg_color'], foreground=style_out['fg_color'])
34
116
 
35
117
  class spacrCheckbutton(ttk.Checkbutton):
36
118
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
@@ -39,6 +121,8 @@ class spacrCheckbutton(ttk.Checkbutton):
39
121
  self.variable = variable if variable else tk.BooleanVar()
40
122
  self.command = command
41
123
  self.configure(text=self.text, variable=self.variable, command=self.command, style='Spacr.TCheckbutton')
124
+ style = ttk.Style()
125
+ _ = set_dark_style(style, widgets=[self])
42
126
 
43
127
  class spacrFrame(ttk.Frame):
44
128
  def __init__(self, container, width=None, *args, bg='black', **kwargs):
@@ -65,25 +149,25 @@ class spacrFrame(ttk.Frame):
65
149
  self.grid_columnconfigure(0, weight=1)
66
150
  self.grid_columnconfigure(1, weight=0)
67
151
 
68
- for child in self.scrollable_frame.winfo_children():
69
- child.configure(bg='black')
152
+ style = ttk.Style()
153
+ _ = set_dark_style(style, containers=[self], widgets=[canvas, scrollbar, self.scrollable_frame])
70
154
 
71
155
  class spacrLabel(tk.Frame):
72
156
  def __init__(self, parent, text="", font=None, style=None, align="right", **kwargs):
73
- label_kwargs = {k: v for k, v in kwargs.items() if k in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
74
- for key in label_kwargs.keys():
75
- kwargs.pop(key)
76
- super().__init__(parent, **kwargs)
157
+ valid_kwargs = {k: v for k, v in kwargs.items() if k not in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
158
+ super().__init__(parent, **valid_kwargs)
159
+
77
160
  self.text = text
78
- self.kwargs = label_kwargs
79
161
  self.align = align
80
162
  screen_height = self.winfo_screenheight()
81
163
  label_height = screen_height // 50
82
164
  label_width = label_height * 10
83
- self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=self.kwargs.get("background", "black"))
165
+ style_out = set_dark_style(ttk.Style())
166
+
167
+ self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=style_out['bg_color'])
84
168
  self.canvas.grid(row=0, column=0, sticky="ew")
85
169
 
86
- 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)
170
+ self.font_style = font if font else tkFont.Font(family=style_out['font_family'], size=style_out['font_size'], weight=tkFont.NORMAL)
87
171
  self.style = style
88
172
 
89
173
  if self.align == "center":
@@ -95,13 +179,15 @@ class spacrLabel(tk.Frame):
95
179
 
96
180
  if self.style:
97
181
  ttk_style = ttk.Style()
98
- ttk_style.configure(self.style, **label_kwargs)
99
- self.label_text = ttk.Label(self.canvas, text=self.text, style=self.style, anchor=text_anchor, justify=text_anchor)
182
+ ttk_style.configure(self.style, font=self.font_style, background=style_out['bg_color'], foreground=style_out['fg_color'])
183
+ self.label_text = ttk.Label(self.canvas, text=self.text, style=self.style, anchor=text_anchor)
100
184
  self.label_text.pack(fill=tk.BOTH, expand=True)
101
185
  else:
102
186
  self.label_text = self.canvas.create_text(label_width // 2 if self.align == "center" else label_width - 5,
103
- label_height // 2, text=self.text, fill=self.kwargs.get("foreground", "white"),
187
+ label_height // 2, text=self.text, fill=style_out['fg_color'],
104
188
  font=self.font_style, anchor=anchor_value, justify=tk.RIGHT)
189
+
190
+ _ = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
105
191
 
106
192
  def set_text(self, text):
107
193
  if self.style:
@@ -110,24 +196,40 @@ class spacrLabel(tk.Frame):
110
196
  self.canvas.itemconfig(self.label_text, text=text)
111
197
 
112
198
  class spacrButton(tk.Frame):
113
- def __init__(self, parent, text="", command=None, font=None, *args, **kwargs):
199
+ def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=True, *args, **kwargs):
114
200
  super().__init__(parent, *args, **kwargs)
115
- self.text = text
201
+
202
+ self.text = text.capitalize() # Capitalize only the first letter of the text
116
203
  self.command = command
117
- #screen_height = self.winfo_screenheight()
118
- button_height = 50 #screen_height // 50
119
- button_width = 140 #button_height * 3
204
+ self.icon_name = icon_name if icon_name else text.lower()
205
+ self.size = size
206
+ self.show_text = show_text
207
+ self.outline = outline
208
+
209
+ style_out = set_dark_style(ttk.Style())
120
210
 
121
- #print(button_height, button_width)
211
+ if self.show_text:
212
+ self.button_width = int(size * 3)
213
+ else:
214
+ self.button_width = self.size # Make the button width equal to the size if show_text is False
122
215
 
123
- # Increase the canvas size to accommodate the button and the rim
124
- self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg="black")
216
+ # Create the canvas first
217
+ self.canvas = tk.Canvas(self, width=self.button_width + 4, height=self.size + 4, highlightthickness=0, bg=style_out['bg_color'])
125
218
  self.canvas.grid(row=0, column=0)
126
219
 
127
- self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill="#000000", outline="#ffffff")
220
+ # Apply dark style and get color settings
221
+ color_settings = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
128
222
 
129
- self.font_style = font if font else tkFont.Font(family="Helvetica", size=12, weight=tkFont.NORMAL)
130
- self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill="white", font=self.font_style)
223
+ if self.outline:
224
+ 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'])
225
+ else:
226
+ 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'])
227
+
228
+ self.load_icon()
229
+ self.font_style = font if font else ("Arial", 12) # Default font if not provided
230
+
231
+ if self.show_text:
232
+ 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
131
233
 
132
234
  self.bind("<Enter>", self.on_enter)
133
235
  self.bind("<Leave>", self.on_leave)
@@ -136,11 +238,45 @@ class spacrButton(tk.Frame):
136
238
  self.canvas.bind("<Leave>", self.on_leave)
137
239
  self.canvas.bind("<Button-1>", self.on_click)
138
240
 
241
+ self.bg_color = color_settings['bg_color']
242
+ self.active_color = color_settings['active_color']
243
+ self.fg_color = color_settings['fg_color']
244
+ self.is_zoomed_in = False # Track zoom state for smooth transitions
245
+
246
+ def load_icon(self):
247
+ icon_path = self.get_icon_path(self.icon_name)
248
+ try:
249
+ icon_image = Image.open(icon_path)
250
+ except (FileNotFoundError, Image.UnidentifiedImageError):
251
+ try:
252
+ icon_path = icon_path.replace(' ', '_')
253
+ icon_image = Image.open(icon_path)
254
+ except (FileNotFoundError, Image.UnidentifiedImageError):
255
+ icon_image = Image.open(self.get_icon_path("default"))
256
+ print(f'Icon not found: {icon_path}. Using default icon instead.')
257
+
258
+ initial_size = int(self.size * 0.9) # Make the initial size slightly smaller
259
+ self.original_icon_image = icon_image.resize((initial_size, initial_size), Image.Resampling.LANCZOS)
260
+ self.icon_photo = ImageTk.PhotoImage(self.original_icon_image)
261
+
262
+ self.button_icon = self.canvas.create_image(self.size // 2 + 2, self.size // 2 + 2, image=self.icon_photo)
263
+ self.canvas.image = self.icon_photo # Keep a reference to avoid garbage collection
264
+
265
+ def get_icon_path(self, icon_name):
266
+ icon_dir = os.path.join(os.path.dirname(__file__), 'resources', 'icons')
267
+ return os.path.join(icon_dir, f"{icon_name}.png")
268
+
139
269
  def on_enter(self, event=None):
140
- self.canvas.itemconfig(self.button_bg, fill="#008080") # Teal color
270
+ self.canvas.itemconfig(self.button_bg, fill=self.active_color)
271
+ self.update_description(event)
272
+ if not self.is_zoomed_in:
273
+ self.animate_zoom(1.1) # Zoom in the icon by 10%
141
274
 
142
275
  def on_leave(self, event=None):
143
- self.canvas.itemconfig(self.button_bg, fill="#000000") # Black color
276
+ self.canvas.itemconfig(self.button_bg, fill=self.bg_color)
277
+ self.clear_description(event)
278
+ if self.is_zoomed_in:
279
+ self.animate_zoom(1.0) # Reset the icon size
144
280
 
145
281
  def on_click(self, event=None):
146
282
  if self.command:
@@ -148,18 +284,15 @@ class spacrButton(tk.Frame):
148
284
 
149
285
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
150
286
  points = [
151
- x1 + radius, y1,
152
287
  x1 + radius, y1,
153
288
  x2 - radius, y1,
154
289
  x2 - radius, y1,
155
290
  x2, y1,
156
291
  x2, y1 + radius,
157
- x2, y1 + radius,
158
292
  x2, y2 - radius,
159
293
  x2, y2 - radius,
160
294
  x2, y2,
161
295
  x2 - radius, y2,
162
- x2 - radius, y2,
163
296
  x1 + radius, y2,
164
297
  x1 + radius, y2,
165
298
  x1, y2,
@@ -170,6 +303,46 @@ class spacrButton(tk.Frame):
170
303
  x1, y1
171
304
  ]
172
305
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
306
+
307
+ def update_description(self, event):
308
+ parent = self.master
309
+ while parent:
310
+ if hasattr(parent, 'show_description'):
311
+ parent.show_description(parent.main_buttons.get(self, "No description available."))
312
+ return
313
+ parent = parent.master
314
+
315
+ def clear_description(self, event):
316
+ parent = self.master
317
+ while parent:
318
+ if hasattr(parent, 'clear_description'):
319
+ parent.clear_description()
320
+ return
321
+ parent = parent.master
322
+
323
+ def animate_zoom(self, target_scale, steps=10, delay=10):
324
+ current_scale = 1.1 if self.is_zoomed_in else 1.0
325
+ step_scale = (target_scale - current_scale) / steps
326
+ self._animate_step(current_scale, step_scale, steps, delay)
327
+
328
+ def _animate_step(self, current_scale, step_scale, steps, delay):
329
+ if steps > 0:
330
+ new_scale = current_scale + step_scale
331
+ self.zoom_icon(new_scale)
332
+ self.after(delay, self._animate_step, new_scale, step_scale, steps - 1, delay)
333
+ else:
334
+ self.is_zoomed_in = not self.is_zoomed_in
335
+
336
+ def zoom_icon(self, scale_factor):
337
+ # Resize the original icon image
338
+ new_size = int(self.size * 0.9 * scale_factor)
339
+ resized_icon = self.original_icon_image.resize((new_size, new_size), Image.Resampling.LANCZOS)
340
+ self.icon_photo = ImageTk.PhotoImage(resized_icon)
341
+
342
+ # Update the icon on the canvas
343
+ self.canvas.itemconfig(self.button_icon, image=self.icon_photo)
344
+ self.canvas.image = self.icon_photo # Keep a reference to avoid garbage collection
345
+
173
346
 
174
347
  class spacrSwitch(ttk.Frame):
175
348
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
@@ -177,17 +350,20 @@ class spacrSwitch(ttk.Frame):
177
350
  self.text = text
178
351
  self.variable = variable if variable else tk.BooleanVar()
179
352
  self.command = command
180
- self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0, bg="black")
353
+ self.canvas = tk.Canvas(self, width=40, height=20, highlightthickness=0, bd=0)
181
354
  self.canvas.grid(row=0, column=1, padx=(10, 0))
182
355
  self.switch_bg = self.create_rounded_rectangle(2, 2, 38, 18, radius=9, outline="", fill="#fff")
183
- self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080") # Purple initially
184
- self.label = spacrLabel(self, text=self.text, background="black", foreground="white")
356
+ self.switch = self.canvas.create_oval(4, 4, 16, 16, outline="", fill="#800080")
357
+ self.label = spacrLabel(self, text=self.text)
185
358
  self.label.grid(row=0, column=0, padx=(0, 10))
186
359
  self.bind("<Button-1>", self.toggle)
187
360
  self.canvas.bind("<Button-1>", self.toggle)
188
361
  self.label.bind("<Button-1>", self.toggle)
189
362
  self.update_switch()
190
363
 
364
+ style = ttk.Style()
365
+ _ = set_dark_style(style, containers=[self], widgets=[self.canvas, self.label])
366
+
191
367
  def toggle(self, event=None):
192
368
  self.variable.set(not self.variable.get())
193
369
  self.animate_switch()
@@ -196,19 +372,19 @@ class spacrSwitch(ttk.Frame):
196
372
 
197
373
  def update_switch(self):
198
374
  if self.variable.get():
199
- self.canvas.itemconfig(self.switch, fill="#008080") # Teal
200
- self.canvas.coords(self.switch, 24, 4, 36, 16) # Move switch to the right
375
+ self.canvas.itemconfig(self.switch, fill="#008080")
376
+ self.canvas.coords(self.switch, 24, 4, 36, 16)
201
377
  else:
202
- self.canvas.itemconfig(self.switch, fill="#800080") # Purple
203
- self.canvas.coords(self.switch, 4, 4, 16, 16) # Move switch to the left
378
+ self.canvas.itemconfig(self.switch, fill="#800080")
379
+ self.canvas.coords(self.switch, 4, 4, 16, 16)
204
380
 
205
381
  def animate_switch(self):
206
382
  if self.variable.get():
207
383
  start_x, end_x = 4, 24
208
- final_color = "#008080" # Teal
384
+ final_color = "#008080"
209
385
  else:
210
386
  start_x, end_x = 24, 4
211
- final_color = "#800080" # Purple
387
+ final_color = "#800080"
212
388
 
213
389
  self.animate_movement(start_x, end_x, final_color)
214
390
 
@@ -217,7 +393,7 @@ class spacrSwitch(ttk.Frame):
217
393
  for i in range(start_x, end_x, step):
218
394
  self.canvas.coords(self.switch, i, 4, i + 12, 16)
219
395
  self.canvas.update()
220
- self.after(10) # Small delay for smooth animation
396
+ self.after(10)
221
397
  self.canvas.itemconfig(self.switch, fill=final_color)
222
398
 
223
399
  def get(self):
@@ -227,7 +403,7 @@ class spacrSwitch(ttk.Frame):
227
403
  self.variable.set(value)
228
404
  self.update_switch()
229
405
 
230
- def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs): # Smaller radius for smaller switch
406
+ def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs):
231
407
  points = [x1 + radius, y1,
232
408
  x1 + radius, y1,
233
409
  x2 - radius, y1,
@@ -265,17 +441,18 @@ class spacrToolTip:
265
441
  self.tooltip_window = tk.Toplevel(self.widget)
266
442
  self.tooltip_window.wm_overrideredirect(True)
267
443
  self.tooltip_window.wm_geometry(f"+{x}+{y}")
268
- self.tooltip_window.configure(bg='black')
269
- label = tk.Label(self.tooltip_window, text=self.text, background="#333333", foreground="white", relief='flat', borderwidth=0)
444
+ label = tk.Label(self.tooltip_window, text=self.text, relief='flat', borderwidth=0)
270
445
  label.grid(row=0, column=0, padx=5, pady=5)
271
446
 
447
+ style = ttk.Style()
448
+ _ = set_dark_style(style, containers=[self.tooltip_window], widgets=[label])
449
+
272
450
  def hide_tooltip(self, event):
273
451
  if self.tooltip_window:
274
452
  self.tooltip_window.destroy()
275
453
  self.tooltip_window = None
276
454
 
277
- class modify_masks:
278
-
455
+ class ModifyMaskApp:
279
456
  def __init__(self, root, folder_path, scale_factor):
280
457
  self.root = root
281
458
  self.folder_path = folder_path
@@ -1119,7 +1296,7 @@ class modify_masks:
1119
1296
  self.mask[labeled_mask == i] = 0 # Remove small objects
1120
1297
  self.update_display()
1121
1298
 
1122
- class ImageApp:
1299
+ class AnnotateApp:
1123
1300
  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):
1124
1301
  self.root = root
1125
1302
  self.db_path = db_path
@@ -1383,7 +1560,7 @@ class ImageApp:
1383
1560
  break
1384
1561
 
1385
1562
  if not self.update_queue.empty():
1386
- ImageApp.update_html("Do not exit, Updating database...")
1563
+ AnnotateApp.update_html("Do not exit, Updating database...")
1387
1564
  self.status_label.config(text='Do not exit, Updating database...')
1388
1565
 
1389
1566
  pending_updates = self.update_queue.get()
@@ -1394,7 +1571,7 @@ class ImageApp:
1394
1571
  c.execute(f'UPDATE png_list SET {self.annotation_column} = ? WHERE png_path = ?', (new_annotation, path))
1395
1572
  conn.commit()
1396
1573
 
1397
- ImageApp.update_html('')
1574
+ AnnotateApp.update_html('')
1398
1575
  self.status_label.config(text='')
1399
1576
  self.root.update()
1400
1577
  time.sleep(0.1)
@@ -1465,35 +1642,4 @@ def create_menu_bar(root):
1465
1642
  app_menu.add_separator()
1466
1643
  app_menu.add_command(label="Exit", command=root.quit)
1467
1644
  # Configure the menu for the root window
1468
- root.config(menu=menu_bar)
1469
-
1470
-
1471
- def set_dark_style(style):
1472
- font_style = tkFont.Font(family="Helvetica", size=24)
1473
- style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground='black', foreground='#ffffff', font=font_style)
1474
- style.configure('TCombobox', fieldbackground='black', background='black', foreground='#ffffff', selectbackground='black', selectforeground='#ffffff', font=font_style)
1475
- style.map('TCombobox', fieldbackground=[('readonly', 'black')], foreground=[('readonly', '#ffffff')], selectbackground=[('readonly', 'black')], selectforeground=[('readonly', '#ffffff')])
1476
- style.configure('Custom.TButton', background='black', foreground='white', bordercolor='white', focusthickness=3, focuscolor='white', font=('Helvetica', 12))
1477
- style.map('Custom.TButton', background=[('active', 'teal'), ('!active', 'black')], foreground=[('active', 'white'), ('!active', 'white')], bordercolor=[('active', 'white'), ('!active', 'white')])
1478
- style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background='black', foreground='#ffffff', font=font_style)
1479
- style.configure('Spacr.TCheckbutton', background='black', foreground='#ffffff', indicatoron=False, relief='flat', font="15")
1480
- style.map('Spacr.TCheckbutton', background=[('selected', 'black'), ('active', 'black')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
1481
- style.configure('TLabel', background='black', foreground='#ffffff', font=font_style)
1482
- style.configure('TFrame', background='black')
1483
- style.configure('TPanedwindow', background='black')
1484
- style.configure('TNotebook', background='black', tabmargins=[2, 5, 2, 0])
1485
- style.configure('TNotebook.Tab', background='black', foreground='#ffffff', padding=[5, 5], font=font_style)
1486
- style.map('TNotebook.Tab', background=[('selected', '#555555'), ('active', '#555555')], foreground=[('selected', '#ffffff'), ('active', '#ffffff')])
1487
- style.configure('TButton', background='black', foreground='#ffffff', padding='5 5 5 5', font=font_style)
1488
- style.map('TButton', background=[('active', '#555555'), ('disabled', '#333333')])
1489
- style.configure('Vertical.TScrollbar', background='black', troughcolor='black', bordercolor='black')
1490
- style.configure('Horizontal.TScrollbar', background='black', troughcolor='black', bordercolor='black')
1491
- style.configure('Custom.TLabelFrame', font=('Helvetica', 10, 'bold'), background='black', foreground='white', relief='solid', borderwidth=1)
1492
- style.configure('Custom.TLabelFrame.Label', background='black', foreground='white', font=('Helvetica', 10, 'bold'))
1493
-
1494
- def set_default_font(root, font_name="Helvetica", size=12):
1495
- default_font = (font_name, size)
1496
- root.option_add("*Font", default_font)
1497
- root.option_add("*TButton.Font", default_font)
1498
- root.option_add("*TLabel.Font", default_font)
1499
- root.option_add("*TEntry.Font", default_font)
1645
+ root.config(menu=menu_bar)
spacr/gui_utils.py CHANGED
@@ -3,7 +3,7 @@ import tkinter as tk
3
3
  from tkinter import ttk
4
4
 
5
5
  from . gui_core import initiate_root
6
- from .gui_elements import spacrLabel, spacrCheckbutton, ImageApp
6
+ from .gui_elements import spacrLabel, spacrCheckbutton, AnnotateApp
7
7
 
8
8
  try:
9
9
  ctypes.windll.shcore.SetProcessDpiAwareness(True)
@@ -202,7 +202,7 @@ def annotate(settings):
202
202
 
203
203
  root = tk.Tk()
204
204
  root.geometry(settings['geom'])
205
- app = ImageApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
205
+ app = AnnotateApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
206
206
  next_button = tk.Button(root, text="Next", command=app.next_page)
207
207
  next_button.grid(row=app.grid_rows, column=app.grid_cols - 1)
208
208
  back_button = tk.Button(root, text="Back", command=app.previous_page)
@@ -302,7 +302,7 @@ def annotate_with_image_refs(settings, root, shutdown_callback):
302
302
  conn.commit()
303
303
  conn.close()
304
304
 
305
- app = ImageApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
305
+ app = AnnotateApp(root, db, src, image_type=settings['image_type'], channels=settings['channels'], image_size=settings['img_size'], grid_rows=settings['rows'], grid_cols=settings['columns'], annotation_column=settings['annotation_column'], normalize=settings['normalize'], percentiles=settings['percentiles'], measurement=settings['measurement'], threshold=settings['threshold'])
306
306
 
307
307
  # Set the canvas background to black
308
308
  root.configure(bg='black')
spacr/icons/abort.png ADDED
Binary file
spacr/icons/abort.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M349.5-349.5h262v-262h-262v262ZM480.57-132q-72.94 0-135.93-27.52-62.99-27.53-110.39-74.85-47.4-47.33-74.82-110.1Q132-407.25 132-480.2q0-72.03 27.52-135.59 27.53-63.56 74.85-110.71 47.33-47.15 110.1-74.32Q407.25-828 480.2-828q72.03 0 135.59 27.39 63.57 27.39 110.72 74.35 47.14 46.96 74.31 110.39Q828-552.43 828-480.57q0 72.94-27.27 135.93-27.28 62.99-74.35 110.21-47.08 47.21-110.51 74.82Q552.43-132 480.57-132Zm-.14-22q135.07 0 230.32-95.18Q806-344.37 806-480.43q0-135.07-95-230.32Q616.01-806 480.07-806q-135.57 0-230.82 95Q154-616.01 154-480.07q0 135.57 95.18 230.82Q344.37-154 480.43-154ZM480-480Z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M306-306h348v-22H306v22Zm173-132 124-123-16-16-97 97v-214h-22v214l-96-97-16 16 123 123Zm1.3 306q-72.21 0-135.43-27.52-63.22-27.53-110.62-74.85-47.4-47.33-74.82-110.26Q132-407.57 132-479.7q0-72.21 27.52-135.93 27.53-63.72 74.85-110.87 47.33-47.15 110.26-74.32Q407.57-828 479.7-828q72.21 0 135.94 27.39 63.72 27.39 110.87 74.35 47.14 46.96 74.31 110.39Q828-552.43 828-480.3q0 72.21-27.27 135.43-27.28 63.22-74.35 110.62-47.08 47.4-110.51 74.82Q552.43-132 480.3-132Zm-.37-22q135.57 0 230.82-95.18Q806-344.37 806-479.93q0-135.57-95.18-230.82Q615.63-806 480.07-806q-135.57 0-230.82 95.18Q154-615.63 154-480.07q0 135.57 95.18 230.82Q344.37-154 479.93-154Zm.07-326Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M306-306h348v-22H306v22Zm173-132 124-123-16-16-97 97v-214h-22v214l-96-97-16 16 123 123Zm1.3 306q-72.21 0-135.43-27.52-63.22-27.53-110.62-74.85-47.4-47.33-74.82-110.26Q132-407.57 132-479.7q0-72.21 27.52-135.93 27.53-63.72 74.85-110.87 47.33-47.15 110.26-74.32Q407.57-828 479.7-828q72.21 0 135.94 27.39 63.72 27.39 110.87 74.35 47.14 46.96 74.31 110.39Q828-552.43 828-480.3q0 72.21-27.27 135.43-27.28 63.22-74.35 110.62-47.08 47.4-110.51 74.82Q552.43-132 480.3-132Zm-.37-22q135.57 0 230.82-95.18Q806-344.37 806-479.93q0-135.57-95.18-230.82Q615.63-806 480.07-806q-135.57 0-230.82 95.18Q154-615.63 154-480.07q0 135.57 95.18 230.82Q344.37-154 479.93-154Zm.07-326Z"/></svg>
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M747-671q12.55 0 23.78-10.51Q782-692.01 782-704.98q0-13.72-11.22-23.87Q759.55-739 747-739q-13.7 0-23.85 10.24Q713-718.52 713-704.68q0 12.68 10.15 23.18T747-671Zm-166.09 0q13.67 0 24.38-10.51Q616-692.01 616-704.98q0-13.72-10.71-23.87Q594.59-739 580.93-739q-12.81 0-22.87 10.24T548-704.68q0 12.68 9.84 23.18 9.83 10.5 23.07 10.5ZM570-535h188q-5-25-31.74-42.5-26.75-17.5-62.22-17.5-37.48 0-64.26 17.5Q573-560 570-535ZM296.79-116q-85.29 0-143.54-59.21Q95-234.42 95-318v-239h401v238.53q0 83.89-57.58 143.18Q380.84-116 296.79-116Zm-1.04-35q69.13 0 117.19-49T461-317.77V-522H130v204.23Q130-249 178.31-200q48.3 49 117.44 49ZM664-405q-28.58 0-57.79-8T561-434l2-38q20.4 14 45.33 23 24.94 9 55.67 9 68.65 0 117.33-47.33Q830-534.65 830-603.26V-810H497v189h-35v-224h403v241.53q0 82.89-58.62 140.68Q747.75-405 664-405Zm-449.23 19q13.37 0 23.3-9.59Q248-405.17 248-419q0-13.1-9.84-24.05Q228.33-454 215.09-454q-13.66 0-24.38 10.84Q180-432.32 180-419.39q0 14.09 10.7 23.74 10.7 9.65 24.07 9.65Zm164.9 0q13.07 0 23.2-9.59Q413-405.17 413-419q0-13.1-9.86-24.05Q393.28-454 380-454q-14.12 0-24.56 10.84T345-419.39q0 14.09 10.8 23.74 10.79 9.65 23.87 9.65Zm-82.71 140q37.48 0 64.26-17Q388-280 391-306H203q3 26 29.74 43 26.75 17 64.22 17Zm.04-92Zm367-286Z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M103-221v-479h71v479h-71Zm103 0v-479h69v479h-69Zm103 0v-479h34v479h-34Zm103 0v-479h68v479h-68Zm103 0v-479h103v479H515Zm137 0v-479h34v479h-34Zm103 0v-479h103v479H755Z"/></svg>
spacr/icons/mask.png ADDED
Binary file
spacr/icons/mask.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M480-309q48.46 0 84.23-29T610-414H349q9 47 45.77 76T480-309ZM301-591h129q0-27-19.08-46-19.09-19-46.5-19Q337-656 319-637.09q-18 18.92-18 46.09Zm229-1h130q0-27-18.87-45.5-18.88-18.5-46-18.5-27.83 0-46.48 18.41Q530-619.17 530-592Zm-50.2 476q-66.8 0-125.64-25.39t-103.5-70Q206-256 181-315.04 156-374.09 156-442v-402h648v402.09q0 67.91-25.2 127.04-25.21 59.12-69.5 103.5Q665-167 605.8-141.5q-59.21 25.5-126 25.5Zm-.3-35q120.5 0 205-84.59Q769-320.17 769-442v-367H191v366.78Q191-320 274.5-235.5t205 84.5Zm.5-329Z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="m90-271-28-22 171-275 119.14 140L512-684l85 127q-7.71 3.67-16.79 6.17-9.07 2.5-16.21 6.83l-51-75-152.11 250L239-510 90-271ZM872-63 728-206q-19 14-38.33 20-19.32 6-40.67 6-54.92 0-92.96-38.06-38.04-38.06-38.04-93t38.06-93.44q38.06-38.5 93-38.5t93.44 38.33Q781-366.33 781-311q0 23-7 43t-22 37.45L897-88l-25 25ZM648.86-215q40.08 0 68.61-28.1T746-310.86q0-40.08-28.4-68.61T649.1-408q-40.1 0-68.1 28.4-28 28.4-28 68.5t28.1 68.1q28.1 28 67.76 28ZM720-555q-11-3-20.6-3.45-9.59-.46-17.4-.55l188-299 29 22-179 281Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="m407-351 202-129-202-129v258Zm73.3 219q-72.21 0-135.43-27.52-63.22-27.53-110.62-74.85-47.4-47.33-74.82-110.26Q132-407.57 132-479.7q0-72.21 27.52-135.93 27.53-63.72 74.85-110.87 47.33-47.15 110.26-74.32Q407.57-828 479.7-828q72.21 0 135.94 27.39 63.72 27.39 110.87 74.35 47.14 46.96 74.31 110.39Q828-552.43 828-480.3q0 72.21-27.27 135.43-27.28 63.22-74.35 110.62-47.08 47.4-110.51 74.82Q552.43-132 480.3-132Zm-.37-22q135.57 0 230.82-95.18Q806-344.37 806-479.93q0-135.57-95.18-230.82Q615.63-806 480.07-806q-135.57 0-230.82 95.18Q154-615.63 154-480.07q0 135.57 95.18 230.82Q344.37-154 479.93-154Zm.07-326Z"/></svg>
spacr/icons/run.png ADDED
Binary file
spacr/icons/run.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="m407-351 202-129-202-129v258Zm73.3 219q-72.21 0-135.43-27.52-63.22-27.53-110.62-74.85-47.4-47.33-74.82-110.26Q132-407.57 132-479.7q0-72.21 27.52-135.93 27.53-63.72 74.85-110.87 47.33-47.15 110.26-74.32Q407.57-828 479.7-828q72.21 0 135.94 27.39 63.72 27.39 110.87 74.35 47.14 46.96 74.31 110.39Q828-552.43 828-480.3q0 72.21-27.27 135.43-27.28 63.22-74.35 110.62-47.08 47.4-110.51 74.82Q552.43-132 480.3-132Zm-.37-22q135.57 0 230.82-95.18Q806-344.37 806-479.93q0-135.57-95.18-230.82Q615.63-806 480.07-806q-135.57 0-230.82 95.18Q154-615.63 154-480.07q0 135.57 95.18 230.82Q344.37-154 479.93-154Zm.07-326Z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M246-80v-20q0-141.45 56.5-215.72Q359-390 455-480q-97-90-153.5-164.28Q245-718.55 245-860v-21h34.66v21q0 17 1.08 33.5Q281.83-810 286-796h388.14q2.93-14 4.4-30.5Q680-843 680-860v-21h35v21q0 141.45-57 215.72Q601-570 506-480q95 89 152 163.78 57 74.77 57 216.22v20h-34.66v-20q0-17-1.58-33.5Q677.17-150 674-164H286.86q-3.93 14-4.9 30.5Q281-117 281-100v20h-35Zm99-565h269.18q18.82-25 33.32-54t23.5-62H290q7 34.65 21.25 62.86Q325.5-669.92 345-645Zm135 141q31.34-29.33 58.17-54.67Q565-584 588-610H371q22.75 26.33 50.25 51.67Q448.76-533 480-504ZM372-350h216q-23-26-49.83-51.33Q511.34-426.67 480-457q-31.34 30.33-58.17 55.67Q395-376 372-350Zm-81 151h380q-9-34.5-23.25-62.76Q633.5-290.03 614-315H346q-18.97 24.67-33.48 53.83Q298-232 291-199Z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="m421.5-132-15-110.94Q383-249.5 355-265.5t-47.25-34l-101.25 46-59-105 90-66.5q-2.5-13.48-3.75-26.99-1.25-13.51-1.25-27.01 0-12 1.25-26t3.25-30l-89.5-68.5 59-102 101 44q21.5-17 47.5-32.5t50.5-22.5l16-111.5h118l15 112q26 9.5 49.25 23T649-661.5l106.5-44 58 102-94 70.54q3 14.96 4.5 27.96t1.5 24.75q0 10.75-1.75 23.84T719-426.42l93 67.92-59 105-104-47q-22.97 20.36-46.49 35.18Q579-250.5 554.5-244l-15 112h-118Zm17.74-22h80.79l15.69-111.5q29.86-8.07 54.11-21.69Q614.08-300.82 641-326l102.5 44 39.5-68.51L693-418q3.5-19 6-33.71t2.5-28.66q0-16.13-2.25-29.88T693-540l92-69-39.5-69-105 44q-19-20-49.75-39t-56.25-22.5L521.43-806H439l-11.5 110.07q-33 6.43-58.25 20.68T318-635l-102.5-43-40.5 69 90.5 65.5q-5 14-7 30.14t-2 33.83q0 17.03 1.75 31.78T264-418l-89 67 40.14 69 102.36-43q23.5 25.5 49.5 39.25T426.28-264l12.96 110Zm38.25-241q36.05 0 60.53-24.5Q562.5-444 562.5-480T538-540.5Q513.5-565 477.5-565T417-540.5Q392.5-516 392.5-480t24.5 60.5q24.5 24.5 60.49 24.5Zm3.01-85.5Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="m421.5-132-15-110.94Q383-249.5 355-265.5t-47.25-34l-101.25 46-59-105 90-66.5q-2.5-13.48-3.75-26.99-1.25-13.51-1.25-27.01 0-12 1.25-26t3.25-30l-89.5-68.5 59-102 101 44q21.5-17 47.5-32.5t50.5-22.5l16-111.5h118l15 112q26 9.5 49.25 23T649-661.5l106.5-44 58 102-94 70.54q3 14.96 4.5 27.96t1.5 24.75q0 10.75-1.75 23.84T719-426.42l93 67.92-59 105-104-47q-22.97 20.36-46.49 35.18Q579-250.5 554.5-244l-15 112h-118Zm17.74-22h80.79l15.69-111.5q29.86-8.07 54.11-21.69Q614.08-300.82 641-326l102.5 44 39.5-68.51L693-418q3.5-19 6-33.71t2.5-28.66q0-16.13-2.25-29.88T693-540l92-69-39.5-69-105 44q-19-20-49.75-39t-56.25-22.5L521.43-806H439l-11.5 110.07q-33 6.43-58.25 20.68T318-635l-102.5-43-40.5 69 90.5 65.5q-5 14-7 30.14t-2 33.83q0 17.03 1.75 31.78T264-418l-89 67 40.14 69 102.36-43q23.5 25.5 49.5 39.25T426.28-264l12.96 110Zm38.25-241q36.05 0 60.53-24.5Q562.5-444 562.5-480T538-540.5Q513.5-565 477.5-565T417-540.5Q392.5-516 392.5-480t24.5 60.5q24.5 24.5 60.49 24.5Zm3.01-85.5Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M349.5-349.5h262v-262h-262v262ZM480.57-132q-72.94 0-135.93-27.52-62.99-27.53-110.39-74.85-47.4-47.33-74.82-110.1Q132-407.25 132-480.2q0-72.03 27.52-135.59 27.53-63.56 74.85-110.71 47.33-47.15 110.1-74.32Q407.25-828 480.2-828q72.03 0 135.59 27.39 63.57 27.39 110.72 74.35 47.14 46.96 74.31 110.39Q828-552.43 828-480.57q0 72.94-27.27 135.93-27.28 62.99-74.35 110.21-47.08 47.21-110.51 74.82Q552.43-132 480.57-132Zm-.14-22q135.07 0 230.32-95.18Q806-344.37 806-480.43q0-135.07-95-230.32Q616.01-806 480.07-806q-135.57 0-230.82 95Q154-616.01 154-480.07q0 135.57 95.18 230.82Q344.37-154 480.43-154ZM480-480Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M747-671q12.55 0 23.78-10.51Q782-692.01 782-704.98q0-13.72-11.22-23.87Q759.55-739 747-739q-13.7 0-23.85 10.24Q713-718.52 713-704.68q0 12.68 10.15 23.18T747-671Zm-166.09 0q13.67 0 24.38-10.51Q616-692.01 616-704.98q0-13.72-10.71-23.87Q594.59-739 580.93-739q-12.81 0-22.87 10.24T548-704.68q0 12.68 9.84 23.18 9.83 10.5 23.07 10.5ZM570-535h188q-5-25-31.74-42.5-26.75-17.5-62.22-17.5-37.48 0-64.26 17.5Q573-560 570-535ZM296.79-116q-85.29 0-143.54-59.21Q95-234.42 95-318v-239h401v238.53q0 83.89-57.58 143.18Q380.84-116 296.79-116Zm-1.04-35q69.13 0 117.19-49T461-317.77V-522H130v204.23Q130-249 178.31-200q48.3 49 117.44 49ZM664-405q-28.58 0-57.79-8T561-434l2-38q20.4 14 45.33 23 24.94 9 55.67 9 68.65 0 117.33-47.33Q830-534.65 830-603.26V-810H497v189h-35v-224h403v241.53q0 82.89-58.62 140.68Q747.75-405 664-405Zm-449.23 19q13.37 0 23.3-9.59Q248-405.17 248-419q0-13.1-9.84-24.05Q228.33-454 215.09-454q-13.66 0-24.38 10.84Q180-432.32 180-419.39q0 14.09 10.7 23.74 10.7 9.65 24.07 9.65Zm164.9 0q13.07 0 23.2-9.59Q413-405.17 413-419q0-13.1-9.86-24.05Q393.28-454 380-454q-14.12 0-24.56 10.84T345-419.39q0 14.09 10.8 23.74 10.79 9.65 23.87 9.65Zm-82.71 140q37.48 0 64.26-17Q388-280 391-306H203q3 26 29.74 43 26.75 17 64.22 17Zm.04-92Zm367-286Z"/></svg>