import tkinter as tk from tkinter import ttk, messagebox, filedialog from tkinter.scrolledtext import ScrolledText from pathlib import Path import threading import queue import csv from datetime import datetime from ..core.scanner import find_source_files from ..config.languages import LANGUAGE_EXTENSIONS from ..utils import logger as app_logger import logging from .countings_tab import CountingsTab from .topbar import TopBar from .file_viewer import FileViewer from .scanner_tab import ScannerTab from .metrics_tab import MetricsTab from ..core.worker import WorkerManager class App(tk.Tk): def __init__(self): super().__init__() self.title("PyUcc - Interfaccia Grafica") self.geometry("1024x700") # Shared top bar (folder selection) placed above the notebook self.topbar = TopBar(self) self.topbar.pack(fill="x", side="top") # Note: top-level cumulative counters and progress bar removed. # Most per-task progress / counters are shown inside the Countings tab. # Note: per-task progress and top-level cumulative counters removed. self.notebook = ttk.Notebook(self) self.notebook.pack(fill="both", expand=True) # Application log panel below the notebook (grouped in a labeled frame) log_frame = ttk.LabelFrame(self, text="Log") log_frame.pack(fill="x", side="bottom", padx=6, pady=6) # ScrolledText for logs (read-only) self.log_text = ScrolledText(log_frame, height=8, wrap="word", state="disabled") self.log_text.pack(fill="both", expand=True, padx=6, pady=6) # Initialize centralized logging system from utils.logger and attach # the module-provided Tkinter handler to the ScrolledText widget. try: app_logger.setup_basic_logging(self) except Exception: pass try: color_map = { logging.INFO: 'black', logging.WARNING: '#d87f0a', logging.ERROR: '#d62728', } app_logger.add_tkinter_handler(self.log_text, {"colors": color_map}) except Exception: pass # small helper: expose a convenient log method that forwards to # the standard logging system so messages flow through the queue. def log(self, msg: str, level: str = "INFO"): lg = app_logger.get_logger("pyucc") lvl = getattr(logging, level.upper(), logging.INFO) try: if lvl >= logging.ERROR: lg.error(msg) elif lvl >= logging.WARNING: lg.warning(msg) else: lg.info(msg) except Exception: try: print(f"[{level}] {msg}") except Exception: pass self.log = log.__get__(self) # Worker manager (background task runner) self.worker = WorkerManager() # Create and add application tabs self.scanner_tab = ScannerTab(self.notebook, self.topbar, app=self) self.notebook.add(self.scanner_tab, text="Scanner") # Add Countings tab self.countings_tab = CountingsTab(self.notebook, self.topbar, app=self) self.notebook.add(self.countings_tab, text="Countings") # Add Metrics tab (cyclomatic complexity, maintainability index) self.metrics_tab = MetricsTab(self.notebook, self.topbar, app=self) self.notebook.add(self.metrics_tab, text="Metrics") # poll the worker UI queue and dispatch callbacks in the main thread self.after(100, self._poll_worker_ui_queue) def _poll_worker_ui_queue(self): try: while True: msg = self.worker.ui_queue.get_nowait() # allow GUI to update task list and status self._handle_worker_msg(msg) # dispatch registered callbacks try: self.worker.dispatch_message(msg) except Exception: pass except Exception: # queue empty or other; schedule next poll pass self.after(100, self._poll_worker_ui_queue) def _handle_worker_msg(self, msg: tuple): """Log worker messages to the application's log so there's a single place (the Log panel) to follow task lifecycle and results. """ typ, task_id, payload = msg if typ == "started": name = payload.get("func") if isinstance(payload, dict) else str(payload) self.log(f"Task {task_id[:8]} started: {name}", level="INFO") elif typ == "progress": # payload is expected to be a partial result or status dictionary # Progress updates can be very frequent and slow down the GUI # so log them at DEBUG level (hidden by default) and only # surface warnings/errors at higher levels. self.log(f"Task {task_id[:8]} progress: {payload}", level="DEBUG") elif typ == "done": # payload may contain final results self.log(f"Task {task_id[:8]} done. Result: {payload}", level="INFO") elif typ == "error": # payload is typically a traceback string or exception info self.log(f"Task {task_id[:8]} error: {payload}", level="ERROR") elif typ == "cancelled": self.log(f"Task {task_id[:8]} cancelled", level="WARNING") def run_app(): app = App() app.mainloop() if __name__ == "__main__": run_app()