# src/gui/gui.py import os import sys import subprocess import tkinter as tk from tkinter import ttk, simpledialog, messagebox from src.core.image_handler import load_icon from src.core.file_manager import FileManager class App: def __init__(self, filehandler: FileManager): self.states = {} # Tree states (checkbox on/off) self.files = {} # Path -> set(tags), napojíš na SQLite3FileHandler self.selected_tree_item_for_context = None self.selected_list_index_for_context = None self.filehandler = filehandler # ================================================== # MAIN GUI # ================================================== def main(self) -> None: root = tk.Tk() root.title("Tagger") root.geometry("900x600") self.root = root # ---- Ikony (už připravené) unchecked = load_icon("src/resources/images/32/32_unchecked.png") checked = load_icon("src/resources/images/32/32_checked.png") self.icons = {"unchecked": unchecked, "checked": checked} root.unchecked_img = unchecked root.checked_img = checked # ---- Layout menu_bar = tk.Menu(root) root.config(menu=menu_bar) file_menu = tk.Menu(menu_bar, tearoff=0) file_menu.add_command(label="Exit", command=root.quit) menu_bar.add_cascade(label="File", menu=file_menu) main_frame = tk.Frame(root) main_frame.pack(fill="both", expand=True) main_frame.columnconfigure(0, weight=1) main_frame.columnconfigure(1, weight=2) main_frame.rowconfigure(0, weight=1) # ---- Tree (left) self.tree = ttk.Treeview(main_frame) self.tree.grid(row=0, column=0, sticky="nsew", padx=4, pady=4) self.tree.bind("", self.on_tree_left_click) self.tree.bind("", self.on_tree_right_click) # ---- Listbox (right) self.listbox = tk.Listbox(main_frame) self.listbox.grid(row=0, column=1, sticky="nsew", padx=4, pady=4) self.listbox.bind("", self.on_list_double) self.listbox.bind("", self.on_list_right_click) # ---- Status bar self.status_bar = tk.Label(root, text="Připraven", anchor="w", relief="sunken") self.status_bar.pack(side="bottom", fill="x") # ---- Context menus self.tree_menu = tk.Menu(root, tearoff=0) self.tree_menu.add_command(label="Nový tag zde", command=self.tree_add_tag) self.tree_menu.add_command(label="Smazat tag", command=self.tree_delete_tag) self.list_menu = tk.Menu(root, tearoff=0) self.list_menu.add_command(label="Otevřít soubor", command=self.list_open_file) self.list_menu.add_command(label="Smazat z indexu", command=self.list_remove_file) # ---- Sample root node root_id = self.tree.insert("", "end", text="Root", image=self.icons["unchecked"]) self.states[root_id] = False self.tree.item(root_id, open=True) self.refresh_list() root.mainloop() # ================================================== # TREE HELPERS # ================================================== def build_full_tag(self, item_id): parts = [] cur = item_id while cur: parts.append(self.tree.item(cur, "text")) cur = self.tree.parent(cur) parts.reverse() return "/".join(parts) if parts else "" def get_checked_full_tags(self): return {self.build_full_tag(i) for i, v in self.states.items() if v} # ================================================== # TREE EVENTS # ================================================== def on_tree_left_click(self, event): region = self.tree.identify("region", event.x, event.y) if region in ("tree", "icon"): item_id = self.tree.identify_row(event.y) if item_id: self.states[item_id] = not self.states.get(item_id, False) self.tree.item(item_id, image=self.icons["checked"] if self.states[item_id] else self.icons["unchecked"]) self.status_bar.config(text=f"Tag {'checked' if self.states[item_id] else 'unchecked'}: {self.build_full_tag(item_id)}") self.refresh_list() def on_tree_right_click(self, event): item_id = self.tree.identify_row(event.y) if item_id: self.selected_tree_item_for_context = item_id self.tree.selection_set(item_id) self.tree_menu.tk_popup(event.x_root, event.y_root) else: # klik do prázdna = nabídka top-level tagu self.selected_tree_item_for_context = None menu = tk.Menu(self.root, tearoff=0) menu.add_command(label="Nový top-level tag", command=lambda: self.tree_add_tag(background=True)) menu.tk_popup(event.x_root, event.y_root) def tree_add_tag(self, background=False): name = simpledialog.askstring("Nový tag", "Název tagu:") if not name: return parent = self.selected_tree_item_for_context if not background else "" new_id = self.tree.insert(parent, "end", text=name, image=self.icons["unchecked"]) self.states[new_id] = False self.status_bar.config(text=f"Vytvořen tag: {self.build_full_tag(new_id)}") def tree_delete_tag(self): item = self.selected_tree_item_for_context if not item: return full = self.build_full_tag(item) ans = messagebox.askyesno("Smazat tag", f"Opravdu chcete smazat '{full}'?") if not ans: return self.tree.delete(item) self.states.pop(item, None) self.refresh_list() self.status_bar.config(text=f"Smazán tag: {full}") # ================================================== # LIST EVENTS # ================================================== def on_list_double(self, event): index = self.listbox.curselection() if index: self.open_file(self.listbox.get(index[0])) def on_list_right_click(self, event): idx = self.listbox.nearest(event.y) if idx is None: return self.selected_list_index_for_context = idx self.listbox.selection_clear(0, "end") self.listbox.selection_set(idx) self.list_menu.tk_popup(event.x_root, event.y_root) def list_open_file(self): idx = self.selected_list_index_for_context if idx is not None: self.open_file(self.listbox.get(idx)) def list_remove_file(self): idx = self.selected_list_index_for_context if idx is not None: path = self.listbox.get(idx) ans = messagebox.askyesno("Smazat z indexu", f"Odstranit '{path}' z indexu?") if ans and path in self.files: del self.files[path] self.refresh_list() self.status_bar.config(text=f"Odstraněno z indexu: {path}") # ================================================== # FILE LIST REFRESH # ================================================== def refresh_list(self): checked = self.get_checked_full_tags() self.listbox.delete(0, "end") for path, tags in self.files.items(): if not checked or checked.issubset(tags): self.listbox.insert("end", path) self.status_bar.config(text=f"Zobrazeno {self.listbox.size()} položek") # ================================================== # OPEN FILE # ================================================== def open_file(self, path): try: if sys.platform.startswith("win"): os.startfile(path) elif sys.platform.startswith("darwin"): subprocess.call(["open", path]) else: subprocess.call(["xdg-open", path]) self.status_bar.config(text=f"Otevírám: {path}") except Exception as e: messagebox.showerror("Chyba", f"Nelze otevřít {path}: {e}")