pyqt_reactive.widgets.log_viewer

PyQt6 Log Viewer Window

Provides comprehensive log viewing capabilities with real-time tailing, search functionality, and integration with OpenHCS subprocess execution. Reimplements log viewing using Qt widgets for native desktop integration.

Classes

HighlightSignals

Signals for background highlighting worker.

HighlightWorker(text, cache_key, ...)

Background worker that does expensive regex parsing to extract formatting info.

HighlightedSegment(start, length, color[, ...])

A segment of text with formatting information.

LogColorScheme([log_critical_fg, ...])

Centralized color scheme for log highlighting with semantic color names.

LogFileDetector([base_log_path])

Detects new log files in directory using efficient file monitoring.

LogFileLoader(log_path[, tail_lines, ...])

Background process for loading large log files without blocking UI.

LogHighlighter(parent[, color_scheme])

Advanced syntax highlighter for log files using Pygments.

LogItemDelegate([color_scheme, parent])

Delegate responsible for per-line coloring, regex-based syntax, and search highlighting.

LogListModel([parent])

Lightweight list model storing log lines in a bounded ring buffer.

LogListView([parent])

Custom QListView that enables text selection within and across items.

LogTailer(log_path[, initial_position])

Background thread for tailing log files without blocking UI.

LogViewerWindow(file_manager, service_adapter)

Main log viewer window with dropdown, search, and real-time tailing.

SelectableDocument([parent])

QTextDocument wrapper that manages text cursor and selection state.

class pyqt_reactive.widgets.log_viewer.LogColorScheme(log_critical_fg: Tuple[int, int, int] = (255, 255, 255), log_critical_bg: Tuple[int, int, int] = (139, 0, 0), log_error_color: Tuple[int, int, int] = (255, 85, 85), log_warning_color: Tuple[int, int, int] = (255, 140, 0), log_info_color: Tuple[int, int, int] = (100, 160, 210), log_debug_color: Tuple[int, int, int] = (160, 160, 160), timestamp_color: Tuple[int, int, int] = (105, 105, 105), logger_name_color: Tuple[int, int, int] = (147, 112, 219), memory_address_color: Tuple[int, int, int] = (255, 182, 193), file_path_color: Tuple[int, int, int] = (34, 139, 34), python_keyword_color: Tuple[int, int, int] = (86, 156, 214), python_string_color: Tuple[int, int, int] = (206, 145, 120), python_number_color: Tuple[int, int, int] = (181, 206, 168), python_operator_color: Tuple[int, int, int] = (212, 212, 212), python_name_color: Tuple[int, int, int] = (156, 220, 254), python_function_color: Tuple[int, int, int] = (220, 220, 170), python_class_color: Tuple[int, int, int] = (78, 201, 176), python_builtin_color: Tuple[int, int, int] = (86, 156, 214), python_comment_color: Tuple[int, int, int] = (106, 153, 85), exception_color: Tuple[int, int, int] = (255, 69, 0), function_call_color: Tuple[int, int, int] = (255, 215, 0), boolean_color: Tuple[int, int, int] = (86, 156, 214), tuple_parentheses_color: Tuple[int, int, int] = (255, 215, 0), set_braces_color: Tuple[int, int, int] = (255, 140, 0), class_representation_color: Tuple[int, int, int] = (78, 201, 176), function_representation_color: Tuple[int, int, int] = (220, 220, 170), module_path_color: Tuple[int, int, int] = (147, 112, 219), hex_number_color: Tuple[int, int, int] = (181, 206, 168), scientific_notation_color: Tuple[int, int, int] = (181, 206, 168), binary_number_color: Tuple[int, int, int] = (181, 206, 168), octal_number_color: Tuple[int, int, int] = (181, 206, 168), python_special_color: Tuple[int, int, int] = (255, 20, 147), single_quoted_string_color: Tuple[int, int, int] = (206, 145, 120), list_comprehension_color: Tuple[int, int, int] = (156, 220, 254), generator_expression_color: Tuple[int, int, int] = (156, 220, 254))[source]

Centralized color scheme for log highlighting with semantic color names.

Supports light/dark theme variants and ensures WCAG accessibility compliance. All colors meet minimum 4.5:1 contrast ratio for normal text readability.

log_critical_fg: Tuple[int, int, int] = (255, 255, 255)
log_critical_bg: Tuple[int, int, int] = (139, 0, 0)
log_error_color: Tuple[int, int, int] = (255, 85, 85)
log_warning_color: Tuple[int, int, int] = (255, 140, 0)
log_info_color: Tuple[int, int, int] = (100, 160, 210)
log_debug_color: Tuple[int, int, int] = (160, 160, 160)
timestamp_color: Tuple[int, int, int] = (105, 105, 105)
logger_name_color: Tuple[int, int, int] = (147, 112, 219)
memory_address_color: Tuple[int, int, int] = (255, 182, 193)
file_path_color: Tuple[int, int, int] = (34, 139, 34)
python_keyword_color: Tuple[int, int, int] = (86, 156, 214)
python_string_color: Tuple[int, int, int] = (206, 145, 120)
python_number_color: Tuple[int, int, int] = (181, 206, 168)
python_operator_color: Tuple[int, int, int] = (212, 212, 212)
python_name_color: Tuple[int, int, int] = (156, 220, 254)
python_function_color: Tuple[int, int, int] = (220, 220, 170)
python_class_color: Tuple[int, int, int] = (78, 201, 176)
python_builtin_color: Tuple[int, int, int] = (86, 156, 214)
python_comment_color: Tuple[int, int, int] = (106, 153, 85)
exception_color: Tuple[int, int, int] = (255, 69, 0)
function_call_color: Tuple[int, int, int] = (255, 215, 0)
boolean_color: Tuple[int, int, int] = (86, 156, 214)
tuple_parentheses_color: Tuple[int, int, int] = (255, 215, 0)
set_braces_color: Tuple[int, int, int] = (255, 140, 0)
class_representation_color: Tuple[int, int, int] = (78, 201, 176)
function_representation_color: Tuple[int, int, int] = (220, 220, 170)
module_path_color: Tuple[int, int, int] = (147, 112, 219)
hex_number_color: Tuple[int, int, int] = (181, 206, 168)
scientific_notation_color: Tuple[int, int, int] = (181, 206, 168)
binary_number_color: Tuple[int, int, int] = (181, 206, 168)
octal_number_color: Tuple[int, int, int] = (181, 206, 168)
python_special_color: Tuple[int, int, int] = (255, 20, 147)
single_quoted_string_color: Tuple[int, int, int] = (206, 145, 120)
list_comprehension_color: Tuple[int, int, int] = (156, 220, 254)
generator_expression_color: Tuple[int, int, int] = (156, 220, 254)
classmethod create_dark_theme() LogColorScheme[source]

Create a dark theme variant with adjusted colors for dark backgrounds.

Returns:

Dark theme color scheme with higher contrast

Return type:

LogColorScheme

classmethod create_light_theme() LogColorScheme[source]

Create a light theme variant with adjusted colors for light backgrounds.

Returns:

Light theme color scheme with appropriate contrast

Return type:

LogColorScheme

to_qcolor(color_tuple: Tuple[int, int, int]) QColor[source]

Convert RGB tuple to QColor object.

Parameters:

color_tuple – RGB color tuple (r, g, b)

Returns:

Corresponding QColor instance.

Return type:

QColor

__init__(log_critical_fg: Tuple[int, int, int] = (255, 255, 255), log_critical_bg: Tuple[int, int, int] = (139, 0, 0), log_error_color: Tuple[int, int, int] = (255, 85, 85), log_warning_color: Tuple[int, int, int] = (255, 140, 0), log_info_color: Tuple[int, int, int] = (100, 160, 210), log_debug_color: Tuple[int, int, int] = (160, 160, 160), timestamp_color: Tuple[int, int, int] = (105, 105, 105), logger_name_color: Tuple[int, int, int] = (147, 112, 219), memory_address_color: Tuple[int, int, int] = (255, 182, 193), file_path_color: Tuple[int, int, int] = (34, 139, 34), python_keyword_color: Tuple[int, int, int] = (86, 156, 214), python_string_color: Tuple[int, int, int] = (206, 145, 120), python_number_color: Tuple[int, int, int] = (181, 206, 168), python_operator_color: Tuple[int, int, int] = (212, 212, 212), python_name_color: Tuple[int, int, int] = (156, 220, 254), python_function_color: Tuple[int, int, int] = (220, 220, 170), python_class_color: Tuple[int, int, int] = (78, 201, 176), python_builtin_color: Tuple[int, int, int] = (86, 156, 214), python_comment_color: Tuple[int, int, int] = (106, 153, 85), exception_color: Tuple[int, int, int] = (255, 69, 0), function_call_color: Tuple[int, int, int] = (255, 215, 0), boolean_color: Tuple[int, int, int] = (86, 156, 214), tuple_parentheses_color: Tuple[int, int, int] = (255, 215, 0), set_braces_color: Tuple[int, int, int] = (255, 140, 0), class_representation_color: Tuple[int, int, int] = (78, 201, 176), function_representation_color: Tuple[int, int, int] = (220, 220, 170), module_path_color: Tuple[int, int, int] = (147, 112, 219), hex_number_color: Tuple[int, int, int] = (181, 206, 168), scientific_notation_color: Tuple[int, int, int] = (181, 206, 168), binary_number_color: Tuple[int, int, int] = (181, 206, 168), octal_number_color: Tuple[int, int, int] = (181, 206, 168), python_special_color: Tuple[int, int, int] = (255, 20, 147), single_quoted_string_color: Tuple[int, int, int] = (206, 145, 120), list_comprehension_color: Tuple[int, int, int] = (156, 220, 254), generator_expression_color: Tuple[int, int, int] = (156, 220, 254)) None
class pyqt_reactive.widgets.log_viewer.LogListModel(parent: QObject | None = None)[source]

Lightweight list model storing log lines in a bounded ring buffer.

The model is pure data; rendering and highlighting are handled by a delegate on top of this model.

MAX_LINES = 100000
__init__(parent: QObject | None = None)[source]
rowCount(self, parent: QModelIndex = QModelIndex()) int[source]
data(self, index: QModelIndex, role: int = Qt.DisplayRole) Any[source]
clear() None[source]

Clear all stored lines.

append_lines(lines: List) None[source]

Append new lines, enforcing the MAX_LINES ring buffer constraint.

max_line_width() int[source]
html_for_row(row: int) str | None[source]
iter_lines() List[str][source]

Expose lines for read-only access (e.g., search).

class pyqt_reactive.widgets.log_viewer.HighlightedSegment(start: int, length: int, color: Tuple[int, int, int], bold: bool = False, italic: bool = False)[source]

A segment of text with formatting information.

start: int
length: int
color: Tuple[int, int, int]
bold: bool = False
italic: bool = False
__init__(start: int, length: int, color: Tuple[int, int, int], bold: bool = False, italic: bool = False) None
class pyqt_reactive.widgets.log_viewer.SelectableDocument(parent: QObject | None = None)[source]

QTextDocument wrapper that manages text cursor and selection state.

Enables text selection within QListView delegate items by tracking cursor position and applying visual selection highlighting using QTextCharFormat.

Based on Qt Forum solution for interactive elements in delegates.

__init__(parent: QObject | None = None) None[source]
setTextWidth(width: float) None[source]

Override to avoid unnecessary relayout when width hasn’t changed.

selectionStart() int[source]

Get the start position of the current selection.

selectionEnd() int[source]

Get the end position of the current selection.

setCursorPosition(pos: int, mode: MoveMode = MoveMode.MoveAnchor) None[source]

Set cursor position and update selection highlighting.

Parameters:
  • pos – Character position in document

  • mode – MoveAnchor (clear selection) or KeepAnchor (extend selection)

moveCursorPosition(op: MoveOperation, mode: MoveMode = MoveMode.MoveAnchor, n: int = 1) bool[source]

Move cursor by operation and update highlighting.

Parameters:
  • op – Move operation (e.g., StartOfWord, EndOfWord)

  • mode – MoveAnchor or KeepAnchor

  • n – Number of times to repeat operation

Returns:

True if move was successful

select(selection: SelectionType) None[source]

Select text by type (word, line, etc.) and update highlighting.

Parameters:

selection – Type of selection (WordUnderCursor, LineUnderCursor, Document)

extendWordwiseSelection(newPos: int) None[source]

Extend selection to word boundaries when dragging after double-click.

This implements the behavior where double-clicking selects a word, then dragging extends the selection by whole words rather than characters.

Parameters:

newPos – New cursor position from mouse drag

selectedText() str[source]

Get the currently selected text.

class pyqt_reactive.widgets.log_viewer.HighlightSignals[source]

Signals for background highlighting worker.

finished

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

class pyqt_reactive.widgets.log_viewer.HighlightWorker(text: str, cache_key: Tuple[str, str, int], color_scheme: LogColorScheme, signals: HighlightSignals)[source]

Background worker that does expensive regex parsing to extract formatting info.

This runs all the expensive regex matching in a background thread and returns just the formatting information (which segments should be what color). The main thread then applies this to a QTextDocument.

__init__(text: str, cache_key: Tuple[str, str, int], color_scheme: LogColorScheme, signals: HighlightSignals)[source]
run()[source]

Request formatting segments from subprocess parser.

class pyqt_reactive.widgets.log_viewer.LogItemDelegate(color_scheme: LogColorScheme | None = None, parent: QWidget | None = None)[source]

Delegate responsible for per-line coloring, regex-based syntax, and search highlighting.

We avoid document-wide highlighting and instead reuse the existing LogHighlighter rules on a per-line QTextDocument that is only used for visible rows. This keeps work bounded by visible rows and line length, independent of total log size.

Performance optimization: 1. Cache highlighted QTextDocuments to avoid re-running expensive regex highlighting 2. Perform highlighting in background thread pool to never block UI 3. Paint with plain text if highlighting not ready yet (progressive enhancement)

MAX_CACHE_SIZE = 10000
__init__(color_scheme: LogColorScheme | None = None, parent: QWidget | None = None) None[source]
set_fixed_row_height(height: int) None[source]
set_search_state(text: str, case_sensitive: bool) None[source]

Update search text used for line-level search highlighting.

hasInteractiveDocument(row: int) bool[source]

Check if a row has an interactive document with selection.

getInteractiveDocument(row: int) SelectableDocument | None[source]

Get the interactive document for a specific row.

setInteractiveDocument(row: int, doc: SelectableDocument) None[source]

Set the interactive document for a specific row.

clearInteractiveDocuments() None[source]

Clear all interactive documents.

createInteractiveDocument(text: str, font: QFont, width: int = 0) SelectableDocument[source]

Create a SelectableDocument with syntax highlighting applied.

This is used by LogListView when creating a new interactive document for text selection. It applies the same syntax highlighting as normal rendering.

Parameters:
  • text – The log line text

  • font – The font to use for rendering

  • width – The width for text wrapping (optional)

Returns:

SelectableDocument with syntax highlighting applied

paint(self, painter: QPainter | None, option: QStyleOptionViewItem, index: QModelIndex)[source]
sizeHint(option, index)[source]

Return height large enough to show wrapped text for this row.

We compute the document layout for the current line using the available viewport width so that long log lines wrap instead of being clipped or forcing a horizontal scrollbar.

This method is heavily optimized with caching since Qt calls it frequently during scrolling, resizing, and layout updates.

clear_cache() None[source]

Clear all caches. Call this when resetting the log viewer or when search changes.

class pyqt_reactive.widgets.log_viewer.LogListView(parent: QWidget | None = None)[source]

Custom QListView that enables text selection within and across items.

Handles mouse events to track cursor position in QTextDocument and manage text selection state. Supports single-line and multi-line selection. Works in conjunction with LogItemDelegate to paint selection highlighting.

Based on Qt Forum solution for interactive elements in delegates.

__init__(parent: QWidget | None = None) None[source]
updateGeometries(self)[source]
mousePressEvent(event) None[source]

Handle mouse press to start text selection.

mouseMoveEvent(event) None[source]

Handle mouse move to update cursor icon and extend selection.

mouseDoubleClickEvent(event) None[source]

Handle double-click to select word.

mouseReleaseEvent(event) None[source]

Handle mouse release to copy selected text to clipboard.

wheelEvent(event) None[source]

Handle mouse wheel with Shift for horizontal scrolling.

selectAllText() None[source]

Select all text in all log lines (Ctrl+A handler).

keyPressEvent(event) None[source]

Handle keyboard events for copy/paste operations.

resizeEvent(event) None[source]

Handle resize events to invalidate width-dependent caches.

class pyqt_reactive.widgets.log_viewer.LogFileDetector(base_log_path: str | None = None)[source]

Detects new log files in directory using efficient file monitoring.

Uses QFileSystemWatcher to monitor directory changes and set operations for efficient new file detection. Handles base_log_path as file prefix and watches the parent directory.

new_log_detected

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

__init__(base_log_path: str | None = None)[source]

Initialize LogFileDetector.

Parameters:

base_log_path – Base path for subprocess log files (file prefix, not directory)

start_watching(directory: Path) None[source]

Start watching directory for new log files.

Parameters:

directory – Directory to watch for new log files

stop_watching() None[source]

Stop file watching and cleanup.

scan_directory(directory: Path) Set[Path][source]

Scan directory for .log files.

Parameters:

directory – Directory to scan

Returns:

Set of Path objects for .log files found

Return type:

Set[Path]

detect_new_files(current_files: Set[Path]) Set[Path][source]

Use set.difference() to find new files efficiently.

Parameters:

current_files – Current set of files in directory

Returns:

Set of newly discovered files

Return type:

Set[Path]

class pyqt_reactive.widgets.log_viewer.LogHighlighter(parent: QTextDocument, color_scheme: LogColorScheme = None)[source]

Advanced syntax highlighter for log files using Pygments.

Provides sophisticated highlighting for OpenHCS log format with support for: - Log levels and timestamps - Python code snippets and data structures - Memory addresses and function signatures - Complex nested dictionaries and lists - Exception tracebacks and file paths

__init__(parent: QTextDocument, color_scheme: LogColorScheme = None)[source]

Initialize the log highlighter with optional color scheme.

Parameters:
  • parent – QTextDocument to apply highlighting to

  • color_scheme – Color scheme to use (defaults to dark theme)

setup_pygments_styles() None[source]

Setup Pygments token to QTextCharFormat mapping using color scheme.

setup_highlighting_rules() None[source]

Setup regex patterns for log-specific highlighting.

set_color_scheme(color_scheme: LogColorScheme) None[source]

Update the color scheme and refresh highlighting.

Parameters:

color_scheme – New color scheme to apply

switch_to_dark_theme() None[source]

Switch to dark theme color scheme.

switch_to_light_theme() None[source]

Switch to light theme color scheme.

classmethod load_color_scheme_from_config(config_path: str = None) LogColorScheme[source]

Load color scheme from external configuration file.

Parameters:

config_path – Path to JSON/YAML config file (optional)

Returns:

Loaded color scheme or default if file not found

Return type:

LogColorScheme

highlightBlock(text: str) None[source]

Apply highlighting to text block using regex patterns.

Uses regex patterns for log-specific content (timestamps, log levels, etc.). Fast and doesn’t block the UI.

class pyqt_reactive.widgets.log_viewer.LogFileLoader(log_path: Path, tail_lines: int = 100000, chunk_lines: int = 1000)[source]

Background process for loading large log files without blocking UI.

chunk_loaded

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

load_finished

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

load_failed

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

__init__(log_path: Path, tail_lines: int = 100000, chunk_lines: int = 1000)[source]
start()[source]

Spawn a worker process to load the file content.

isRunning(self) bool[source]
wait(self, deadline: QDeadlineTimer = QDeadlineTimer(QDeadlineTimer.Forever)) bool[source]
wait(self, time: int) bool
class pyqt_reactive.widgets.log_viewer.LogTailer(log_path: Path, initial_position: int = 0)[source]

Background thread for tailing log files without blocking UI.

new_content

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

log_rotated

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

error_occurred

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

__init__(log_path: Path, initial_position: int = 0)[source]
run()[source]

Continuously tail log file in background thread.

stop()[source]

Stop the tailing thread.

class pyqt_reactive.widgets.log_viewer.LogViewerWindow(file_manager, service_adapter, parent=None)[source]

Main log viewer window with dropdown, search, and real-time tailing.

window_closed

int = …, arguments: Sequence = …) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal’s arguments.

Type:

pyqtSignal(*types, name

Type:

str = …, revision

__init__(file_manager, service_adapter, parent=None)[source]
current_log_path: Path | None
current_file_position: int
auto_scroll_enabled: bool
tailing_paused: bool
current_search_text: str
log_selector: QComboBox
search_toolbar: QToolBar
log_view: QListView
log_model: LogListModel | None
log_delegate: LogItemDelegate | None
file_detector: LogFileDetector
tail_timer: QTimer
log_tailer: LogTailer | None
highlighter: LogHighlighter | None
file_loader: LogFileLoader | None
server_scan_timer: QTimer
process_update_timer: QTimer
show_alive_only: bool
setup_ui() None[source]

Setup complete UI layout with exact widget hierarchy.

setup_connections() None[source]

Setup signal/slot connections.

showEvent(event)[source]

Override showEvent to load log when window is first shown.

initialize_logs() None[source]

Initialize with main log only, then scan for subprocess logs in background.

populate_log_dropdown(log_files: List[LogFileInfo]) None[source]

Populate QComboBox with log files with process status indicators.

Parameters:

log_files – List of LogFileInfo objects to add to dropdown

clear_subprocess_logs() None[source]

Remove all non-TUI logs from dropdown and switch to TUI log.

add_new_log(log_file_info: LogFileInfo) None[source]

Add new log to dropdown maintaining sort order.

Parameters:

log_file_info – New LogFileInfo to add

on_log_selection_changed(index: int) None[source]

Handle dropdown selection change - switch log display.

Parameters:

index – Selected index in dropdown

switch_to_log(log_path: Path) None[source]

Switch log display to show specified log file.

Parameters:

log_path – Path to log file to display

toggle_search_toolbar() None[source]

Show/hide search toolbar (Ctrl+F handler).

Search forwards in the current log using the model/view backend.

clear_search_highlights() None[source]

Clear search state and delegate highlights.

find_next() None[source]

Find next search result.

find_previous() None[source]

Find previous search result.

toggle_auto_scroll(enabled: bool) None[source]

Toggle auto-scroll to bottom.

toggle_pause_tailing(paused: bool) None[source]

Toggle pause/resume log tailing.

clear_log_display() None[source]

Clear current log display content.

reset_memory() None[source]

Reset memory usage by clearing the in-memory log model and reloading current log.

This completely clears the text buffer and reloads the log file from disk, freeing memory accumulated from long-running log tailing.

scroll_to_bottom() None[source]

Scroll log view to bottom.

start_log_tailing(log_path: Path) None[source]

Start tailing log file with async background thread (never blocks UI).

Parameters:

log_path – Path to log file to tail

stop_log_tailing() None[source]

Stop current log tailing.

read_log_incremental() None[source]

Deprecated - kept for compatibility. Tailing now handled by LogTailer thread.

start_monitoring(base_log_path: str | None = None) None[source]

Start monitoring for new logs.

stop_monitoring() None[source]

Stop monitoring for new logs.

start_process_tracking() None[source]

Start periodic process status updates.

update_process_status() None[source]

Update process status and refresh dropdown if needed.

on_filter_changed(state: int) None[source]

Handle filter checkbox state change.

Parameters:

state – Qt.CheckState value

cleanup() None[source]

Cleanup all resources and background processes.

closeEvent(event) None[source]

Handle window close event.