spacr 0.2.68__tar.gz → 0.2.81__tar.gz

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 (120) hide show
  1. {spacr-0.2.68/spacr.egg-info → spacr-0.2.81}/PKG-INFO +2 -2
  2. {spacr-0.2.68 → spacr-0.2.81}/setup.py +2 -2
  3. {spacr-0.2.68 → spacr-0.2.81}/spacr/core.py +1 -1
  4. {spacr-0.2.68 → spacr-0.2.81}/spacr/gui_core.py +152 -105
  5. {spacr-0.2.68 → spacr-0.2.81}/spacr/gui_elements.py +190 -18
  6. {spacr-0.2.68 → spacr-0.2.81}/spacr/sequencing.py +234 -422
  7. {spacr-0.2.68 → spacr-0.2.81}/spacr/settings.py +13 -9
  8. {spacr-0.2.68 → spacr-0.2.81}/spacr/utils.py +4 -1
  9. {spacr-0.2.68 → spacr-0.2.81/spacr.egg-info}/PKG-INFO +2 -2
  10. {spacr-0.2.68 → spacr-0.2.81}/spacr.egg-info/requires.txt +1 -1
  11. {spacr-0.2.68 → spacr-0.2.81}/LICENSE +0 -0
  12. {spacr-0.2.68 → spacr-0.2.81}/MANIFEST.in +0 -0
  13. {spacr-0.2.68 → spacr-0.2.81}/README.rst +0 -0
  14. {spacr-0.2.68 → spacr-0.2.81}/setup.cfg +0 -0
  15. {spacr-0.2.68 → spacr-0.2.81}/spacr/__init__.py +0 -0
  16. {spacr-0.2.68 → spacr-0.2.81}/spacr/__main__.py +0 -0
  17. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_annotate.py +0 -0
  18. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_classify.py +0 -0
  19. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_make_masks.py +0 -0
  20. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_mask.py +0 -0
  21. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_measure.py +0 -0
  22. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_sequencing.py +0 -0
  23. {spacr-0.2.68 → spacr-0.2.81}/spacr/app_umap.py +0 -0
  24. {spacr-0.2.68 → spacr-0.2.81}/spacr/deep_spacr.py +0 -0
  25. {spacr-0.2.68 → spacr-0.2.81}/spacr/gui.py +0 -0
  26. {spacr-0.2.68 → spacr-0.2.81}/spacr/gui_utils.py +0 -0
  27. {spacr-0.2.68 → spacr-0.2.81}/spacr/io.py +0 -0
  28. {spacr-0.2.68 → spacr-0.2.81}/spacr/logger.py +0 -0
  29. {spacr-0.2.68 → spacr-0.2.81}/spacr/measure.py +0 -0
  30. {spacr-0.2.68 → spacr-0.2.81}/spacr/plot.py +0 -0
  31. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/OFL.txt +0 -0
  32. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  33. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  34. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/README.txt +0 -0
  35. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  36. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  37. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  38. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  39. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  40. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  41. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  42. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  43. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  44. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  45. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  46. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  47. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  48. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  49. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  50. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  51. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  52. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  53. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  54. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  55. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  56. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  57. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  58. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  59. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  60. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  61. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  62. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  63. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  64. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  65. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  66. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  67. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  68. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  69. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  70. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  71. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/abort.png +0 -0
  72. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/annotate.png +0 -0
  73. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/cellpose_all.png +0 -0
  74. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/cellpose_masks.png +0 -0
  75. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/classify.png +0 -0
  76. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/default.png +0 -0
  77. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/download.png +0 -0
  78. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/logo.pdf +0 -0
  79. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/logo_spacr.png +0 -0
  80. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/logo_spacr_1.png +0 -0
  81. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/make_masks.png +0 -0
  82. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/map_barcodes.png +0 -0
  83. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/mask.png +0 -0
  84. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/measure.png +0 -0
  85. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/ml_analyze.png +0 -0
  86. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/recruitment.png +0 -0
  87. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/regression.png +0 -0
  88. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/run.png +0 -0
  89. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/sequencing.png +0 -0
  90. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/settings.png +0 -0
  91. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/spacr_logo_rotation.gif +0 -0
  92. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/train_cellpose.png +0 -0
  93. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/icons/umap.png +0 -0
  94. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model +0 -0
  95. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/models/cp/toxo_plaque_cyto_e25000_X1120_Y1120.CP_model_settings.csv +0 -0
  96. {spacr-0.2.68 → spacr-0.2.81}/spacr/resources/models/cp/toxo_pv_lumen.CP_model +0 -0
  97. {spacr-0.2.68 → spacr-0.2.81}/spacr/sim.py +0 -0
  98. {spacr-0.2.68 → spacr-0.2.81}/spacr/sim_app.py +0 -0
  99. {spacr-0.2.68 → spacr-0.2.81}/spacr/timelapse.py +0 -0
  100. {spacr-0.2.68 → spacr-0.2.81}/spacr/version.py +0 -0
  101. {spacr-0.2.68 → spacr-0.2.81}/spacr.egg-info/SOURCES.txt +0 -0
  102. {spacr-0.2.68 → spacr-0.2.81}/spacr.egg-info/dependency_links.txt +0 -0
  103. {spacr-0.2.68 → spacr-0.2.81}/spacr.egg-info/entry_points.txt +0 -0
  104. {spacr-0.2.68 → spacr-0.2.81}/spacr.egg-info/top_level.txt +0 -0
  105. {spacr-0.2.68 → spacr-0.2.81}/tests/test_annotate_app.py +0 -0
  106. {spacr-0.2.68 → spacr-0.2.81}/tests/test_core.py +0 -0
  107. {spacr-0.2.68 → spacr-0.2.81}/tests/test_gui_classify_app.py +0 -0
  108. {spacr-0.2.68 → spacr-0.2.81}/tests/test_gui_mask_app.py +0 -0
  109. {spacr-0.2.68 → spacr-0.2.81}/tests/test_gui_measure_app.py +0 -0
  110. {spacr-0.2.68 → spacr-0.2.81}/tests/test_gui_sim_app.py +0 -0
  111. {spacr-0.2.68 → spacr-0.2.81}/tests/test_gui_utils.py +0 -0
  112. {spacr-0.2.68 → spacr-0.2.81}/tests/test_io.py +0 -0
  113. {spacr-0.2.68 → spacr-0.2.81}/tests/test_mask_app.py +0 -0
  114. {spacr-0.2.68 → spacr-0.2.81}/tests/test_measure.py +0 -0
  115. {spacr-0.2.68 → spacr-0.2.81}/tests/test_plot.py +0 -0
  116. {spacr-0.2.68 → spacr-0.2.81}/tests/test_sim.py +0 -0
  117. {spacr-0.2.68 → spacr-0.2.81}/tests/test_timelapse.py +0 -0
  118. {spacr-0.2.68 → spacr-0.2.81}/tests/test_train.py +0 -0
  119. {spacr-0.2.68 → spacr-0.2.81}/tests/test_umap.py +0 -0
  120. {spacr-0.2.68 → spacr-0.2.81}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.2.68
3
+ Version: 0.2.81
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -47,9 +47,9 @@ Requires-Dist: gpustat<2.0,>=1.1.1
47
47
  Requires-Dist: pyautogui<1.0,>=0.9.54
48
48
  Requires-Dist: tables<4.0,>=3.8.0
49
49
  Requires-Dist: rapidfuzz<4.0,>=3.9
50
- Requires-Dist: importlib-metadata<4.0,>=3.6
51
50
  Requires-Dist: keyring<16.0,>=15.1
52
51
  Requires-Dist: screeninfo<1.0,>=0.8.1
52
+ Requires-Dist: ipykernel
53
53
  Requires-Dist: huggingface-hub<0.25,>=0.24.0
54
54
  Provides-Extra: dev
55
55
  Requires-Dist: pytest<3.11,>=3.9; extra == "dev"
@@ -44,15 +44,15 @@ dependencies = [
44
44
  'pyautogui>=0.9.54,<1.0',
45
45
  'tables>=3.8.0,<4.0',
46
46
  'rapidfuzz>=3.9, <4.0',
47
- 'importlib-metadata>=3.6, <4.0',
48
47
  'keyring>=15.1, <16.0',
49
48
  'screeninfo>=0.8.1,<1.0',
49
+ 'ipykernel',
50
50
  'huggingface-hub>=0.24.0,<0.25'
51
51
  ]
52
52
 
53
53
  setup(
54
54
  name="spacr",
55
- version="0.2.68",
55
+ version="0.2.81",
56
56
  author="Einar Birnir Olafsson",
57
57
  author_email="olafsson@med.umich.com",
58
58
  description="Spatial phenotype analysis of crisp screens (SpaCr)",
@@ -1638,7 +1638,7 @@ def preprocess_generate_masks(src, settings={}):
1638
1638
 
1639
1639
  settings = set_default_settings_preprocess_generate_masks(src, settings)
1640
1640
  settings['src'] = src
1641
- save_settings(settings)
1641
+ save_settings(settings, name='gen_mask')
1642
1642
 
1643
1643
  if not settings['pathogen_channel'] is None:
1644
1644
  custom_model_ls = ['toxo_pv_lumen','toxo_cyto']
@@ -1,4 +1,4 @@
1
- import traceback, ctypes, csv, re
1
+ import traceback, ctypes, csv, re, platform, time
2
2
  import tkinter as tk
3
3
  from tkinter import ttk
4
4
  from tkinter import filedialog
@@ -19,7 +19,7 @@ try:
19
19
  except AttributeError:
20
20
  pass
21
21
 
22
- from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , set_dark_style
22
+ from .gui_elements import spacrProgressBar, spacrButton, spacrLabel, spacrFrame, spacrDropdownMenu , spacrSlider, set_dark_style, standardize_figure
23
23
 
24
24
  # Define global variables
25
25
  q = None
@@ -35,8 +35,6 @@ figures = None
35
35
  figure_index = None
36
36
  progress_bar = None
37
37
  usage_bars = None
38
- fig_memory_limit = None
39
- figure_current_memory_usage = None
40
38
 
41
39
  thread_control = {"run_thread": None, "stop_requested": False}
42
40
 
@@ -80,35 +78,10 @@ def toggle_settings(button_scrollable_frame):
80
78
  category_dropdown.grid(row=0, column=4, sticky="ew", pady=2, padx=2)
81
79
  vars_dict = hide_all_settings(vars_dict, categories)
82
80
 
83
- def process_fig_queue():
84
- global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index
85
-
86
- from .gui_elements import standardize_figure
87
- try:
88
- while not fig_queue.empty():
89
- fig = fig_queue.get_nowait()
90
-
91
- if fig is None:
92
- print("Warning: Retrieved a None figure from fig_queue.")
93
- continue # Skip processing if the figure is None
94
-
95
- # Standardize the figure appearance before adding it to the list
96
- standardize_figure(fig)
97
-
98
- figures.append(fig)
99
- if figure_index == len(figures) - 2:
100
- figure_index += 1
101
- display_figure(fig)
102
- except Exception as e:
103
- traceback.print_exc()
104
- finally:
105
- after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
106
- parent_frame.after_tasks.append(after_id)
107
-
108
81
  def display_figure(fig):
109
82
  global canvas, canvas_widget
110
83
 
111
- from .gui_elements import modify_figure_properties, save_figure_as_format, modify_figure
84
+ from .gui_elements import save_figure_as_format, modify_figure
112
85
 
113
86
  # Apply the dark style to the context menu
114
87
  style_out = set_dark_style(ttk.Style())
@@ -197,7 +170,7 @@ def display_figure(fig):
197
170
  #flash_feedback("right")
198
171
  show_next_figure()
199
172
 
200
- def zoom(event):
173
+ def zoom_v1(event):
201
174
  nonlocal scale_factor
202
175
 
203
176
  zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
@@ -230,16 +203,55 @@ def display_figure(fig):
230
203
  # Redraw the figure
231
204
  fig.canvas.draw_idle()
232
205
 
206
+ def zoom(event):
207
+ nonlocal scale_factor
208
+
209
+ zoom_speed = 0.1 # Adjust the zoom speed for smoother experience
210
+
211
+ # Determine the zoom direction based on the scroll event
212
+ if event.num == 4 or event.delta > 0: # Scroll up (zoom in)
213
+ scale_factor /= (1 + zoom_speed) # Divide to zoom in
214
+ elif event.num == 5 or event.delta < 0: # Scroll down (zoom out)
215
+ scale_factor *= (1 + zoom_speed) # Multiply to zoom out
216
+
217
+ # Adjust the axes limits based on the new scale factor
218
+ for ax in canvas.figure.get_axes():
219
+ xlim = ax.get_xlim()
220
+ ylim = ax.get_ylim()
221
+
222
+ x_center = (xlim[1] + xlim[0]) / 2
223
+ y_center = (ylim[1] + ylim[0]) / 2
224
+
225
+ x_range = (xlim[1] - xlim[0]) * scale_factor
226
+ y_range = (ylim[1] - ylim[0]) * scale_factor
227
+
228
+ # Set the new limits
229
+ ax.set_xlim([x_center - x_range / 2, x_center + x_range / 2])
230
+ ax.set_ylim([y_center - y_range / 2, y_center + y_range / 2])
231
+
232
+ # Redraw the figure efficiently
233
+ canvas.draw_idle()
234
+
235
+
233
236
  # Bind events for hover, click interactions, and zoom
234
237
  canvas_widget.bind("<Motion>", on_hover)
235
238
  canvas_widget.bind("<Leave>", on_leave)
236
239
  canvas_widget.bind("<Button-1>", on_click)
237
240
  canvas_widget.bind("<Button-3>", on_right_click)
238
241
 
239
- # Bind mouse wheel for zooming (cross-platform)
240
- canvas_widget.bind("<MouseWheel>", zoom) # Windows
241
- canvas_widget.bind("<Button-4>", zoom) # Linux/macOS Scroll Up
242
- canvas_widget.bind("<Button-5>", zoom) # Linux/macOS Scroll Down
242
+
243
+ # Detect the operating system and bind the appropriate mouse wheel events
244
+ current_os = platform.system()
245
+
246
+ if current_os == "Windows":
247
+ canvas_widget.bind("<MouseWheel>", zoom) # Windows
248
+ elif current_os == "Darwin": # macOS
249
+ canvas_widget.bind("<MouseWheel>", zoom)
250
+ canvas_widget.bind("<Button-4>", zoom) # Scroll up
251
+ canvas_widget.bind("<Button-5>", zoom) # Scroll down
252
+ elif current_os == "Linux":
253
+ canvas_widget.bind("<Button-4>", zoom) # Linux Scroll up
254
+ canvas_widget.bind("<Button-5>", zoom) # Linux Scroll down
243
255
 
244
256
  def clear_unused_figures():
245
257
  global figures, figure_index
@@ -253,6 +265,7 @@ def clear_unused_figures():
253
265
 
254
266
  def show_previous_figure():
255
267
  global figure_index, figures, fig_queue
268
+
256
269
  if figure_index is not None and figure_index > 0:
257
270
  figure_index -= 1
258
271
  display_figure(figures[figure_index])
@@ -270,8 +283,104 @@ def show_next_figure():
270
283
  figure_index += 1
271
284
  display_figure(fig)
272
285
 
273
- def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, figures_var, figure_index_var, progress_bar_var, usage_bars_var, fig_memory_limit_var, figure_current_memory_usage_var):
274
- global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, fig_memory_limit, figure_current_memory_usage
286
+ def process_fig_queue():
287
+ global canvas, fig_queue, canvas_widget, parent_frame, uppdate_frequency, figures, figure_index, index_control
288
+
289
+ from .gui_elements import standardize_figure
290
+ try:
291
+ while not fig_queue.empty():
292
+ fig = fig_queue.get_nowait()
293
+
294
+ if fig is None:
295
+ print("Warning: Retrieved a None figure from fig_queue.")
296
+ continue # Skip processing if the figure is None
297
+
298
+ # Standardize the figure appearance before adding it to the list
299
+ fig = standardize_figure(fig)
300
+
301
+ figures.append(fig)
302
+
303
+ # Update the slider range and set the value to the latest figure index
304
+ index_control.set_to(len(figures) - 1)
305
+
306
+ if figure_index == -1:
307
+ figure_index += 1
308
+ display_figure(figures[figure_index])
309
+ index_control.set(figure_index)
310
+
311
+ except Exception as e:
312
+ traceback.print_exc()
313
+ finally:
314
+ after_id = canvas_widget.after(uppdate_frequency, process_fig_queue)
315
+ parent_frame.after_tasks.append(after_id)
316
+
317
+ def update_figure(value):
318
+ global figure_index, figures
319
+
320
+ # Convert the value to an integer
321
+ index = int(value)
322
+
323
+ # Check if the index is valid
324
+ if 0 <= index < len(figures):
325
+ figure_index = index
326
+ display_figure(figures[figure_index])
327
+
328
+ # Update the index control widget's range and value
329
+ index_control.set_to(len(figures) - 1)
330
+ index_control.set(figure_index)
331
+
332
+ def setup_plot_section(vertical_container):
333
+ global canvas, canvas_widget, figures, figure_index, index_control
334
+
335
+ # Initialize deque for storing figures and the current index
336
+ figures = deque()
337
+
338
+ # Create a frame for the plot section
339
+ plot_frame = tk.Frame(vertical_container)
340
+ vertical_container.add(plot_frame, stretch="always")
341
+
342
+ # Set up the plot
343
+ figure = Figure(figsize=(30, 4), dpi=100)
344
+ plot = figure.add_subplot(111)
345
+ plot.plot([], [])
346
+ plot.axis('off')
347
+
348
+ canvas = FigureCanvasTkAgg(figure, master=plot_frame)
349
+ canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
350
+ canvas_widget = canvas.get_tk_widget()
351
+ canvas_widget.grid(row=0, column=0, sticky="nsew")
352
+
353
+ plot_frame.grid_rowconfigure(0, weight=1)
354
+ plot_frame.grid_columnconfigure(0, weight=1)
355
+
356
+ canvas.draw()
357
+ canvas.figure = figure # Ensure that the figure is linked to the canvas
358
+ style_out = set_dark_style(ttk.Style())
359
+ bg = style_out['bg_color']
360
+ fg = style_out['fg_color']
361
+
362
+ figure.patch.set_facecolor(bg)
363
+ plot.set_facecolor(bg)
364
+ containers = [plot_frame]
365
+
366
+ # Create slider
367
+ control_frame = tk.Frame(plot_frame, height=15*2, bg=bg) # Fixed height based on knob_radius
368
+ control_frame.grid(row=1, column=0, sticky="ew", padx=10, pady=5)
369
+ control_frame.grid_propagate(False) # Prevent the frame from resizing
370
+
371
+ # Pass the update_figure function as the command to spacrSlider
372
+ index_control = spacrSlider(control_frame, from_=0, to=0, value=0, thickness=2, knob_radius=10, position="center", show_index=True, command=update_figure)
373
+ index_control.grid(row=0, column=0, sticky="ew")
374
+ control_frame.grid_columnconfigure(0, weight=1)
375
+
376
+ widgets = [canvas_widget, index_control]
377
+ style = ttk.Style(vertical_container)
378
+ _ = set_dark_style(style, containers=containers, widgets=widgets)
379
+
380
+ return canvas, canvas_widget
381
+
382
+ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var, vars_dict_var, canvas_var, canvas_widget_var, scrollable_frame_var, fig_queue_var, figures_var, figure_index_var, index_control_var, progress_bar_var, usage_bars_var):
383
+ global thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, index_control
275
384
  thread_control = thread_control_var
276
385
  q = q_var
277
386
  console_output = console_output_var
@@ -285,8 +394,7 @@ def set_globals(thread_control_var, q_var, console_output_var, parent_frame_var,
285
394
  figure_index = figure_index_var
286
395
  progress_bar = progress_bar_var
287
396
  usage_bars = usage_bars_var
288
- fig_memory_limit = fig_memory_limit_var
289
- figure_current_memory_usage = figure_current_memory_usage_var
397
+ index_control = index_control_var
290
398
 
291
399
  def import_settings(settings_type='mask'):
292
400
  from .gui_utils import convert_settings_dict_for_gui, hide_all_settings
@@ -400,46 +508,6 @@ def setup_settings_panel(vertical_container, settings_type='mask'):
400
508
  print("Settings panel setup complete")
401
509
  return scrollable_frame, vars_dict
402
510
 
403
- def setup_plot_section(vertical_container):
404
- global canvas, canvas_widget, figures, figure_index
405
-
406
- from .gui_elements import set_element_size
407
-
408
- # Initialize deque for storing figures and the current index
409
- figures = deque()
410
- figure_index = -1
411
-
412
- # Create a frame for the plot section
413
- plot_frame = tk.Frame(vertical_container)
414
- vertical_container.add(plot_frame, stretch="always")
415
-
416
- # Set up the plot
417
- figure = Figure(figsize=(30, 4), dpi=100)
418
- plot = figure.add_subplot(111)
419
- plot.plot([], [])
420
- plot.axis('off')
421
-
422
- canvas = FigureCanvasTkAgg(figure, master=plot_frame)
423
- canvas.get_tk_widget().configure(cursor='arrow', highlightthickness=0)
424
- canvas_widget = canvas.get_tk_widget()
425
- canvas_widget.grid(row=0, column=0, sticky="nsew")
426
-
427
- plot_frame.grid_rowconfigure(0, weight=1)
428
- plot_frame.grid_columnconfigure(0, weight=1)
429
-
430
- canvas.draw()
431
- canvas.figure = figure # Ensure that the figure is linked to the canvas
432
- style_out = set_dark_style(ttk.Style())
433
-
434
- figure.patch.set_facecolor(style_out['bg_color'])
435
- plot.set_facecolor(style_out['bg_color'])
436
- containers = [plot_frame]
437
- widgets = [canvas_widget]
438
- style = ttk.Style(vertical_container)
439
- _ = set_dark_style(style, containers=containers, widgets=widgets)
440
-
441
- return canvas, canvas_widget
442
-
443
511
  def setup_console(vertical_container):
444
512
  global console_output
445
513
  from .gui_elements import set_dark_style
@@ -479,27 +547,6 @@ def setup_console(vertical_container):
479
547
 
480
548
  return console_output, console_frame
481
549
 
482
- def setup_progress_frame(vertical_container):
483
- global progress_output
484
- style_out = set_dark_style(ttk.Style())
485
- font_loader = style_out['font_loader']
486
- font_size = style_out['font_size']
487
- progress_frame = tk.Frame(vertical_container)
488
- vertical_container.add(progress_frame, stretch="always")
489
- label_frame = tk.Frame(progress_frame)
490
- label_frame.grid(row=0, column=0, sticky="ew", pady=(5, 0), padx=10)
491
- progress_label = spacrLabel(label_frame, text="Processing: 0%", font=font_loader.get_font(size=font_size), anchor='w', justify='left', align="left")
492
- progress_label.grid(row=0, column=0, sticky="w")
493
- progress_output = scrolledtext.ScrolledText(progress_frame, height=10)
494
- progress_output.grid(row=1, column=0, sticky="nsew")
495
- progress_frame.grid_rowconfigure(1, weight=1)
496
- progress_frame.grid_columnconfigure(0, weight=1)
497
- containers = [progress_frame, label_frame]
498
- widgets = [progress_label, progress_output]
499
- style = ttk.Style(vertical_container)
500
- _ = set_dark_style(style, containers=containers, widgets=widgets)
501
- return progress_output
502
-
503
550
  def setup_button_section(horizontal_container, settings_type='mask', run=True, abort=True, download=True, import_btn=True):
504
551
  global thread_control, parent_frame, button_frame, button_scrollable_frame, run_button, abort_button, download_dataset_button, import_button, q, fig_queue, vars_dict, progress_bar
505
552
  from .gui_utils import download_hug_dataset
@@ -836,7 +883,7 @@ def initiate_root(parent, settings_type='mask'):
836
883
  tuple: A tuple containing the parent frame and the dictionary of variables used in the GUI.
837
884
  """
838
885
 
839
- global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index, fig_memory_limit, figure_current_memory_usage
886
+ global q, fig_queue, thread_control, parent_frame, scrollable_frame, button_frame, vars_dict, canvas, canvas_widget, button_scrollable_frame, progress_bar, uppdate_frequency, figures, figure_index, index_control, usage_bars
840
887
 
841
888
  from .gui_utils import setup_frame
842
889
  from .settings import descriptions
@@ -853,8 +900,6 @@ def initiate_root(parent, settings_type='mask'):
853
900
  # Initialize global variables
854
901
  figures = deque()
855
902
  figure_index = -1
856
- fig_memory_limit = 200 * 1024 * 1024 # 200 MB limit
857
- figure_current_memory_usage = 0
858
903
 
859
904
  parent_frame = parent
860
905
 
@@ -886,7 +931,7 @@ def initiate_root(parent, settings_type='mask'):
886
931
  button_scrollable_frame, btn_col = setup_button_section(horizontal_container, settings_type)
887
932
  _, usage_bars, btn_col = setup_usage_panel(horizontal_container, btn_col, uppdate_frequency)
888
933
 
889
- set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, progress_bar, usage_bars, fig_memory_limit, figure_current_memory_usage)
934
+ set_globals(thread_control, q, console_output, parent_frame, vars_dict, canvas, canvas_widget, scrollable_frame, fig_queue, figures, figure_index, index_control, progress_bar, usage_bars)
890
935
  description_text = descriptions.get(settings_type, "No description available for this module.")
891
936
 
892
937
  q.put(f"Console")
@@ -899,4 +944,6 @@ def initiate_root(parent, settings_type='mask'):
899
944
  parent_window.after_tasks.append(after_id)
900
945
 
901
946
  print("Root initialization complete")
902
- return parent_frame, vars_dict
947
+ return parent_frame, vars_dict
948
+
949
+
@@ -670,25 +670,189 @@ class spacrProgressBar(ttk.Progressbar):
670
670
 
671
671
  def update_label(self):
672
672
  if self.label and self.progress_label:
673
- # Update the progress label with current progress and additional info
673
+ # Start with the base progress information
674
674
  label_text = f"Processing: {self['value']}/{self['maximum']}"
675
+
676
+ # Include the operation type if it exists
675
677
  if self.operation_type:
676
678
  label_text += f", {self.operation_type}"
679
+
680
+ # Handle additional info without adding newlines
677
681
  if hasattr(self, 'additional_info') and self.additional_info:
678
- # Add a space between progress information and additional information
679
- label_text += "\n\n"
680
- # Split the additional_info into a list of items
682
+ # Join all additional info items with a space and ensure they're on the same line
681
683
  items = self.additional_info.split(", ")
682
- formatted_additional_info = ""
683
- # Group the items in pairs, adding them to formatted_additional_info
684
- for i in range(0, len(items), 2):
685
- if i + 1 < len(items):
686
- formatted_additional_info += f"{items[i]}, {items[i + 1]}\n\n"
687
- else:
688
- formatted_additional_info += f"{items[i]}\n\n" # If there's an odd item out, add it alone
689
- label_text += formatted_additional_info.strip()
684
+ formatted_additional_info = " ".join(items)
685
+
686
+ # Append the additional info to the label_text, ensuring it's all in one line
687
+ label_text += f" {formatted_additional_info.strip()}"
688
+
689
+ # Update the progress label
690
690
  self.progress_label.config(text=label_text)
691
691
 
692
+ class spacrSlider(tk.Frame):
693
+ 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):
694
+ super().__init__(master, **kwargs)
695
+
696
+ self.specified_length = length # Store the specified length, if any
697
+ self.knob_radius = knob_radius
698
+ self.thickness = thickness
699
+ self.knob_position = knob_radius # Start at the beginning of the slider
700
+ self.slider_line = None
701
+ self.knob = None
702
+ self.position = position.lower() # Store the position option
703
+ self.offset = 0 # Initialize offset
704
+ self.from_ = from_ # Minimum value of the slider
705
+ self.to = to # Maximum value of the slider
706
+ self.value = value if value is not None else from_ # Initial value of the slider
707
+ self.show_index = show_index # Whether to show the index Entry widget
708
+ self.command = command # Callback function to handle value changes
709
+
710
+ # Initialize the style and colors
711
+ style_out = set_dark_style(ttk.Style())
712
+ self.fg_color = style_out['fg_color']
713
+ self.bg_color = style_out['bg_color']
714
+ self.active_color = style_out['active_color']
715
+ self.inactive_color = style_out['inactive_color']
716
+
717
+ # Configure the frame's background color
718
+ self.configure(bg=self.bg_color)
719
+
720
+ # Create a frame for the slider and entry if needed
721
+ self.grid_columnconfigure(1, weight=1)
722
+
723
+ # Entry widget for showing and editing index, if enabled
724
+ if self.show_index:
725
+ self.index_var = tk.StringVar(value=str(int(self.value)))
726
+ self.index_entry = tk.Entry(self, textvariable=self.index_var, width=5, bg=self.bg_color, fg=self.fg_color, insertbackground=self.fg_color)
727
+ self.index_entry.grid(row=0, column=0, padx=5)
728
+ # Bind the entry to update the slider on change
729
+ self.index_entry.bind("<Return>", self.update_slider_from_entry)
730
+
731
+ # Create the slider canvas
732
+ self.canvas = tk.Canvas(self, height=knob_radius * 2, bg=self.bg_color, highlightthickness=0)
733
+ self.canvas.grid(row=0, column=1, sticky="ew")
734
+
735
+ # Set initial length to specified length or default value
736
+ self.length = self.specified_length if self.specified_length is not None else self.canvas.winfo_reqwidth()
737
+
738
+ # Calculate initial knob position based on the initial value
739
+ self.knob_position = self.value_to_position(self.value)
740
+
741
+ # Bind resize event to dynamically adjust the slider length if no length is specified
742
+ self.canvas.bind("<Configure>", self.resize_slider)
743
+
744
+ # Draw the slider components
745
+ self.draw_slider(inactive=True)
746
+
747
+ # Bind mouse events to the knob and slider
748
+ self.canvas.bind("<B1-Motion>", self.move_knob)
749
+ self.canvas.bind("<Button-1>", self.activate_knob) # Activate knob on click
750
+ self.canvas.bind("<ButtonRelease-1>", self.release_knob) # Trigger command on release
751
+
752
+ def resize_slider(self, event):
753
+ if self.specified_length is not None:
754
+ self.length = self.specified_length
755
+ else:
756
+ self.length = int(event.width * 0.9) # 90% of the container width
757
+
758
+ # Calculate the horizontal offset based on the position
759
+ if self.position == "center":
760
+ self.offset = (event.width - self.length) // 2
761
+ elif self.position == "right":
762
+ self.offset = event.width - self.length
763
+ else: # position is "left"
764
+ self.offset = 0
765
+
766
+ # Update the knob position after resizing
767
+ self.knob_position = self.value_to_position(self.value)
768
+ self.draw_slider(inactive=True)
769
+
770
+ def value_to_position(self, value):
771
+ if self.to == self.from_:
772
+ return self.knob_radius
773
+ relative_value = (value - self.from_) / (self.to - self.from_)
774
+ return self.knob_radius + relative_value * (self.length - 2 * self.knob_radius)
775
+
776
+ def position_to_value(self, position):
777
+ if self.to == self.from_:
778
+ return self.from_
779
+ relative_position = (position - self.knob_radius) / (self.length - 2 * self.knob_radius)
780
+ return self.from_ + relative_position * (self.to - self.from_)
781
+
782
+ def draw_slider(self, inactive=False):
783
+ self.canvas.delete("all")
784
+
785
+ self.slider_line = self.canvas.create_line(
786
+ self.offset + self.knob_radius,
787
+ self.knob_radius,
788
+ self.offset + self.length - self.knob_radius,
789
+ self.knob_radius,
790
+ fill=self.fg_color,
791
+ width=self.thickness
792
+ )
793
+
794
+ knob_color = self.inactive_color if inactive else self.active_color
795
+ self.knob = self.canvas.create_oval(
796
+ self.offset + self.knob_position - self.knob_radius,
797
+ self.knob_radius - self.knob_radius,
798
+ self.offset + self.knob_position + self.knob_radius,
799
+ self.knob_radius + self.knob_radius,
800
+ fill=knob_color,
801
+ outline=""
802
+ )
803
+
804
+ def move_knob(self, event):
805
+ new_position = min(max(event.x - self.offset, self.knob_radius), self.length - self.knob_radius)
806
+ self.knob_position = new_position
807
+ self.value = self.position_to_value(self.knob_position)
808
+ self.canvas.coords(
809
+ self.knob,
810
+ self.offset + self.knob_position - self.knob_radius,
811
+ self.knob_radius - self.knob_radius,
812
+ self.offset + self.knob_position + self.knob_radius,
813
+ self.knob_radius + self.knob_radius
814
+ )
815
+ if self.show_index:
816
+ self.index_var.set(str(int(self.value)))
817
+
818
+ def activate_knob(self, event):
819
+ self.draw_slider(inactive=False)
820
+ self.move_knob(event)
821
+
822
+ def release_knob(self, event):
823
+ self.draw_slider(inactive=True)
824
+ if self.command:
825
+ self.command(self.value) # Call the command with the final value when the knob is released
826
+
827
+ def set_to(self, new_to):
828
+ self.to = new_to
829
+ self.knob_position = self.value_to_position(self.value)
830
+ self.draw_slider(inactive=False)
831
+
832
+ def get(self):
833
+ return self.value
834
+
835
+ def set(self, value):
836
+ """Set the slider's value and update the knob position."""
837
+ self.value = max(self.from_, min(value, self.to)) # Ensure the value is within bounds
838
+ self.knob_position = self.value_to_position(self.value)
839
+ self.draw_slider(inactive=False)
840
+ if self.show_index:
841
+ self.index_var.set(str(int(self.value)))
842
+
843
+ def jump_to_click(self, event):
844
+ self.activate_knob(event)
845
+
846
+ def update_slider_from_entry(self, event):
847
+ """Update the slider's value from the entry."""
848
+ try:
849
+ index = int(self.index_var.get())
850
+ self.set(index)
851
+ if self.command:
852
+ self.command(self.value)
853
+ except ValueError:
854
+ pass
855
+
692
856
  def spacrScrollbarStyle(style, inactive_color, active_color):
693
857
  # Check if custom elements already exist to avoid duplication
694
858
  if not style.element_names().count('custom.Vertical.Scrollbar.trough'):
@@ -1987,6 +2151,11 @@ class AnnotateApp:
1987
2151
  style_out = set_dark_style(ttk.Style())
1988
2152
  self.font_loader = style_out['font_loader']
1989
2153
  self.font_size = style_out['font_size']
2154
+ self.bg_color = style_out['bg_color']
2155
+ self.fg_color = style_out['fg_color']
2156
+ self.active_color = style_out['active_color']
2157
+ self.inactive_color = style_out['inactive_color']
2158
+
1990
2159
 
1991
2160
  if self.font_loader:
1992
2161
  self.font_style = self.font_loader.get_font(size=self.font_size)
@@ -2013,13 +2182,13 @@ class AnnotateApp:
2013
2182
  self.button_frame = Frame(root, bg=self.root.cget('bg'))
2014
2183
  self.button_frame.grid(row=2, column=1, padx=10, pady=10, sticky="se")
2015
2184
 
2016
- self.next_button = Button(self.button_frame, text="Next", command=self.next_page, bg='black', fg='white', highlightbackground='white', highlightcolor='white', highlightthickness=1)
2185
+ self.next_button = Button(self.button_frame, text="Next", command=self.next_page, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color, highlightcolor=self.fg_color, highlightthickness=1)
2017
2186
  self.next_button.pack(side="right", padx=5)
2018
2187
 
2019
- self.previous_button = Button(self.button_frame, text="Back", command=self.previous_page, bg='black', fg='white', highlightbackground='white', highlightcolor='white', highlightthickness=1)
2188
+ self.previous_button = Button(self.button_frame, text="Back", command=self.previous_page, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color, highlightcolor=self.fg_color, highlightthickness=1)
2020
2189
  self.previous_button.pack(side="right", padx=5)
2021
2190
 
2022
- self.exit_button = Button(self.button_frame, text="Exit", command=self.shutdown, bg='black', fg='white', highlightbackground='white', highlightcolor='white', highlightthickness=1)
2191
+ self.exit_button = Button(self.button_frame, text="Exit", command=self.shutdown, bg=self.bg_color, fg=self.fg_color, highlightbackground=self.fg_color, highlightcolor=self.fg_color, highlightthickness=1)
2023
2192
  self.exit_button.pack(side="right", padx=5)
2024
2193
 
2025
2194
  # Calculate grid rows and columns based on the root window size and image size
@@ -2169,7 +2338,7 @@ class AnnotateApp:
2169
2338
 
2170
2339
  for i, (img, annotation) in enumerate(loaded_images):
2171
2340
  if annotation:
2172
- border_color = 'teal' if annotation == 1 else 'red'
2341
+ border_color = self.active_color if annotation == 1 else 'red'
2173
2342
  img = self.add_colored_border(img, border_width=5, border_color=border_color)
2174
2343
 
2175
2344
  photo = ImageTk.PhotoImage(img)
@@ -2215,7 +2384,7 @@ class AnnotateApp:
2215
2384
  left_border = Image.new('RGB', (border_width, img.height), color=border_color)
2216
2385
  right_border = Image.new('RGB', (border_width, img.height), color=border_color)
2217
2386
 
2218
- bordered_img = Image.new('RGB', (img.width + 2 * border_width, img.height + 2 * border_width), color='white')
2387
+ bordered_img = Image.new('RGB', (img.width + 2 * border_width, img.height + 2 * border_width), color=self.fg_color)
2219
2388
  bordered_img.paste(top_border, (border_width, 0))
2220
2389
  bordered_img.paste(bottom_border, (border_width, img.height + border_width))
2221
2390
  bordered_img.paste(left_border, (0, border_width))
@@ -2255,7 +2424,7 @@ class AnnotateApp:
2255
2424
  print(f"Image {os.path.split(path)[1]} annotated: {new_annotation}")
2256
2425
 
2257
2426
  img_ = img.crop((5, 5, img.width-5, img.height-5))
2258
- border_fill = 'teal' if new_annotation == 1 else ('red' if new_annotation == 2 else None)
2427
+ border_fill = self.active_color if new_annotation == 1 else ('red' if new_annotation == 2 else None)
2259
2428
  img_ = ImageOps.expand(img_, border=5, fill=border_fill) if border_fill else img_
2260
2429
 
2261
2430
  photo = ImageTk.PhotoImage(img_)
@@ -2400,6 +2569,7 @@ def standardize_figure(fig):
2400
2569
  - Line width: 1
2401
2570
  - Line color: from style
2402
2571
  """
2572
+
2403
2573
 
2404
2574
  for ax in fig.get_axes():
2405
2575
  # Set font properties for title and labels
@@ -2448,6 +2618,8 @@ def standardize_figure(fig):
2448
2618
 
2449
2619
  fig.canvas.draw_idle()
2450
2620
 
2621
+ return fig
2622
+
2451
2623
  def modify_figure_properties(fig, scale_x=None, scale_y=None, line_width=None, font_size=None, x_lim=None, y_lim=None, grid=False, legend=None, title=None, x_label_rotation=None, remove_axes=False, bg_color=None, text_color=None, line_color=None):
2452
2624
  """
2453
2625
  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.