chore/adopt-local-tree #4
1 changed files with 108 additions and 0 deletions
feat: console log dock
commit
1d21006281
108
src/otko/views/docks/console.py
Normal file
108
src/otko/views/docks/console.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# Ported from otko-development (MIT), (c) 2026 OTKO contributors
|
||||
"""The console dock: the timestamped analysis log."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from PySide6.QtGui import QFont, QFontDatabase
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QDockWidget,
|
||||
QHBoxLayout,
|
||||
QPlainTextEdit,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
__all__ = ["ConsoleDock"]
|
||||
|
||||
#: Recognised severities; anything else is treated as information.
|
||||
_SEVERITIES = ("info", "warning", "error", "success")
|
||||
|
||||
|
||||
class ConsoleDock(QDockWidget):
|
||||
"""Show analysis log lines with a timestamp and severity label.
|
||||
|
||||
The dock never decides anything about the model: it only renders text the
|
||||
viewmodel or worker emits. Lines are appended, so the log is a faithful
|
||||
running record of the session.
|
||||
"""
|
||||
|
||||
def __init__(self, parent: QWidget | None = None) -> None:
|
||||
"""Create the console dock with a read-only, monospaced log view."""
|
||||
super().__init__("Console", parent)
|
||||
self.setObjectName("consoleDock")
|
||||
|
||||
self._view = QPlainTextEdit(self)
|
||||
self._view.setReadOnly(True)
|
||||
self._view.setObjectName("consoleView")
|
||||
self._view.setMaximumBlockCount(5000)
|
||||
self._view.setPlaceholderText("Logs and OpenSeesPy output will appear here.")
|
||||
self._view.setFont(self._monospace_font())
|
||||
|
||||
clear_button = QPushButton("Clear", self)
|
||||
clear_button.setToolTip("Clear the console log")
|
||||
clear_button.setStatusTip("Clear the console log")
|
||||
clear_button.clicked.connect(self.clear)
|
||||
copy_button = QPushButton("Copy All", self)
|
||||
copy_button.setToolTip("Copy the whole log to the clipboard")
|
||||
copy_button.setStatusTip("Copy the whole log to the clipboard")
|
||||
copy_button.clicked.connect(self._copy_all)
|
||||
|
||||
buttons = QHBoxLayout()
|
||||
buttons.addStretch(1)
|
||||
buttons.addWidget(copy_button)
|
||||
buttons.addWidget(clear_button)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(4, 4, 4, 4)
|
||||
layout.setSpacing(4)
|
||||
layout.addWidget(self._view, 1)
|
||||
layout.addLayout(buttons)
|
||||
|
||||
container = QWidget(self)
|
||||
container.setLayout(layout)
|
||||
self.setWidget(container)
|
||||
|
||||
# -- API -----------------------------------------------------------
|
||||
|
||||
def log(self, message: str, severity: str = "info") -> None:
|
||||
"""Append ``message`` with a timestamp and ``severity`` tag.
|
||||
|
||||
Every line carries a wall-clock time and a severity so a failure is
|
||||
easy to spot after a run.
|
||||
"""
|
||||
tag = severity if severity in _SEVERITIES else "info"
|
||||
stamp = datetime.now().strftime("%H:%M:%S")
|
||||
self._view.appendPlainText(f"{stamp} [{tag.upper():<7}] {message}")
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Remove every line from the log."""
|
||||
self._view.clear()
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
"""Return the full log text (used by tests and copy-all)."""
|
||||
return self._view.toPlainText()
|
||||
|
||||
@property
|
||||
def view(self) -> QPlainTextEdit:
|
||||
"""Return the inner text view (for legacy append-only call sites)."""
|
||||
return self._view
|
||||
|
||||
# -- Internals -----------------------------------------------------
|
||||
|
||||
def _copy_all(self) -> None:
|
||||
"""Copy the whole log to the clipboard."""
|
||||
clipboard = QApplication.clipboard()
|
||||
if clipboard is not None:
|
||||
clipboard.setText(self.text)
|
||||
|
||||
@staticmethod
|
||||
def _monospace_font() -> QFont:
|
||||
"""Return a fixed-width font so timestamps align."""
|
||||
font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
|
||||
font.setPointSize(10)
|
||||
return font
|
||||
Loading…
Reference in a new issue