spacr 0.2.67__py3-none-any.whl → 0.2.81__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
@@ -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.
spacr/gui_utils.py CHANGED
@@ -574,32 +574,43 @@ def setup_frame(parent_frame):
574
574
  size_dict = set_element_size()
575
575
  style_out = set_dark_style(style)
576
576
 
577
- settings_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['settings_width'], bg=style_out['bg_color'])
578
- vertical_container = tk.PanedWindow(parent_frame, orient=tk.VERTICAL, width=size_dict['panel_width'], bg=style_out['bg_color'])
579
- horizontal_container = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], width=size_dict['panel_width'], bg=style_out['bg_color'])
577
+ # Configure the main layout using PanedWindow
578
+ main_paned = tk.PanedWindow(parent_frame, orient=tk.HORIZONTAL, bg=style_out['bg_color'], bd=0, relief='flat')
579
+ main_paned.grid(row=0, column=0, sticky="nsew")
580
580
 
581
+ # Allow the main_paned to expand and fill the window
581
582
  parent_frame.grid_rowconfigure(0, weight=1)
582
- parent_frame.grid_rowconfigure(1, weight=0)
583
- parent_frame.grid_columnconfigure(0, weight=0)
584
- parent_frame.grid_columnconfigure(1, weight=1)
583
+ parent_frame.grid_columnconfigure(0, weight=1)
585
584
 
586
- settings_container.grid(row=0, column=0, rowspan=2, sticky="nsew")
587
- vertical_container.grid(row=0, column=1, sticky="nsew")
588
- horizontal_container.grid(row=1, column=1, sticky="ew")
585
+ # Create the settings container on the left
586
+ settings_container = tk.PanedWindow(main_paned, orient=tk.VERTICAL, width=size_dict['settings_width'], bg=style_out['bg_color'], bd=0, relief='flat')
587
+ main_paned.add(settings_container, minsize=100) # Allow resizing with a minimum size
589
588
 
590
- # Ensure settings_container maintains its width
591
- settings_container.grid_propagate(False)
592
- settings_container.update_idletasks()
589
+ # Create a right container frame to hold vertical and horizontal containers
590
+ right_frame = tk.Frame(main_paned, bg=style_out['bg_color'], bd=0, highlightthickness=0, relief='flat')
591
+ main_paned.add(right_frame, stretch="always")
593
592
 
594
- tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).grid(row=0, column=0, sticky="ew")
593
+ # Configure the right_frame grid layout
594
+ right_frame.grid_rowconfigure(0, weight=1) # Vertical container expands
595
+ right_frame.grid_rowconfigure(1, weight=0) # Horizontal container at bottom
596
+ right_frame.grid_columnconfigure(0, weight=1)
595
597
 
596
- set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container])
597
-
598
- #size = style_out['font_size'] - 2
599
- #set_default_font(parent_frame, font_name=style_out['font_family'], size=size)
598
+ # Inside right_frame, add vertical_container at the top
599
+ vertical_container = tk.PanedWindow(right_frame, orient=tk.VERTICAL, bg=style_out['bg_color'], bd=0, relief='flat')
600
+ vertical_container.grid(row=0, column=0, sticky="nsew")
601
+
602
+ # Add horizontal_container aligned with the bottom of settings_container
603
+ horizontal_container = tk.PanedWindow(right_frame, orient=tk.HORIZONTAL, height=size_dict['panel_height'], bg=style_out['bg_color'], bd=0, relief='flat')
604
+ horizontal_container.grid(row=1, column=0, sticky="ew")
605
+
606
+ # Example content for settings_container
607
+ tk.Label(settings_container, text="Settings Container", bg=style_out['bg_color']).pack(fill=tk.BOTH, expand=True)
608
+
609
+ set_dark_style(style, parent_frame, [settings_container, vertical_container, horizontal_container, main_paned])
600
610
 
601
611
  return parent_frame, vertical_container, horizontal_container, settings_container
602
612
 
613
+
603
614
  def download_hug_dataset(q, vars_dict):
604
615
  dataset_repo_id = "einarolafsson/toxo_mito"
605
616
  settings_repo_id = "einarolafsson/spacr_settings"