diff --git a/requirements.txt b/requirements.txt index 7794aec..882f079 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,7 +51,7 @@ pywin32-ctypes==0.2.3 # via pyinstaller six==1.16.0 # via python-dateutil -tksheet==7.2.22 +tksheet==7.2.23 # via -r requirements.in tzdata==2024.2 # via pandas diff --git a/yagat/app.py b/yagat/app.py index da7c403..007cd7b 100644 --- a/yagat/app.py +++ b/yagat/app.py @@ -9,13 +9,9 @@ import os import tkinter as tk -import pypowsybl as pp - from yagat import get_app_path from yagat.frames import SplashScreen, MainApplication -pp.print_version() - logging.getLogger('powsybl').setLevel(logging.INFO) logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO) diff --git a/yagat/frames/impl/load_flow_parameters.py b/yagat/frames/impl/load_flow_parameters.py index e73db26..68eadcf 100644 --- a/yagat/frames/impl/load_flow_parameters.py +++ b/yagat/frames/impl/load_flow_parameters.py @@ -12,7 +12,7 @@ import pypowsybl.loadflow as lf import tksheet as tks -from pypowsybl._pypowsybl import BalanceType, VoltageInitMode, ConnectedComponentMode +from pypowsybl.loadflow import BalanceType, VoltageInitMode, ConnectedComponentMode from yagat.app_context import AppContext diff --git a/yagat/frames/impl/logs_view.py b/yagat/frames/impl/logs_view.py new file mode 100644 index 0000000..1ed468f --- /dev/null +++ b/yagat/frames/impl/logs_view.py @@ -0,0 +1,58 @@ +# +# Copyright (c) 2024, Damien Jeandemange (https://github.com/jeandemanged) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# +import logging +import tkinter as tk + +import tksheet as tks +from pypowsybl import _pypowsybl + +# we will make this configurable some day +MAX_LOG_ROWS = 2000 + + +class LogsView(tk.Frame, logging.Handler): + + def __init__(self, parent, *args, **kwargs): + tk.Frame.__init__(self, parent, *args, **kwargs) + logging.Handler.__init__(self) + logger = logging.getLogger() + logger.addHandler(self) + self.sheet = tks.Sheet(self, index_align='left') + self.sheet.hide(canvas="top_left") + self.sheet.hide(canvas="row_index") + self.sheet.font(newfont=("Monaco", 12, "normal")) + self.sheet.set_header_data(c=0, value="Time") + self.sheet.set_header_data(c=1, value="Level") + self.sheet.set_header_data(c=2, value="Message") + self.sheet['A:C'].readonly(readonly=True) + self.sheet.enable_bindings('edit_cell', + 'single_select', + 'drag_select', + 'row_select', + 'column_select', + 'copy', + 'column_width_resize', + 'double_click_column_resize', + 'double_click_row_resize', + 'row_width_resize', + 'column_height_resize', + 'arrowkeys', + ) + self.sheet.pack(fill="both", expand=True) + logging.info(_pypowsybl.get_version_table()) + + def emit(self, record): + msg = self.format(record) + self.sheet.insert_row(row=[record.asctime, record.levelname, msg], idx=0) + if record.levelname == 'WARNING': + self.sheet[0:1].highlight(bg="orange") + elif record.levelname == 'ERROR': + self.sheet[0:1].highlight(bg="red") + if len(self.sheet.data) > MAX_LOG_ROWS: + self.sheet.delete_row(idx=MAX_LOG_ROWS) + self.sheet.set_all_cell_sizes_to_text() diff --git a/yagat/frames/impl/main_application.py b/yagat/frames/impl/main_application.py index 5406eed..7883f71 100644 --- a/yagat/frames/impl/main_application.py +++ b/yagat/frames/impl/main_application.py @@ -12,9 +12,10 @@ import yagat from yagat.app_context import AppContext -from yagat.frames.impl.tree_and_tabs import TreeAndTabs -from yagat.frames.impl.status_bar import StatusBar from yagat.frames.impl.load_flow_parameters import LoadFlowParametersView +from yagat.frames.impl.logs_view import LogsView +from yagat.frames.impl.status_bar import StatusBar +from yagat.frames.impl.tree_and_tabs import TreeAndTabs from yagat.menus import MenuBar from yagat.utils import get_centered_geometry @@ -58,6 +59,9 @@ def __init__(self, parent, *args, **kwargs): self.lfp = LoadFlowParametersView(container, self.context) self.lfp.grid(row=0, column=0, sticky="nsew") + self.logs = LogsView(container) + self.logs.grid(row=0, column=0, sticky="nsew") + self.tree_and_diagram.paned_window.tkraise() self.statusbar = StatusBar(self.parent, self.context) @@ -68,6 +72,8 @@ def on_view_changed(self, new_view): self.tree_and_diagram.paned_window.tkraise() elif new_view == 'LoadFlowParameters': self.lfp.tkraise() + elif new_view == 'Logs': + self.logs.tkraise() else: raise ValueError(f'Unknown view {new_view}') diff --git a/yagat/menus/impl/view.py b/yagat/menus/impl/view.py index 57a1764..b95fa25 100644 --- a/yagat/menus/impl/view.py +++ b/yagat/menus/impl/view.py @@ -42,6 +42,8 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): command=lambda: self.update_view_and_tab_group('TreeAndTabs', 'Areas List')) self.add_separator() self.add_command(label='Load Flow Parameters', command=self.view_load_flow_parameters) + self.add_separator() + self.add_command(label='Logs', command=self.view_logs) def update_view_and_tab_group(self, view: str, tab_group: str) -> None: self.context.selected_view = view @@ -49,3 +51,6 @@ def update_view_and_tab_group(self, view: str, tab_group: str) -> None: def view_load_flow_parameters(self): self.context.selected_view = 'LoadFlowParameters' + + def view_logs(self): + self.context.selected_view = 'Logs'