spacr 0.2.32__py3-none-any.whl → 0.2.45__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
@@ -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
@@ -47,29 +47,11 @@ def set_dark_style(style, parent_frame=None, containers=None, widgets=None, font
47
47
  style.configure('TEntry', padding=padding)
48
48
  style.configure('Spacr.TEntry', padding=padding)
49
49
  style.configure('Custom.TLabel', padding=padding)
50
- #style.configure('Spacr.TCheckbutton', padding=padding)
51
50
  style.configure('TButton', padding=padding)
52
-
53
51
  style.configure('TFrame', background=bg_color)
54
52
  style.configure('TPanedwindow', background=bg_color)
55
53
  style.configure('TLabel', background=bg_color, foreground=fg_color, font=font_style)
56
54
 
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
55
  if parent_frame:
74
56
  parent_frame.configure(bg=bg_color)
75
57
  parent_frame.grid_rowconfigure(0, weight=1)
@@ -305,6 +287,73 @@ class spacrDropdownMenu(tk.OptionMenu):
305
287
  # Create custom button
306
288
  self.create_custom_button()
307
289
 
290
+ def create_custom_button(self):
291
+ self.canvas_width = self.winfo_reqwidth() + 20 # Adjust width based on required size
292
+ self.canvas_height = 40 # Adjust the height as needed
293
+ self.canvas = tk.Canvas(self, width=self.canvas_width, height=self.canvas_height, bd=0, highlightthickness=0, relief='ridge', bg='#2B2B2B')
294
+ self.canvas.pack()
295
+ self.label = tk.Label(self.canvas, text="Settings Category", bg='#2B2B2B', fg='#ffffff', font=('Arial', 12))
296
+ self.label.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
297
+ self.draw_rounded_rectangle('#2B2B2B')
298
+
299
+ # Bind the click event to open the dropdown menu
300
+ self.canvas.bind("<Button-1>", self.on_click)
301
+ self.label.bind("<Button-1>", self.on_click)
302
+
303
+ def draw_rounded_rectangle(self, color):
304
+ radius = 15
305
+ x0, y0 = 10, 5
306
+ x1, y1 = self.canvas_width - 10, self.canvas_height - 5 # Adjust based on canvas size
307
+
308
+ self.canvas.delete("all")
309
+
310
+ # Create the rounded rectangle
311
+ self.canvas.create_arc(x0, y0, x0 + 2 * radius, y0 + 2 * radius, start=90, extent=90, fill=color, outline=color)
312
+ self.canvas.create_arc(x1 - 2 * radius, y0, x1, y0 + 2 * radius, start=0, extent=90, fill=color, outline=color)
313
+ self.canvas.create_arc(x0, y1 - 2 * radius, x0 + 2 * radius, y1, start=180, extent=90, fill=color, outline=color)
314
+ self.canvas.create_arc(x1 - 2 * radius, y1 - 2 * radius, x1, y1, start=270, extent=90, fill=color, outline=color)
315
+
316
+ self.canvas.create_rectangle(x0 + radius, y0, x1 - radius, y1, fill=color, outline=color)
317
+ self.canvas.create_rectangle(x0, y0 + radius, x1, y1 - radius, fill=color, outline=color)
318
+
319
+ self.label.config(bg=color) # Update label background to match rectangle color
320
+
321
+ def on_click(self, event):
322
+ self.post_menu()
323
+
324
+ def post_menu(self):
325
+ x, y, width, height = self.winfo_rootx(), self.winfo_rooty(), self.winfo_width(), self.winfo_height()
326
+ self.menu.post(x, y + height)
327
+
328
+ def update_styles(self, active_categories=None):
329
+ style = ttk.Style()
330
+ style_out = set_dark_style(style, widgets=[self])
331
+ self.menu = self['menu']
332
+ style_out = set_dark_style(style, widgets=[self.menu])
333
+
334
+ if active_categories is not None:
335
+ for idx in range(self.menu.index("end") + 1):
336
+ option = self.menu.entrycget(idx, "label")
337
+ if option in active_categories:
338
+ self.menu.entryconfig(idx, background=style_out['active_color'], foreground=style_out['fg_color'])
339
+ else:
340
+ self.menu.entryconfig(idx, background=style_out['bg_color'], foreground=style_out['fg_color'])
341
+
342
+
343
+
344
+ class spacrDropdownMenu_v1(tk.OptionMenu):
345
+ def __init__(self, parent, variable, options, command=None, **kwargs):
346
+ self.variable = variable
347
+ self.variable.set("Settings Category")
348
+ super().__init__(parent, self.variable, *options, command=command, **kwargs)
349
+ self.update_styles()
350
+
351
+ # Hide the original button
352
+ self.configure(highlightthickness=0, relief='flat', bg='#2B2B2B', fg='#2B2B2B')
353
+
354
+ # Create custom button
355
+ self.create_custom_button()
356
+
308
357
  def create_custom_button(self):
309
358
  self.canvas_width = self.winfo_reqwidth() # Use the required width of the widget
310
359
  self.canvas_height = 40 # Adjust the height as needed
@@ -363,12 +412,12 @@ class spacrCheckbutton(ttk.Checkbutton):
363
412
  _ = set_dark_style(style, widgets=[self])
364
413
 
365
414
  class spacrProgressBar(ttk.Progressbar):
366
- def __init__(self, parent, *args, **kwargs):
415
+ def __init__(self, parent, label=True, *args, **kwargs):
367
416
  super().__init__(parent, *args, **kwargs)
368
417
 
369
418
  # Get the style colors
370
419
  style_out = set_dark_style(ttk.Style())
371
-
420
+
372
421
  self.fg_color = style_out['fg_color']
373
422
  self.bg_color = style_out['bg_color']
374
423
  self.active_color = style_out['active_color']
@@ -389,9 +438,13 @@ class spacrProgressBar(ttk.Progressbar):
389
438
  # Set initial value to 0
390
439
  self['value'] = 0
391
440
 
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')
441
+ # Track whether to show the progress label
442
+ self.label = label
443
+
444
+ # Create the progress label (defer placement)
445
+ if self.label:
446
+ self.progress_label = tk.Label(parent, text="Processing: 0/0", anchor='w', justify='left', bg=self.inactive_color, fg=self.fg_color)
447
+ self.progress_label.grid_forget() # Temporarily hide it
395
448
 
396
449
  # Initialize attributes for time and operation
397
450
  self.operation_type = None
@@ -399,57 +452,141 @@ class spacrProgressBar(ttk.Progressbar):
399
452
  self.time_batch = None
400
453
  self.time_left = None
401
454
 
455
+ def set_label_position(self):
456
+ if self.label and self.progress_label:
457
+ row_info = self.grid_info().get('row', 0)
458
+ col_info = self.grid_info().get('column', 0)
459
+ col_span = self.grid_info().get('columnspan', 1)
460
+ self.progress_label.grid(row=row_info + 1, column=col_info, columnspan=col_span, pady=5, padx=5, sticky='ew')
461
+
402
462
  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)
463
+ if self.label and self.progress_label:
464
+ # Update the progress label with current progress and additional info
465
+ label_text = f"Processing: {self['value']}/{self['maximum']}"
466
+ if self.operation_type:
467
+ label_text += f", {self.operation_type}"
468
+ if self.time_image:
469
+ label_text += f", Time/image: {self.time_image:.3f} sec"
470
+ if self.time_batch:
471
+ label_text += f", Time/batch: {self.time_batch:.3f} sec"
472
+ if self.time_left:
473
+ label_text += f", Time_left: {self.time_left:.3f} min"
474
+ self.progress_label.config(text=label_text)
475
+
476
+ def spacrScrollbarStyle(style, inactive_color, active_color):
477
+ # Check if custom elements already exist to avoid duplication
478
+ if not style.element_names().count('custom.Vertical.Scrollbar.trough'):
479
+ style.element_create('custom.Vertical.Scrollbar.trough', 'from', 'clam')
480
+ if not style.element_names().count('custom.Vertical.Scrollbar.thumb'):
481
+ style.element_create('custom.Vertical.Scrollbar.thumb', 'from', 'clam')
482
+
483
+ style.layout('Custom.Vertical.TScrollbar',
484
+ [('Vertical.Scrollbar.trough', {'children': [('Vertical.Scrollbar.thumb', {'expand': '1', 'sticky': 'nswe'})], 'sticky': 'ns'})])
485
+
486
+ style.configure('Custom.Vertical.TScrollbar',
487
+ background=inactive_color,
488
+ troughcolor=inactive_color,
489
+ bordercolor=inactive_color,
490
+ lightcolor=inactive_color,
491
+ darkcolor=inactive_color)
492
+
493
+ style.map('Custom.Vertical.TScrollbar',
494
+ background=[('!active', inactive_color), ('active', active_color)],
495
+ troughcolor=[('!active', inactive_color), ('active', inactive_color)],
496
+ bordercolor=[('!active', inactive_color), ('active', inactive_color)],
497
+ lightcolor=[('!active', inactive_color), ('active', active_color)],
498
+ darkcolor=[('!active', inactive_color), ('active', active_color)])
414
499
 
415
500
  class spacrFrame(ttk.Frame):
416
- def __init__(self, container, width=None, *args, bg='black', **kwargs):
501
+ def __init__(self, container, width=None, *args, bg='black', radius=20, scrollbar=True, textbox=False, **kwargs):
417
502
  super().__init__(container, *args, **kwargs)
418
503
  self.configure(style='TFrame')
419
504
  if width is None:
420
505
  screen_width = self.winfo_screenwidth()
421
506
  width = screen_width // 4
422
- canvas = tk.Canvas(self, bg=bg, width=width)
423
- scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
507
+
508
+ # Create the canvas
509
+ canvas = tk.Canvas(self, bg=bg, width=width, highlightthickness=0)
510
+ self.rounded_rectangle(canvas, 0, 0, width, self.winfo_screenheight(), radius, fill=bg)
511
+
512
+ # Define scrollbar styles
513
+ style_out = set_dark_style(ttk.Style())
514
+ self.inactive_color = style_out['inactive_color']
515
+ self.active_color = style_out['active_color']
516
+ self.fg_color = style_out['fg_color'] # Foreground color for text
517
+
518
+ # Set custom scrollbar style
519
+ style = ttk.Style()
520
+ spacrScrollbarStyle(style, self.inactive_color, self.active_color)
521
+
522
+ # Create scrollbar with custom style if scrollbar option is True
523
+ if scrollbar:
524
+ scrollbar_widget = ttk.Scrollbar(self, orient="vertical", command=canvas.yview, style='Custom.Vertical.TScrollbar')
525
+
526
+ if textbox:
527
+ self.scrollable_frame = tk.Text(canvas, bg=bg, fg=self.fg_color, wrap=tk.WORD)
528
+ else:
529
+ self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
424
530
 
425
- self.scrollable_frame = ttk.Frame(canvas, style='TFrame')
426
531
  self.scrollable_frame.bind(
427
532
  "<Configure>",
428
533
  lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
429
534
  )
430
535
  canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
431
- canvas.configure(yscrollcommand=scrollbar.set)
536
+ if scrollbar:
537
+ canvas.configure(yscrollcommand=scrollbar_widget.set)
432
538
 
433
539
  canvas.grid(row=0, column=0, sticky="nsew")
434
- scrollbar.grid(row=0, column=1, sticky="ns")
540
+ if scrollbar:
541
+ scrollbar_widget.grid(row=0, column=1, sticky="ns")
435
542
 
436
543
  self.grid_rowconfigure(0, weight=1)
437
544
  self.grid_columnconfigure(0, weight=1)
438
- self.grid_columnconfigure(1, weight=0)
545
+ if scrollbar:
546
+ self.grid_columnconfigure(1, weight=0)
439
547
 
440
- style = ttk.Style()
441
- _ = set_dark_style(style, containers=[self], widgets=[canvas, scrollbar, self.scrollable_frame])
548
+ _ = set_dark_style(style, containers=[self], widgets=[canvas, self.scrollable_frame])
549
+ if scrollbar:
550
+ _ = set_dark_style(style, widgets=[scrollbar_widget])
551
+
552
+ def rounded_rectangle(self, canvas, x1, y1, x2, y2, radius=20, **kwargs):
553
+ points = [
554
+ x1 + radius, y1,
555
+ x2 - radius, y1,
556
+ x2 - radius, y1,
557
+ x2, y1,
558
+ x2, y1 + radius,
559
+ x2, y2 - radius,
560
+ x2, y2 - radius,
561
+ x2, y2,
562
+ x2 - radius, y2,
563
+ x1 + radius, y2,
564
+ x1 + radius, y2,
565
+ x1, y2,
566
+ x1, y2 - radius,
567
+ x1, y2 - radius,
568
+ x1, y1 + radius,
569
+ x1, y1 + radius,
570
+ x1, y1
571
+ ]
572
+ return canvas.create_polygon(points, **kwargs, smooth=True)
442
573
 
443
574
  class spacrLabel(tk.Frame):
444
- def __init__(self, parent, text="", font=None, style=None, align="right", **kwargs):
575
+ def __init__(self, parent, text="", font=None, style=None, align="right", height=None, **kwargs):
445
576
  valid_kwargs = {k: v for k, v in kwargs.items() if k not in ['foreground', 'background', 'font', 'anchor', 'justify', 'wraplength']}
446
577
  super().__init__(parent, **valid_kwargs)
447
578
 
448
579
  self.text = text
449
580
  self.align = align
450
- screen_height = self.winfo_screenheight()
451
- label_height = screen_height // 50
452
- label_width = label_height * 10
581
+
582
+ if height is None:
583
+ screen_height = self.winfo_screenheight()
584
+ label_height = screen_height // 50
585
+ label_width = label_height * 10
586
+ else:
587
+ label_height = height
588
+ label_width = label_height * 10
589
+
453
590
  style_out = set_dark_style(ttk.Style())
454
591
 
455
592
  self.canvas = tk.Canvas(self, width=label_width, height=label_height, highlightthickness=0, bg=style_out['bg_color'])
@@ -484,7 +621,7 @@ class spacrLabel(tk.Frame):
484
621
  self.canvas.itemconfig(self.label_text, text=text)
485
622
 
486
623
  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):
624
+ def __init__(self, parent, text="", command=None, font=None, icon_name=None, size=50, show_text=True, outline=False, animation=True, *args, **kwargs):
488
625
  super().__init__(parent, *args, **kwargs)
489
626
 
490
627
  self.text = text.capitalize() # Capitalize only the first letter of the text
@@ -493,6 +630,7 @@ class spacrButton(tk.Frame):
493
630
  self.size = size
494
631
  self.show_text = show_text
495
632
  self.outline = outline
633
+ self.animation = animation # Add animation attribute
496
634
 
497
635
  style_out = set_dark_style(ttk.Style())
498
636
 
@@ -559,13 +697,13 @@ class spacrButton(tk.Frame):
559
697
  def on_enter(self, event=None):
560
698
  self.canvas.itemconfig(self.button_bg, fill=self.active_color)
561
699
  self.update_description(event)
562
- if not self.is_zoomed_in:
700
+ if self.animation and not self.is_zoomed_in:
563
701
  self.animate_zoom(0.85) # Zoom in the icon to 85% of button size
564
702
 
565
703
  def on_leave(self, event=None):
566
704
  self.canvas.itemconfig(self.button_bg, fill=self.inactive_color)
567
705
  self.clear_description(event)
568
- if self.is_zoomed_in:
706
+ if self.animation and self.is_zoomed_in:
569
707
  self.animate_zoom(0.65) # Reset the icon size to 65% of button size
570
708
 
571
709
  def on_click(self, event=None):
@@ -1945,11 +2083,14 @@ class AnnotateApp:
1945
2083
  def shutdown(self):
1946
2084
  self.terminate = True
1947
2085
  self.update_queue.put(self.pending_updates.copy())
1948
- self.pending_updates.clear()
1949
- self.db_update_thread.join()
1950
- self.root.quit()
1951
- self.root.destroy()
1952
- print(f'Quit application')
2086
+ if not self.pending_updates:
2087
+ self.pending_updates.clear()
2088
+ self.db_update_thread.join()
2089
+ self.root.quit()
2090
+ self.root.destroy()
2091
+ print(f'Quit application')
2092
+ else:
2093
+ print('Waiting for pending updates to finish before quitting')
1953
2094
 
1954
2095
  def create_menu_bar(root):
1955
2096
  from .gui import initiate_root
@@ -1987,6 +2128,7 @@ def create_menu_bar(root):
1987
2128
 
1988
2129
  # Add a separator and an exit option
1989
2130
  app_menu.add_separator()
2131
+ app_menu.add_command(label="Help", command=lambda: webbrowser.open("https://readthedocs.org/projects/spacr/badge/?version=latest"))
1990
2132
  app_menu.add_command(label="Exit", command=root.quit)
1991
2133
 
1992
2134
  # Configure the menu for the root window