Source code for ironflow.gui.log

# coding: utf-8
# Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
# Distributed under the terms of "New BSD License", see the LICENSE file.
"""
Control the underlying Ryven logging system, and route logs to a widget.
"""

from __future__ import annotations

import sys
from io import TextIOBase
from typing import TYPE_CHECKING

import ipywidgets as widgets
from pyiron_base.interfaces.singleton import Singleton

from ironflow.gui.draws_widgets import DrawsWidgets

if TYPE_CHECKING:
    from ironflow.model.model import HasSession


[docs] class StdOutPut(TextIOBase): """ Helper class that can be assigned to stdout and/or stderr, passing string to a widget """ def __init__(self): self.output = widgets.HTML() self.messages = ""
[docs] def write(self, s): self.messages += s self.output.value = ( "<style>p{" "word-wrap: break-word; " "margin: 0px; " "font-family: monospace;" "white-space: pre" "}</style> <p>" + self.messages + " </p>" )
[docs] class LogController(metaclass=Singleton): """ Singleton pattern ensures that whatever `sys.stdout/err` was at the beginning of the session gets preserved. """ def __init__(self): self.stdoutput = StdOutPut() self._standard_stdout = sys.stdout self._standard_stderr = sys.stderr @property def output(self): return self.stdoutput.output
[docs] def log_to_display(self): sys.stdout = self.stdoutput sys.stderr = self.stdoutput
[docs] def log_to_stdout(self): sys.stdout = self._standard_stdout sys.stderr = self._standard_stderr
[docs] def clear_log(self): self.stdoutput.messages = "" self.stdoutput.output.value = "" self.stdoutput.flush()
[docs] class LogGUI(DrawsWidgets): """ A class that can redirect stdout and stderr to a widget, and gives controls for both this and toggling the Ryven logger. """ main_widget_class = widgets.VBox def __new__(cls, model, enable_ryven_log, log_to_display, *args, **kwargs): return super().__new__(cls, *args, **kwargs) def __init__( self, model: HasSession, enable_ryven_log: bool, log_to_display: bool, *args, **kwargs, ): super().__init__(*args, **kwargs) self.widget = self.main_widget_class([]) self.model = model self._log_controller = LogController() if log_to_display: self.log_to_display() self.ryven_log_button = widgets.Checkbox( value=enable_ryven_log, description="Use Ryven's InfoMsgs system" ) self.display_log_button = widgets.Checkbox( value=log_to_display, description="Route stdout to ironflow" ) self.clear_button = widgets.Button( description="Clear", tooltip="Clear the log output and flush the stdout." ) self.ryven_log_button.observe(self._toggle_ryven_log) self.display_log_button.observe(self._toggle_display_log) self.clear_button.on_click(self._click_clear) self.widget.children = [ widgets.HBox( [self.display_log_button, self.ryven_log_button, self.clear_button], layout=widgets.Layout(min_height="35px"), ), widgets.HBox([self.output], layout=widgets.Layout(height="435px")), ] @property def output(self): return self._log_controller.output
[docs] def log_to_display(self): self._log_controller.log_to_display()
[docs] def log_to_stdout(self): self._log_controller.log_to_stdout()
def _toggle_ryven_log(self, change: dict): if change["name"] == "value": if change["new"]: self.model.session.info_messenger().enable() else: self.model.session.info_messenger().disable() def _toggle_display_log(self, change: dict): if change["name"] == "value": if change["new"]: self.log_to_display() else: self.log_to_stdout() def _click_clear(self, button: widgets.Button): self._log_controller.clear_log()