From 2510ca3003c00e868d501efed69621aca3c55e95 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Mon, 11 Nov 2024 15:17:07 +0100 Subject: [PATCH 01/14] bus list view Signed-off-by: Damien Jeandemange --- yagat/frames/__init__.py | 2 +- yagat/frames/impl/bus_list_view.py | 77 +++++++++++++++++++ yagat/frames/impl/diagram_view_bus.py | 4 +- .../impl/{diagram_view.py => tabs_view.py} | 14 +++- yagat/frames/impl/tree_and_diagram.py | 4 +- yagat/frames/impl/tree_view.py | 5 +- .../impl/network_structure.py | 4 + 7 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 yagat/frames/impl/bus_list_view.py rename yagat/frames/impl/{diagram_view.py => tabs_view.py} (77%) diff --git a/yagat/frames/__init__.py b/yagat/frames/__init__.py index c1623e5..cac2f55 100644 --- a/yagat/frames/__init__.py +++ b/yagat/frames/__init__.py @@ -5,10 +5,10 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # -from .impl.diagram_view import DiagramView from .impl.load_flow_parameters import LoadFlowParametersView from .impl.main_application import MainApplication from .impl.splash_screen import SplashScreen from .impl.status_bar import StatusBar +from .impl.tabs_view import TabsView from .impl.tree_and_diagram import TreeAndDiagram from .impl.tree_view import TreeView diff --git a/yagat/frames/impl/bus_list_view.py b/yagat/frames/impl/bus_list_view.py new file mode 100644 index 0000000..a77a70c --- /dev/null +++ b/yagat/frames/impl/bus_list_view.py @@ -0,0 +1,77 @@ +# +# 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 os +import tkinter as tk +from typing import Optional + +import pypowsybl.network as pn +import tksheet as tks + +from yagat.app_context import AppContext +from yagat.networkstructure import Connection + + +class BusListView(tk.Frame): + + def __init__(self, parent, context: AppContext, tab_name: str, *args, **kwargs): + tk.Frame.__init__(self, parent, *args, **kwargs) + self._tab_name = tab_name + self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) + self.sheet.enable_bindings('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.context = context + self.context.add_selection_changed_listener(self.on_selection_changed) + self.variables = [] + + self.sheet.set_index_width(300) + self.sheet.pack(fill="both", expand=True) + + def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): + buses = self.context.network_structure.buses + voltage_levels = [] + if selection[0] == 'voltage_level': + voltage_levels = [selection[1]] + elif selection[0] == 'substation': + voltage_levels = [vl.voltage_level_id for vl in self.context.network_structure.get_substation(selection[1]).voltage_levels] + if voltage_levels: + buses = buses.loc[buses['voltage_level_id'].isin(voltage_levels)] + self.sheet.data = [l.tolist() for l in buses.to_numpy()] + self.sheet.set_index_data(buses.index.tolist()) + self.sheet.set_header_data(buses.columns) + self.sheet.set_all_cell_sizes_to_text() + + @property + def tab_name(self) -> str: + return self._tab_name + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = BusListView(root, ctx, 'Bus list') + bw.pack(fill="both", expand=True) + ctx.network = pn.create_ieee9() + ctx.selection = 'S1' + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/diagram_view_bus.py b/yagat/frames/impl/diagram_view_bus.py index f87b3ff..cd2ff2c 100644 --- a/yagat/frames/impl/diagram_view_bus.py +++ b/yagat/frames/impl/diagram_view_bus.py @@ -46,7 +46,7 @@ def tab_name(self) -> str: return self._tab_name def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): - _, selection_id, selection_connection = selection + selection_type, selection_id, selection_connection = selection selected_connection_y = 0 if self.context.selected_tab != self.tab_name: return @@ -54,6 +54,8 @@ def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Op for w in self.widgets: w.destroy() self.widgets = [] + if selection_type not in ['substation', 'voltage_level']: + return if not selection_id: return if self.context.selected_tab != self.tab_name: diff --git a/yagat/frames/impl/diagram_view.py b/yagat/frames/impl/tabs_view.py similarity index 77% rename from yagat/frames/impl/diagram_view.py rename to yagat/frames/impl/tabs_view.py index 6384ece..b5140cc 100644 --- a/yagat/frames/impl/diagram_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -12,15 +12,16 @@ from yagat.app_context import AppContext from yagat.frames.impl.diagram_view_bus import DiagramViewBus +from yagat.frames.impl.bus_list_view import BusListView from yagat.networkstructure import BusView -class DiagramView(tk.Frame): +class TabsView(tk.Frame): def __init__(self, parent, context: AppContext, *args, **kwargs): tk.Frame.__init__(self, parent, *args, **kwargs) self.context = context self.tab_control = ttk.Notebook(self) - self.tab_control.bind("<>", lambda _: self.on_tab_changed()) + self.tab_control.bind('<>', lambda _: self.on_tab_changed()) # Bus-Breaker view tab self.tab_bus_breaker = DiagramViewBus(self.tab_control, context, 'Bus-Breaker View', BusView.BUS_BREAKER) @@ -32,14 +33,19 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self.tab_control.add(self.tab_bus_branch, text=self.tab_bus_branch.tab_name) self.tab_control.pack(expand=True, fill=tk.BOTH) + # Bus List view tab + self.tab_bus_list = BusListView(self.tab_control, context, 'Bus list') + self.tab_control.add(self.tab_bus_list, text=self.tab_bus_list.tab_name) + self.tab_control.pack(expand=True, fill=tk.BOTH) + def on_tab_changed(self): - self.context.selected_tab = self.tab_control.tab(self.tab_control.select(), "text") + self.context.selected_tab = self.tab_control.tab(self.tab_control.select(), 'text') if __name__ == "__main__": root = tk.Tk() ctx = AppContext(root) - DiagramView(root, ctx).pack(fill="both", expand=True) + TabsView(root, ctx).pack(fill="both", expand=True) ctx.network = pn.create_ieee9() ctx.selection = 'S1' root.mainloop() diff --git a/yagat/frames/impl/tree_and_diagram.py b/yagat/frames/impl/tree_and_diagram.py index 0acdcda..a5e9dda 100644 --- a/yagat/frames/impl/tree_and_diagram.py +++ b/yagat/frames/impl/tree_and_diagram.py @@ -8,7 +8,7 @@ import tkinter as tk from yagat.app_context import AppContext -from yagat.frames.impl.diagram_view import DiagramView +from yagat.frames.impl.tabs_view import TabsView from yagat.frames.impl.tree_view import TreeView @@ -22,7 +22,7 @@ def __init__(self, parent, context, *args, **kwargs): sashpad=4, sashwidth=8) self.tree_view = TreeView(self.paned_window, self.context) - self.right_frame = DiagramView(self.paned_window, self.context) + self.right_frame = TabsView(self.paned_window, self.context) self.paned_window.add(self.tree_view) self.paned_window.add(self.right_frame) diff --git a/yagat/frames/impl/tree_view.py b/yagat/frames/impl/tree_view.py index 0cf85f9..fd28fb3 100644 --- a/yagat/frames/impl/tree_view.py +++ b/yagat/frames/impl/tree_view.py @@ -137,7 +137,8 @@ def on_network_changed(self, network: pn.Network): self.tree_parent = None if not network: return - self.tree_parent = self.tree.insert('', 'end', text=network.name, open=True) + self.tree_parent = self.tree.insert('', 'end', text=network.name, + values=['network', network.id], open=True) for substation in self.context.network_structure.substations: node = self.tree.insert(self.tree_parent, "end", text=f"{substation.name} ({substation.substation_id})", @@ -157,7 +158,7 @@ def on_network_changed(self, network: pn.Network): def on_tree_select(self, event): tree = event.widget selection = [tree.item(item)["values"] for item in tree.selection()] - if selection and selection[0] != '' and selection[0][1] != self.context.selection[1]: + if selection and selection[0][1] != self.context.selection[1]: self.context.selection = (selection[0][0], selection[0][1], None) diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index 1b96382..c5e8848 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -101,6 +101,10 @@ def __init__(self, network: pn.Network): def network(self) -> pn.Network: return self._network + @property + def buses(self) -> pd.DataFrame: + return self._buses_df + def refresh(self): logging.info('refresh start') self._substations_df = self._network.get_substations(all_attributes=True) From 803bfdbd197ebcebe6a7a20a78624c8a7035ad6b Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Mon, 11 Nov 2024 15:37:49 +0100 Subject: [PATCH 02/14] load and generators (need refacto for dup code) Signed-off-by: Damien Jeandemange --- yagat/frames/impl/bus_list_view.py | 7 +- yagat/frames/impl/generator_list_view.py | 76 +++++++++++++++++++ yagat/frames/impl/load_list_view.py | 76 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 14 +++- .../impl/network_structure.py | 8 ++ 5 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 yagat/frames/impl/generator_list_view.py create mode 100644 yagat/frames/impl/load_list_view.py diff --git a/yagat/frames/impl/bus_list_view.py b/yagat/frames/impl/bus_list_view.py index a77a70c..4d9325c 100644 --- a/yagat/frames/impl/bus_list_view.py +++ b/yagat/frames/impl/bus_list_view.py @@ -18,9 +18,8 @@ class BusListView(tk.Frame): - def __init__(self, parent, context: AppContext, tab_name: str, *args, **kwargs): + def __init__(self, parent, context: AppContext, *args, **kwargs): tk.Frame.__init__(self, parent, *args, **kwargs) - self._tab_name = tab_name self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) self.sheet.enable_bindings('single_select', 'drag_select', @@ -57,7 +56,7 @@ def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Op @property def tab_name(self) -> str: - return self._tab_name + return 'Bus list' if __name__ == "__main__": @@ -69,7 +68,7 @@ def tab_name(self) -> str: windll.shcore.SetProcessDpiAwareness(2) root = tk.Tk() ctx = AppContext(root) - bw = BusListView(root, ctx, 'Bus list') + bw = BusListView(root, ctx) bw.pack(fill="both", expand=True) ctx.network = pn.create_ieee9() ctx.selection = 'S1' diff --git a/yagat/frames/impl/generator_list_view.py b/yagat/frames/impl/generator_list_view.py new file mode 100644 index 0000000..4ad8c0e --- /dev/null +++ b/yagat/frames/impl/generator_list_view.py @@ -0,0 +1,76 @@ +# +# 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 os +import tkinter as tk +from typing import Optional + +import pypowsybl.network as pn +import tksheet as tks + +from yagat.app_context import AppContext +from yagat.networkstructure import Connection + + +class GeneratorListView(tk.Frame): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + tk.Frame.__init__(self, parent, *args, **kwargs) + self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) + self.sheet.enable_bindings('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.context = context + self.context.add_selection_changed_listener(self.on_selection_changed) + self.variables = [] + + self.sheet.set_index_width(300) + self.sheet.pack(fill="both", expand=True) + + def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): + generators = self.context.network_structure.generators + voltage_levels = [] + if selection[0] == 'voltage_level': + voltage_levels = [selection[1]] + elif selection[0] == 'substation': + voltage_levels = [vl.voltage_level_id for vl in self.context.network_structure.get_substation(selection[1]).voltage_levels] + if voltage_levels: + generators = generators.loc[generators['voltage_level_id'].isin(voltage_levels)] + self.sheet.data = [l.tolist() for l in generators.to_numpy()] + self.sheet.set_index_data(generators.index.tolist()) + self.sheet.set_header_data(generators.columns) + self.sheet.set_all_cell_sizes_to_text() + + @property + def tab_name(self) -> str: + return 'Generator list' + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = GeneratorListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_ieee9() + ctx.selection = 'S1' + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/load_list_view.py b/yagat/frames/impl/load_list_view.py new file mode 100644 index 0000000..3c7f0e1 --- /dev/null +++ b/yagat/frames/impl/load_list_view.py @@ -0,0 +1,76 @@ +# +# 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 os +import tkinter as tk +from typing import Optional + +import pypowsybl.network as pn +import tksheet as tks + +from yagat.app_context import AppContext +from yagat.networkstructure import Connection + + +class LoadListView(tk.Frame): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + tk.Frame.__init__(self, parent, *args, **kwargs) + self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) + self.sheet.enable_bindings('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.context = context + self.context.add_selection_changed_listener(self.on_selection_changed) + self.variables = [] + + self.sheet.set_index_width(300) + self.sheet.pack(fill="both", expand=True) + + def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): + loads = self.context.network_structure.loads + voltage_levels = [] + if selection[0] == 'voltage_level': + voltage_levels = [selection[1]] + elif selection[0] == 'substation': + voltage_levels = [vl.voltage_level_id for vl in self.context.network_structure.get_substation(selection[1]).voltage_levels] + if voltage_levels: + loads = loads.loc[loads['voltage_level_id'].isin(voltage_levels)] + self.sheet.data = [l.tolist() for l in loads.to_numpy()] + self.sheet.set_index_data(loads.index.tolist()) + self.sheet.set_header_data(loads.columns) + self.sheet.set_all_cell_sizes_to_text() + + @property + def tab_name(self) -> str: + return 'Load list' + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = LoadListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_ieee9() + ctx.selection = 'S1' + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index b5140cc..234ef32 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -13,6 +13,8 @@ from yagat.app_context import AppContext from yagat.frames.impl.diagram_view_bus import DiagramViewBus from yagat.frames.impl.bus_list_view import BusListView +from yagat.frames.impl.generator_list_view import GeneratorListView +from yagat.frames.impl.load_list_view import LoadListView from yagat.networkstructure import BusView @@ -34,10 +36,20 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self.tab_control.pack(expand=True, fill=tk.BOTH) # Bus List view tab - self.tab_bus_list = BusListView(self.tab_control, context, 'Bus list') + self.tab_bus_list = BusListView(self.tab_control, context) self.tab_control.add(self.tab_bus_list, text=self.tab_bus_list.tab_name) self.tab_control.pack(expand=True, fill=tk.BOTH) + # Generators List view tab + self.tab_gen_list = GeneratorListView(self.tab_control, context) + self.tab_control.add(self.tab_gen_list, text=self.tab_gen_list.tab_name) + self.tab_control.pack(expand=True, fill=tk.BOTH) + + # Loads List view tab + self.tab_load_list = LoadListView(self.tab_control, context) + self.tab_control.add(self.tab_load_list, text=self.tab_load_list.tab_name) + self.tab_control.pack(expand=True, fill=tk.BOTH) + def on_tab_changed(self): self.context.selected_tab = self.tab_control.tab(self.tab_control.select(), 'text') diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index c5e8848..5a112da 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -105,6 +105,14 @@ def network(self) -> pn.Network: def buses(self) -> pd.DataFrame: return self._buses_df + @property + def generators(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.GENERATOR] + + @property + def loads(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.LOAD] + def refresh(self): logging.info('refresh start') self._substations_df = self._network.get_substations(all_attributes=True) From 5488efd169bee849f9c981dff6b5948421c78e80 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 21:04:00 +0100 Subject: [PATCH 03/14] refacto Signed-off-by: Damien Jeandemange --- yagat/app_context.py | 16 ++--- yagat/frames/impl/base_list_view.py | 78 ++++++++++++++++++++++++ yagat/frames/impl/bus_list_view.py | 50 ++++----------- yagat/frames/impl/generator_list_view.py | 50 ++++----------- yagat/frames/impl/load_list_view.py | 50 ++++----------- 5 files changed, 119 insertions(+), 125 deletions(-) create mode 100644 yagat/frames/impl/base_list_view.py diff --git a/yagat/app_context.py b/yagat/app_context.py index 4dc5df4..58446cf 100644 --- a/yagat/app_context.py +++ b/yagat/app_context.py @@ -67,14 +67,6 @@ def selected_view(self, value: str) -> None: def network(self) -> Optional[pn.Network]: return self._network - @property - def lf_parameters(self) -> lf.Parameters: - return self._lf_parameters - - @property - def network_structure(self) -> Optional[ns.NetworkStructure]: - return self._network_structure - @network.setter def network(self, new_network: Optional[pn.Network]) -> None: self._network = new_network @@ -85,6 +77,14 @@ def network(self, new_network: Optional[pn.Network]) -> None: self.selection = (None, None, None) self.notify_network_changed() + @property + def lf_parameters(self) -> lf.Parameters: + return self._lf_parameters + + @property + def network_structure(self) -> Optional[ns.NetworkStructure]: + return self._network_structure + @property def selection(self) -> tuple[Optional[str], Optional[str], Optional[ns.Connection]]: return self._selection diff --git a/yagat/frames/impl/base_list_view.py b/yagat/frames/impl/base_list_view.py new file mode 100644 index 0000000..fde2d9d --- /dev/null +++ b/yagat/frames/impl/base_list_view.py @@ -0,0 +1,78 @@ +# +# 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 tkinter as tk +from abc import ABC, abstractmethod +from typing import Optional + +import pandas as pd +import tksheet as tks + +from yagat.app_context import AppContext +from yagat.networkstructure import Connection + + +class BaseListView(tk.Frame, ABC): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + tk.Frame.__init__(self, parent, *args, **kwargs) + self.sheet = tks.Sheet(self, index_align='left') + self.sheet.enable_bindings('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.context = context + self.context.add_selection_changed_listener(self.on_selection_changed) + + self.sheet.set_index_width(300) + self.sheet.pack(fill="both", expand=True) + + @property + @abstractmethod + def tab_name(self) -> str: + pass + + @abstractmethod + def get_data_frame(self) -> pd.DataFrame: + pass + + @abstractmethod + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + pass + + def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): + if not self.context.network_structure: + self.sheet.data = [] + return + df = self.get_data_frame() + voltage_levels = self.filtered_voltage_levels(selection) + if voltage_levels: + df = self.filter_data_frame(df, voltage_levels) + self.sheet.data = [l.tolist() for l in df.to_numpy()] + self.sheet.set_index_data(df.index.tolist()) + self.sheet.set_header_data(df.columns) + self.sheet.set_all_cell_sizes_to_text() + + def filtered_voltage_levels(self, + selection: tuple[Optional[str], Optional[str], Optional[Connection]]) -> list[str]: + voltage_levels: list[str] = [] + if selection[0] == 'network' or not selection[0] or not selection[1]: + return voltage_levels + elif selection[0] == 'voltage_level': + voltage_levels = [selection[1]] + elif selection[0] == 'substation': + voltage_levels = [vl.voltage_level_id for vl in + self.context.network_structure.get_substation(selection[1]).voltage_levels] + return voltage_levels diff --git a/yagat/frames/impl/bus_list_view.py b/yagat/frames/impl/bus_list_view.py index 4d9325c..2d58886 100644 --- a/yagat/frames/impl/bus_list_view.py +++ b/yagat/frames/impl/bus_list_view.py @@ -7,57 +7,29 @@ # import os import tkinter as tk -from typing import Optional +import pandas as pd import pypowsybl.network as pn -import tksheet as tks from yagat.app_context import AppContext -from yagat.networkstructure import Connection +from yagat.frames.impl.base_list_view import BaseListView -class BusListView(tk.Frame): +class BusListView(BaseListView): def __init__(self, parent, context: AppContext, *args, **kwargs): - tk.Frame.__init__(self, parent, *args, **kwargs) - self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) - self.sheet.enable_bindings('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.context = context - self.context.add_selection_changed_listener(self.on_selection_changed) - self.variables = [] - - self.sheet.set_index_width(300) - self.sheet.pack(fill="both", expand=True) - - def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): - buses = self.context.network_structure.buses - voltage_levels = [] - if selection[0] == 'voltage_level': - voltage_levels = [selection[1]] - elif selection[0] == 'substation': - voltage_levels = [vl.voltage_level_id for vl in self.context.network_structure.get_substation(selection[1]).voltage_levels] - if voltage_levels: - buses = buses.loc[buses['voltage_level_id'].isin(voltage_levels)] - self.sheet.data = [l.tolist() for l in buses.to_numpy()] - self.sheet.set_index_data(buses.index.tolist()) - self.sheet.set_header_data(buses.columns) - self.sheet.set_all_cell_sizes_to_text() + BaseListView.__init__(self, parent, context, *args, **kwargs) @property def tab_name(self) -> str: return 'Bus list' + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.buses + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + if __name__ == "__main__": @@ -71,6 +43,6 @@ def tab_name(self) -> str: bw = BusListView(root, ctx) bw.pack(fill="both", expand=True) ctx.network = pn.create_ieee9() - ctx.selection = 'S1' + ctx.selection = ('network', '', None) ctx.selected_tab = bw.tab_name root.mainloop() diff --git a/yagat/frames/impl/generator_list_view.py b/yagat/frames/impl/generator_list_view.py index 4ad8c0e..cada62e 100644 --- a/yagat/frames/impl/generator_list_view.py +++ b/yagat/frames/impl/generator_list_view.py @@ -7,57 +7,29 @@ # import os import tkinter as tk -from typing import Optional +import pandas as pd import pypowsybl.network as pn -import tksheet as tks from yagat.app_context import AppContext -from yagat.networkstructure import Connection +from yagat.frames.impl.base_list_view import BaseListView -class GeneratorListView(tk.Frame): +class GeneratorListView(BaseListView): def __init__(self, parent, context: AppContext, *args, **kwargs): - tk.Frame.__init__(self, parent, *args, **kwargs) - self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) - self.sheet.enable_bindings('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.context = context - self.context.add_selection_changed_listener(self.on_selection_changed) - self.variables = [] - - self.sheet.set_index_width(300) - self.sheet.pack(fill="both", expand=True) - - def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): - generators = self.context.network_structure.generators - voltage_levels = [] - if selection[0] == 'voltage_level': - voltage_levels = [selection[1]] - elif selection[0] == 'substation': - voltage_levels = [vl.voltage_level_id for vl in self.context.network_structure.get_substation(selection[1]).voltage_levels] - if voltage_levels: - generators = generators.loc[generators['voltage_level_id'].isin(voltage_levels)] - self.sheet.data = [l.tolist() for l in generators.to_numpy()] - self.sheet.set_index_data(generators.index.tolist()) - self.sheet.set_header_data(generators.columns) - self.sheet.set_all_cell_sizes_to_text() + BaseListView.__init__(self, parent, context, *args, **kwargs) @property def tab_name(self) -> str: return 'Generator list' + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.generators + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + if __name__ == "__main__": @@ -71,6 +43,6 @@ def tab_name(self) -> str: bw = GeneratorListView(root, ctx) bw.pack(fill="both", expand=True) ctx.network = pn.create_ieee9() - ctx.selection = 'S1' + ctx.selection = ('network', '', None) ctx.selected_tab = bw.tab_name root.mainloop() diff --git a/yagat/frames/impl/load_list_view.py b/yagat/frames/impl/load_list_view.py index 3c7f0e1..0830070 100644 --- a/yagat/frames/impl/load_list_view.py +++ b/yagat/frames/impl/load_list_view.py @@ -7,57 +7,29 @@ # import os import tkinter as tk -from typing import Optional +import pandas as pd import pypowsybl.network as pn -import tksheet as tks from yagat.app_context import AppContext -from yagat.networkstructure import Connection +from yagat.frames.impl.base_list_view import BaseListView -class LoadListView(tk.Frame): +class LoadListView(BaseListView): def __init__(self, parent, context: AppContext, *args, **kwargs): - tk.Frame.__init__(self, parent, *args, **kwargs) - self.sheet = tks.Sheet(self, index_align='left', data=[[1, 2], [3, 4]]) - self.sheet.enable_bindings('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.context = context - self.context.add_selection_changed_listener(self.on_selection_changed) - self.variables = [] - - self.sheet.set_index_width(300) - self.sheet.pack(fill="both", expand=True) - - def on_selection_changed(self, selection: tuple[Optional[str], Optional[str], Optional[Connection]]): - loads = self.context.network_structure.loads - voltage_levels = [] - if selection[0] == 'voltage_level': - voltage_levels = [selection[1]] - elif selection[0] == 'substation': - voltage_levels = [vl.voltage_level_id for vl in self.context.network_structure.get_substation(selection[1]).voltage_levels] - if voltage_levels: - loads = loads.loc[loads['voltage_level_id'].isin(voltage_levels)] - self.sheet.data = [l.tolist() for l in loads.to_numpy()] - self.sheet.set_index_data(loads.index.tolist()) - self.sheet.set_header_data(loads.columns) - self.sheet.set_all_cell_sizes_to_text() + BaseListView.__init__(self, parent, context, *args, **kwargs) @property def tab_name(self) -> str: return 'Load list' + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.loads + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + if __name__ == "__main__": @@ -71,6 +43,6 @@ def tab_name(self) -> str: bw = LoadListView(root, ctx) bw.pack(fill="both", expand=True) ctx.network = pn.create_ieee9() - ctx.selection = 'S1' + ctx.selection = ('network', '', None) ctx.selected_tab = bw.tab_name root.mainloop() From f87f98a12e48954036dc27091af3900a3460138c Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 21:31:56 +0100 Subject: [PATCH 04/14] wip Signed-off-by: Damien Jeandemange --- .../impl/buses_bus_breaker_view_list_view.py | 48 +++++++++++++++++++ ...st_view.py => buses_bus_view_list_view.py} | 6 +-- yagat/frames/impl/generator_list_view.py | 2 +- yagat/frames/impl/load_list_view.py | 2 +- yagat/frames/impl/tabs_view.py | 20 ++++---- .../impl/network_structure.py | 18 ++++--- 6 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 yagat/frames/impl/buses_bus_breaker_view_list_view.py rename yagat/frames/impl/{bus_list_view.py => buses_bus_view_list_view.py} (92%) diff --git a/yagat/frames/impl/buses_bus_breaker_view_list_view.py b/yagat/frames/impl/buses_bus_breaker_view_list_view.py new file mode 100644 index 0000000..9cf0c83 --- /dev/null +++ b/yagat/frames/impl/buses_bus_breaker_view_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class BusesBusBreakerViewListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Buses (Bus/Breaker View)' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.buses_bus_breaker_view + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = BusesBusBreakerViewListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_ieee9() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/bus_list_view.py b/yagat/frames/impl/buses_bus_view_list_view.py similarity index 92% rename from yagat/frames/impl/bus_list_view.py rename to yagat/frames/impl/buses_bus_view_list_view.py index 2d58886..22b7d89 100644 --- a/yagat/frames/impl/bus_list_view.py +++ b/yagat/frames/impl/buses_bus_view_list_view.py @@ -15,14 +15,14 @@ from yagat.frames.impl.base_list_view import BaseListView -class BusListView(BaseListView): +class BusesListView(BaseListView): def __init__(self, parent, context: AppContext, *args, **kwargs): BaseListView.__init__(self, parent, context, *args, **kwargs) @property def tab_name(self) -> str: - return 'Bus list' + return 'Buses (Bus View)' def get_data_frame(self) -> pd.DataFrame: return self.context.network_structure.buses @@ -40,7 +40,7 @@ def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.D windll.shcore.SetProcessDpiAwareness(2) root = tk.Tk() ctx = AppContext(root) - bw = BusListView(root, ctx) + bw = BusesListView(root, ctx) bw.pack(fill="both", expand=True) ctx.network = pn.create_ieee9() ctx.selection = ('network', '', None) diff --git a/yagat/frames/impl/generator_list_view.py b/yagat/frames/impl/generator_list_view.py index cada62e..66901f7 100644 --- a/yagat/frames/impl/generator_list_view.py +++ b/yagat/frames/impl/generator_list_view.py @@ -22,7 +22,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): @property def tab_name(self) -> str: - return 'Generator list' + return 'Generators' def get_data_frame(self) -> pd.DataFrame: return self.context.network_structure.generators diff --git a/yagat/frames/impl/load_list_view.py b/yagat/frames/impl/load_list_view.py index 0830070..4bd3e72 100644 --- a/yagat/frames/impl/load_list_view.py +++ b/yagat/frames/impl/load_list_view.py @@ -22,7 +22,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): @property def tab_name(self) -> str: - return 'Load list' + return 'Loads' def get_data_frame(self) -> pd.DataFrame: return self.context.network_structure.loads diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index 234ef32..49347e0 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -12,7 +12,8 @@ from yagat.app_context import AppContext from yagat.frames.impl.diagram_view_bus import DiagramViewBus -from yagat.frames.impl.bus_list_view import BusListView +from yagat.frames.impl.buses_bus_view_list_view import BusesListView +from yagat.frames.impl.buses_bus_breaker_view_list_view import BusesBusBreakerViewListView from yagat.frames.impl.generator_list_view import GeneratorListView from yagat.frames.impl.load_list_view import LoadListView from yagat.networkstructure import BusView @@ -26,28 +27,29 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self.tab_control.bind('<>', lambda _: self.on_tab_changed()) # Bus-Breaker view tab - self.tab_bus_breaker = DiagramViewBus(self.tab_control, context, 'Bus-Breaker View', BusView.BUS_BREAKER) + self.tab_bus_breaker = DiagramViewBus(self.tab_control, context, 'Bus/Breaker View', BusView.BUS_BREAKER) self.tab_control.add(self.tab_bus_breaker, text=self.tab_bus_breaker.tab_name) - self.tab_control.pack(expand=True, fill=tk.BOTH) # Bus-Branch view tab - self.tab_bus_branch = DiagramViewBus(self.tab_control, context, 'Bus-Branch View', BusView.BUS_BRANCH) + self.tab_bus_branch = DiagramViewBus(self.tab_control, context, 'Bus View', BusView.BUS_BRANCH) self.tab_control.add(self.tab_bus_branch, text=self.tab_bus_branch.tab_name) - self.tab_control.pack(expand=True, fill=tk.BOTH) # Bus List view tab - self.tab_bus_list = BusListView(self.tab_control, context) + self.tab_bus_list = BusesListView(self.tab_control, context) self.tab_control.add(self.tab_bus_list, text=self.tab_bus_list.tab_name) - self.tab_control.pack(expand=True, fill=tk.BOTH) + + # Bus List view tab + self.tab_bus_bus_breaker_view_list = BusesBusBreakerViewListView(self.tab_control, context) + self.tab_control.add(self.tab_bus_bus_breaker_view_list, text=self.tab_bus_bus_breaker_view_list.tab_name) # Generators List view tab self.tab_gen_list = GeneratorListView(self.tab_control, context) self.tab_control.add(self.tab_gen_list, text=self.tab_gen_list.tab_name) - self.tab_control.pack(expand=True, fill=tk.BOTH) # Loads List view tab self.tab_load_list = LoadListView(self.tab_control, context) self.tab_control.add(self.tab_load_list, text=self.tab_load_list.tab_name) + self.tab_control.pack(expand=True, fill=tk.BOTH) def on_tab_changed(self): @@ -59,5 +61,5 @@ def on_tab_changed(self): ctx = AppContext(root) TabsView(root, ctx).pack(fill="both", expand=True) ctx.network = pn.create_ieee9() - ctx.selection = 'S1' + ctx.selection = ('substation', 'S1', None) root.mainloop() diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index 5a112da..464d7ef 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -6,7 +6,7 @@ # SPDX-License-Identifier: MPL-2.0 # import logging -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Tuple import pandas as pd import pypowsybl.network as pn @@ -19,7 +19,7 @@ def __init__(self, network: pn.Network): self._network: pn.Network = network self._substations: Dict[str, ns.Substation] = {} self._voltage_levels: Dict[str, ns.VoltageLevel] = {} - self._connections: Dict[(str, Optional[int]), ns.Connection] = {} + self._connections: Dict[Tuple[str, Optional[int]], ns.Connection] = {} self._substations_df: pd.DataFrame = pd.DataFrame() self._voltage_levels_df: pd.DataFrame = pd.DataFrame() @@ -29,6 +29,7 @@ def __init__(self, network: pn.Network): self._three_windings_transformers_df: pd.DataFrame = pd.DataFrame() self._tie_lines_df: pd.DataFrame = pd.DataFrame() self._buses_df: pd.DataFrame = pd.DataFrame() + self._buses_bus_breaker_view_df: pd.DataFrame = pd.DataFrame() self._switches_df: pd.DataFrame = pd.DataFrame() self._hvdc_lines_df: pd.DataFrame = pd.DataFrame() @@ -105,6 +106,10 @@ def network(self) -> pn.Network: def buses(self) -> pd.DataFrame: return self._buses_df + @property + def buses_bus_breaker_view(self) -> pd.DataFrame: + return self._buses_bus_breaker_view_df + @property def generators(self) -> pd.DataFrame: return self._injections_df[ns.EquipmentType.GENERATOR] @@ -135,6 +140,7 @@ def refresh(self): self._branches_df[ns.EquipmentType.TWO_WINDINGS_TRANSFORMER] = self._network.get_2_windings_transformers( all_attributes=True) self._buses_df = self._network.get_buses(all_attributes=True) + self._buses_bus_breaker_view_df = self._network.get_bus_breaker_view_buses(all_attributes=True) self._linear_shunt_compensator_sections_df = self._network.get_linear_shunt_compensator_sections( all_attributes=True) self._non_linear_shunt_compensator_sections_df = self._network.get_non_linear_shunt_compensator_sections( @@ -200,10 +206,10 @@ def get_connection(self, connection_id: str, side: Optional[int]) -> 'Optional[n return self._connections[(connection_id, side)] return None - def get_voltage_level_data(self, voltage_level: 'ns.VoltageLevel') -> pd.Series: + def get_voltage_level_data(self, voltage_level: 'ns.VoltageLevel') -> pd.DataFrame: return self._voltage_levels_df.loc[voltage_level.voltage_level_id] - def get_connection_data(self, connection_id: str, side: Optional[int]) -> pd.Series: + def get_connection_data(self, connection_id: str, side: Optional[int]) -> pd.DataFrame: connection = self._connections[(connection_id, side)] if not connection: return pd.Series() @@ -235,12 +241,12 @@ def get_shunt_compensator_type(self, connection: ns.Connection) -> 'ns.ShuntComp def is_retained(self, connection: ns.Connection) -> bool: if connection.equipment_type != ns.EquipmentType.SWITCH: raise RuntimeError('Not a switch') - return self._switches_df.loc[connection.equipment_id]['retained'] + return bool(self._switches_df.loc[connection.equipment_id]['retained']) def is_open(self, connection: ns.Connection) -> bool: if connection.equipment_type != ns.EquipmentType.SWITCH: raise RuntimeError('Not a switch') - return self._switches_df.loc[connection.equipment_id]['open'] + return bool(self._switches_df.loc[connection.equipment_id]['open']) def get_other_sides(self, connection: ns.Connection) -> List[ns.Connection]: if connection.equipment_type in ns.EquipmentType.branch_types() or connection.equipment_type == ns.EquipmentType.SWITCH: From 3182365fcc57b36c5f7025ea6e8c611d83f9e699 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 21:56:01 +0100 Subject: [PATCH 05/14] lines and 2wt Signed-off-by: Damien Jeandemange --- yagat/frames/impl/line_list_view.py | 48 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 9 ++++ .../two_windings_transformer_list_view.py | 48 +++++++++++++++++++ .../impl/network_structure.py | 8 ++++ 4 files changed, 113 insertions(+) create mode 100644 yagat/frames/impl/line_list_view.py create mode 100644 yagat/frames/impl/two_windings_transformer_list_view.py diff --git a/yagat/frames/impl/line_list_view.py b/yagat/frames/impl/line_list_view.py new file mode 100644 index 0000000..194a240 --- /dev/null +++ b/yagat/frames/impl/line_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class LineListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Lines' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.lines + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level1_id'].isin(voltage_levels) | df['voltage_level2_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = LineListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_ieee9() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index 49347e0..3ac1753 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -16,6 +16,8 @@ from yagat.frames.impl.buses_bus_breaker_view_list_view import BusesBusBreakerViewListView from yagat.frames.impl.generator_list_view import GeneratorListView from yagat.frames.impl.load_list_view import LoadListView +from yagat.frames.impl.line_list_view import LineListView +from yagat.frames.impl.two_windings_transformer_list_view import TwoWindingsTransformerListView from yagat.networkstructure import BusView @@ -50,6 +52,13 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self.tab_load_list = LoadListView(self.tab_control, context) self.tab_control.add(self.tab_load_list, text=self.tab_load_list.tab_name) + # Lines List view tab + self.tab_lines_list = LineListView(self.tab_control, context) + self.tab_control.add(self.tab_lines_list, text=self.tab_lines_list.tab_name) + + self.tab_t2wt_list = TwoWindingsTransformerListView(self.tab_control, context) + self.tab_control.add(self.tab_t2wt_list, text=self.tab_t2wt_list.tab_name) + self.tab_control.pack(expand=True, fill=tk.BOTH) def on_tab_changed(self): diff --git a/yagat/frames/impl/two_windings_transformer_list_view.py b/yagat/frames/impl/two_windings_transformer_list_view.py new file mode 100644 index 0000000..ad12d1d --- /dev/null +++ b/yagat/frames/impl/two_windings_transformer_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class TwoWindingsTransformerListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return '2W Transformers' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.two_windings_transformers + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level1_id'].isin(voltage_levels) | df['voltage_level2_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = TwoWindingsTransformerListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_ieee9() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index 464d7ef..4886777 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -118,6 +118,14 @@ def generators(self) -> pd.DataFrame: def loads(self) -> pd.DataFrame: return self._injections_df[ns.EquipmentType.LOAD] + @property + def lines(self) -> pd.DataFrame: + return self._branches_df[ns.EquipmentType.LINE] + + @property + def two_windings_transformers(self) -> pd.DataFrame: + return self._branches_df[ns.EquipmentType.TWO_WINDINGS_TRANSFORMER] + def refresh(self): logging.info('refresh start') self._substations_df = self._network.get_substations(all_attributes=True) From 2ddc6a2f560a63092ef9be6b8c53389672a14634 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:14:28 +0100 Subject: [PATCH 06/14] wip Signed-off-by: Damien Jeandemange --- yagat/frames/impl/tabs_view.py | 40 ++++++++++------------------------ 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index 3ac1753..d650a81 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -28,38 +28,20 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self.tab_control = ttk.Notebook(self) self.tab_control.bind('<>', lambda _: self.on_tab_changed()) - # Bus-Breaker view tab - self.tab_bus_breaker = DiagramViewBus(self.tab_control, context, 'Bus/Breaker View', BusView.BUS_BREAKER) - self.tab_control.add(self.tab_bus_breaker, text=self.tab_bus_breaker.tab_name) + self._add_tab(DiagramViewBus(self.tab_control, context, 'Bus/Breaker View', BusView.BUS_BREAKER)) + self._add_tab(DiagramViewBus(self.tab_control, context, 'Bus View', BusView.BUS_BRANCH)) + self._add_tab(BusesListView(self.tab_control, self.context)) + self._add_tab(BusesBusBreakerViewListView(self.tab_control, self.context)) + self._add_tab(GeneratorListView(self.tab_control, self.context)) + self._add_tab(LoadListView(self.tab_control, self.context)) + self._add_tab(LineListView(self.tab_control, self.context)) + self._add_tab(TwoWindingsTransformerListView(self.tab_control, self.context)) - # Bus-Branch view tab - self.tab_bus_branch = DiagramViewBus(self.tab_control, context, 'Bus View', BusView.BUS_BRANCH) - self.tab_control.add(self.tab_bus_branch, text=self.tab_bus_branch.tab_name) - - # Bus List view tab - self.tab_bus_list = BusesListView(self.tab_control, context) - self.tab_control.add(self.tab_bus_list, text=self.tab_bus_list.tab_name) - - # Bus List view tab - self.tab_bus_bus_breaker_view_list = BusesBusBreakerViewListView(self.tab_control, context) - self.tab_control.add(self.tab_bus_bus_breaker_view_list, text=self.tab_bus_bus_breaker_view_list.tab_name) - - # Generators List view tab - self.tab_gen_list = GeneratorListView(self.tab_control, context) - self.tab_control.add(self.tab_gen_list, text=self.tab_gen_list.tab_name) - - # Loads List view tab - self.tab_load_list = LoadListView(self.tab_control, context) - self.tab_control.add(self.tab_load_list, text=self.tab_load_list.tab_name) - - # Lines List view tab - self.tab_lines_list = LineListView(self.tab_control, context) - self.tab_control.add(self.tab_lines_list, text=self.tab_lines_list.tab_name) + self.tab_control.pack(expand=True, fill=tk.BOTH) - self.tab_t2wt_list = TwoWindingsTransformerListView(self.tab_control, context) - self.tab_control.add(self.tab_t2wt_list, text=self.tab_t2wt_list.tab_name) + def _add_tab(self, tab): + self.tab_control.add(tab, text=tab.tab_name) - self.tab_control.pack(expand=True, fill=tk.BOTH) def on_tab_changed(self): self.context.selected_tab = self.tab_control.tab(self.tab_control.select(), 'text') From cc9ab634b288bec4a9a54e625109b314413c3dcd Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:17:12 +0100 Subject: [PATCH 07/14] t3wt Signed-off-by: Damien Jeandemange --- yagat/frames/impl/tabs_view.py | 2 + .../three_windings_transformer_list_view.py | 48 +++++++++++++++++++ .../impl/network_structure.py | 4 ++ 3 files changed, 54 insertions(+) create mode 100644 yagat/frames/impl/three_windings_transformer_list_view.py diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index d650a81..aa00933 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -18,6 +18,7 @@ from yagat.frames.impl.load_list_view import LoadListView from yagat.frames.impl.line_list_view import LineListView from yagat.frames.impl.two_windings_transformer_list_view import TwoWindingsTransformerListView +from yagat.frames.impl.three_windings_transformer_list_view import ThreeWindingsTransformerListView from yagat.networkstructure import BusView @@ -36,6 +37,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(LoadListView(self.tab_control, self.context)) self._add_tab(LineListView(self.tab_control, self.context)) self._add_tab(TwoWindingsTransformerListView(self.tab_control, self.context)) + self._add_tab(ThreeWindingsTransformerListView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/frames/impl/three_windings_transformer_list_view.py b/yagat/frames/impl/three_windings_transformer_list_view.py new file mode 100644 index 0000000..3fc8aa0 --- /dev/null +++ b/yagat/frames/impl/three_windings_transformer_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class ThreeWindingsTransformerListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return '3W Transformers' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.three_windings_transformers + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level1_id'].isin(voltage_levels) | df['voltage_level2_id'].isin(voltage_levels) | df['voltage_level3_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = ThreeWindingsTransformerListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_micro_grid_be_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index 4886777..a6eacda 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -126,6 +126,10 @@ def lines(self) -> pd.DataFrame: def two_windings_transformers(self) -> pd.DataFrame: return self._branches_df[ns.EquipmentType.TWO_WINDINGS_TRANSFORMER] + @property + def three_windings_transformers(self) -> pd.DataFrame: + return self._three_windings_transformers_df + def refresh(self): logging.info('refresh start') self._substations_df = self._network.get_substations(all_attributes=True) From 6371ec80e45afdee8bd730a746d268b6302385ea Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:22:19 +0100 Subject: [PATCH 08/14] dl Signed-off-by: Damien Jeandemange --- yagat/frames/impl/dangling_line_list_view.py | 48 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 2 + .../impl/network_structure.py | 4 ++ 3 files changed, 54 insertions(+) create mode 100644 yagat/frames/impl/dangling_line_list_view.py diff --git a/yagat/frames/impl/dangling_line_list_view.py b/yagat/frames/impl/dangling_line_list_view.py new file mode 100644 index 0000000..b37436e --- /dev/null +++ b/yagat/frames/impl/dangling_line_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class DanglingLineListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Dangling Lines' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.dangling_lines + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = DanglingLineListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_micro_grid_be_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index aa00933..a022d4e 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -19,6 +19,7 @@ from yagat.frames.impl.line_list_view import LineListView from yagat.frames.impl.two_windings_transformer_list_view import TwoWindingsTransformerListView from yagat.frames.impl.three_windings_transformer_list_view import ThreeWindingsTransformerListView +from yagat.frames.impl.dangling_line_list_view import DanglingLineListView from yagat.networkstructure import BusView @@ -38,6 +39,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(LineListView(self.tab_control, self.context)) self._add_tab(TwoWindingsTransformerListView(self.tab_control, self.context)) self._add_tab(ThreeWindingsTransformerListView(self.tab_control, self.context)) + self._add_tab(DanglingLineListView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index a6eacda..515360d 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -126,6 +126,10 @@ def lines(self) -> pd.DataFrame: def two_windings_transformers(self) -> pd.DataFrame: return self._branches_df[ns.EquipmentType.TWO_WINDINGS_TRANSFORMER] + @property + def dangling_lines(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.DANGLING_LINE] + @property def three_windings_transformers(self) -> pd.DataFrame: return self._three_windings_transformers_df From 65858c289ba272f6f0bb227bd9e03a67b7732864 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:25:44 +0100 Subject: [PATCH 09/14] sc Signed-off-by: Damien Jeandemange --- .../impl/shunt_compensator_list_view.py | 48 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 2 + .../impl/network_structure.py | 4 ++ 3 files changed, 54 insertions(+) create mode 100644 yagat/frames/impl/shunt_compensator_list_view.py diff --git a/yagat/frames/impl/shunt_compensator_list_view.py b/yagat/frames/impl/shunt_compensator_list_view.py new file mode 100644 index 0000000..4bccf39 --- /dev/null +++ b/yagat/frames/impl/shunt_compensator_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class ShuntCompensatorListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Shunt Compensators' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.shunt_compensators + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = ShuntCompensatorListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_micro_grid_be_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index a022d4e..e8fcf60 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -20,6 +20,7 @@ from yagat.frames.impl.two_windings_transformer_list_view import TwoWindingsTransformerListView from yagat.frames.impl.three_windings_transformer_list_view import ThreeWindingsTransformerListView from yagat.frames.impl.dangling_line_list_view import DanglingLineListView +from yagat.frames.impl.shunt_compensator_list_view import ShuntCompensatorListView from yagat.networkstructure import BusView @@ -40,6 +41,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(TwoWindingsTransformerListView(self.tab_control, self.context)) self._add_tab(ThreeWindingsTransformerListView(self.tab_control, self.context)) self._add_tab(DanglingLineListView(self.tab_control, self.context)) + self._add_tab(ShuntCompensatorListView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index 515360d..a30e4c2 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -130,6 +130,10 @@ def two_windings_transformers(self) -> pd.DataFrame: def dangling_lines(self) -> pd.DataFrame: return self._injections_df[ns.EquipmentType.DANGLING_LINE] + @property + def shunt_compensators(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.SHUNT_COMPENSATOR] + @property def three_windings_transformers(self) -> pd.DataFrame: return self._three_windings_transformers_df From 2f374d984dfb96a5d737c119e01623726fd55060 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:39:44 +0100 Subject: [PATCH 10/14] sc Signed-off-by: Damien Jeandemange --- .../impl/static_var_compensator_list_view.py | 48 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 2 + .../impl/network_structure.py | 4 ++ 3 files changed, 54 insertions(+) create mode 100644 yagat/frames/impl/static_var_compensator_list_view.py diff --git a/yagat/frames/impl/static_var_compensator_list_view.py b/yagat/frames/impl/static_var_compensator_list_view.py new file mode 100644 index 0000000..e668228 --- /dev/null +++ b/yagat/frames/impl/static_var_compensator_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class StaticVarCompensatorListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Static VAR Compensators' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.static_var_compensators + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = StaticVarCompensatorListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_four_substations_node_breaker_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index e8fcf60..bd3b4b1 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -21,6 +21,7 @@ from yagat.frames.impl.three_windings_transformer_list_view import ThreeWindingsTransformerListView from yagat.frames.impl.dangling_line_list_view import DanglingLineListView from yagat.frames.impl.shunt_compensator_list_view import ShuntCompensatorListView +from yagat.frames.impl.static_var_compensator_list_view import StaticVarCompensatorListView from yagat.networkstructure import BusView @@ -42,6 +43,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(ThreeWindingsTransformerListView(self.tab_control, self.context)) self._add_tab(DanglingLineListView(self.tab_control, self.context)) self._add_tab(ShuntCompensatorListView(self.tab_control, self.context)) + self._add_tab(StaticVarCompensatorListView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index a30e4c2..de961a3 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -134,6 +134,10 @@ def dangling_lines(self) -> pd.DataFrame: def shunt_compensators(self) -> pd.DataFrame: return self._injections_df[ns.EquipmentType.SHUNT_COMPENSATOR] + @property + def static_var_compensators(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.STATIC_VAR_COMPENSATOR] + @property def three_windings_transformers(self) -> pd.DataFrame: return self._three_windings_transformers_df From f988c2c52934d2ba81f6ff9fa56e24010585c48d Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:45:35 +0100 Subject: [PATCH 11/14] lcc and vsc hvdc Signed-off-by: Damien Jeandemange --- yagat/frames/impl/lcc_list_view.py | 48 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 4 ++ yagat/frames/impl/vsc_list_view.py | 48 +++++++++++++++++++ .../impl/network_structure.py | 8 ++++ 4 files changed, 108 insertions(+) create mode 100644 yagat/frames/impl/lcc_list_view.py create mode 100644 yagat/frames/impl/vsc_list_view.py diff --git a/yagat/frames/impl/lcc_list_view.py b/yagat/frames/impl/lcc_list_view.py new file mode 100644 index 0000000..514cb3b --- /dev/null +++ b/yagat/frames/impl/lcc_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class LccListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'LCC HVDC' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.lcc_hvdc + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = LccListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_four_substations_node_breaker_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index bd3b4b1..b35e8f1 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -22,6 +22,8 @@ from yagat.frames.impl.dangling_line_list_view import DanglingLineListView from yagat.frames.impl.shunt_compensator_list_view import ShuntCompensatorListView from yagat.frames.impl.static_var_compensator_list_view import StaticVarCompensatorListView +from yagat.frames.impl.lcc_list_view import LccListView +from yagat.frames.impl.vsc_list_view import VscListView from yagat.networkstructure import BusView @@ -44,6 +46,8 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(DanglingLineListView(self.tab_control, self.context)) self._add_tab(ShuntCompensatorListView(self.tab_control, self.context)) self._add_tab(StaticVarCompensatorListView(self.tab_control, self.context)) + self._add_tab(LccListView(self.tab_control, self.context)) + self._add_tab(VscListView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/frames/impl/vsc_list_view.py b/yagat/frames/impl/vsc_list_view.py new file mode 100644 index 0000000..df85980 --- /dev/null +++ b/yagat/frames/impl/vsc_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class VscListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'VSC HVDC' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.vsc_hvdc + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = VscListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_four_substations_node_breaker_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index de961a3..e400baf 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -138,6 +138,14 @@ def shunt_compensators(self) -> pd.DataFrame: def static_var_compensators(self) -> pd.DataFrame: return self._injections_df[ns.EquipmentType.STATIC_VAR_COMPENSATOR] + @property + def lcc_hvdc(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.LCC_CONVERTER_STATION] + + @property + def vsc_hvdc(self) -> pd.DataFrame: + return self._injections_df[ns.EquipmentType.VSC_CONVERTER_STATION] + @property def three_windings_transformers(self) -> pd.DataFrame: return self._three_windings_transformers_df From 8663364d8c8e711ef160122725ed33e7a1db234b Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:49:05 +0100 Subject: [PATCH 12/14] switches Signed-off-by: Damien Jeandemange --- yagat/frames/impl/switch_list_view.py | 48 +++++++++++++++++++ yagat/frames/impl/tabs_view.py | 2 + .../impl/network_structure.py | 4 ++ 3 files changed, 54 insertions(+) create mode 100644 yagat/frames/impl/switch_list_view.py diff --git a/yagat/frames/impl/switch_list_view.py b/yagat/frames/impl/switch_list_view.py new file mode 100644 index 0000000..6c0b53f --- /dev/null +++ b/yagat/frames/impl/switch_list_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class SwitchListView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Switches' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.switches + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df.loc[df['voltage_level_id'].isin(voltage_levels)] + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = SwitchListView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_four_substations_node_breaker_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index b35e8f1..30dec02 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -24,6 +24,7 @@ from yagat.frames.impl.static_var_compensator_list_view import StaticVarCompensatorListView from yagat.frames.impl.lcc_list_view import LccListView from yagat.frames.impl.vsc_list_view import VscListView +from yagat.frames.impl.switch_list_view import SwitchListView from yagat.networkstructure import BusView @@ -48,6 +49,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(StaticVarCompensatorListView(self.tab_control, self.context)) self._add_tab(LccListView(self.tab_control, self.context)) self._add_tab(VscListView(self.tab_control, self.context)) + self._add_tab(SwitchListView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index e400baf..3bdf7c9 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -150,6 +150,10 @@ def vsc_hvdc(self) -> pd.DataFrame: def three_windings_transformers(self) -> pd.DataFrame: return self._three_windings_transformers_df + @property + def switches(self) -> pd.DataFrame: + return self._switches_df + def refresh(self): logging.info('refresh start') self._substations_df = self._network.get_substations(all_attributes=True) From 24f07f60cb1f30ff01ca1a2a669a0c37ef0f18d8 Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 22:58:03 +0100 Subject: [PATCH 13/14] tielines Signed-off-by: Damien Jeandemange --- yagat/frames/impl/tabs_view.py | 2 + yagat/frames/impl/tie_line_view.py | 48 +++++++++++++++++++ yagat/menus/impl/file.py | 10 ++++ .../impl/network_structure.py | 8 ++++ 4 files changed, 68 insertions(+) create mode 100644 yagat/frames/impl/tie_line_view.py diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index 30dec02..266976c 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -25,6 +25,7 @@ from yagat.frames.impl.lcc_list_view import LccListView from yagat.frames.impl.vsc_list_view import VscListView from yagat.frames.impl.switch_list_view import SwitchListView +from yagat.frames.impl.tie_line_view import TieLineView from yagat.networkstructure import BusView @@ -50,6 +51,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(LccListView(self.tab_control, self.context)) self._add_tab(VscListView(self.tab_control, self.context)) self._add_tab(SwitchListView(self.tab_control, self.context)) + self._add_tab(TieLineView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/frames/impl/tie_line_view.py b/yagat/frames/impl/tie_line_view.py new file mode 100644 index 0000000..02bcce3 --- /dev/null +++ b/yagat/frames/impl/tie_line_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class TieLineView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'Tie-Lines' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.tie_lines + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df #TODO + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = TieLineView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_four_substations_node_breaker_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/menus/impl/file.py b/yagat/menus/impl/file.py index b86bfb8..f22e32b 100644 --- a/yagat/menus/impl/file.py +++ b/yagat/menus/impl/file.py @@ -14,6 +14,7 @@ from yagat.app_context import AppContext + class FileMenu(tk.Menu): def __init__(self, parent, context: AppContext, *args, **kwargs): tk.Menu.__init__(self, parent, *args, **kwargs) @@ -36,6 +37,13 @@ def load_sample_network(network: pn.Network): context.network = network context.status_text = 'Network ' + network.name + ' loaded' + def load_be_nl(): + be = pn.create_micro_grid_be_network() + be.merge(pn.create_micro_grid_nl_network()) + context.network = be + context.status_text = 'Network CGMES MicroGrid BE+NL loaded' + + self.sample_networks_menu.add_command(label='IEEE 9 Bus', command=lambda: load_sample_network(pn.create_ieee9())) self.sample_networks_menu.add_command(label='IEEE 14 Bus', @@ -52,6 +60,8 @@ def load_sample_network(network: pn.Network): command=lambda: load_sample_network(pn.create_micro_grid_be_network())) self.sample_networks_menu.add_command(label='CGMES MicroGrid NL', command=lambda: load_sample_network(pn.create_micro_grid_nl_network())) + self.sample_networks_menu.add_command(label='CGMES MicroGrid BE+NL', + command=lambda: load_be_nl()) self.sample_networks_menu.add_command(label='PowSyBl Metrix 6 Bus', command=lambda: load_sample_network( pn.create_metrix_tutorial_six_buses_network())) self.sample_networks_menu.add_command(label='Eurostag Tutorial', command=lambda: load_sample_network( diff --git a/yagat/networkstructure/impl/network_structure.py b/yagat/networkstructure/impl/network_structure.py index 3bdf7c9..fb8f20d 100644 --- a/yagat/networkstructure/impl/network_structure.py +++ b/yagat/networkstructure/impl/network_structure.py @@ -154,6 +154,14 @@ def three_windings_transformers(self) -> pd.DataFrame: def switches(self) -> pd.DataFrame: return self._switches_df + @property + def tie_lines(self) -> pd.DataFrame: + return self._tie_lines_df + + @property + def hvdc_lines(self) -> pd.DataFrame: + return self._hvdc_lines_df + def refresh(self): logging.info('refresh start') self._substations_df = self._network.get_substations(all_attributes=True) From 702b146654cf6a26e9be42433914604706bc3b9e Mon Sep 17 00:00:00 2001 From: Damien Jeandemange Date: Tue, 12 Nov 2024 23:02:42 +0100 Subject: [PATCH 14/14] hvdc lines Signed-off-by: Damien Jeandemange --- yagat/frames/impl/hvdc_line_view.py | 48 +++++++++++++++++++++++++++++ yagat/frames/impl/tabs_view.py | 2 ++ yagat/frames/impl/tie_line_view.py | 4 ++- 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 yagat/frames/impl/hvdc_line_view.py diff --git a/yagat/frames/impl/hvdc_line_view.py b/yagat/frames/impl/hvdc_line_view.py new file mode 100644 index 0000000..e0842b1 --- /dev/null +++ b/yagat/frames/impl/hvdc_line_view.py @@ -0,0 +1,48 @@ +# +# 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 os +import tkinter as tk + +import pandas as pd +import pypowsybl.network as pn + +from yagat.app_context import AppContext +from yagat.frames.impl.base_list_view import BaseListView + + +class HvdcLineView(BaseListView): + + def __init__(self, parent, context: AppContext, *args, **kwargs): + BaseListView.__init__(self, parent, context, *args, **kwargs) + + @property + def tab_name(self) -> str: + return 'HVDC Lines' + + def get_data_frame(self) -> pd.DataFrame: + return self.context.network_structure.hvdc_lines + + def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.DataFrame: + return df #TODO + + +if __name__ == "__main__": + + if os.name == 'nt': + # Fixing the blur UI on Windows + from ctypes import windll + + windll.shcore.SetProcessDpiAwareness(2) + root = tk.Tk() + ctx = AppContext(root) + bw = HvdcLineView(root, ctx) + bw.pack(fill="both", expand=True) + ctx.network = pn.create_four_substations_node_breaker_network() + ctx.selection = ('network', '', None) + ctx.selected_tab = bw.tab_name + root.mainloop() diff --git a/yagat/frames/impl/tabs_view.py b/yagat/frames/impl/tabs_view.py index 266976c..ca3c8b5 100644 --- a/yagat/frames/impl/tabs_view.py +++ b/yagat/frames/impl/tabs_view.py @@ -26,6 +26,7 @@ from yagat.frames.impl.vsc_list_view import VscListView from yagat.frames.impl.switch_list_view import SwitchListView from yagat.frames.impl.tie_line_view import TieLineView +from yagat.frames.impl.hvdc_line_view import HvdcLineView from yagat.networkstructure import BusView @@ -52,6 +53,7 @@ def __init__(self, parent, context: AppContext, *args, **kwargs): self._add_tab(VscListView(self.tab_control, self.context)) self._add_tab(SwitchListView(self.tab_control, self.context)) self._add_tab(TieLineView(self.tab_control, self.context)) + self._add_tab(HvdcLineView(self.tab_control, self.context)) self.tab_control.pack(expand=True, fill=tk.BOTH) diff --git a/yagat/frames/impl/tie_line_view.py b/yagat/frames/impl/tie_line_view.py index 02bcce3..48813f8 100644 --- a/yagat/frames/impl/tie_line_view.py +++ b/yagat/frames/impl/tie_line_view.py @@ -42,7 +42,9 @@ def filter_data_frame(self, df: pd.DataFrame, voltage_levels: list[str]) -> pd.D ctx = AppContext(root) bw = TieLineView(root, ctx) bw.pack(fill="both", expand=True) - ctx.network = pn.create_four_substations_node_breaker_network() + be_network = pn.create_micro_grid_be_network() + be_network.merge(pn.create_micro_grid_nl_network()) + ctx.network = be_network ctx.selection = ('network', '', None) ctx.selected_tab = bw.tab_name root.mainloop()