Source code for simba.ui.pop_ups.about_simba_pop_up

import os
import webbrowser
from tkinter import *
from typing import Union

import cv2
from PIL import Image, ImageDraw, ImageFilter, ImageTk

import simba
from simba.ui.tkinter_functions import SimBALabel
from simba.utils.enums import OS, Formats, Links, Paths
from simba.utils.lookups import rgb_to_hex
from simba.utils.read_write import get_video_meta_data

LINKS = [
         ("GitHub", "https://github.com/sgoldenlab/simba", "github_2"),
         ("API", "https://simba-uw-tf-dev.readthedocs.io/en/latest/api.html", "documentation_large"),
         ("Gitter", "https://app.gitter.im/#/room/#sgoldenlab_simba:gitter.im", "gitter_large"),
         ("bioRxiv", "https://www.biorxiv.org/content/10.1101/2020.04.19.049452v1", "documentation_large"),
         ("Nature Neuroscience", "https://www.nature.com/articles/s41593-024-01649-9", "pdf_large"),
         ("OSF data buckets", "https://osf.io/user/mutws", "osf_large"),
         ("PyPI", "https://pypi.org/project/simba-uw-tf-dev/", "rocket")
         ]

DEVELOPER_URL = Links.SIMON_WEBSITE.value
DEVELOPER_IMG = os.path.join(os.path.dirname(simba.__file__), Paths.SIMON_SMALL_IMG.value)
VERSION_TXT = f"SimBA v{OS.SIMBA_VERSION.value}" if OS.SIMBA_VERSION.value else "SimBA"
LANDING_MOVIE_PATH = os.path.join(os.path.dirname(simba.__file__), Paths.LANDING_MOVIE.value)

# RGB color definitions
LINK_COLOR_RGB = (91, 163, 245)
LINK_HOVER_COLOR_RGB = (123, 181, 247)
BG_COLOR_RGB = (10, 10, 10)
SEPARATOR_COLOR_RGB = (42, 42, 42)
UNDERLINE_COLOR_RGB = (58, 58, 58)
BLACK_RGB = (0, 0, 0)
WHITE_RGB = (255, 255, 255)

LINK_COLOR = rgb_to_hex(LINK_COLOR_RGB)
LINK_HOVER_COLOR = rgb_to_hex(LINK_HOVER_COLOR_RGB)
BG_COLOR = rgb_to_hex(BG_COLOR_RGB)
SEPARATOR_COLOR = rgb_to_hex(SEPARATOR_COLOR_RGB)
UNDERLINE_COLOR = rgb_to_hex(UNDERLINE_COLOR_RGB)
BLACK = rgb_to_hex(BLACK_RGB)
WHITE = rgb_to_hex(WHITE_RGB)


[docs]class AboutSimBAPopUp: def __init__(self, video_path: Union[str, os.PathLike] = LANDING_MOVIE_PATH, title: str = "ABOUT SIMBA"): self.video_meta = get_video_meta_data(video_path=video_path, fps_as_int=False) video_width, video_height = int(self.video_meta['width']), int(self.video_meta['height']) window_height = video_height * 2 self.root = Toplevel() icon_path = os.path.join(os.path.dirname(simba.__file__), "assets", "icons", "SimBA_logo_3_small.png") try: icon_img = PhotoImage(file=icon_path) self.root.iconphoto(False, icon_img) self.root.iconimage = icon_img except Exception: pass self.root.title(title) self.root.geometry(f"{video_width}x{window_height}") self.root.resizable(False, False) self.root.attributes('-topmost', True) self.root.grid_rowconfigure(0, minsize=video_height) self.root.grid_rowconfigure(1, minsize=video_height) self.root.grid_columnconfigure(0, weight=1) self.video_frame = Frame(self.root, bg=BLACK, height=video_height) self.video_frame.grid(row=0, column=0, sticky="nsew") self.video_frame.grid_propagate(False) self.video_frame.grid_rowconfigure(0, weight=1) self.video_frame.grid_columnconfigure(0, weight=1) self.bottom_frame = Frame(self.root, bg=BG_COLOR, height=video_height) self.bottom_frame.grid(row=1, column=0, sticky="nsew") self.bottom_frame.grid_propagate(False) self.bottom_frame.grid_rowconfigure(0, weight=1) self.bottom_frame.grid_columnconfigure(0, weight=1) self.bottom_frame.grid_columnconfigure(1, weight=0) # Separator column self.bottom_frame.grid_columnconfigure(2, weight=1) left_container = Frame(self.bottom_frame, bg=BG_COLOR) left_container.grid(row=0, column=0, sticky="nsew", padx=(40, 20), pady=25) left_container.grid_rowconfigure(0, weight=1) left_container.grid_columnconfigure(0, weight=1) separator = Frame(self.bottom_frame, bg=SEPARATOR_COLOR, width=1) separator.grid(row=0, column=1, sticky="ns", pady=25) self.right_container = Frame(self.bottom_frame, bg=BG_COLOR) self.right_container.grid(row=0, column=2, sticky="nsew", padx=(20, 40), pady=25) self.right_container.grid_rowconfigure(0, weight=1) self.right_container.grid_columnconfigure(0, weight=1) header_label = SimBALabel(parent=left_container, txt="R E S O U R C E S", txt_clr=WHITE, bg_clr=BG_COLOR, font=Formats.FONT_REGULAR.value) header_label.pack(pady=(0, 5)) header_underline = Frame(left_container, bg=UNDERLINE_COLOR, height=1) header_underline.pack(fill=X, pady=(0, 5)) developer_header = SimBALabel(parent=self.right_container, txt="D E V E L O P E R", txt_clr=WHITE, bg_clr=BG_COLOR, font=Formats.FONT_REGULAR.value) developer_header.pack(pady=(0, 5)) dev_underline = Frame(self.right_container, bg=UNDERLINE_COLOR, height=1) dev_underline.pack(fill=X, pady=(0, 5)) def add_rounded_corners_with_shadow(img, radius=10, shadow_offset=5): shadow = Image.new('RGBA', (img.width + shadow_offset * 2, img.height + shadow_offset * 2), (0, 0, 0, 0)) shadow_draw = ImageDraw.Draw(shadow) try: shadow_draw.rounded_rectangle( [(shadow_offset, shadow_offset), (img.width + shadow_offset, img.height + shadow_offset)], radius=radius, fill=(0, 0, 0, 100)) except AttributeError: width, height = img.size shadow_draw.rectangle( [shadow_offset + radius, shadow_offset, width + shadow_offset - radius, height + shadow_offset], fill=(0, 0, 0, 100)) shadow_draw.rectangle( [shadow_offset, shadow_offset + radius, width + shadow_offset, height + shadow_offset - radius], fill=(0, 0, 0, 100)) shadow_draw.ellipse( [shadow_offset, shadow_offset, shadow_offset + radius * 2, shadow_offset + radius * 2], fill=(0, 0, 0, 100)) shadow_draw.ellipse([width + shadow_offset - radius * 2, shadow_offset, width + shadow_offset, shadow_offset + radius * 2], fill=(0, 0, 0, 100)) shadow_draw.ellipse([shadow_offset, height + shadow_offset - radius * 2, shadow_offset + radius * 2, height + shadow_offset], fill=(0, 0, 0, 100)) shadow_draw.ellipse( [width + shadow_offset - radius * 2, height + shadow_offset - radius * 2, width + shadow_offset, height + shadow_offset], fill=(0, 0, 0, 100)) shadow = shadow.filter(ImageFilter.GaussianBlur(radius=8)) mask = Image.new('L', img.size, 0) mask_draw = ImageDraw.Draw(mask) try: mask_draw.rounded_rectangle([(0, 0), img.size], radius=radius, fill=255) except AttributeError: width, height = img.size mask_draw.rectangle([radius, 0, width - radius, height], fill=255) mask_draw.rectangle([0, radius, width, height - radius], fill=255) mask_draw.ellipse([0, 0, radius * 2, radius * 2], fill=255) mask_draw.ellipse([width - radius * 2, 0, width, radius * 2], fill=255) mask_draw.ellipse([0, height - radius * 2, radius * 2, height], fill=255) mask_draw.ellipse([width - radius * 2, height - radius * 2, width, height], fill=255) output = Image.new('RGBA', img.size, (0, 0, 0, 0)) output.paste(img, (0, 0), mask) final = Image.new('RGBA', shadow.size, (0, 0, 0, 0)) final.paste(shadow, (0, 0)) final.paste(output, (shadow_offset, shadow_offset), output) return final dev_image_container = Frame(self.right_container, bg=BG_COLOR) dev_image_container.pack(pady=(0, 10)) if os.path.exists(DEVELOPER_IMG): try: dev_image = Image.open(DEVELOPER_IMG) if dev_image.mode != 'RGBA': dev_image = dev_image.convert('RGBA') max_width = 180 aspect_ratio = dev_image.width / dev_image.height new_width = min(max_width, dev_image.width) new_height = int(new_width / aspect_ratio) dev_image = dev_image.resize((new_width, new_height), Image.LANCZOS) dev_image = add_rounded_corners_with_shadow(dev_image, radius=20, shadow_offset=20) dev_photo = ImageTk.PhotoImage(dev_image) dev_image_label = Label(dev_image_container, image=dev_photo, bg=BG_COLOR, cursor="hand2") dev_image_label.image = dev_photo dev_image_label.pack() dev_image_label.bind("<Button-1>", lambda e, u=DEVELOPER_URL: webbrowser.open(u)) except Exception as r: print(r.args) pass developer_link_frame = Frame(self.right_container, bg=BG_COLOR, cursor="hand2") developer_link_frame.pack(pady=(0, 0)) developer_link = SimBALabel(parent=developer_link_frame, txt='SIMON NILSSON', txt_clr=LINK_COLOR, font=Formats.FONT_HEADER.value, bg_clr=BG_COLOR, hover_fg_clr=WHITE, hover_font=Formats.FONT_LARGE_BOLD.value, cursor="hand2") developer_link.pack() def dev_on_enter(e): developer_link.config(fg=LINK_HOVER_COLOR) if not hasattr(developer_link, 'underline_widget'): underline = Frame(developer_link_frame, bg=LINK_HOVER_COLOR, height=2) underline.pack(fill=X, pady=(3, 0)) developer_link.underline_widget = underline def dev_on_leave(e): developer_link.config(fg=LINK_COLOR) if hasattr(developer_link, 'underline_widget'): developer_link.underline_widget.destroy() del developer_link.underline_widget developer_link.bind("<Button-1>", lambda e, u=DEVELOPER_URL: webbrowser.open(u)) developer_link.bind("<Enter>", dev_on_enter) developer_link.bind("<Leave>", dev_on_leave) developer_link_frame.bind("<Button-1>", lambda e, u=DEVELOPER_URL: webbrowser.open(u)) developer_link_frame.bind("<Enter>", dev_on_enter) developer_link_frame.bind("<Leave>", dev_on_leave) for widget in developer_link_frame.winfo_children(): widget.bind("<Button-1>", lambda e, u=DEVELOPER_URL: webbrowser.open(u)) widget.bind("<Enter>", dev_on_enter) widget.bind("<Leave>", dev_on_leave) links_container = Frame(left_container, bg=BG_COLOR) links_container.pack(fill=BOTH, expand=True) for i, (text, url, icon) in enumerate(LINKS): link_frame = Frame(links_container, bg=BG_COLOR, cursor="hand2") link_frame.pack(pady=2) icon_label = SimBALabel(parent=link_frame, txt='', txt_clr=LINK_COLOR, bg_clr=BG_COLOR, font=Formats.FONT_REGULAR.value, hover_font=Formats.FONT_LARGE_ITALICS.value, cursor="hand2", link=url, img=icon, compound='left') icon_label.pack(side=LEFT, padx=(0, 3)) link_label = SimBALabel(parent=link_frame, txt=text, txt_clr=LINK_COLOR, font=Formats.FONT_REGULAR.value, bg_clr=BG_COLOR, hover_font=Formats.FONT_LARGE_ITALICS.value, cursor="hand2", link=url) link_label.pack(side=LEFT) self.video_canvas = Canvas(self.video_frame, bg=BLACK, highlightthickness=0) self.video_canvas.grid(row=0, column=0, sticky="nsew") version_label = SimBALabel(parent=self.video_frame, txt=VERSION_TXT, txt_clr=WHITE, bg_clr=BLACK, font=Formats.FONT_HEADER.value) version_label.place(relx=0.5, rely=0.90, anchor="center") self.cap = cv2.VideoCapture(video_path) self.frame_delay = int(1000 / self.video_meta['fps']) self.video_frame_width, self.video_frame_height = video_width, video_height self.root.bind("<Escape>", lambda e: self.on_closing()) self.root.focus_set() self.root.update_idletasks() self.play_video() self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
[docs] def play_video(self): ret, frame = self.cap.read() if ret: frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) self.video_canvas.update_idletasks() canvas_width = self.video_canvas.winfo_width() canvas_height = self.video_canvas.winfo_height() if canvas_width <= 1 or canvas_height <= 1: canvas_width, canvas_height = self.video_frame_width, self.video_frame_height frame_height, frame_width = frame_rgb.shape[:2] aspect_ratio = frame_width / frame_height if canvas_width / canvas_height > aspect_ratio: new_height = canvas_height new_width = int(new_height * aspect_ratio) else: new_width = canvas_width new_height = int(new_width / aspect_ratio) frame_resized = cv2.resize(frame_rgb, (new_width, new_height)) pil_image = Image.fromarray(frame_resized) photo = ImageTk.PhotoImage(image=pil_image) self.video_canvas.delete("all") x = (canvas_width - new_width) // 2 y = (canvas_height - new_height) // 2 self.video_canvas.create_image(x, y, anchor=NW, image=photo) self.video_canvas.image = photo self.root.after(self.frame_delay, self.play_video) else: self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0) self.root.after(self.frame_delay, self.play_video)
[docs] def on_closing(self, event=None): if hasattr(self, 'cap'): self.cap.release() self.root.destroy()
# # if __name__ == "__main__": # root = Tk() # root.withdraw() # AboutSimBAPopUp() # root.mainloop()