GUI rework
This commit is contained in:
244
src/gui/gui.py
244
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("<Button-1>", self.on_tree_left_click)
|
||||
self.tree.bind("<Button-3>", 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("<Double-1>", self.on_list_double)
|
||||
self.listbox.bind("<Button-3>", 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("<Button-1>", 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("<Button-3>", tree_right_click)
|
||||
listbox.bind("<Button-3>", 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()
|
||||
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}")
|
||||
|
||||
Reference in New Issue
Block a user