S1005403_RisCC/target_simulator/gui/logger_panel.py

160 lines
5.8 KiB
Python

# target_simulator/gui/logger_panel.py
"""
A small Toplevel UI to inspect and change logger levels at runtime.
"""
import tkinter as tk
from tkinter import ttk, messagebox
import logging
from typing import List
LEVELS = [
("NOTSET", logging.NOTSET),
("DEBUG", logging.DEBUG),
("INFO", logging.INFO),
("WARNING", logging.WARNING),
("ERROR", logging.ERROR),
("CRITICAL", logging.CRITICAL),
]
class LoggerPanel(tk.Toplevel):
"""Toplevel window that allows setting logger levels at runtime."""
def __init__(self, master=None):
super().__init__(master)
self.title("Logger Levels")
self.geometry("520x420")
self.transient(master)
self.grab_set()
self.logger_names = [] # type: List[str]
self._create_widgets()
self._populate_logger_list()
def _create_widgets(self):
top = ttk.Frame(self)
top.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
# Left: list of logger names
left = ttk.Frame(top)
left.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
ttk.Label(left, text="Available loggers:").pack(anchor=tk.W)
self.logger_listbox = tk.Listbox(left, exportselection=False)
self.logger_listbox.pack(fill=tk.BOTH, expand=True, padx=(0, 6), pady=(4, 0))
self.logger_listbox.bind("<<ListboxSelect>>", self._on_select_logger)
# Right: controls
right = ttk.Frame(top)
right.pack(side=tk.RIGHT, fill=tk.Y)
ttk.Label(right, text="Selected logger:").pack(anchor=tk.W)
self.selected_name_var = tk.StringVar(value="")
ttk.Label(right, textvariable=self.selected_name_var, foreground="blue").pack(anchor=tk.W, pady=(0, 6))
ttk.Label(right, text="Level:").pack(anchor=tk.W)
self.level_var = tk.StringVar(value="INFO")
level_names = [n for n, v in LEVELS]
self.level_combo = ttk.Combobox(right, values=level_names, textvariable=self.level_var, state="readonly", width=12)
self.level_combo.pack(anchor=tk.W, pady=(0, 6))
ttk.Button(right, text="Apply", command=self._apply_level).pack(fill=tk.X, pady=(6, 4))
ttk.Button(right, text="Reset to NOTSET", command=self._reset_level).pack(fill=tk.X)
ttk.Separator(self).pack(fill=tk.X, pady=6)
bottom = ttk.Frame(self)
bottom.pack(fill=tk.X, padx=8, pady=6)
ttk.Label(bottom, text="Add / open logger by name:").pack(anchor=tk.W)
self.new_logger_var = tk.StringVar()
entry = ttk.Entry(bottom, textvariable=self.new_logger_var)
entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 6))
ttk.Button(bottom, text="Open", command=self._open_named_logger).pack(side=tk.LEFT)
ttk.Button(self, text="Refresh", command=self._populate_logger_list).pack(side=tk.RIGHT, padx=8, pady=(0, 8))
ttk.Button(self, text="Close", command=self._on_close).pack(side=tk.RIGHT, pady=(0, 8))
def _gather_logger_names(self) -> List[str]:
# Gather logger names from the logging manager plus some defaults
manager = logging.root.manager
names = list(getattr(manager, 'loggerDict', {}).keys())
# Add a few commonly useful module names if missing
defaults = [
'target_simulator',
'target_simulator.analysis.simulation_state_hub',
'target_simulator.gui.sfp_debug_window',
'target_simulator.gui.ppi_display',
'target_simulator.gui.payload_router',
'target_simulator.core.sfp_transport',
]
for d in defaults:
if d not in names:
names.append(d)
names = sorted(set(names))
return names
def _populate_logger_list(self):
self.logger_listbox.delete(0, tk.END)
self.logger_names = self._gather_logger_names()
for n in self.logger_names:
self.logger_listbox.insert(tk.END, n)
def _on_select_logger(self, event=None):
sel = self.logger_listbox.curselection()
if not sel:
return
idx = sel[0]
name = self.logger_names[idx]
self.selected_name_var.set(name)
lg = logging.getLogger(name)
lvl = lg.getEffectiveLevel()
# Translate to name
lvl_name = logging.getLevelName(lvl)
self.level_var.set(lvl_name)
def _apply_level(self):
name = self.selected_name_var.get()
if not name:
messagebox.showwarning("No logger selected", "Select a logger from the list first.")
return
lvl_name = self.level_var.get()
lvl = next((v for n, v in LEVELS if n == lvl_name), logging.INFO)
logging.getLogger(name).setLevel(lvl)
messagebox.showinfo("Logger level set", f"Logger '{name}' set to {lvl_name}.")
def _reset_level(self):
name = self.selected_name_var.get()
if not name:
messagebox.showwarning("No logger selected", "Select a logger from the list first.")
return
logging.getLogger(name).setLevel(logging.NOTSET)
self.level_var.set('NOTSET')
messagebox.showinfo("Logger reset", f"Logger '{name}' reset to NOTSET.")
def _open_named_logger(self):
name = self.new_logger_var.get().strip()
if not name:
return
# If exists in list, select it
try:
idx = self.logger_names.index(name)
except ValueError:
# Add to list
self.logger_names.append(name)
self.logger_names.sort()
self._populate_logger_list()
idx = self.logger_names.index(name)
self.logger_listbox.select_clear(0, tk.END)
self.logger_listbox.select_set(idx)
self.logger_listbox.see(idx)
self._on_select_logger()
def _on_close(self):
try:
self.grab_release()
except Exception:
pass
self.destroy()