150 lines
4.7 KiB
Python
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()
|