SXXXXXXX_PyUCC/pyucc/gui/gui.py
2025-11-25 12:18:55 +01:00

150 lines
4.7 KiB
Python

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()