spacr 1.0.9__py3-none-any.whl → 1.1.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.
spacr/gui_elements.py CHANGED
@@ -12,8 +12,14 @@ fig = None
12
12
 
13
13
  def restart_gui_app(root):
14
14
  """
15
- Restarts the GUI application by destroying the current instance
16
- and launching a fresh one.
15
+ Restarts the SpaCr GUI application by destroying the current root window
16
+ and launching a fresh instance.
17
+
18
+ Args:
19
+ root (tk.Tk): The current Tkinter root window to be destroyed.
20
+
21
+ Note:
22
+ The new instance is launched by importing and invoking `gui_app()`.
17
23
  """
18
24
  try:
19
25
  # Destroy the current root window
@@ -27,6 +33,20 @@ def restart_gui_app(root):
27
33
  print(f"Error restarting GUI application: {e}")
28
34
 
29
35
  def create_menu_bar(root):
36
+ """
37
+ Creates a top-level menu bar for the SpaCr GUI containing shortcuts to all
38
+ major application modules and help resources.
39
+
40
+ Args:
41
+ root (tk.Tk): The root window where the menu bar will be attached.
42
+
43
+ Adds:
44
+ - A 'SpaCr Applications' menu with links to:
45
+ 'Mask', 'Measure', 'Classify', 'ML Analyze', 'Map Barcodes',
46
+ 'Regression', 'Activation', and 'Recruitment'.
47
+ - A Help option linking to the online documentation.
48
+ - An Exit option to quit the application.
49
+ """
30
50
  from .gui import initiate_root
31
51
  gui_apps = {
32
52
  "Mask": lambda: initiate_root(root, settings_type='mask'),
@@ -63,7 +83,18 @@ def create_menu_bar(root):
63
83
  root.config(menu=menu_bar)
64
84
 
65
85
  def set_element_size():
66
-
86
+ """
87
+ Calculates and returns standardized UI element dimensions
88
+ based on the current screen size.
89
+
90
+ Returns:
91
+ dict: A dictionary with element dimensions including:
92
+ - 'btn_size' (int): Size of buttons.
93
+ - 'bar_size' (int): Height of progress bars.
94
+ - 'settings_width' (int): Width of the settings panel.
95
+ - 'panel_width' (int): Width of the plotting panel.
96
+ - 'panel_height' (int): Height of the bottom control panel.
97
+ """
67
98
  screen_width, screen_height = pyautogui.size()
68
99
  screen_area = screen_width * screen_height
69
100
 
@@ -84,7 +115,24 @@ def set_element_size():
84
115
  return size_dict
85
116
 
86
117
  def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font_family="OpenSans", font_size=12, bg_color='black', fg_color='white', active_color='blue', inactive_color='dark_gray'):
87
-
118
+ """
119
+ Applies a dark theme to the SpaCr GUI using the provided styling options.
120
+
121
+ Args:
122
+ style (ttk.Style): The ttk style instance to configure.
123
+ parent_frame (tk.Widget, optional): The top-level container to apply styles to.
124
+ containers (list, optional): Additional containers (ttk.Frame or tk.Frame) to style.
125
+ widgets (list, optional): List of individual widgets to apply colors and fonts.
126
+ font_family (str): Font family for all labels and buttons.
127
+ font_size (int): Font size for all text elements.
128
+ bg_color (str): Background color.
129
+ fg_color (str): Foreground/text color.
130
+ active_color (str): Highlight or selected color.
131
+ inactive_color (str): Secondary background color.
132
+
133
+ Returns:
134
+ dict: Style parameters used, including resolved font and color values.
135
+ """
88
136
  if active_color == 'teal':
89
137
  active_color = '#008080'
90
138
  if inactive_color == 'dark_gray':
@@ -163,7 +211,7 @@ class spacrFont:
163
211
  """
164
212
  Initializes the FontLoader class.
165
213
 
166
- Parameters:
214
+ Args:
167
215
  - font_name: str, the name of the font (e.g., 'OpenSans').
168
216
  - font_style: str, the style of the font (e.g., 'Regular', 'Bold').
169
217
  - font_size: int, the size of the font (default: 12).
@@ -182,7 +230,7 @@ class spacrFont:
182
230
  """
183
231
  Returns the font path based on the font name and style.
184
232
 
185
- Parameters:
233
+ Args:
186
234
  - font_name: str, the name of the font.
187
235
  - font_style: str, the style of the font.
188
236
 
@@ -221,7 +269,7 @@ class spacrFont:
221
269
  """
222
270
  Returns the font in the specified size.
223
271
 
224
- Parameters:
272
+ Args:
225
273
  - size: int, the size of the font (optional).
226
274
 
227
275
  Returns:
@@ -232,7 +280,24 @@ class spacrFont:
232
280
  return font.Font(family=self.font_name, size=size)
233
281
 
234
282
  class spacrContainer(tk.Frame):
283
+ """
284
+ A custom container widget that manages multiple resizable panes arranged
285
+ either vertically or horizontally, separated by draggable sashes.
286
+
287
+ Args:
288
+ parent (tk.Widget): The parent widget.
289
+ orient (str): Orientation of the layout ('tk.VERTICAL' or 'tk.HORIZONTAL'). Default is vertical.
290
+ bg (str): Background color of the container and sashes. Defaults to 'lightgrey'.
291
+ """
235
292
  def __init__(self, parent, orient=tk.VERTICAL, bg=None, *args, **kwargs):
293
+ """
294
+ Initialize the spacrContainer with the specified orientation and background color.
295
+
296
+ Args:
297
+ parent (tk.Widget): Parent widget.
298
+ orient (str): Layout orientation (tk.VERTICAL or tk.HORIZONTAL).
299
+ bg (str, optional): Background color. Defaults to 'lightgrey'.
300
+ """
236
301
  super().__init__(parent, *args, **kwargs)
237
302
  self.orient = orient
238
303
  self.bg = bg if bg else 'lightgrey'
@@ -246,6 +311,13 @@ class spacrContainer(tk.Frame):
246
311
  self.grid_columnconfigure(0, weight=1)
247
312
 
248
313
  def add(self, widget, stretch='always'):
314
+ """
315
+ Add a new widget as a pane to the container.
316
+
317
+ Args:
318
+ widget (tk.Widget): Widget to add.
319
+ stretch (str): Stretch policy (currently unused).
320
+ """
249
321
  print(f"Adding widget: {widget} with stretch: {stretch}")
250
322
  pane = tk.Frame(self, bg=self.bg)
251
323
  pane.grid_propagate(False)
@@ -258,6 +330,9 @@ class spacrContainer(tk.Frame):
258
330
  self.reposition_panes()
259
331
 
260
332
  def create_sash(self):
333
+ """
334
+ Create a draggable sash between panes.
335
+ """
261
336
  sash = tk.Frame(self, bg=self.bg, cursor='sb_v_double_arrow' if self.orient == tk.VERTICAL else 'sb_h_double_arrow', height=self.sash_thickness, width=self.sash_thickness)
262
337
  sash.bind("<Enter>", self.on_enter_sash)
263
338
  sash.bind("<Leave>", self.on_leave_sash)
@@ -265,6 +340,9 @@ class spacrContainer(tk.Frame):
265
340
  self.sashes.append(sash)
266
341
 
267
342
  def reposition_panes(self):
343
+ """
344
+ Reposition panes and sashes within the container based on orientation.
345
+ """
268
346
  if not self.panes:
269
347
  return
270
348
 
@@ -286,22 +364,52 @@ class spacrContainer(tk.Frame):
286
364
  sash.grid(row=0, column=(i * 2) + 1, sticky="ns")
287
365
 
288
366
  def on_configure(self, event):
367
+ """
368
+ Event handler triggered on container resize.
369
+
370
+ Args:
371
+ event (tk.Event): Tkinter event object.
372
+ """
289
373
  print(f"Configuring container: {self}")
290
374
  self.reposition_panes()
291
375
 
292
376
  def on_enter_sash(self, event):
377
+ """
378
+ Change sash color on mouse enter.
379
+
380
+ Args:
381
+ event (tk.Event): Tkinter event object.
382
+ """
293
383
  event.widget.config(bg='blue')
294
384
 
295
385
  def on_leave_sash(self, event):
386
+ """
387
+ Reset sash color on mouse leave.
388
+
389
+ Args:
390
+ event (tk.Event): Tkinter event object.
391
+ """
296
392
  event.widget.config(bg=self.bg)
297
393
 
298
394
  def start_resize(self, event):
395
+ """
396
+ Initiate resizing behavior when mouse press begins on a sash.
397
+
398
+ Args:
399
+ event (tk.Event): Tkinter event object.
400
+ """
299
401
  sash = event.widget
300
402
  self.start_pos = event.y_root if self.orient == tk.VERTICAL else event.x_root
301
403
  self.start_size = sash.winfo_y() if self.orient == tk.VERTICAL else sash.winfo_x()
302
404
  sash.bind("<B1-Motion>", self.perform_resize)
303
405
 
304
406
  def perform_resize(self, event):
407
+ """
408
+ Adjust pane sizes during mouse drag on a sash.
409
+
410
+ Args:
411
+ event (tk.Event): Tkinter event object.
412
+ """
305
413
  sash = event.widget
306
414
  delta = (event.y_root - self.start_pos) if self.orient == tk.VERTICAL else (event.x_root - self.start_pos)
307
415
  new_size = self.start_size + delta
@@ -325,7 +433,20 @@ class spacrContainer(tk.Frame):
325
433
  self.reposition_panes()
326
434
 
327
435
  class spacrEntry(tk.Frame):
436
+ """
437
+ A custom Tkinter entry widget with rounded corners, dark theme styling, and active/inactive color handling.
438
+
439
+ Args:
440
+ parent (tk.Widget): Parent widget.
441
+ textvariable (tk.StringVar, optional): Tkinter textvariable to bind to the entry.
442
+ outline (bool, optional): Whether to show an outline. Currently unused. Defaults to False.
443
+ width (int, optional): Width of the entry widget. Defaults to 220 if not provided.
444
+ \*args, \*\*kwargs: Additional arguments passed to the parent Frame.
445
+ """
328
446
  def __init__(self, parent, textvariable=None, outline=False, width=None, *args, **kwargs):
447
+ """
448
+ Initialize the custom entry widget with dark theme and rounded styling.
449
+ """
329
450
  super().__init__(parent, *args, **kwargs)
330
451
 
331
452
  # Set dark style
@@ -364,6 +485,12 @@ class spacrEntry(tk.Frame):
364
485
  self.draw_rounded_rectangle(self.bg_color)
365
486
 
366
487
  def draw_rounded_rectangle(self, color):
488
+ """
489
+ Draws a rounded rectangle with the given color as background.
490
+
491
+ Args:
492
+ color (str): Fill color for the rounded rectangle.
493
+ """
367
494
  radius = 15 # Increased radius for more rounded corners
368
495
  x0, y0 = 10, 5
369
496
  x1, y1 = self.canvas_width - 10, self.canvas_height - 5
@@ -376,15 +503,33 @@ class spacrEntry(tk.Frame):
376
503
  self.canvas.create_rectangle((x0, y0 + radius / 2, x1, y1 - radius / 2), fill=color, outline=color)
377
504
 
378
505
  def on_focus_in(self, event):
506
+ """
507
+ Event handler for focus in. Changes the background to the active color.
508
+ """
379
509
  self.draw_rounded_rectangle(self.active_color)
380
510
  self.entry.config(bg=self.active_color)
381
511
 
382
512
  def on_focus_out(self, event):
513
+ """
514
+ Event handler for focus out. Reverts the background to the inactive color.
515
+ """
383
516
  self.draw_rounded_rectangle(self.bg_color)
384
517
  self.entry.config(bg=self.bg_color)
385
518
 
386
519
  class spacrCheck(tk.Frame):
520
+ """
521
+ A custom checkbox widget with rounded square appearance and dark style.
522
+
523
+ Args:
524
+ parent (tk.Widget): Parent widget.
525
+ text (str, optional): Label text (currently unused).
526
+ variable (tk.BooleanVar): Tkinter variable to bind the checkbox state.
527
+ \*args, \*\*kwargs: Additional arguments passed to the parent Frame.
528
+ """
387
529
  def __init__(self, parent, text="", variable=None, *args, **kwargs):
530
+ """
531
+ Initializes the custom checkbox widget and binds visual updates to variable state.
532
+ """
388
533
  super().__init__(parent, *args, **kwargs)
389
534
 
390
535
  style_out = set_dark_style(ttk.Style())
@@ -412,6 +557,12 @@ class spacrCheck(tk.Frame):
412
557
  self.canvas.bind("<Button-1>", self.toggle_variable)
413
558
 
414
559
  def draw_rounded_square(self, color):
560
+ """
561
+ Draws a rounded square with border and fill.
562
+
563
+ Args:
564
+ color (str): The fill color based on the current checkbox state.
565
+ """
415
566
  radius = 5 # Adjust the radius for more rounded corners
416
567
  x0, y0 = 2, 2
417
568
  x1, y1 = 18, 18
@@ -428,13 +579,43 @@ class spacrCheck(tk.Frame):
428
579
  self.canvas.create_line(x1, y0 + radius / 2, x1, y1 - radius / 2, fill=self.fg_color)
429
580
 
430
581
  def update_check(self, *args):
582
+ """
583
+ Redraws the checkbox based on the current value of the associated variable.
584
+ """
431
585
  self.draw_rounded_square(self.active_color if self.variable.get() else self.inactive_color)
432
586
 
433
587
  def toggle_variable(self, event):
588
+ """
589
+ Toggles the value of the associated variable when the checkbox is clicked.
590
+
591
+ Args:
592
+ event (tk.Event): The mouse click event.
593
+ """
434
594
  self.variable.set(not self.variable.get())
435
595
 
436
596
  class spacrCombo(tk.Frame):
597
+ """
598
+ A custom styled combo box widget with rounded edges and dropdown functionality.
599
+
600
+ This widget mimics a modern dropdown menu with dark-themed styling, allowing
601
+ users to select from a list of values in a visually appealing interface.
602
+
603
+ Args:
604
+ parent (tk.Widget): Parent widget.
605
+ textvariable (tk.StringVar, optional): Variable linked to the combo box selection.
606
+ values (list, optional): List of selectable values. Defaults to empty list.
607
+ width (int, optional): Width of the combo box in pixels. Defaults to 220.
608
+ """
437
609
  def __init__(self, parent, textvariable=None, values=None, width=None, *args, **kwargs):
610
+ """
611
+ Initialize the combo box UI and style settings.
612
+
613
+ Args:
614
+ parent (tk.Widget): The parent widget.
615
+ textvariable (tk.StringVar, optional): A Tkinter StringVar linked to the selected value.
616
+ values (list, optional): List of values to populate the dropdown.
617
+ width (int, optional): Width of the combo box. Defaults to 220 pixels.
618
+ """
438
619
  super().__init__(parent, *args, **kwargs)
439
620
 
440
621
  # Set dark style
@@ -474,6 +655,12 @@ class spacrCombo(tk.Frame):
474
655
  self.dropdown_menu = None
475
656
 
476
657
  def draw_rounded_rectangle(self, color):
658
+ """
659
+ Draw a rounded rectangle on the canvas with the specified background color.
660
+
661
+ Args:
662
+ color (str): The fill color for the rounded rectangle.
663
+ """
477
664
  radius = 15 # Increased radius for more rounded corners
478
665
  x0, y0 = 10, 5
479
666
  x1, y1 = self.canvas_width - 10, self.canvas_height - 5
@@ -487,12 +674,21 @@ class spacrCombo(tk.Frame):
487
674
  self.label.config(bg=color) # Update label background to match rectangle color
488
675
 
489
676
  def on_click(self, event):
677
+ """
678
+ Handle click event on the combo box to toggle the dropdown menu.
679
+
680
+ Args:
681
+ event (tk.Event): The mouse click event.
682
+ """
490
683
  if self.dropdown_menu is None:
491
684
  self.open_dropdown()
492
685
  else:
493
686
  self.close_dropdown()
494
687
 
495
688
  def open_dropdown(self):
689
+ """
690
+ Display the dropdown menu with available values.
691
+ """
496
692
  self.draw_rounded_rectangle(self.active_color)
497
693
 
498
694
  self.dropdown_menu = tk.Toplevel(self)
@@ -513,6 +709,9 @@ class spacrCombo(tk.Frame):
513
709
  item.bind("<Leave>", lambda e, w=item: w.config(bg=self.inactive_color))
514
710
 
515
711
  def close_dropdown(self):
712
+ """
713
+ Close the dropdown menu if it is open.
714
+ """
516
715
  self.draw_rounded_rectangle(self.inactive_color)
517
716
 
518
717
  if self.dropdown_menu:
@@ -520,6 +719,12 @@ class spacrCombo(tk.Frame):
520
719
  self.dropdown_menu = None
521
720
 
522
721
  def on_select(self, value):
722
+ """
723
+ Update the displayed label and internal variable when a value is selected.
724
+
725
+ Args:
726
+ value (str): The selected value from the dropdown.
727
+ """
523
728
  display_text = value if value is not None else 'None'
524
729
  self.var.set(value)
525
730
  self.label.config(text=display_text)
@@ -527,14 +732,50 @@ class spacrCombo(tk.Frame):
527
732
  self.close_dropdown()
528
733
 
529
734
  def set(self, value):
735
+ """
736
+ Programmatically set the combo box selection to the specified value.
737
+
738
+ Args:
739
+ value (str): The value to set in the combo box.
740
+ """
530
741
  display_text = value if value is not None else 'None'
531
742
  self.var.set(value)
532
743
  self.label.config(text=display_text)
533
744
  self.selected_value = value
534
745
 
535
746
  class spacrDropdownMenu(tk.Frame):
536
-
747
+ """
748
+ A custom dark-themed dropdown menu widget with rounded edges and hover interaction.
749
+
750
+ This widget displays a labeled button that reveals a menu of selectable options
751
+ when clicked. It supports external callback functions, styling updates, and dynamic
752
+ highlighting of active menu items.
753
+
754
+ Args:
755
+ parent (tk.Widget): Parent widget in which the dropdown menu is placed.
756
+ variable (tk.StringVar): A Tkinter variable to store the selected option.
757
+ options (list): A list of option labels to populate the dropdown menu.
758
+ command (callable, optional): A function to call when an option is selected.
759
+ font (tuple or tk.Font, optional): Font used for the button label.
760
+ size (int, optional): Height of the button in pixels. Defaults to 50.
761
+ **kwargs: Additional keyword arguments passed to the `tk.Frame` base class.
762
+ """
537
763
  def __init__(self, parent, variable, options, command=None, font=None, size=50, **kwargs):
764
+ """
765
+ Initialize the spacrDropdownMenu with a canvas-based button and popup menu.
766
+
767
+ Sets up the button appearance, binds mouse interaction events,
768
+ and constructs the dropdown menu with the given options.
769
+
770
+ Args:
771
+ parent (tk.Widget): Parent container.
772
+ variable (tk.StringVar): Variable that stores the selected option.
773
+ options (list): List of strings representing the dropdown options.
774
+ command (callable, optional): Callback function when an option is selected.
775
+ font (tk.Font or tuple, optional): Font used for the button text.
776
+ size (int, optional): Button height in pixels. Defaults to 50.
777
+ **kwargs: Additional keyword arguments for the Frame.
778
+ """
538
779
  super().__init__(parent, **kwargs)
539
780
  self.variable = variable
540
781
  self.options = options
@@ -588,6 +829,20 @@ class spacrDropdownMenu(tk.Frame):
588
829
  self.menu.add_command(label=option, command=lambda opt=option: self.on_select(opt))
589
830
 
590
831
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
832
+ """
833
+ Draw a rounded rectangle polygon on the internal canvas.
834
+
835
+ Args:
836
+ x1 (int): Top-left x coordinate.
837
+ y1 (int): Top-left y coordinate.
838
+ x2 (int): Bottom-right x coordinate.
839
+ y2 (int): Bottom-right y coordinate.
840
+ radius (int, optional): Radius of the corners. Defaults to 20.
841
+ **kwargs: Canvas polygon configuration options (fill, outline, etc.).
842
+
843
+ Returns:
844
+ int: Canvas item ID of the created polygon.
845
+ """
591
846
  points = [
592
847
  x1 + radius, y1,
593
848
  x2 - radius, y1,
@@ -610,23 +865,57 @@ class spacrDropdownMenu(tk.Frame):
610
865
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
611
866
 
612
867
  def on_enter(self, event=None):
868
+ """
869
+ Handle mouse enter event by updating the button's background color.
870
+
871
+ Args:
872
+ event (tk.Event, optional): The event object. Defaults to None.
873
+ """
613
874
  self.canvas.itemconfig(self.button_bg, fill=self.active_color)
614
875
 
615
876
  def on_leave(self, event=None):
877
+ """
878
+ Handle mouse leave event by resetting the button's background color.
879
+
880
+ Args:
881
+ event (tk.Event, optional): The event object. Defaults to None.
882
+ """
616
883
  self.canvas.itemconfig(self.button_bg, fill=self.inactive_color)
617
884
 
618
885
  def on_click(self, event=None):
886
+ """
887
+ Handle button click event to display the dropdown menu.
888
+
889
+ Args:
890
+ event (tk.Event, optional): The event object. Defaults to None.
891
+ """
619
892
  self.post_menu()
620
893
 
621
894
  def post_menu(self):
895
+ """
896
+ Display the dropdown menu below the button using screen coordinates.
897
+ """
622
898
  x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
623
899
  self.menu.post(x, y + height)
624
900
 
625
901
  def on_select(self, option):
902
+ """
903
+ Callback when an option is selected from the dropdown menu.
904
+
905
+ Args:
906
+ option (str): The selected option label.
907
+ """
626
908
  if self.command:
627
909
  self.command(option)
628
910
 
629
911
  def update_styles(self, active_categories=None):
912
+ """
913
+ Update the styles of the dropdown menu entries based on active categories.
914
+
915
+ Args:
916
+ active_categories (list or None): List of option labels to highlight
917
+ with the active color. If None, all entries are styled as inactive.
918
+ """
630
919
  style_out = set_dark_style(ttk.Style(), widgets=[self.menu])
631
920
 
632
921
  if active_categories is not None:
@@ -638,6 +927,21 @@ class spacrDropdownMenu(tk.Frame):
638
927
  self.menu.entryconfig(idx, background=style_out['bg_color'], foreground=style_out['fg_color'])
639
928
 
640
929
  class spacrCheckbutton(ttk.Checkbutton):
930
+ """
931
+ A dark-themed styled Checkbutton widget for use in the SpaCr GUI.
932
+
933
+ This class wraps a `ttk.Checkbutton` with a custom style and binds it to a
934
+ `BooleanVar`, allowing it to integrate seamlessly into dark-themed interfaces.
935
+
936
+ Args:
937
+ parent (tk.Widget): The parent widget in which to place the checkbutton.
938
+ text (str, optional): The label text displayed next to the checkbutton.
939
+ variable (tk.BooleanVar, optional): Variable linked to the checkbutton's state.
940
+ If None, a new `BooleanVar` is created.
941
+ command (callable, optional): A callback function to execute when the checkbutton is toggled.
942
+ *args: Additional positional arguments passed to `ttk.Checkbutton`.
943
+ **kwargs: Additional keyword arguments passed to `ttk.Checkbutton`.
944
+ """
641
945
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
642
946
  super().__init__(parent, *args, **kwargs)
643
947
  self.text = text
@@ -648,7 +952,30 @@ class spacrCheckbutton(ttk.Checkbutton):
648
952
  _ = set_dark_style(style, widgets=[self])
649
953
 
650
954
  class spacrProgressBar(ttk.Progressbar):
955
+ """
956
+ A dark-themed progress bar widget with optional progress label display.
957
+
958
+ This class extends `ttk.Progressbar` and applies a dark visual theme consistent
959
+ with SpaCr GUI styling. It also provides an optional label that displays real-time
960
+ progress, operation type, and additional information.
961
+
962
+ Args:
963
+ parent (tk.Widget): The parent widget in which to place the progress bar.
964
+ label (bool, optional): Whether to show a label below the progress bar. Defaults to True.
965
+ *args: Additional positional arguments passed to `ttk.Progressbar`.
966
+ **kwargs: Additional keyword arguments passed to `ttk.Progressbar`.
967
+ """
651
968
  def __init__(self, parent, label=True, *args, **kwargs):
969
+ """
970
+ Initialize the progress bar and optional label with dark theme styling.
971
+
972
+ Sets the initial value to 0, applies custom style attributes, and creates
973
+ a label for displaying progress information.
974
+
975
+ Args:
976
+ parent (tk.Widget): Parent container for the widget.
977
+ label (bool, optional): Whether to show a label for progress updates. Defaults to True.
978
+ """
652
979
  super().__init__(parent, *args, **kwargs)
653
980
 
654
981
  # Get the style colors
@@ -705,6 +1032,11 @@ class spacrProgressBar(ttk.Progressbar):
705
1032
  self.additional_info = None
706
1033
 
707
1034
  def set_label_position(self):
1035
+ """
1036
+ Place the progress label one row below the progress bar in the grid layout.
1037
+
1038
+ Should be called after the progress bar has been placed with `.grid(...)`.
1039
+ """
708
1040
  if self.label and self.progress_label:
709
1041
  row_info = self.grid_info().get('rowID', 0)
710
1042
  col_info = self.grid_info().get('columnID', 0)
@@ -712,6 +1044,14 @@ class spacrProgressBar(ttk.Progressbar):
712
1044
  self.progress_label.grid(row=row_info + 1, column=col_info, columnspan=col_span, pady=5, padx=5, sticky='ew')
713
1045
 
714
1046
  def update_label(self):
1047
+ """
1048
+ Update the progress label with current progress, operation type, and extra info.
1049
+
1050
+ Constructs a single-line status message with:
1051
+ - Current progress value
1052
+ - Operation type (if set)
1053
+ - Additional info (if set)
1054
+ """
715
1055
  if self.label and self.progress_label:
716
1056
  # Start with the base progress information
717
1057
  label_text = f"Processing: {self['value']}/{self['maximum']}"
@@ -733,7 +1073,45 @@ class spacrProgressBar(ttk.Progressbar):
733
1073
  self.progress_label.config(text=label_text)
734
1074
 
735
1075
  class spacrSlider(tk.Frame):
1076
+ """
1077
+ A custom slider widget with dark-themed styling, optional numeric entry, and mouse interaction.
1078
+
1079
+ This slider is designed for GUI applications where numeric control is needed,
1080
+ supporting dynamic resizing, labeled value entry, and a callback on release.
1081
+
1082
+ Args:
1083
+ master (tk.Widget): Parent widget.
1084
+ length (int, optional): Fixed pixel length of the slider. If None, adapts to canvas width.
1085
+ thickness (int, optional): Thickness of the slider bar in pixels. Defaults to 2.
1086
+ knob_radius (int, optional): Radius of the slider knob in pixels. Defaults to 10.
1087
+ position (str, optional): Alignment of slider within the frame. One of "left", "center", "right".
1088
+ from_ (float): Minimum slider value.
1089
+ to (float): Maximum slider value.
1090
+ value (float, optional): Initial value. Defaults to `from_`.
1091
+ show_index (bool, optional): Whether to show an entry for numeric value. Defaults to False.
1092
+ command (Callable, optional): Function to call with the final value upon knob release.
1093
+ **kwargs: Additional options passed to `tk.Frame`.
1094
+ """
736
1095
  def __init__(self, master=None, length=None, thickness=2, knob_radius=10, position="center", from_=0, to=100, value=None, show_index=False, command=None, **kwargs):
1096
+ """
1097
+ Initialize a custom dark-themed slider widget.
1098
+
1099
+ This slider supports mouse interaction, optional direct numeric input via an Entry,
1100
+ and dynamically adapts its layout based on container resizing unless a fixed `length` is specified.
1101
+
1102
+ Args:
1103
+ master (tk.Widget, optional): Parent widget.
1104
+ length (int, optional): Fixed length of the slider in pixels. If None, dynamically resizes with the container.
1105
+ thickness (int, optional): Thickness of the slider bar in pixels. Default is 2.
1106
+ knob_radius (int, optional): Radius of the draggable knob in pixels. Default is 10.
1107
+ position (str, optional): Alignment of the slider within the frame. One of {"left", "center", "right"}. Default is "center".
1108
+ from_ (float): Minimum value of the slider.
1109
+ to (float): Maximum value of the slider.
1110
+ value (float, optional): Initial value of the slider. Defaults to `from_` if not specified.
1111
+ show_index (bool, optional): If True, displays a text entry box to manually input the slider value. Default is False.
1112
+ command (Callable, optional): Optional function to be called with the final value when the knob is released.
1113
+ **kwargs: Additional keyword arguments passed to the `tk.Frame` initializer.
1114
+ """
737
1115
  super().__init__(master, **kwargs)
738
1116
 
739
1117
  self.specified_length = length # Store the specified length, if any
@@ -793,6 +1171,12 @@ class spacrSlider(tk.Frame):
793
1171
  self.canvas.bind("<ButtonRelease-1>", self.release_knob) # Trigger command on release
794
1172
 
795
1173
  def resize_slider(self, event):
1174
+ """
1175
+ Recalculate slider dimensions and redraw upon resizing.
1176
+
1177
+ Args:
1178
+ event (tk.Event): Resize event.
1179
+ """
796
1180
  if self.specified_length is not None:
797
1181
  self.length = self.specified_length
798
1182
  else:
@@ -811,18 +1195,42 @@ class spacrSlider(tk.Frame):
811
1195
  self.draw_slider(inactive=True)
812
1196
 
813
1197
  def value_to_position(self, value):
1198
+ """
1199
+ Convert a numerical slider value to a pixel position on the canvas.
1200
+
1201
+ Args:
1202
+ value (float): The numerical value to convert.
1203
+
1204
+ Returns:
1205
+ float: Corresponding position in pixels on the slider.
1206
+ """
814
1207
  if self.to == self.from_:
815
1208
  return self.knob_radius
816
1209
  relative_value = (value - self.from_) / (self.to - self.from_)
817
1210
  return self.knob_radius + relative_value * (self.length - 2 * self.knob_radius)
818
1211
 
819
1212
  def position_to_value(self, position):
1213
+ """
1214
+ Convert a pixel position on the slider to a numerical value.
1215
+
1216
+ Args:
1217
+ position (float): Pixel position on the slider.
1218
+
1219
+ Returns:
1220
+ float: Corresponding numerical slider value.
1221
+ """
820
1222
  if self.to == self.from_:
821
1223
  return self.from_
822
1224
  relative_position = (position - self.knob_radius) / (self.length - 2 * self.knob_radius)
823
1225
  return self.from_ + relative_position * (self.to - self.from_)
824
1226
 
825
1227
  def draw_slider(self, inactive=False):
1228
+ """
1229
+ Draw the slider bar and knob on the canvas.
1230
+
1231
+ Args:
1232
+ inactive (bool): If True, draw knob in inactive color. Otherwise, use active color.
1233
+ """
826
1234
  self.canvas.delete("all")
827
1235
 
828
1236
  self.slider_line = self.canvas.create_line(
@@ -845,6 +1253,12 @@ class spacrSlider(tk.Frame):
845
1253
  )
846
1254
 
847
1255
  def move_knob(self, event):
1256
+ """
1257
+ Move the knob in response to mouse drag, updating internal value and position.
1258
+
1259
+ Args:
1260
+ event (tk.Event): Motion event.
1261
+ """
848
1262
  new_position = min(max(event.x - self.offset, self.knob_radius), self.length - self.knob_radius)
849
1263
  self.knob_position = new_position
850
1264
  self.value = self.position_to_value(self.knob_position)
@@ -859,24 +1273,53 @@ class spacrSlider(tk.Frame):
859
1273
  self.index_var.set(str(int(self.value)))
860
1274
 
861
1275
  def activate_knob(self, event):
1276
+ """
1277
+ Highlight knob and respond to click by positioning knob at mouse location.
1278
+
1279
+ Args:
1280
+ event (tk.Event): Click event.
1281
+ """
862
1282
  self.draw_slider(inactive=False)
863
1283
  self.move_knob(event)
864
1284
 
865
1285
  def release_knob(self, event):
1286
+ """
1287
+ Finalize knob movement and call the `command` callback with final value.
1288
+
1289
+ Args:
1290
+ event (tk.Event): Mouse release event.
1291
+ """
866
1292
  self.draw_slider(inactive=True)
867
1293
  if self.command:
868
1294
  self.command(self.value) # Call the command with the final value when the knob is released
869
1295
 
870
1296
  def set_to(self, new_to):
1297
+ """
1298
+ Change the maximum value (`to`) of the slider.
1299
+
1300
+ Args:
1301
+ new_to (float): New upper bound of the slider.
1302
+ """
871
1303
  self.to = new_to
872
1304
  self.knob_position = self.value_to_position(self.value)
873
1305
  self.draw_slider(inactive=False)
874
1306
 
875
1307
  def get(self):
1308
+ """
1309
+ Get the current slider value.
1310
+
1311
+ Returns:
1312
+ float: Current value of the slider.
1313
+ """
876
1314
  return self.value
877
1315
 
878
1316
  def set(self, value):
879
- """Set the slider's value and update the knob position."""
1317
+ """
1318
+ Set the slider to a specific value and redraw.
1319
+
1320
+ Args:
1321
+ value (float): New value to set (clipped to bounds).
1322
+ """
880
1323
  self.value = max(self.from_, min(value, self.to)) # Ensure the value is within bounds
881
1324
  self.knob_position = self.value_to_position(self.value)
882
1325
  self.draw_slider(inactive=False)
@@ -884,10 +1327,21 @@ class spacrSlider(tk.Frame):
884
1327
  self.index_var.set(str(int(self.value)))
885
1328
 
886
1329
  def jump_to_click(self, event):
1330
+ """
1331
+ Move the knob directly to the mouse click position.
1332
+
1333
+ Args:
1334
+ event (tk.Event): Click event.
1335
+ """
887
1336
  self.activate_knob(event)
888
1337
 
889
1338
  def update_slider_from_entry(self, event):
890
- """Update the slider's value from the entry."""
1339
+ """
1340
+ Update the slider from a value entered manually in the index entry.
1341
+
1342
+ Args:
1343
+ event (tk.Event): Return key press event.
1344
+ """
891
1345
  try:
892
1346
  index = int(self.index_var.get())
893
1347
  self.set(index)
@@ -897,6 +1351,19 @@ class spacrSlider(tk.Frame):
897
1351
  pass
898
1352
 
899
1353
  def spacrScrollbarStyle(style, inactive_color, active_color):
1354
+ """
1355
+ Applies a custom vertical scrollbar style using the given colors.
1356
+
1357
+ This function defines a new ttk scrollbar style named 'Custom.Vertical.TScrollbar'.
1358
+ It reuses the base elements from the 'clam' theme and sets the colors for active
1359
+ and inactive states accordingly. If the required elements do not exist, it creates
1360
+ them from the base theme.
1361
+
1362
+ Args:
1363
+ style (ttk.Style): The ttk Style object to configure.
1364
+ inactive_color (str): Hex or color name for the scrollbar in its default (inactive) state.
1365
+ active_color (str): Hex or color name for the scrollbar when hovered or active.
1366
+ """
900
1367
  # Check if custom elements already exist to avoid duplication
901
1368
  if not style.element_names().count('custom.Vertical.Scrollbar.trough'):
902
1369
  style.element_create('custom.Vertical.Scrollbar.trough', 'from', 'clam')
@@ -921,7 +1388,28 @@ def spacrScrollbarStyle(style, inactive_color, active_color):
921
1388
  darkcolor=[('!active', inactive_color), ('active', active_color)])
922
1389
 
923
1390
  class spacrFrame(ttk.Frame):
1391
+ """
1392
+ A styled frame with optional rounded background, vertical scrollbar, and embedded content area (text or widgets).
1393
+
1394
+ This frame supports both scrollable `ttk.Frame` containers and scrollable `tk.Text` areas, with a dark custom theme.
1395
+
1396
+ Attributes:
1397
+ scrollable_frame (Union[ttk.Frame, tk.Text]): The inner content widget.
1398
+ """
924
1399
  def __init__(self, container, width=None, *args, bg='black', radius=20, scrollbar=True, textbox=False, **kwargs):
1400
+ """
1401
+ Initialize the spacrFrame.
1402
+
1403
+ Args:
1404
+ container (tk.Widget): The parent container for this frame.
1405
+ width (int, optional): Width of the frame in pixels. Defaults to 1/4 of screen width if None.
1406
+ *args: Additional positional arguments for ttk.Frame.
1407
+ bg (str): Background color of the frame. Defaults to 'black'.
1408
+ radius (int): Radius of the rounded rectangle background. Defaults to 20.
1409
+ scrollbar (bool): Whether to include a vertical scrollbar. Defaults to True.
1410
+ textbox (bool): If True, use a scrollable `tk.Text` widget. Otherwise, use a `ttk.Frame`. Defaults to False.
1411
+ **kwargs: Additional keyword arguments for ttk.Frame.
1412
+ """
925
1413
  super().__init__(container, *args, **kwargs)
926
1414
  self.configure(style='TFrame')
927
1415
  if width is None:
@@ -973,6 +1461,21 @@ class spacrFrame(ttk.Frame):
973
1461
  _ = set_dark_style(style, widgets=[scrollbar_widget])
974
1462
 
975
1463
  def rounded_rectangle(self, canvas, x1, y1, x2, y2, radius=20, **kwargs):
1464
+ """
1465
+ Draw a rounded rectangle on a canvas.
1466
+
1467
+ Args:
1468
+ canvas (tk.Canvas): The canvas to draw on.
1469
+ x1 (int): Left coordinate.
1470
+ y1 (int): Top coordinate.
1471
+ x2 (int): Right coordinate.
1472
+ y2 (int): Bottom coordinate.
1473
+ radius (int): Radius of the rounded corners. Defaults to 20.
1474
+ **kwargs: Options passed to the canvas `create_polygon` method.
1475
+
1476
+ Returns:
1477
+ int: ID of the created polygon.
1478
+ """
976
1479
  points = [
977
1480
  x1 + radius, y1,
978
1481
  x2 - radius, y1,
@@ -995,7 +1498,24 @@ class spacrFrame(ttk.Frame):
995
1498
  return canvas.create_polygon(points, **kwargs, smooth=True)
996
1499
 
997
1500
  class spacrLabel(tk.Frame):
1501
+ """
1502
+ A custom label widget with optional dark styling, alignment options, and support for both `ttk.Label` and `Canvas`-rendered text.
1503
+
1504
+ The label adapts to screen size or a given height, and can display text either centered or right-aligned.
1505
+ """
998
1506
  def __init__(self, parent, text="", font=None, style=None, align="right", height=None, **kwargs):
1507
+ """
1508
+ Initialize the spacrLabel widget.
1509
+
1510
+ Args:
1511
+ parent (tk.Widget): The parent widget.
1512
+ text (str): The text to display on the label. Defaults to "".
1513
+ font (tkFont.Font, optional): A custom font to use if not using the default style. Defaults to None.
1514
+ style (str, optional): A ttk style name to apply to the label. If set, uses a `ttk.Label` instead of `Canvas` text.
1515
+ align (str): Text alignment, either "right" or "center". Defaults to "right".
1516
+ height (int, optional): Height of the label. If None, scales based on screen height.
1517
+ **kwargs: Additional keyword arguments for the outer frame (excluding font/background/anchor-specific ones).
1518
+ """
999
1519
  valid_kwargs = {k: v for k, v in kwargs.items() if k not in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
1000
1520
  super().__init__(parent, **valid_kwargs)
1001
1521
 
@@ -1050,14 +1570,43 @@ class spacrLabel(tk.Frame):
1050
1570
  _ = set_dark_style(ttk.Style(), containers=[self], widgets=[self.canvas])
1051
1571
 
1052
1572
  def set_text(self, text):
1573
+ """
1574
+ Update the label text.
1575
+
1576
+ Args:
1577
+ text (str): The new text to display.
1578
+ """
1053
1579
  if self.style:
1054
1580
  self.label_text.config(text=text)
1055
1581
  else:
1056
1582
  self.canvas.itemconfig(self.label_text, text=text)
1057
1583
 
1058
1584
  class spacrButton(tk.Frame):
1585
+ """
1586
+ A custom animated button widget with icon and optional text, styled with dark mode and zoom animation on hover.
1587
+
1588
+ The button is rendered using a Canvas to support rounded corners and icon embedding. Optional description
1589
+ display is supported via parent methods `show_description` and `clear_description`.
1590
+ """
1059
1591
  def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=False, animation=True, *args, **kwargs):
1592
+ """
1593
+ Initialize the spacrButton.
1594
+
1595
+ Args:
1596
+ parent (tk.Widget): The parent container.
1597
+ text (str): Button text to display.
1598
+ command (callable, optional): Function to call when button is clicked.
1599
+ font (tkFont.Font or tuple, optional): Font to use if font loader is unavailable.
1600
+ icon_name (str, optional): Name of icon (without extension) to load from resources/icons.
1601
+ size (int): Button height (and icon size reference). Defaults to 50.
1602
+ show_text (bool): Whether to show text next to the icon. Defaults to True.
1603
+ outline (bool): Whether to draw a border around the button. Defaults to False.
1604
+ animation (bool): Whether to animate the icon on hover. Defaults to True.
1605
+ *args: Additional positional arguments for the Frame.
1606
+ **kwargs: Additional keyword arguments for the Frame.
1607
+ """
1060
1608
  super().__init__(parent, *args, **kwargs)
1609
+
1061
1610
 
1062
1611
  self.text = text.capitalize() # Capitalize only the first letter of the text
1063
1612
  self.command = command
@@ -1112,6 +1661,11 @@ class spacrButton(tk.Frame):
1112
1661
  self.is_zoomed_in = False # Track zoom state for smooth transitions
1113
1662
 
1114
1663
  def load_icon(self):
1664
+ """
1665
+ Load and resize the icon from the icon folder.
1666
+
1667
+ Falls back to 'default.png' if the specified icon cannot be found.
1668
+ """
1115
1669
  icon_path = self.get_icon_path(self.icon_name)
1116
1670
  try:
1117
1671
  icon_image = Image.open(icon_path)
@@ -1131,26 +1685,59 @@ class spacrButton(tk.Frame):
1131
1685
  self.canvas.image = self.icon_photo # Keep a reference to avoid garbage collection
1132
1686
 
1133
1687
  def get_icon_path(self, icon_name):
1688
+ """
1689
+ Get the full path to the icon file.
1690
+
1691
+ Args:
1692
+ icon_name (str): Icon name without extension.
1693
+
1694
+ Returns:
1695
+ str: Full path to the icon file.
1696
+ """
1134
1697
  icon_dir = os.path.join(os.path.dirname(__file__), 'resources', 'icons')
1135
1698
  return os.path.join(icon_dir, f"{icon_name}.png")
1136
1699
 
1137
1700
  def on_enter(self, event=None):
1701
+ """
1702
+ Handle mouse hover enter event.
1703
+
1704
+ Changes button color, shows description, and animates zoom-in if enabled.
1705
+ """
1138
1706
  self.canvas.itemconfig(self.button_bg, fill=self.active_color)
1139
1707
  self.update_description(event)
1140
1708
  if self.animation and not self.is_zoomed_in:
1141
1709
  self.animate_zoom(0.85) # Zoom in the icon to 85% of button size
1142
1710
 
1143
1711
  def on_leave(self, event=None):
1712
+ """
1713
+ Handle mouse hover leave event.
1714
+
1715
+ Resets button color, clears description, and animates zoom-out if enabled.
1716
+ """
1144
1717
  self.canvas.itemconfig(self.button_bg, fill=self.inactive_color)
1145
1718
  self.clear_description(event)
1146
1719
  if self.animation and self.is_zoomed_in:
1147
1720
  self.animate_zoom(0.65) # Reset the icon size to 65% of button size
1148
1721
 
1149
1722
  def on_click(self, event=None):
1723
+ """
1724
+ Trigger the button's command callback when clicked.
1725
+ """
1150
1726
  if self.command:
1151
1727
  self.command()
1152
1728
 
1153
1729
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=20, **kwargs):
1730
+ """
1731
+ Create a rounded rectangle on the canvas.
1732
+
1733
+ Args:
1734
+ x1, y1, x2, y2 (int): Coordinates of the rectangle.
1735
+ radius (int): Radius of the corners.
1736
+ **kwargs: Passed to `create_polygon`.
1737
+
1738
+ Returns:
1739
+ int: Canvas item ID of the rounded rectangle.
1740
+ """
1154
1741
  points = [
1155
1742
  x1 + radius, y1,
1156
1743
  x2 - radius, y1,
@@ -1173,6 +1760,10 @@ class spacrButton(tk.Frame):
1173
1760
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
1174
1761
 
1175
1762
  def update_description(self, event):
1763
+ """
1764
+ Call parent container’s `show_description()` if available,
1765
+ passing the description based on `main_buttons` or `additional_buttons` maps.
1766
+ """
1176
1767
  parent = self.master
1177
1768
  while parent:
1178
1769
  if hasattr(parent, 'show_description'):
@@ -1181,6 +1772,9 @@ class spacrButton(tk.Frame):
1181
1772
  parent = parent.master
1182
1773
 
1183
1774
  def clear_description(self, event):
1775
+ """
1776
+ Call parent container’s `clear_description()` if available.
1777
+ """
1184
1778
  parent = self.master
1185
1779
  while parent:
1186
1780
  if hasattr(parent, 'clear_description'):
@@ -1189,11 +1783,28 @@ class spacrButton(tk.Frame):
1189
1783
  parent = parent.master
1190
1784
 
1191
1785
  def animate_zoom(self, target_scale, steps=10, delay=10):
1786
+ """
1787
+ Animate zoom effect by resizing icon incrementally.
1788
+
1789
+ Args:
1790
+ target_scale (float): Final scale factor relative to base icon size.
1791
+ steps (int): Number of animation steps. Defaults to 10.
1792
+ delay (int): Delay between steps in milliseconds. Defaults to 10.
1793
+ """
1192
1794
  current_scale = 0.85 if self.is_zoomed_in else 0.65
1193
1795
  step_scale = (target_scale - current_scale) / steps
1194
1796
  self._animate_step(current_scale, step_scale, steps, delay)
1195
1797
 
1196
1798
  def _animate_step(self, current_scale, step_scale, steps, delay):
1799
+ """
1800
+ Helper method to perform recursive icon zoom animation.
1801
+
1802
+ Args:
1803
+ current_scale (float): Current zoom scale.
1804
+ step_scale (float): Incremental change per step.
1805
+ steps (int): Steps remaining.
1806
+ delay (int): Delay per step in ms.
1807
+ """
1197
1808
  if steps > 0:
1198
1809
  new_scale = current_scale + step_scale
1199
1810
  self.zoom_icon(new_scale)
@@ -1202,6 +1813,12 @@ class spacrButton(tk.Frame):
1202
1813
  self.is_zoomed_in = not self.is_zoomed_in
1203
1814
 
1204
1815
  def zoom_icon(self, scale_factor):
1816
+ """
1817
+ Resize and update the icon image on the canvas.
1818
+
1819
+ Args:
1820
+ scale_factor (float): Scaling factor relative to base icon size.
1821
+ """
1205
1822
  # Resize the original icon image
1206
1823
  new_size = int(self.size * scale_factor)
1207
1824
  resized_icon = self.original_icon_image.resize((new_size, new_size), Image.Resampling.LANCZOS)
@@ -1212,7 +1829,23 @@ class spacrButton(tk.Frame):
1212
1829
  self.canvas.image = self.icon_photo
1213
1830
 
1214
1831
  class spacrSwitch(ttk.Frame):
1832
+ """
1833
+ A custom toggle switch widget with animated transitions and label, styled using the spacr dark theme.
1834
+
1835
+ This switch mimics a physical toggle with animated motion of the switch knob and changes in color.
1836
+ """
1215
1837
  def __init__(self, parent, text="", variable=None, command=None, *args, **kwargs):
1838
+ """
1839
+ Initialize the spacrSwitch widget.
1840
+
1841
+ Args:
1842
+ parent (tk.Widget): Parent container.
1843
+ text (str): Label displayed to the left of the switch.
1844
+ variable (tk.BooleanVar, optional): Tkinter BooleanVar linked to the switch state.
1845
+ command (callable, optional): Function to call when the switch is toggled.
1846
+ *args: Additional positional arguments for the Frame.
1847
+ **kwargs: Additional keyword arguments for the Frame.
1848
+ """
1216
1849
  super().__init__(parent, *args, **kwargs)
1217
1850
  self.text = text
1218
1851
  self.variable = variable if variable else tk.BooleanVar()
@@ -1232,12 +1865,20 @@ class spacrSwitch(ttk.Frame):
1232
1865
  _ = set_dark_style(style, containers=[self], widgets=[self.canvas, self.label])
1233
1866
 
1234
1867
  def toggle(self, event=None):
1868
+ """
1869
+ Toggle the state of the switch.
1870
+
1871
+ Updates the linked variable, animates the movement, and calls the `command` callback if defined.
1872
+ """
1235
1873
  self.variable.set(not self.variable.get())
1236
1874
  self.animate_switch()
1237
1875
  if self.command:
1238
1876
  self.command()
1239
1877
 
1240
1878
  def update_switch(self):
1879
+ """
1880
+ Immediately update the switch position and color based on the current value of the variable.
1881
+ """
1241
1882
  if self.variable.get():
1242
1883
  self.canvas.itemconfig(self.switch, fill="#008080")
1243
1884
  self.canvas.coords(self.switch, 24, 4, 36, 16)
@@ -1246,6 +1887,9 @@ class spacrSwitch(ttk.Frame):
1246
1887
  self.canvas.coords(self.switch, 4, 4, 16, 16)
1247
1888
 
1248
1889
  def animate_switch(self):
1890
+ """
1891
+ Trigger an animated transition of the switch knob between on and off states.
1892
+ """
1249
1893
  if self.variable.get():
1250
1894
  start_x, end_x = 4, 24
1251
1895
  final_color = "#008080"
@@ -1256,6 +1900,14 @@ class spacrSwitch(ttk.Frame):
1256
1900
  self.animate_movement(start_x, end_x, final_color)
1257
1901
 
1258
1902
  def animate_movement(self, start_x, end_x, final_color):
1903
+ """
1904
+ Animate the horizontal movement of the switch knob.
1905
+
1906
+ Args:
1907
+ start_x (int): Starting x-coordinate of the knob.
1908
+ end_x (int): Ending x-coordinate of the knob.
1909
+ final_color (str): Fill color of the knob at the end of the animation.
1910
+ """
1259
1911
  step = 1 if start_x < end_x else -1
1260
1912
  for i in range(start_x, end_x, step):
1261
1913
  self.canvas.coords(self.switch, i, 4, i + 12, 16)
@@ -1264,13 +1916,36 @@ class spacrSwitch(ttk.Frame):
1264
1916
  self.canvas.itemconfig(self.switch, fill=final_color)
1265
1917
 
1266
1918
  def get(self):
1919
+ """
1920
+ Get the current Boolean value of the switch.
1921
+
1922
+ Returns:
1923
+ bool: True if switch is on, False otherwise.
1924
+ """
1267
1925
  return self.variable.get()
1268
1926
 
1269
1927
  def set(self, value):
1928
+ """
1929
+ Set the switch to a given Boolean value.
1930
+
1931
+ Args:
1932
+ value (bool): New state for the switch.
1933
+ """
1270
1934
  self.variable.set(value)
1271
1935
  self.update_switch()
1272
1936
 
1273
1937
  def create_rounded_rectangle(self, x1, y1, x2, y2, radius=9, **kwargs):
1938
+ """
1939
+ Draw a rounded rectangle polygon on the canvas.
1940
+
1941
+ Args:
1942
+ x1, y1, x2, y2 (int): Coordinates of the rectangle bounds.
1943
+ radius (int): Radius of corner curvature.
1944
+ **kwargs: Options passed to `create_polygon`.
1945
+
1946
+ Returns:
1947
+ int: ID of the created polygon item on the canvas.
1948
+ """
1274
1949
  points = [x1 + radius, y1,
1275
1950
  x1 + radius, y1,
1276
1951
  x2 - radius, y1,
@@ -1295,7 +1970,17 @@ class spacrSwitch(ttk.Frame):
1295
1970
  return self.canvas.create_polygon(points, **kwargs, smooth=True)
1296
1971
 
1297
1972
  class spacrToolTip:
1973
+ """
1974
+ A simple tooltip widget for displaying hover text in a Tkinter application using spacr dark styling.
1975
+ """
1298
1976
  def __init__(self, widget, text):
1977
+ """
1978
+ Initialize the tooltip for a given widget.
1979
+
1980
+ Args:
1981
+ widget (tk.Widget): The widget to attach the tooltip to.
1982
+ text (str): The text to display in the tooltip.
1983
+ """
1299
1984
  self.widget = widget
1300
1985
  self.text = text
1301
1986
  self.tooltip_window = None
@@ -1303,6 +1988,9 @@ class spacrToolTip:
1303
1988
  widget.bind("<Leave>", self.hide_tooltip)
1304
1989
 
1305
1990
  def show_tooltip(self, event):
1991
+ """
1992
+ Display the tooltip near the cursor when mouse enters the widget.
1993
+ """
1306
1994
  x = event.x_root + 20
1307
1995
  y = event.y_root + 10
1308
1996
  self.tooltip_window = tk.Toplevel(self.widget)
@@ -1315,11 +2003,31 @@ class spacrToolTip:
1315
2003
  _ = set_dark_style(style, containers=[self.tooltip_window], widgets=[label])
1316
2004
 
1317
2005
  def hide_tooltip(self, event):
2006
+ """
2007
+ Hide and destroy the tooltip when mouse leaves the widget.
2008
+ """
1318
2009
  if self.tooltip_window:
1319
2010
  self.tooltip_window.destroy()
1320
2011
  self.tooltip_window = None
1321
2012
 
1322
2013
  def standardize_figure(fig):
2014
+ """
2015
+ Apply standardized appearance settings to a matplotlib figure using spaCR GUI style preferences.
2016
+
2017
+ This includes:
2018
+ - Setting font size and font family based on spaCR's theme
2019
+ - Setting text and spine colors to match spaCR foreground color
2020
+ - Applying OpenSans font via `font_loader`
2021
+ - Removing top and right spines
2022
+ - Setting line and spine widths
2023
+ - Adjusting background and grid colors
2024
+
2025
+ Args:
2026
+ fig (matplotlib.figure.Figure): The matplotlib figure to standardize.
2027
+
2028
+ Returns:
2029
+ matplotlib.figure.Figure: The updated figure with standardized style.
2030
+ """
1323
2031
  from .gui_elements import set_dark_style
1324
2032
  from matplotlib.font_manager import FontProperties
1325
2033
 
@@ -1398,7 +2106,7 @@ def modify_figure_properties(fig, scale_x=None, scale_y=None, line_width=None, f
1398
2106
  """
1399
2107
  Modifies the properties of the figure, including scaling, line widths, font sizes, axis limits, x-axis label rotation, background color, text color, line color, and other common options.
1400
2108
 
1401
- Parameters:
2109
+ Args:
1402
2110
  - fig: The Matplotlib figure object to modify.
1403
2111
  - scale_x: Scaling factor for the width of subplots (optional).
1404
2112
  - scale_y: Scaling factor for the height of subplots (optional).
@@ -1497,6 +2205,19 @@ def modify_figure_properties(fig, scale_x=None, scale_y=None, line_width=None, f
1497
2205
  fig.canvas.draw_idle()
1498
2206
 
1499
2207
  def save_figure_as_format(fig, file_format):
2208
+ """
2209
+ Opens a file dialog to save a matplotlib figure in the specified format.
2210
+
2211
+ Prompts the user to choose a save location and filename using a file dialog.
2212
+ The figure is saved using the provided format if a valid path is selected.
2213
+
2214
+ Args:
2215
+ fig (matplotlib.figure.Figure): The figure to save.
2216
+ file_format (str): The file format to save as (e.g., 'png', 'pdf', 'svg').
2217
+
2218
+ Returns:
2219
+ None
2220
+ """
1500
2221
  file_path = filedialog.asksaveasfilename(defaultextension=f".{file_format}", filetypes=[(f"{file_format.upper()} files", f"*.{file_format}"), ("All files", "*.*")])
1501
2222
  if file_path:
1502
2223
  try:
@@ -1506,6 +2227,26 @@ def save_figure_as_format(fig, file_format):
1506
2227
  print(f"Error saving figure: {e}")
1507
2228
 
1508
2229
  def modify_figure(fig):
2230
+ """
2231
+ Opens a GUI window for interactively modifying various properties of a matplotlib figure.
2232
+
2233
+ This function allows users to:
2234
+ - Rescale the X and Y axes
2235
+ - Change line width and font size
2236
+ - Set axis limits and title
2237
+ - Customize colors (background, text, line)
2238
+ - Rotate x-axis labels
2239
+ - Enable/disable grid, legend, and axes
2240
+ - Toggle spine visibility ("spleens")
2241
+
2242
+ Once modifications are entered and applied, the figure is updated and re-rendered via `display_figure`.
2243
+
2244
+ Args:
2245
+ fig (matplotlib.figure.Figure): The matplotlib figure to modify.
2246
+
2247
+ Returns:
2248
+ None
2249
+ """
1509
2250
  from .gui_core import display_figure
1510
2251
  def apply_modifications():
1511
2252
  try:
@@ -1620,9 +2361,27 @@ def modify_figure(fig):
1620
2361
 
1621
2362
  def generate_dna_matrix(output_path='dna_matrix.gif', canvas_width=1500, canvas_height=1000, duration=30, fps=20, base_size=20, transition_frames=30, font_type='arial.ttf', enhance=[1.1, 1.5, 1.2, 1.5], lowercase_prob=0.3):
1622
2363
  """
1623
- Generate a DNA matrix animation and save it as GIF, MP4, or AVI using OpenCV for videos.
2364
+ Generates an animated matrix-style DNA sequence visual and saves it as a GIF or video.
2365
+
2366
+ The animation simulates vertical streams of random DNA bases ('A', 'T', 'C', 'G') cascading down the screen.
2367
+ Each column has independently scrolling strings of bases. The latest base in each stream is highlighted,
2368
+ and fading effects, coloring, and random lowercase transformations are applied for visual flair.
2369
+
2370
+ Args:
2371
+ output_path (str): Path to the output file (should end in .gif, .mp4, or .avi).
2372
+ canvas_width (int): Width of the canvas in pixels.
2373
+ canvas_height (int): Height of the canvas in pixels.
2374
+ duration (int): Duration of the animation in seconds.
2375
+ fps (int): Frames per second of the animation.
2376
+ base_size (int): Font size (in pixels) for the bases.
2377
+ transition_frames (int): Number of frames for the looping transition.
2378
+ font_type (str): Path to a .ttf font to use. Defaults to Arial.
2379
+ enhance (list): List of four enhancement multipliers for [brightness, sharpness, contrast, saturation].
2380
+ lowercase_prob (float): Probability that a given base will be drawn in lowercase.
2381
+
2382
+ Returns:
2383
+ None
1624
2384
  """
1625
-
1626
2385
  def save_output(frames, output_path, fps, output_format):
1627
2386
  """Save the animation based on output format."""
1628
2387
  if output_format in ['.mp4', '.avi']: