spacr 0.2.4__py3-none-any.whl → 0.2.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. spacr/core.py +56 -67
  2. spacr/gui.py +20 -38
  3. spacr/gui_core.py +390 -489
  4. spacr/gui_elements.py +309 -59
  5. spacr/gui_utils.py +361 -73
  6. spacr/io.py +42 -46
  7. spacr/measure.py +198 -151
  8. spacr/plot.py +108 -42
  9. spacr/resources/font/open_sans/OFL.txt +93 -0
  10. spacr/resources/font/open_sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  11. spacr/resources/font/open_sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  12. spacr/resources/font/open_sans/README.txt +100 -0
  13. spacr/resources/font/open_sans/static/OpenSans-Bold.ttf +0 -0
  14. spacr/resources/font/open_sans/static/OpenSans-BoldItalic.ttf +0 -0
  15. spacr/resources/font/open_sans/static/OpenSans-ExtraBold.ttf +0 -0
  16. spacr/resources/font/open_sans/static/OpenSans-ExtraBoldItalic.ttf +0 -0
  17. spacr/resources/font/open_sans/static/OpenSans-Italic.ttf +0 -0
  18. spacr/resources/font/open_sans/static/OpenSans-Light.ttf +0 -0
  19. spacr/resources/font/open_sans/static/OpenSans-LightItalic.ttf +0 -0
  20. spacr/resources/font/open_sans/static/OpenSans-Medium.ttf +0 -0
  21. spacr/resources/font/open_sans/static/OpenSans-MediumItalic.ttf +0 -0
  22. spacr/resources/font/open_sans/static/OpenSans-Regular.ttf +0 -0
  23. spacr/resources/font/open_sans/static/OpenSans-SemiBold.ttf +0 -0
  24. spacr/resources/font/open_sans/static/OpenSans-SemiBoldItalic.ttf +0 -0
  25. spacr/resources/font/open_sans/static/OpenSans_Condensed-Bold.ttf +0 -0
  26. spacr/resources/font/open_sans/static/OpenSans_Condensed-BoldItalic.ttf +0 -0
  27. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBold.ttf +0 -0
  28. spacr/resources/font/open_sans/static/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
  29. spacr/resources/font/open_sans/static/OpenSans_Condensed-Italic.ttf +0 -0
  30. spacr/resources/font/open_sans/static/OpenSans_Condensed-Light.ttf +0 -0
  31. spacr/resources/font/open_sans/static/OpenSans_Condensed-LightItalic.ttf +0 -0
  32. spacr/resources/font/open_sans/static/OpenSans_Condensed-Medium.ttf +0 -0
  33. spacr/resources/font/open_sans/static/OpenSans_Condensed-MediumItalic.ttf +0 -0
  34. spacr/resources/font/open_sans/static/OpenSans_Condensed-Regular.ttf +0 -0
  35. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBold.ttf +0 -0
  36. spacr/resources/font/open_sans/static/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
  37. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Bold.ttf +0 -0
  38. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
  39. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
  40. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
  41. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Italic.ttf +0 -0
  42. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Light.ttf +0 -0
  43. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
  44. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Medium.ttf +0 -0
  45. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
  46. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-Regular.ttf +0 -0
  47. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
  48. spacr/resources/font/open_sans/static/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  49. spacr/resources/icons/logo.pdf +2786 -6
  50. spacr/resources/icons/logo_spacr.png +0 -0
  51. spacr/resources/icons/logo_spacr_1.png +0 -0
  52. spacr/settings.py +11 -83
  53. spacr/utils.py +13 -33
  54. {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/METADATA +5 -1
  55. spacr-0.2.5.dist-info/RECORD +100 -0
  56. spacr-0.2.4.dist-info/RECORD +0 -58
  57. {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/LICENSE +0 -0
  58. {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/WHEEL +0 -0
  59. {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/entry_points.txt +0 -0
  60. {spacr-0.2.4.dist-info → spacr-0.2.5.dist-info}/top_level.txt +0 -0
spacr/gui_elements.py CHANGED
@@ -1,4 +1,4 @@
1
- import os, threading, time, sqlite3
1
+ import os, threading, time, sqlite3, webbrowser
2
2
  import tkinter as tk
3
3
  from tkinter import ttk
4
4
  import tkinter.font as tkFont
@@ -17,7 +17,21 @@ from scipy.ndimage import binary_fill_holes, label
17
17
  from tkinter import ttk, scrolledtext
18
18
 
19
19
  def set_default_font(root, font_name="Arial", size=12):
20
- default_font = (font_name, size)
20
+
21
+ # Define default_font before the condition
22
+ default_font = ("Arial", size)
23
+
24
+ if font_name == 'OpenSans':
25
+ try:
26
+ font_path = os.path.join(os.path.dirname(__file__), 'resources/font/open_sans/static/OpenSans-Regular.ttf')
27
+ default_font = tkFont.Font(family=font_name, size=size, file=font_path)
28
+ except Exception as e:
29
+ print(f"Failed to load OpenSans font: {e}")
30
+ # Fallback to a default font in case of an error
31
+ default_font = ("Arial", size)
32
+ else:
33
+ default_font = (font_name, size)
34
+
21
35
  root.option_add("*Font", default_font)
22
36
  root.option_add("*TButton.Font", default_font)
23
37
  root.option_add("*TLabel.Font", default_font)
@@ -47,29 +61,11 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
47
61
  style.configure('TEntry', padding=padding)
48
62
  style.configure('Spacr.TEntry', padding=padding)
49
63
  style.configure('Custom.TLabel', padding=padding)
50
- #style.configure('Spacr.TCheckbutton', padding=padding)
51
64
  style.configure('TButton', padding=padding)
52
-
53
65
  style.configure('TFrame', background=bg_color)
54
66
  style.configure('TPanedwindow', background=bg_color)
55
67
  style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_style)
56
68
 
57
-
58
- #style.configure('Custom.TLabel', padding='5 5 5 5', borderwidth=1, relief='flat', background=bg_color, foreground=fg_color, font=font_style)
59
- #style.configure('Spacr.TCheckbutton', background=bg_color, foreground=fg_color, indicatoron=False, relief='flat', font="15")
60
- #style.map('Spacr.TCheckbutton', background=[('selected', bg_color), ('active', bg_color)], foreground=[('selected', fg_color), ('active', fg_color)])
61
-
62
-
63
- #style.configure('TNotebook', background=bg_color, tabmargins=[2, 5, 2, 0])
64
- #style.configure('TNotebook.Tab', background=bg_color, foreground=fg_color, padding=[5, 5], font=font_style)
65
- #style.map('TNotebook.Tab', background=[('selected', active_color), ('active', active_color)], foreground=[('selected', fg_color), ('active', fg_color)])
66
- #style.configure('TButton', background=bg_color, foreground=fg_color, padding='5 5 5 5', font=font_style)
67
- #style.map('TButton', background=[('active', active_color), ('disabled', inactive_color)])
68
- #style.configure('Vertical.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
69
- #style.configure('Horizontal.TScrollbar', background=bg_color, troughcolor=bg_color, bordercolor=bg_color)
70
- #style.configure('Custom.TLabelFrame', font=(font_family, font_size, 'bold'), background=bg_color, foreground='white', relief='solid', borderwidth=1)
71
- #style.configure('Custom.TLabelFrame.Label', background=bg_color, foreground='white', font=(font_family, font_size, 'bold'))
72
-
73
69
  if parent_frame:
74
70
  parent_frame.configure(bg=bg_color)
75
71
  parent_frame.grid_rowconfigure(0, weight=1)
@@ -99,8 +95,101 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
99
95
 
100
96
  return {'font_family': font_family, 'font_size': font_size, 'bg_color': bg_color, 'fg_color': fg_color, 'active_color': active_color, 'inactive_color': inactive_color}
101
97
 
98
+ class spacrContainer(tk.Frame):
99
+ def __init__(self, parent, orient=tk.VERTICAL, bg=None, *args, **kwargs):
100
+ super().__init__(parent, *args, **kwargs)
101
+ self.orient = orient
102
+ self.bg = bg if bg else 'lightgrey'
103
+ self.sash_thickness = 10
104
+
105
+ self.panes = []
106
+ self.sashes = []
107
+ self.bind("<Configure>", self.on_configure)
108
+
109
+ self.grid_rowconfigure(0, weight=1)
110
+ self.grid_columnconfigure(0, weight=1)
111
+
112
+ def add(self, widget, stretch='always'):
113
+ print(f"Adding widget: {widget} with stretch: {stretch}")
114
+ pane = tk.Frame(self, bg=self.bg)
115
+ pane.grid_propagate(False)
116
+ widget.grid(in_=pane, sticky="nsew") # Use grid for the widget within the pane
117
+ self.panes.append((pane, widget))
118
+
119
+ if len(self.panes) > 1:
120
+ self.create_sash()
121
+
122
+ self.reposition_panes()
123
+
124
+ def create_sash(self):
125
+ 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)
126
+ sash.bind("<Enter>", self.on_enter_sash)
127
+ sash.bind("<Leave>", self.on_leave_sash)
128
+ sash.bind("<ButtonPress-1>", self.start_resize)
129
+ self.sashes.append(sash)
130
+
131
+ def reposition_panes(self):
132
+ if not self.panes:
133
+ return
134
+
135
+ total_size = self.winfo_height() if self.orient == tk.VERTICAL else self.winfo_width()
136
+ pane_size = total_size // len(self.panes)
137
+
138
+ print(f"Total size: {total_size}, Pane size: {pane_size}, Number of panes: {len(self.panes)}")
139
+
140
+ for i, (pane, widget) in enumerate(self.panes):
141
+ if self.orient == tk.VERTICAL:
142
+ pane.grid(row=i * 2, column=0, sticky="nsew", pady=(0, self.sash_thickness if i < len(self.panes) - 1 else 0))
143
+ else:
144
+ pane.grid(row=0, column=i * 2, sticky="nsew", padx=(0, self.sash_thickness if i < len(self.panes) - 1 else 0))
145
+
146
+ for i, sash in enumerate(self.sashes):
147
+ if self.orient == tk.VERTICAL:
148
+ sash.grid(row=(i * 2) + 1, column=0, sticky="ew")
149
+ else:
150
+ sash.grid(row=0, column=(i * 2) + 1, sticky="ns")
151
+
152
+ def on_configure(self, event):
153
+ print(f"Configuring container: {self}")
154
+ self.reposition_panes()
155
+
156
+ def on_enter_sash(self, event):
157
+ event.widget.config(bg='blue')
158
+
159
+ def on_leave_sash(self, event):
160
+ event.widget.config(bg=self.bg)
161
+
162
+ def start_resize(self, event):
163
+ sash = event.widget
164
+ self.start_pos = event.y_root if self.orient == tk.VERTICAL else event.x_root
165
+ self.start_size = sash.winfo_y() if self.orient == tk.VERTICAL else sash.winfo_x()
166
+ sash.bind("<B1-Motion>", self.perform_resize)
167
+
168
+ def perform_resize(self, event):
169
+ sash = event.widget
170
+ delta = (event.y_root - self.start_pos) if self.orient == tk.VERTICAL else (event.x_root - self.start_pos)
171
+ new_size = self.start_size + delta
172
+
173
+ for i, (pane, widget) in enumerate(self.panes):
174
+ if self.orient == tk.VERTICAL:
175
+ new_row = max(0, new_size // self.sash_thickness)
176
+ if pane.winfo_y() >= new_size:
177
+ pane.grid_configure(row=new_row)
178
+ elif pane.winfo_y() < new_size and i > 0:
179
+ previous_row = max(0, (new_size - pane.winfo_height()) // self.sash_thickness)
180
+ self.panes[i - 1][0].grid_configure(row=previous_row)
181
+ else:
182
+ new_col = max(0, new_size // self.sash_thickness)
183
+ if pane.winfo_x() >= new_size:
184
+ pane.grid_configure(column=new_col)
185
+ elif pane.winfo_x() < new_size and i > 0:
186
+ previous_col = max(0, (new_size - pane.winfo_width()) // self.sash_thickness)
187
+ self.panes[i - 1][0].grid_configure(column=previous_col)
188
+
189
+ self.reposition_panes()
190
+
102
191
  class spacrEntry(tk.Frame):
103
- def __init__(self, parent, textvariable=None, outline=False, *args, **kwargs):
192
+ def __init__(self, parent, textvariable=None, outline=False, width=None, *args, **kwargs):
104
193
  super().__init__(parent, *args, **kwargs)
105
194
 
106
195
  # Set dark style
@@ -116,13 +205,17 @@ class spacrEntry(tk.Frame):
116
205
  self.configure(bg=style_out['bg_color'])
117
206
 
118
207
  # Create a canvas for the rounded rectangle background
119
- self.canvas_width = 220 # Adjusted for padding
208
+ if width is None:
209
+ self.canvas_width = 220 # Adjusted for padding
210
+ else:
211
+ self.canvas_width = width
120
212
  self.canvas_height = 40 # Adjusted for padding
121
213
  self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=style_out['bg_color'])
122
214
  self.canvas.pack()
123
215
 
216
+ # Create the entry widget
124
217
  self.entry = tk.Entry(self, textvariable=textvariable, bd=0, highlightthickness=0, fg=self.fg_color, font=(self.font_family, self.font_size), bg=self.bg_color)
125
- self.entry.place(relx=0.5, rely=0.5, anchor=tk.CENTER, width=190, height=20) # Centered positioning
218
+ self.entry.place(relx=0.5, rely=0.5, anchor=tk.CENTER, width=self.canvas_width - 30, height=20) # Centered positioning
126
219
 
127
220
  # Bind events to change the background color on focus
128
221
  self.entry.bind("<FocusIn>", self.on_focus_in)
@@ -133,7 +226,7 @@ class spacrEntry(tk.Frame):
133
226
  def draw_rounded_rectangle(self, color):
134
227
  radius = 15 # Increased radius for more rounded corners
135
228
  x0, y0 = 10, 5
136
- x1, y1 = 210, 35
229
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5
137
230
  self.canvas.delete("all")
138
231
  self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
139
232
  self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
@@ -201,7 +294,7 @@ class spacrCheck(tk.Frame):
201
294
  self.variable.set(not self.variable.get())
202
295
 
203
296
  class spacrCombo(tk.Frame):
204
- def __init__(self, parent, textvariable=None, values=None, *args, **kwargs):
297
+ def __init__(self, parent, textvariable=None, values=None, width=None, *args, **kwargs):
205
298
  super().__init__(parent, *args, **kwargs)
206
299
 
207
300
  # Set dark style
@@ -216,7 +309,7 @@ class spacrCombo(tk.Frame):
216
309
  self.values = values or []
217
310
 
218
311
  # Create a canvas for the rounded rectangle background
219
- self.canvas_width = 220 # Adjusted for padding
312
+ self.canvas_width = width if width is not None else 220 # Adjusted for padding
220
313
  self.canvas_height = 40 # Adjusted for padding
221
314
  self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg=self.bg_color)
222
315
  self.canvas.pack()
@@ -239,7 +332,7 @@ class spacrCombo(tk.Frame):
239
332
  def draw_rounded_rectangle(self, color):
240
333
  radius = 15 # Increased radius for more rounded corners
241
334
  x0, y0 = 10, 5
242
- x1, y1 = 210, 35
335
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5
243
336
  self.canvas.delete("all")
244
337
  self.canvas.create_arc((x0, y0, x0 + radius, y0 + radius), start=90, extent=90, fill=color, outline=color)
245
338
  self.canvas.create_arc((x1 - radius, y0, x1, y0 + radius), start=0, extent=90, fill=color, outline=color)
@@ -305,6 +398,73 @@ class spacrDropdownMenu(tk.OptionMenu):
305
398
  # Create custom button
306
399
  self.create_custom_button()
307
400
 
401
+ def create_custom_button(self):
402
+ self.canvas_width = self.winfo_reqwidth() + 20 # Adjust width based on required size
403
+ self.canvas_height = 40 # Adjust the height as needed
404
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg='#2B2B2B')
405
+ self.canvas.pack()
406
+ self.label = tk.Label(self.canvas, text="Settings Category", bg='#2B2B2B', fg='#ffffff', font=('Arial', 12))
407
+ self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
408
+ self.draw_rounded_rectangle('#2B2B2B')
409
+
410
+ # Bind the click event to open the dropdown menu
411
+ self.canvas.bind("<Button-1>", self.on_click)
412
+ self.label.bind("<Button-1>", self.on_click)
413
+
414
+ def draw_rounded_rectangle(self, color):
415
+ radius = 15
416
+ x0, y0 = 10, 5
417
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5 # Adjust based on canvas size
418
+
419
+ self.canvas.delete("all")
420
+
421
+ # Create the rounded rectangle
422
+ self.canvas.create_arc(x0, y0, x0 + 2 * radius, y0 + 2 * radius, start=90, extent=90, fill=color, outline=color)
423
+ self.canvas.create_arc(x1 - 2 * radius, y0, x1, y0 + 2 * radius, start=0, extent=90, fill=color, outline=color)
424
+ self.canvas.create_arc(x0, y1 - 2 * radius, x0 + 2 * radius, y1, start=180, extent=90, fill=color, outline=color)
425
+ self.canvas.create_arc(x1 - 2 * radius, y1 - 2 * radius, x1, y1, start=270, extent=90, fill=color, outline=color)
426
+
427
+ self.canvas.create_rectangle(x0 + radius, y0, x1 - radius, y1, fill=color, outline=color)
428
+ self.canvas.create_rectangle(x0, y0 + radius, x1, y1 - radius, fill=color, outline=color)
429
+
430
+ self.label.config(bg=color) # Update label background to match rectangle color
431
+
432
+ def on_click(self, event):
433
+ self.post_menu()
434
+
435
+ def post_menu(self):
436
+ x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
437
+ self.menu.post(x, y + height)
438
+
439
+ def update_styles(self, active_categories=None):
440
+ style = ttk.Style()
441
+ style_out = set_dark_style(style, widgets=[self])
442
+ self.menu = self['menu']
443
+ style_out = set_dark_style(style, widgets=[self.menu])
444
+
445
+ if active_categories is not None:
446
+ for idx in range(self.menu.index("end") + 1):
447
+ option = self.menu.entrycget(idx, "label")
448
+ if option in active_categories:
449
+ self.menu.entryconfig(idx, background=style_out['active_color'], foreground=style_out['fg_color'])
450
+ else:
451
+ self.menu.entryconfig(idx, background=style_out['bg_color'], foreground=style_out['fg_color'])
452
+
453
+
454
+
455
+ class spacrDropdownMenu_v1(tk.OptionMenu):
456
+ def __init__(self, parent, variable, options, command=None, **kwargs):
457
+ self.variable = variable
458
+ self.variable.set("Settings Category")
459
+ super().__init__(parent, self.variable, *options, command=command, **kwargs)
460
+ self.update_styles()
461
+
462
+ # Hide the original button
463
+ self.configure(highlightthickness=0, relief='flat', bg='#2B2B2B', fg='#2B2B2B')
464
+
465
+ # Create custom button
466
+ self.create_custom_button()
467
+
308
468
  def create_custom_button(self):
309
469
  self.canvas_width = self.winfo_reqwidth() # Use the required width of the widget
310
470
  self.canvas_height = 40 # Adjust the height as needed
@@ -363,12 +523,12 @@ class spacrCheckbutton(ttk.Checkbutton):
363
523
  _ = set_dark_style(style, widgets=[self])
364
524
 
365
525
  class spacrProgressBar(ttk.Progressbar):
366
- def __init__(self, parent, *args, **kwargs):
526
+ def __init__(self, parent, label=True, *args, **kwargs):
367
527
  super().__init__(parent, *args, **kwargs)
368
528
 
369
529
  # Get the style colors
370
530
  style_out = set_dark_style(ttk.Style())
371
-
531
+
372
532
  self.fg_color = style_out['fg_color']
373
533
  self.bg_color = style_out['bg_color']
374
534
  self.active_color = style_out['active_color']
@@ -389,9 +549,13 @@ class spacrProgressBar(ttk.Progressbar):
389
549
  # Set initial value to 0
390
550
  self['value'] = 0
391
551
 
392
- # Create the progress label
393
- self.progress_label = tk.Label(parent, text="Processing: 0/0", anchor='w', justify='left', bg=self.inactive_color, fg=self.fg_color)
394
- self.progress_label.grid(row=1, column=0, columnspan=2, pady=5, padx=5, sticky='ew')
552
+ # Track whether to show the progress label
553
+ self.label = label
554
+
555
+ # Create the progress label (defer placement)
556
+ if self.label:
557
+ self.progress_label = tk.Label(parent, text="Processing: 0/0", anchor='w', justify='left', bg=self.inactive_color, fg=self.fg_color)
558
+ self.progress_label.grid_forget() # Temporarily hide it
395
559
 
396
560
  # Initialize attributes for time and operation
397
561
  self.operation_type = None
@@ -399,57 +563,141 @@ class spacrProgressBar(ttk.Progressbar):
399
563
  self.time_batch = None
400
564
  self.time_left = None
401
565
 
566
+ def set_label_position(self):
567
+ if self.label and self.progress_label:
568
+ row_info = self.grid_info().get('row', 0)
569
+ col_info = self.grid_info().get('column', 0)
570
+ col_span = self.grid_info().get('columnspan', 1)
571
+ self.progress_label.grid(row=row_info + 1, column=col_info, columnspan=col_span, pady=5, padx=5, sticky='ew')
572
+
402
573
  def update_label(self):
403
- # Update the progress label with current progress and additional info
404
- label_text = f"Processing: {self['value']}/{self['maximum']}"
405
- if self.operation_type:
406
- label_text += f", {self.operation_type}"
407
- if self.time_image:
408
- label_text += f", Time/image: {self.time_image:.3f} sec"
409
- if self.time_batch:
410
- label_text += f", Time/batch: {self.time_batch:.3f} sec"
411
- if self.time_left:
412
- label_text += f", Time_left: {self.time_left:.3f} min"
413
- self.progress_label.config(text=label_text)
574
+ if self.label and self.progress_label:
575
+ # Update the progress label with current progress and additional info
576
+ label_text = f"Processing: {self['value']}/{self['maximum']}"
577
+ if self.operation_type:
578
+ label_text += f", {self.operation_type}"
579
+ if self.time_image:
580
+ label_text += f", Time/image: {self.time_image:.3f} sec"
581
+ if self.time_batch:
582
+ label_text += f", Time/batch: {self.time_batch:.3f} sec"
583
+ if self.time_left:
584
+ label_text += f", Time_left: {self.time_left:.3f} min"
585
+ self.progress_label.config(text=label_text)
586
+
587
+ def spacrScrollbarStyle(style, inactive_color, active_color):
588
+ # Check if custom elements already exist to avoid duplication
589
+ if not style.element_names().count('custom.Vertical.Scrollbar.trough'):
590
+ style.element_create('custom.Vertical.Scrollbar.trough', 'from', 'clam')
591
+ if not style.element_names().count('custom.Vertical.Scrollbar.thumb'):
592
+ style.element_create('custom.Vertical.Scrollbar.thumb', 'from', 'clam')
593
+
594
+ style.layout('Custom.Vertical.TScrollbar',
595
+ [('Vertical.Scrollbar.trough', {'children': [('Vertical.Scrollbar.thumb', {'expand': '1', 'sticky': 'nswe'})], 'sticky': 'ns'})])
596
+
597
+ style.configure('Custom.Vertical.TScrollbar',
598
+ background=inactive_color,
599
+ troughcolor=inactive_color,
600
+ bordercolor=inactive_color,
601
+ lightcolor=inactive_color,
602
+ darkcolor=inactive_color)
603
+
604
+ style.map('Custom.Vertical.TScrollbar',
605
+ background=[('!active', inactive_color), ('active', active_color)],
606
+ troughcolor=[('!active', inactive_color), ('active', inactive_color)],
607
+ bordercolor=[('!active', inactive_color), ('active', inactive_color)],
608
+ lightcolor=[('!active', inactive_color), ('active', active_color)],
609
+ darkcolor=[('!active', inactive_color), ('active', active_color)])
414
610
 
415
611
  class spacrFrame(ttk.Frame):
416
- def __init__(self, container, width=None, *args, bg='black', **kwargs):
612
+ def __init__(self, container, width=None, *args, bg='black', radius=20, scrollbar=True, textbox=False, **kwargs):
417
613
  super().__init__(container, *args, **kwargs)
418
614
  self.configure(style='TFrame')
419
615
  if width is None:
420
616
  screen_width = self.winfo_screenwidth()
421
617
  width = screen_width // 4
422
- canvas = tk.Canvas(self, bg=bg, width=width)
423
- scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
618
+
619
+ # Create the canvas
620
+ canvas = tk.Canvas(self, bg=bg, width=width, highlightthickness=0)
621
+ self.rounded_rectangle(canvas, 0, 0, width, self.winfo_screenheight(), radius, fill=bg)
622
+
623
+ # Define scrollbar styles
624
+ style_out = set_dark_style(ttk.Style())
625
+ self.inactive_color = style_out['inactive_color']
626
+ self.active_color = style_out['active_color']
627
+ self.fg_color = style_out['fg_color'] # Foreground color for text
628
+
629
+ # Set custom scrollbar style
630
+ style = ttk.Style()
631
+ spacrScrollbarStyle(style, self.inactive_color, self.active_color)
632
+
633
+ # Create scrollbar with custom style if scrollbar option is True
634
+ if scrollbar:
635
+ scrollbar_widget = ttk.Scrollbar(self, orient="vertical", command=canvas.yview, style='Custom.Vertical.TScrollbar')
636
+
637
+ if textbox:
638
+ self.scrollable_frame = tk.Text(canvas, bg=bg, fg=self.fg_color, wrap=tk.WORD)
639
+ else:
640
+ self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
424
641
 
425
- self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
426
642
  self.scrollable_frame.bind(
427
643
  "<Configure>",
428
644
  lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
429
645
  )
430
646
  canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
431
- canvas.configure(yscrollcommand=scrollbar.set)
647
+ if scrollbar:
648
+ canvas.configure(yscrollcommand=scrollbar_widget.set)
432
649
 
433
650
  canvas.grid(row=0, column=0, sticky="nsew")
434
- scrollbar.grid(row=0, column=1, sticky="ns")
651
+ if scrollbar:
652
+ scrollbar_widget.grid(row=0, column=1, sticky="ns")
435
653
 
436
654
  self.grid_rowconfigure(0, weight=1)
437
655
  self.grid_columnconfigure(0, weight=1)
438
- self.grid_columnconfigure(1, weight=0)
656
+ if scrollbar:
657
+ self.grid_columnconfigure(1, weight=0)
439
658
 
440
- style = ttk.Style()
441
- _ = set_dark_style(style, containers=[self], widgets=[canvas, scrollbar, self.scrollable_frame])
659
+ _ = set_dark_style(style, containers=[self], widgets=[canvas, self.scrollable_frame])
660
+ if scrollbar:
661
+ _ = set_dark_style(style, widgets=[scrollbar_widget])
662
+
663
+ def rounded_rectangle(self, canvas, x1, y1, x2, y2, radius=20, **kwargs):
664
+ points = [
665
+ x1 + radius, y1,
666
+ x2 - radius, y1,
667
+ x2 - radius, y1,
668
+ x2, y1,
669
+ x2, y1 + radius,
670
+ x2, y2 - radius,
671
+ x2, y2 - radius,
672
+ x2, y2,
673
+ x2 - radius, y2,
674
+ x1 + radius, y2,
675
+ x1 + radius, y2,
676
+ x1, y2,
677
+ x1, y2 - radius,
678
+ x1, y2 - radius,
679
+ x1, y1 + radius,
680
+ x1, y1 + radius,
681
+ x1, y1
682
+ ]
683
+ return canvas.create_polygon(points, **kwargs, smooth=True)
442
684
 
443
685
  class spacrLabel(tk.Frame):
444
- def __init__(self, parent, text="", font=None, style=None, align="right", **kwargs):
686
+ def __init__(self, parent, text="", font=None, style=None, align="right", height=None, **kwargs):
445
687
  valid_kwargs = {k: v for k, v in kwargs.items() if k not in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
446
688
  super().__init__(parent, **valid_kwargs)
447
689
 
448
690
  self.text = text
449
691
  self.align = align
450
- screen_height = self.winfo_screenheight()
451
- label_height = screen_height // 50
452
- label_width = label_height * 10
692
+
693
+ if height is None:
694
+ screen_height = self.winfo_screenheight()
695
+ label_height = screen_height // 50
696
+ label_width = label_height * 10
697
+ else:
698
+ label_height = height
699
+ label_width = label_height * 10
700
+
453
701
  style_out = set_dark_style(ttk.Style())
454
702
 
455
703
  self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=style_out['bg_color'])
@@ -484,7 +732,7 @@ class spacrLabel(tk.Frame):
484
732
  self.canvas.itemconfig(self.label_text, text=text)
485
733
 
486
734
  class spacrButton(tk.Frame):
487
- def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=False, *args, **kwargs):
735
+ def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=False, animation=True, *args, **kwargs):
488
736
  super().__init__(parent, *args, **kwargs)
489
737
 
490
738
  self.text = text.capitalize() # Capitalize only the first letter of the text
@@ -493,6 +741,7 @@ class spacrButton(tk.Frame):
493
741
  self.size = size
494
742
  self.show_text = show_text
495
743
  self.outline = outline
744
+ self.animation = animation # Add animation attribute
496
745
 
497
746
  style_out = set_dark_style(ttk.Style())
498
747
 
@@ -559,13 +808,13 @@ class spacrButton(tk.Frame):
559
808
  def on_enter(self, event=None):
560
809
  self.canvas.itemconfig(self.button_bg, fill=self.active_color)
561
810
  self.update_description(event)
562
- if not self.is_zoomed_in:
811
+ if self.animation and not self.is_zoomed_in:
563
812
  self.animate_zoom(0.85) # Zoom in the icon to 85% of button size
564
813
 
565
814
  def on_leave(self, event=None):
566
815
  self.canvas.itemconfig(self.button_bg, fill=self.inactive_color)
567
816
  self.clear_description(event)
568
- if self.is_zoomed_in:
817
+ if self.animation and self.is_zoomed_in:
569
818
  self.animate_zoom(0.65) # Reset the icon size to 65% of button size
570
819
 
571
820
  def on_click(self, event=None):
@@ -1990,6 +2239,7 @@ def create_menu_bar(root):
1990
2239
 
1991
2240
  # Add a separator and an exit option
1992
2241
  app_menu.add_separator()
2242
+ app_menu.add_command(label="Help", command=lambda: webbrowser.open("https://readthedocs.org/projects/spacr/badge/?version=latest"))
1993
2243
  app_menu.add_command(label="Exit", command=root.quit)
1994
2244
 
1995
2245
  # Configure the menu for the root window