diff --git a/Tagger.py b/Tagger.py index c093cf1..4b22630 100644 --- a/Tagger.py +++ b/Tagger.py @@ -2,10 +2,15 @@ import tkinter as tk from tkinter import ttk -from src.gui.gui import main +from src.gui.gui import App from src.core.file_handler import list_files +from src.core.SQL_handler import SQLite3FileHandler -# Functions -main() -a = list_files("./src/core") -print(a) \ No newline at end of file +class State(): + def __init__(self) -> None: + self.app = App() + self.sql_handler = SQLite3FileHandler(Path("Test.db3")) + + +STATE = State() +STATE.app.main() \ No newline at end of file diff --git a/src/core/SQL_handler.py b/src/core/SQL_handler.py index e69de29..de219eb 100644 --- a/src/core/SQL_handler.py +++ b/src/core/SQL_handler.py @@ -0,0 +1,125 @@ +import sqlite3 +from pathlib import Path +from typing import Any, List, Tuple + + +class SQLite3FileHandler: + def __init__(self, db_path: Path): + self.db_path = Path(db_path) + need_init = not self.db_path.exists() # zjistíme, jestli soubor existuje před connect + self.conn = sqlite3.connect(str(self.db_path)) + self.cursor = self.conn.cursor() + + if need_init: + self.create_tables() + + + def create_tables(self) -> None: + """ + Vytvoří tabulky (bez chyby pokud už existují). + """ + script = """ + CREATE TABLE IF NOT EXISTS TAGS ( + ID INTEGER PRIMARY KEY, + TAG_NAME TEXT NOT NULL UNIQUE + ); + + CREATE TABLE IF NOT EXISTS FILES ( + ID INTEGER PRIMARY KEY, + PATH TEXT NOT NULL, + FILE_NAME TEXT NOT NULL, + HASH TEXT NOT NULL + ); + + CREATE TABLE IF NOT EXISTS RELATIONS ( + ID INTEGER PRIMARY KEY, + IDS INTEGER NOT NULL, + TYPE TEXT NOT NULL, + IDT INTEGER NOT NULL + ); + + CREATE TABLE IF NOT EXISTS TAG_CATEGORY ( + ID INTEGER PRIMARY KEY, + TAG_CAT_NAME TEXT NOT NULL UNIQUE + ); + """ + # executescript umí více příkazů najednou + self.conn.executescript(script) + self.conn.commit() + + def insert_tag(self, tag_name: str) -> None: + query = "INSERT INTO TAGS (TAG_NAME) VALUES (?)" + self.cursor.execute(query, (tag_name,)) + self.conn.commit() + + def delete_tag(self, tag_name: str) -> None: + query = "DELETE FROM TAGS WHERE TAG_NAME = ?" + self.cursor.execute(query, (tag_name,)) + self.conn.commit() + + def insert_category(self, tag_cat_name: str) -> None: + query = "INSERT INTO TAG_CATEGORY (TAG_CAT_NAME) VALUES (?)" + self.cursor.execute(query, (tag_cat_name,)) + self.conn.commit() + + def delete_category(self, tag_cat_name: str) -> None: + """ + Smaže kategorii podle názvu. + """ + query = "DELETE FROM TAG_CATEGORY WHERE TAG_CAT_NAME = ?" + self.cursor.execute(query, (tag_cat_name,)) + self.conn.commit() + + def insert_relation_tag_cat(self, tag_id: int, tag_cat_id: int) -> None: + query = "INSERT INTO RELATIONS (IDS, TYPE, IDT) VALUES (?, ?, ?)" + self.cursor.execute(query, (tag_id, "Category", tag_cat_id)) + self.conn.commit() + + def delete_relation_tag_cat(self, tag_id: int, tag_cat_id: int) -> None: + """ + Smaže vztah mezi tagem a kategorií podle IDS a IDT. + TYPE je pevně "Category". + """ + query = "DELETE FROM RELATIONS WHERE IDS = ? AND TYPE = ? AND IDT = ?" + self.cursor.execute(query, (tag_id, "Category", tag_cat_id)) + self.conn.commit() + + def insert_file(self, path: str, file_name: str, hash_value: str) -> None: + """ + Vloží nový soubor do tabulky FILES. + """ + query = "INSERT INTO FILES (PATH, FILE_NAME, HASH) VALUES (?, ?, ?)" + self.cursor.execute(query, (path, file_name, hash_value)) + self.conn.commit() + + def delete_file(self, file_id: int) -> None: + """ + Smaže soubor podle jeho ID. + """ + query = "DELETE FROM FILES WHERE ID = ?" + self.cursor.execute(query, (file_id,)) + self.conn.commit() + + def insert_relation_tag_file(self, tag_id: int, file_id: int) -> None: + query = "INSERT INTO RELATIONS (IDS, TYPE, IDT) VALUES (?, ?, ?)" + self.cursor.execute(query, (tag_id, "File", file_id)) + self.conn.commit() + + def delete_relation_tag_file(self, tag_id: int, file_id: int) -> None: + """ + Smaže vztah mezi tagem a souborem podle IDS a IDT. + TYPE je pevně "File". + """ + query = "DELETE FROM RELATIONS WHERE IDS = ? AND TYPE = ? AND IDT = ?" + self.cursor.execute(query, (tag_id, "File", file_id)) + self.conn.commit() + + def fetch_all(self, table_name: str) -> List[Tuple[Any]]: + """Vrátí všechny řádky z tabulky.""" + query = f"SELECT * FROM {table_name}" + self.cursor.execute(query) + return self.cursor.fetchall() + + def close(self) -> None: + """Zavře připojení k databázi.""" + self.conn.close() diff --git a/src/core/dataclass.py b/src/core/dataclass.py new file mode 100644 index 0000000..3de7064 --- /dev/null +++ b/src/core/dataclass.py @@ -0,0 +1,12 @@ +from pathlib import Path + +class State(self): + + +class TagManager(self, SaveHandler, ): + self.SaveHandler = SaveHandler + +class FileManager(): + + + diff --git a/src/gui/gui.py b/src/gui/gui.py index 23dce52..41163c0 100644 --- a/src/gui/gui.py +++ b/src/gui/gui.py @@ -9,102 +9,105 @@ import tkinter as tk from tkinter import ttk from src.core.image_handler import load_icon -# Functions -def main() -> None: - root = tk.Tk() - root.title("Ukázka rozložení") - root.geometry("800x600") +class App(): + def __init__(self): + pass - # ==== MENU ==== - menu_bar = tk.Menu(root) - root.config(menu=menu_bar) + def main(self) -> None: + root = tk.Tk() + root.title("Ukázka rozložení") + root.geometry("800x600") - 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) + # ==== MENU ==== + menu_bar = tk.Menu(root) + root.config(menu=menu_bar) - # ==== HLAVNÍ RÁM ==== - main_frame = tk.Frame(root) - main_frame.pack(fill="both", expand=True) + 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) - main_frame.columnconfigure(0, weight=1) - main_frame.columnconfigure(1, weight=2) - main_frame.rowconfigure(0, weight=1) + # ==== HLAVNÍ RÁM ==== + main_frame = tk.Frame(root) + main_frame.pack(fill="both", expand=True) - # ==== 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} + main_frame.columnconfigure(0, weight=1) + main_frame.columnconfigure(1, weight=2) + main_frame.rowconfigure(0, weight=1) - # ==== VLEVO: STROM ==== - tree = ttk.Treeview(main_frame) - tree.grid(row=0, column=0, sticky="nsew", padx=2, pady=2) + # ==== 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} - # Slovník pro stavy checkboxů - states = {} + # ==== VLEVO: STROM ==== + tree = ttk.Treeview(main_frame) + tree.grid(row=0, column=0, sticky="nsew", padx=2, pady=2) - # Funkce pro přepnutí checkboxu - def toggle(event) -> None: - region = tree.identify("region", event.x, event.y) - if region == "tree": + # Slovník pro stavy checkboxů + states = {} + + # 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"]) + + tree.bind("", toggle) + + # Přidání uzlů se stavem + root_node = tree.insert("", "end", text="Root", image=icons["unchecked"]) + states[root_node] = False + + 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 + + tree.item(root_node, open=True) + + # ==== VPRAVO: SEZNAM ==== + listbox = tk.Listbox(main_frame) + listbox.grid(row=0, column=1, sticky="nsew", padx=2, pady=2) + + for i in range(1, 21): + listbox.insert("end", f"Položka {i}") + + # ==== STAVOVÝ ŘÁDEK ==== + status_bar = tk.Label(root, text="Připraven", anchor="w", relief="sunken") + status_bar.pack(side="bottom", fill="x") + + # ==== 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") + ) + + 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") + ) + + # ==== HANDLERY ==== + def tree_right_click(event): 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"]) + if item_id: # klik na uzel + tree.selection_set(item_id) + tree_menu.tk_popup(event.x_root, event.y_root) - tree.bind("", toggle) + 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) - # Přidání uzlů se stavem - root_node = tree.insert("", "end", text="Root", image=icons["unchecked"]) - states[root_node] = False + tree.bind("", tree_right_click) + listbox.bind("", list_right_click) - 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 - - tree.item(root_node, open=True) - - # ==== VPRAVO: SEZNAM ==== - listbox = tk.Listbox(main_frame) - listbox.grid(row=0, column=1, sticky="nsew", padx=2, pady=2) - - for i in range(1, 21): - listbox.insert("end", f"Položka {i}") - - # ==== STAVOVÝ ŘÁDEK ==== - status_bar = tk.Label(root, text="Připraven", anchor="w", relief="sunken") - status_bar.pack(side="bottom", fill="x") - - # ==== 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") - ) - - 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") - ) - - # ==== 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) - - 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) - - tree.bind("", tree_right_click) - listbox.bind("", list_right_click) - - root.mainloop() \ No newline at end of file + root.mainloop() \ No newline at end of file diff --git a/test/SQL_handler_test.py b/test/SQL_handler_test.py new file mode 100644 index 0000000..23877cf --- /dev/null +++ b/test/SQL_handler_test.py @@ -0,0 +1,21 @@ +import sys, os +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + +from src.core.SQL_handler import SQLite3FileHandler +from pathlib import Path + +db_path = Path("Test.db3") + +test = SQLite3FileHandler(db_path=db_path) +try: + test.insert_tag("test") +except: + pass +try: + test.insert_category("Test") +except: + pass +test.insert_relation_tag_cat(4, 7) +print(test.fetch_all("RELATIONS")) + +test.close() \ No newline at end of file