import customtkinter as ctk
import sys
import os
[docs]
class Outliner(ctk.CTkFrame):
"""Custom class that generates an outliner with clickable index buttons that navigates to the linked image."""
def __init__(self, *args, master,
width: int = 100,
height: int = 30,
fg_color="#383838",
corner_radius=0,
parent_app,
**kwargs):
super().__init__(*args, master=master, width=width,
height=height, fg_color=fg_color,
corner_radius=corner_radius, **kwargs)
self.app = parent_app
self.image_data = self.app.image_data
self.list_is_sequence = True
self.outliner_font = ctk.CTkFont(family="Arial", size=13, weight="bold")
self.pack_propagate(False)
self.outliner_label_frame = ctk.CTkFrame(master=self, fg_color="#153463",
height=height)
self.outliner_label_frame.columnconfigure((0, 1, 2), weight=1)
self.outliner_label_frame.rowconfigure(0, weight=1)
self.outliner_label_frame.pack(fill="x", side="top")
self.sequence_switch = ctk.CTkSwitch(self.outliner_label_frame, text="", width=0,
button_color="#199133",
command=self.toggle_sequence_naming, switch_height=15, )
self.sequence_switch.grid(column=0, row=0)
self.outliner_label = ctk.CTkLabel(master=self.outliner_label_frame, text="Outliner",
text_color="#D6D5D5", font=self.outliner_font)
self.outliner_label.grid(column=1, row=0)
self.file_index_frame = ctk.CTkScrollableFrame(master=self, fg_color="#383838",
corner_radius=0)
if sys.platform.startswith('linux'):
self.file_index_frame.bind("<Button-4>", self.outline_scroll_handler, "+")
self.file_index_frame.bind("<Button-5>", self.outline_scroll_handler, "+")
self.file_index_frame._scrollbar.bind("<Button-4>", self.outline_scroll_handler, "+")
self.file_index_frame._scrollbar.bind("<Button-5>", self.outline_scroll_handler, "+")
# bind("<Button-5>", self.mouse_scroll_handler, "+"
self.file_index_frame.pack(expand=True, fill="both", side="top")
self.file_index_list = []
self.create_file_index()
# Setting the increment to 1, (didn't have to if it was just windows. :/ )
self.file_index_frame._parent_canvas.configure(yscrollincrement=1)
[docs]
def create_file_index(self):
"""
Loops through the loaded images, creates and assigns an outliner button element for each image and hardcodes the command with the index as parameter.
Returns:
None
"""
files_indexed = 0
self.index_queue_col = "#1F57AB"
self.index_queue_txt_col = "#EBEBEB"
self.index_removed_col = "#585A5B"
self.index_removed_txt_col = "#A1A1A1"
self.index_queue_comment_col = "#279258"
self.index_queue_comment_txt_col = "white"
self.index_queue_comment_remove_col = "#496F5A"
# Change the fg color of the file_load progress bar to make it visible.
self.app.file_load_window.file_load_progressbar.configure(fg_color="#3D3D3D") # dark grey
for index in self.image_data:
# button_text = f"{index + 1}# {self.image_data[index]['sequence_code']}"
# Start with actual filenames.
button_text = os.path.basename(self.image_data[index]['file'])
button = ctk.CTkButton(master=self.file_index_frame, corner_radius=0, anchor="w", border_spacing=3,
text=button_text, height=27, width=600, text_color_disabled="#A1A1A1")
files_indexed += 1
if self.app.protocol == "project":
has_graphic_elements = self.app.loaded_graphics_data[index]
# No graphical elements but queued.
if not has_graphic_elements and self.image_data[index]["in_queue"]:
index_color = self.index_queue_col
text_color = self.index_queue_txt_col
# No graphical elements but not queued.
elif not has_graphic_elements and not self.image_data[index]["in_queue"]:
index_color = self.index_removed_col
text_color = self.index_removed_txt_col
# Has graphical elements and is queued.
elif has_graphic_elements and self.image_data[index]["in_queue"]:
index_color = self.index_queue_comment_col
text_color = self.index_queue_comment_txt_col
# Has graphical elements but not queued.
elif has_graphic_elements and not self.image_data[index]["in_queue"]:
index_color = self.index_queue_comment_remove_col
text_color = self.index_removed_txt_col
# 0 to 0.5 range here, because other .5 reserved for the progress of redrawing the graphic elements.
progress = files_indexed / (len(self.image_data) * 2)
self.app.file_load_window.update_file_window_progressbar(progress)
else:
index_color = self.index_queue_col
text_color = self.index_queue_txt_col
progress = files_indexed / len(self.image_data) # 0 to 1 range
self.app.file_load_window.update_file_window_progressbar(progress)
button.configure(text_color=text_color, fg_color=index_color, font=self.outliner_font)
# binding call functions as parameters for each instance of the index button.
button.configure(command=lambda id=index,: self.app.fetch_from_outline(index=id))
button.bind("<Button-3>", command=lambda event, id=index: self.app.on_right_click(index=id))
button.bind("<Enter>", command=lambda event, id=index: self.app.on_mouse_enter(event=event, index=id))
button.bind("<Leave>", command=lambda event, id=index: self.app.on_mouse_leave(event=event, index=id))
if sys.platform.startswith('linux'):
button.bind("<Button-4>", self.outline_scroll_handler, "+")
button.bind("<Button-5>", self.outline_scroll_handler, "+")
button.grid(row=index, sticky="we", pady=2)
# Stores the created index buttons to a list.
self.file_index_list.append(button)
[docs]
def toggle_sequence_naming(self):
"""
When Called, the outliner changes the text of the indexes from file name to sequence codes.
Returns:
False
"""
self.image_data = self.image_data
if self.sequence_switch.get() == 1:
self.sequence_switch.configure(button_color="#19CC40")
for index, button in enumerate(self.file_index_list):
button.configure(text=f"{index + 1}# {self.image_data[index]['sequence_code']}")
else:
self.sequence_switch.configure(button_color="#199133")
for index, button in enumerate(self.file_index_list):
button.configure(text=os.path.basename(self.image_data[index]['file']))