"""Logging helpers for the finger dashboard.""" from __future__ import annotations from PyQt5.QtGui import QFont, QTextCursor from PyQt5.QtWidgets import QPlainTextEdit class TeeStream: """Write to multiple text streams.""" def __init__(self, *streams): self.streams = streams def write(self, text): for stream in self.streams: stream.write(text) def flush(self): for stream in self.streams: stream.flush() class WidgetLogHandler: """Mirror print output into a QPlainTextEdit with a live status line at the bottom.""" def __init__(self, widget: QPlainTextEdit): self.widget = widget self.buf = "" self._has_status_line = False def write(self, text: str): self.buf += text while "\n" in self.buf: line, self.buf = self.buf.split("\n", 1) if line: self._append_log_line(line) def flush(self): if self.buf: self._append_log_line(self.buf) self.buf = "" def set_status_line(self, text: str) -> None: """Update the pinned bottom line (FPS / forces) without scrolling the log.""" text = text.rstrip("\n") if self._has_status_line: cursor = QTextCursor(self.widget.document().lastBlock()) cursor.select(QTextCursor.BlockUnderCursor) cursor.insertText(text) else: self.widget.appendPlainText(text) self._has_status_line = True scrollbar = self.widget.verticalScrollBar() scrollbar.setValue(scrollbar.maximum()) def clear_status_line(self) -> None: if not self._has_status_line: return cursor = QTextCursor(self.widget.document().lastBlock()) cursor.select(QTextCursor.BlockUnderCursor) cursor.removeSelectedText() self._has_status_line = False def _append_log_line(self, line: str) -> None: status = None if self._has_status_line: status = self.widget.document().lastBlock().text() cursor = QTextCursor(self.widget.document().lastBlock()) cursor.select(QTextCursor.BlockUnderCursor) cursor.removeSelectedText() self._has_status_line = False self.widget.appendPlainText(line) if status is not None: self.set_status_line(status) def make_log_widget(config=None) -> QPlainTextEdit: if config is None: from ..layout import LOG_CONFIG config = LOG_CONFIG widget = QPlainTextEdit() widget.setReadOnly(True) widget.setMaximumBlockCount(config.max_lines) font = QFont(config.font_family, config.font_pt) font.setStyleHint(QFont.Monospace) widget.setFont(font) return widget