diff --git a/Tagger.py b/Tagger.py index 4b22630..220772b 100644 --- a/Tagger.py +++ b/Tagger.py @@ -5,6 +5,7 @@ from tkinter import ttk from src.gui.gui import App from src.core.file_handler import list_files from src.core.SQL_handler import SQLite3FileHandler +from pathlib import Path class State(): def __init__(self) -> None: diff --git a/src/gui/gui.py b/src/gui/gui.py index 41163c0..6e453d6 100644 --- a/src/gui/gui.py +++ b/src/gui/gui.py @@ -1,113 +1,199 @@ -# Module header +# src/gui/gui.py +import os import sys - -if __name__ == "__main__": - sys.exit("This module is not intended to be executed as the main program.") - -# Imports +import subprocess import tkinter as tk -from tkinter import ttk +from tkinter import ttk, simpledialog, messagebox + from src.core.image_handler import load_icon -class App(): - def __init__(self): - pass +class App: + def __init__(self): + 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 + + # ================================================== + # MAIN GUI + # ================================================== def main(self) -> None: root = tk.Tk() - root.title("Ukázka rozložení") - root.geometry("800x600") + root.title("Tag manager") + root.geometry("900x600") + self.root = root - # ==== MENU ==== + # ---- 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="Otevřít") - file_menu.add_command(label="Ukončit", command=root.quit) - menu_bar.add_cascade(label="Soubor", menu=file_menu) + file_menu.add_command(label="Exit", command=root.quit) + menu_bar.add_cascade(label="File", menu=file_menu) - # ==== HLAVNÍ RÁM ==== 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) - # ==== Ikony ==== - unchecked = load_icon("src/resources/images/32/32_unchecked.png") - checked = load_icon("src/resources/images/32/32_checked.png") - icons = {"unchecked": unchecked, "checked": checked} + # ---- 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) - # ==== VLEVO: STROM ==== - tree = ttk.Treeview(main_frame) - tree.grid(row=0, column=0, sticky="nsew", padx=2, pady=2) + # ---- 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) - # Slovník pro stavy checkboxů - states = {} + # ---- Status bar + self.status_bar = tk.Label(root, text="Připraven", anchor="w", relief="sunken") + self.status_bar.pack(side="bottom", fill="x") - # Funkce pro přepnutí checkboxu - def toggle(event) -> None: - region = tree.identify("region", event.x, event.y) - if region == "tree": - item_id = tree.identify_row(event.y) - if item_id: - states[item_id] = not states[item_id] - tree.item(item_id, image=icons["checked"] if states[item_id] else icons["unchecked"]) + # ---- 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) - tree.bind("", toggle) + 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) - # Přidání uzlů se stavem - root_node = tree.insert("", "end", text="Root", image=icons["unchecked"]) - states[root_node] = False + # ---- 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) - child1 = tree.insert(root_node, "end", text="Child 1", image=icons["unchecked"]) - states[child1] = False - child2 = tree.insert(root_node, "end", text="Child 2", image=icons["unchecked"]) - states[child2] = False + self.refresh_list() + root.mainloop() - tree.item(root_node, open=True) + # ================================================== + # 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 "" - # ==== VPRAVO: SEZNAM ==== - listbox = tk.Listbox(main_frame) - listbox.grid(row=0, column=1, sticky="nsew", padx=2, pady=2) + def get_checked_full_tags(self): + return {self.build_full_tag(i) for i, v in self.states.items() if v} - for i in range(1, 21): - listbox.insert("end", f"Položka {i}") + # ================================================== + # 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() - # ==== STAVOVÝ ŘÁDEK ==== - status_bar = tk.Label(root, text="Připraven", anchor="w", relief="sunken") - status_bar.pack(side="bottom", fill="x") + 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) - # ==== KONTEXTOVÁ MENU ==== - tree_menu = tk.Menu(root, tearoff=0) - tree_menu.add_command( - label="Akce na stromu", - command=lambda: status_bar.config(text="Klikl jsi na strom") - ) + 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)}") - list_menu = tk.Menu(root, tearoff=0) - list_menu.add_command( - label="Akce na seznamu", - command=lambda: status_bar.config(text="Klikl jsi na seznam") - ) + 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}") - # ==== HANDLERY ==== - def tree_right_click(event): - item_id = tree.identify_row(event.y) - if item_id: # klik na uzel - tree.selection_set(item_id) - tree_menu.tk_popup(event.x_root, event.y_root) + # ================================================== + # LIST EVENTS + # ================================================== + def on_list_double(self, event): + index = self.listbox.curselection() + if index: + self.open_file(self.listbox.get(index[0])) - def list_right_click(event): - index = listbox.nearest(event.y) - if index >= 0: # klik na položku - listbox.selection_clear(0, "end") - listbox.selection_set(index) - list_menu.tk_popup(event.x_root, event.y_root) + 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) - tree.bind("", tree_right_click) - listbox.bind("", list_right_click) + def list_open_file(self): + idx = self.selected_list_index_for_context + if idx is not None: + self.open_file(self.listbox.get(idx)) - root.mainloop() \ No newline at end of file + 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}")