spacr 0.1.85__py3-none-any.whl → 0.2.1__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 (82) 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 +62 -100
  6. spacr/gui_2.py +110 -113
  7. spacr/gui_core.py +43 -16
  8. spacr/gui_elements.py +116 -25
  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-0.1.85.dist-info → spacr-0.2.1.dist-info}/METADATA +1 -1
  77. spacr-0.2.1.dist-info/RECORD +126 -0
  78. spacr-0.1.85.dist-info/RECORD +0 -61
  79. {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/LICENSE +0 -0
  80. {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/WHEEL +0 -0
  81. {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/entry_points.txt +0 -0
  82. {spacr-0.1.85.dist-info → spacr-0.2.1.dist-info}/top_level.txt +0 -0
spacr/gui_core.py CHANGED
@@ -231,25 +231,43 @@ def import_settings(settings_type='mask'):
231
231
  vars_dict = hide_all_settings(vars_dict, categories=None)
232
232
 
233
233
  def convert_settings_dict_for_gui(settings):
234
+ from torchvision import models as torch_models
235
+ torchvision_models = [name for name, obj in torch_models.__dict__.items() if callable(obj)]
236
+ chans = ['0', '1', '2', '3', '4', '5', '6', '7', '8', None]
237
+ chans_v2 = [0, 1, 2, 3, None]
234
238
  variables = {}
235
239
  special_cases = {
236
240
  'metadata_type': ('combo', ['cellvoyager', 'cq1', 'nikon', 'zeis', 'custom'], 'cellvoyager'),
237
241
  'channels': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
238
- 'cell_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
239
- 'nucleus_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
240
- 'pathogen_mask_dim': ('combo', ['0', '1', '2', '3', '4', '5', '6', '7', '8', None], None),
241
- #'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
242
+ 'channel_dims': ('combo', ['[0,1,2,3]', '[0,1,2]', '[0,1]', '[0]'], '[0,1,2,3]'),
243
+ 'cell_mask_dim': ('combo', chans, None),
244
+ 'cell_chann_dim': ('combo', chans, None),
245
+ 'nucleus_mask_dim': ('combo', chans, None),
246
+ 'nucleus_chann_dim': ('combo', chans, None),
247
+ 'pathogen_mask_dim': ('combo', chans, None),
248
+ 'pathogen_chann_dim': ('combo', chans, None),
249
+ 'crop_mode': ('combo', ['cell', 'nucleus', 'pathogen', '[cell, nucleus, pathogen]', '[cell,nucleus, pathogen]'], ['cell']),
242
250
  'magnification': ('combo', [20, 40, 60], 20),
243
- 'nucleus_channel': ('combo', [0, 1, 2, 3, None], None),
244
- 'cell_channel': ('combo', [0, 1, 2, 3, None], None),
245
- 'pathogen_channel': ('combo', [0, 1, 2, 3, None], None),
251
+ 'nucleus_channel': ('combo', chans_v2, None),
252
+ 'cell_channel': ('combo', chans_v2, None),
253
+ 'channel_of_interest': ('combo', chans_v2, None),
254
+ 'pathogen_channel': ('combo', chans_v2, None),
246
255
  'timelapse_mode': ('combo', ['trackpy', 'btrack'], 'trackpy'),
256
+ 'train_mode': ('combo', ['erm', 'irm'], 'erm'),
257
+ 'clustering': ('combo', ['dbscan', 'kmean'], 'dbscan'),
258
+ 'reduction_method': ('combo', ['umap', 'tsne'], 'umap'),
259
+ 'model_name': ('combo', ['cyto', 'cyto_2', 'cyto_3', 'nuclei'], 'cyto'),
260
+ 'regression_type': ('combo', ['ols','gls','wls','rlm','glm','mixed','quantile','logit','probit','poisson','lasso','ridge'], 'ols'),
247
261
  'timelapse_objects': ('combo', ['cell', 'nucleus', 'pathogen', 'cytoplasm', None], None),
248
- 'model_type': ('combo', ['resnet50', 'other_model'], 'resnet50'),
262
+ 'model_type': ('combo', torchvision_models, 'resnet50'),
249
263
  'optimizer_type': ('combo', ['adamw', 'adam'], 'adamw'),
250
264
  'schedule': ('combo', ['reduce_lr_on_plateau', 'step_lr'], 'reduce_lr_on_plateau'),
251
265
  'loss_type': ('combo', ['focal_loss', 'binary_cross_entropy_with_logits'], 'focal_loss'),
252
266
  'normalize_by': ('combo', ['fov', 'png'], 'png'),
267
+ 'agg_type': ('combo', ['mean', 'median'], 'mean'),
268
+ 'grouping': ('combo', ['mean', 'median'], 'mean'),
269
+ 'min_max': ('combo', ['allq', 'all'], 'allq'),
270
+ 'transform': ('combo', ['log', 'sqrt', 'square', None], None)
253
271
  }
254
272
 
255
273
  for key, value in settings.items():
@@ -338,9 +356,10 @@ def setup_plot_section(vertical_container):
338
356
  plot_frame.add(canvas_widget, stretch="always")
339
357
  canvas.draw()
340
358
  canvas.figure = figure
341
- # Set the figure and axes background to black
342
- figure.patch.set_facecolor('black')
343
- plot.set_facecolor('black')
359
+ style_out = set_dark_style(ttk.Style())
360
+
361
+ figure.patch.set_facecolor(style_out['bg_color'])
362
+ plot.set_facecolor(style_out['bg_color'])
344
363
  containers = [plot_frame]
345
364
  widgets = [canvas_widget]
346
365
  style = ttk.Style(vertical_container)
@@ -488,25 +507,25 @@ def setup_button_section(horizontal_container, settings_type='mask', window_dime
488
507
  btn_row = 1
489
508
 
490
509
  if run:
491
- run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Run", command=lambda: start_process(q, fig_queue, settings_type))
510
+ run_button = spacrButton(button_scrollable_frame.scrollable_frame, text="run", command=lambda: start_process(q, fig_queue, settings_type))
492
511
  run_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
493
512
  widgets.append(run_button)
494
513
  btn_row += 1
495
514
 
496
515
  if abort and settings_type in ['mask', 'measure', 'classify', 'sequencing', 'umap']:
497
- abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Abort", command=initiate_abort)
516
+ abort_button = spacrButton(button_scrollable_frame.scrollable_frame, text="abort", command=initiate_abort)
498
517
  abort_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
499
518
  widgets.append(abort_button)
500
519
  btn_row += 1
501
520
 
502
521
  if download and settings_type in ['mask']:
503
- download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Download", command=download_hug_dataset)
522
+ download_dataset_button = spacrButton(button_scrollable_frame.scrollable_frame, text="download", command=download_hug_dataset)
504
523
  download_dataset_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
505
524
  widgets.append(download_dataset_button)
506
525
  btn_row += 1
507
526
 
508
527
  if import_btn:
509
- import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="Import", command=lambda: import_settings(settings_type))
528
+ import_button = spacrButton(button_scrollable_frame.scrollable_frame, text="settings", command=lambda: import_settings(settings_type))
510
529
  import_button.grid(row=btn_row, column=btn_col, pady=5, padx=5, sticky='ew')
511
530
  widgets.append(import_button)
512
531
 
@@ -516,11 +535,19 @@ def setup_button_section(horizontal_container, settings_type='mask', window_dime
516
535
  description_frame = tk.Frame(horizontal_container)
517
536
  horizontal_container.add(description_frame, stretch="always", sticky="nsew")
518
537
  description_frame.grid_columnconfigure(0, weight=1)
538
+ description_frame.grid_rowconfigure(0, weight=1) # Add this line to make the row expandable
539
+
519
540
  description_label = tk.Label(description_frame, text="Module Description", anchor='nw', justify='left', wraplength=width - 50)
520
541
  description_label.grid(row=0, column=0, pady=50, padx=20, sticky='nsew')
521
542
  description_text = descriptions.get(settings_type, "No description available for this module.")
522
543
  description_label.config(text=description_text)
523
544
 
545
+ def update_wraplength(event):
546
+ new_width = event.width - 40 # Adjust as needed
547
+ description_label.config(wraplength=new_width)
548
+
549
+ description_label.bind('<Configure>', update_wraplength)
550
+
524
551
  containers = [button_frame, description_frame]
525
552
  widgets.extend([description_label])
526
553
 
@@ -588,7 +615,7 @@ def toggle_settings(button_scrollable_frame):
588
615
  category_var = tk.StringVar()
589
616
  non_empty_categories = [category for category, settings in categories.items() if any(setting in vars_dict for setting in settings)]
590
617
  category_dropdown = spacrDropdownMenu(button_scrollable_frame.scrollable_frame, category_var, non_empty_categories, command=on_category_select)
591
- category_dropdown.grid(row=1, column=3, sticky="ew", pady=2, padx=2)
618
+ category_dropdown.grid(row=5, column=0, sticky="ew", pady=2, padx=2)
592
619
  vars_dict = hide_all_settings(vars_dict, categories)
593
620
 
594
621
  def process_fig_queue():
spacr/gui_elements.py CHANGED
@@ -15,17 +15,25 @@ 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
18
19
 
19
- 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='teal', inactive_color='gray'):
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'
20
26
 
21
27
  if active_color == 'teal':
22
28
  active_color = '#008080'
23
- if inactive_color == 'gray':
24
- inactive_color = '#555555'
29
+ if inactive_color == 'dark_gray':
30
+ inactive_color = '#050505'
25
31
  if bg_color == 'black':
26
32
  bg_color = '#000000'
27
33
  if fg_color == 'white':
28
34
  fg_color = '#ffffff'
35
+ if active_color == 'blue':
36
+ active_color = '#007BFF'
29
37
 
30
38
  font_style = tkFont.Font(family=font_family, size=font_size)
31
39
  style.configure('TEntry', padding='5 5 5 5', borderwidth=1, relief='solid', fieldbackground=bg_color, foreground=fg_color, font=font_style)
@@ -76,7 +84,7 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
76
84
  menu = widget['menu']
77
85
  menu.configure(bg=bg_color, fg=fg_color, font=(font_family, font_size))
78
86
 
79
- 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}
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}
80
88
 
81
89
  def set_default_font(root, font_name="Arial", size=12):
82
90
  default_font = (font_name, size)
@@ -116,7 +124,6 @@ class spacrCheckbutton(ttk.Checkbutton):
116
124
  style = ttk.Style()
117
125
  _ = set_dark_style(style, widgets=[self])
118
126
 
119
-
120
127
  class spacrFrame(ttk.Frame):
121
128
  def __init__(self, container, width=None, *args, bg='black', **kwargs):
122
129
  super().__init__(container, *args, **kwargs)
@@ -189,25 +196,40 @@ class spacrLabel(tk.Frame):
189
196
  self.canvas.itemconfig(self.label_text, text=text)
190
197
 
191
198
  class spacrButton(tk.Frame):
192
- 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):
193
200
  super().__init__(parent, *args, **kwargs)
194
201
 
195
- self.text = text
202
+ self.text = text.capitalize() # Capitalize only the first letter of the text
196
203
  self.command = command
197
- button_height = 50
198
- button_width = 140
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
+
199
209
  style_out = set_dark_style(ttk.Style())
200
210
 
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
215
+
201
216
  # Create the canvas first
202
- self.canvas = tk.Canvas(self, width=button_width + 4, height=button_height + 4, highlightthickness=0, bg=style_out['bg_color'])
217
+ self.canvas = tk.Canvas(self, width=self.button_width + 4, height=self.size + 4, highlightthickness=0, bg=style_out['bg_color'])
203
218
  self.canvas.grid(row=0, column=0)
204
219
 
205
220
  # Apply dark style and get color settings
206
221
  color_settings = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
207
222
 
208
- self.button_bg = self.create_rounded_rectangle(2, 2, button_width + 2, button_height + 2, radius=20, fill=color_settings['bg_color'], outline=color_settings['fg_color'])
209
- self.font_style = font if font else tkFont.Font(family=color_settings['font_family'], size=color_settings['font_size'], weight=tkFont.NORMAL)
210
- self.button_text = self.canvas.create_text((button_width + 4) // 2, (button_height + 4) // 2, text=self.text, fill=color_settings['fg_color'], 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
211
233
 
212
234
  self.bind("<Enter>", self.on_enter)
213
235
  self.bind("<Leave>", self.on_leave)
@@ -219,12 +241,42 @@ class spacrButton(tk.Frame):
219
241
  self.bg_color = color_settings['bg_color']
220
242
  self.active_color = color_settings['active_color']
221
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")
222
268
 
223
269
  def on_enter(self, event=None):
224
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%
225
274
 
226
275
  def on_leave(self, event=None):
227
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
228
280
 
229
281
  def on_click(self, event=None):
230
282
  if self.command:
@@ -232,18 +284,15 @@ class spacrButton(tk.Frame):
232
284
 
233
285
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
234
286
  points = [
235
- x1 + radius, y1,
236
287
  x1 + radius, y1,
237
288
  x2 - radius, y1,
238
289
  x2 - radius, y1,
239
290
  x2, y1,
240
291
  x2, y1 + radius,
241
- x2, y1 + radius,
242
292
  x2, y2 - radius,
243
293
  x2, y2 - radius,
244
294
  x2, y2,
245
295
  x2 - radius, y2,
246
- x2 - radius, y2,
247
296
  x1 + radius, y2,
248
297
  x1 + radius, y2,
249
298
  x1, y2,
@@ -254,6 +303,46 @@ class spacrButton(tk.Frame):
254
303
  x1, y1
255
304
  ]
256
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
+
257
346
 
258
347
  class spacrSwitch(ttk.Frame):
259
348
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
@@ -363,7 +452,7 @@ class spacrToolTip:
363
452
  self.tooltip_window.destroy()
364
453
  self.tooltip_window = None
365
454
 
366
- class modify_masks:
455
+ class ModifyMaskApp:
367
456
  def __init__(self, root, folder_path, scale_factor):
368
457
  self.root = root
369
458
  self.folder_path = folder_path
@@ -1207,7 +1296,7 @@ class modify_masks:
1207
1296
  self.mask[labeled_mask == i] = 0 # Remove small objects
1208
1297
  self.update_display()
1209
1298
 
1210
- class ImageApp:
1299
+ class AnnotateApp:
1211
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):
1212
1301
  self.root = root
1213
1302
  self.db_path = db_path
@@ -1471,7 +1560,7 @@ class ImageApp:
1471
1560
  break
1472
1561
 
1473
1562
  if not self.update_queue.empty():
1474
- ImageApp.update_html("Do not exit, Updating database...")
1563
+ AnnotateApp.update_html("Do not exit, Updating database...")
1475
1564
  self.status_label.config(text='Do not exit, Updating database...')
1476
1565
 
1477
1566
  pending_updates = self.update_queue.get()
@@ -1482,7 +1571,7 @@ class ImageApp:
1482
1571
  c.execute(f'UPDATE png_list SET {self.annotation_column} = ? WHERE png_path = ?', (new_annotation, path))
1483
1572
  conn.commit()
1484
1573
 
1485
- ImageApp.update_html('')
1574
+ AnnotateApp.update_html('')
1486
1575
  self.status_label.config(text='')
1487
1576
  self.root.update()
1488
1577
  time.sleep(0.1)
@@ -1517,9 +1606,7 @@ class ImageApp:
1517
1606
  print(f'Quit application')
1518
1607
 
1519
1608
  def create_menu_bar(root):
1520
- from .gui_utils import load_app
1521
- from .gui_core import initiate_root
1522
-
1609
+ from .gui import initiate_root
1523
1610
  gui_apps = {
1524
1611
  "Mask": (lambda frame: initiate_root(frame, 'mask'), "Generate cellpose masks for cells, nuclei and pathogen images."),
1525
1612
  "Measure": (lambda frame: initiate_root(frame, 'measure'), "Measure single object intensity and morphological feature. Crop and save single object image"),
@@ -1538,19 +1625,23 @@ def create_menu_bar(root):
1538
1625
  }
1539
1626
 
1540
1627
  def load_app_wrapper(app_name, app_func):
1541
- load_app(root, app_name, app_func)
1628
+ root.load_app(app_name, app_func)
1542
1629
 
1543
1630
  # Create the menu bar
1544
1631
  menu_bar = tk.Menu(root, bg="#008080", fg="white")
1632
+
1545
1633
  # Create a "SpaCr Applications" menu
1546
1634
  app_menu = tk.Menu(menu_bar, tearoff=0, bg="#008080", fg="white")
1547
1635
  menu_bar.add_cascade(label="SpaCr Applications", menu=app_menu)
1636
+
1548
1637
  # Add options to the "SpaCr Applications" menu
1549
1638
  for app_name, app_data in gui_apps.items():
1550
1639
  app_func, app_desc = app_data
1551
1640
  app_menu.add_command(label=app_name, command=lambda app_name=app_name, app_func=app_func: load_app_wrapper(app_name, app_func))
1641
+
1552
1642
  # Add a separator and an exit option
1553
1643
  app_menu.add_separator()
1554
1644
  app_menu.add_command(label="Exit", command=root.quit)
1645
+
1555
1646
  # Configure the menu for the root window
1556
- root.config(menu=menu_bar)
1647
+ 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>
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="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="M463-132q-35.43 0-66.81-13.75Q364.82-159.5 345-187L164-430.5l9.5-9.5q4.5-5.5 11.65-7t15.35.5l141.5 66v-309.37q0-3.87 3.23-7t8-3.13q4.77 0 8.02 3.36t3.25 7.56v340.58L193-427l173.5 229q16.1 23.7 42.15 33.85Q434.69-154 463-154h153.5q45.03 0 77.27-31.65Q726-217.29 726-263.5V-402q0-29.4-19.62-48.95-19.63-19.55-47.88-19.55h-174v-22h174q36.38 0 62.94 26.4Q748-439.71 748-402v138.49q0 55.01-38.91 93.26Q670.19-132 616.5-132H463ZM233.4-620.5q-9.47-13.75-14.19-31.47-4.71-17.71-4.71-36.86 0-58.64 40.62-98.91Q295.74-828 353.15-828q57.4 0 98.37 40.5 40.98 40.5 40.98 99.13 0 19.07-5.21 36.63-5.22 17.56-13.73 31.24l-19.06-10.34q7.5-13.16 11.75-27.74 4.25-14.59 4.25-30.83 0-48.51-34.28-82.55T352.97-806q-48.97 0-82.72 33.95-33.75 33.96-33.75 83.27 0 15.82 3.75 30.3T252.5-631l-19.1 10.5Zm227.1 297Z"/></svg>
Binary file
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="M480.35-367Q535-367 574-406.35q39-39.36 39-94Q613-555 573.65-594q-39.36-39-94-39Q425-633 386-593.65q-39 39.36-39 94Q347-445 386.35-406q39.36 39 94 39Zm-.76-24q-45.59 0-77.09-31.91t-31.5-77.5q0-45.59 31.91-77.09t77.5-31.5q45.59 0 77.09 31.91t31.5 77.5q0 45.59-31.91 77.09t-77.5 31.5Zm.55 139Q356.5-252 253-319.25 149.5-386.5 96-500q53.5-113.5 156.86-180.75Q356.21-748 479.86-748 603.5-748 707-680.75 810.5-613.5 864-500q-53.5 113.5-156.86 180.75Q603.79-252 480.14-252ZM480-500Zm-.17 226q116.47 0 212.89-60.91Q789.14-395.81 840-500q-50.86-104.19-147.11-165.09Q596.64-726 480.17-726t-212.89 60.91Q170.86-604.19 120-500q50.86 104.19 147.11 165.09Q363.36-274 479.83-274Z"/></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="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
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="M212-172v-66l541.92-543.42q3.08-3.08 8.08-4.83t9.25-1.75q4.3 0 9.28 1.75 4.97 1.75 7.47 4.75l33 33q3 3.5 5 7.97 2 4.48 2 9.28 0 4.25-1.75 9.25t-4.83 8.08L278-172h-66Zm22-22h35.5L716-641l-16.98-18.02L681-676 234-229.5v35.5Zm572.5-537.5-35-35 35 35Zm-107.48 72.48L681-676l35 35-16.98-18.02ZM574.5-172q64.87 0 119.18-29.25Q748-230.5 748-286.5q0-34.3-28.25-64.15Q691.5-380.5 635.5-397l-18 19q50.5 14 79.5 38.75t29 52.75q0 40.62-44.04 66.56Q637.91-194 574.5-194q-5.5 0-8.5 3.04-3 3.03-3 7.46 0 5.5 3 8.5t8.5 3Zm-320-241.5 16.5-18q-40.5-14-58.75-29.75T194-494.5q0-21.5 24.5-41t91.14-46.7q72.72-29.8 100.04-55.05Q437-662.5 437-694.5q0-39-36.75-66.25T306.5-788q-36 0-67.5 12.5T193.5-744q-2.5 4.5-2.25 8.75t3.75 6.75q2.5 2.5 7.24 2.12 4.75-.39 8.26-4.12 16.5-16.5 41.58-26t54.33-9.5q51.09 0 79.84 20.78T415-694.56q0 26.46-23.5 45.01Q368-631 300.5-602q-78 32-103.25 55.54T172-494.5q0 22.5 19.25 44t63.25 37Z"/></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>
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="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>
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="M372-295v-372l292 186-292 186Zm22-186Zm0 145 230-145-230-145v290Z"/></svg>
Binary file