From 3cd548c88ccac069e360164cf9db6213ebc31c22 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:19:07 -0700 Subject: [PATCH 01/28] Refactor: rename --- ryven/ironflow/gui.py | 16 ++++++++-------- ryven/ironflow/models.py | 2 +- ryven/ironflow/node_interface.py | 4 ++-- tests/unit/test_flow_canvas.py | 2 +- tests/unit/test_gui.py | 10 +++++----- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index e3226b1c..f4ddbbcb 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -59,7 +59,7 @@ def delete_script(self) -> None: super().delete_script() @property - def flow_canvas_widget(self): + def flow_canvas(self): return self._flow_canvases[self.active_script_index] @property @@ -199,10 +199,10 @@ def draw(self) -> widgets.VBox: # Type hinting for unused `change` argument in callbacks taken from ipywidgets docs: # https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html#Traitlet-events def click_add_node(self, change: dict) -> None: - self.flow_canvas_widget.add_node(10, 10, self.new_node_class) + self.flow_canvas.add_node(10, 10, self.new_node_class) def click_delete_node(self, change: Dict) -> None: - self.flow_canvas_widget.delete_selected() + self.flow_canvas.delete_selected() def change_modules_dropdown(self, change: Dict) -> None: self.node_selector.options = sorted(self._nodes_dict[self.modules_dropdown.value].keys()) @@ -221,7 +221,7 @@ def change_script_tabs(self, change: Dict): self._update_tabs_from_model() else: self.active_script_index = self.script_tabs.selected_index - self.flow_canvas_widget.redraw() + self.flow_canvas.redraw() def _populate_text_input_panel(self, description, initial_value, description_tooltip=None): self.text_input_panel.children = [ @@ -300,7 +300,7 @@ def click_create_script(self, change: dict) -> None: self._update_tabs_from_model() self.script_tabs.selected_index = self.n_scripts - 1 self.active_script_index = self.script_tabs.selected_index - self.flow_canvas_widget.redraw() + self.flow_canvas.redraw() def click_rename_script(self, change: Dict) -> None: self._depopulate_text_input_panel() @@ -326,9 +326,9 @@ def click_delete_script(self, change: Dict) -> None: self._update_tabs_from_model() def click_zero_location(self, change: dict) -> None: - self.flow_canvas_widget.x = 0 - self.flow_canvas_widget.y = 0 - self.flow_canvas_widget.redraw() + self.flow_canvas.x = 0 + self.flow_canvas.y = 0 + self.flow_canvas.redraw() def _update_tabs_from_model(self): self.script_tabs.selected_index = None diff --git a/ryven/ironflow/models.py b/ryven/ironflow/models.py index e8797cad..2eebbbb3 100644 --- a/ryven/ironflow/models.py +++ b/ryven/ironflow/models.py @@ -126,7 +126,7 @@ def serialize(self) -> Dict: for i_script, script in enumerate(self.session.scripts): all_data = data["scripts"][i_script]["flow"]["nodes"] self.active_script_index = i_script - for i, node_widget in enumerate(self.flow_canvas_widget.objects_to_draw): + for i, node_widget in enumerate(self.flow_canvas.objects_to_draw): all_data[i]["pos x"] = node_widget.x all_data[i]["pos y"] = node_widget.y self.active_script_index = currently_active diff --git a/ryven/ironflow/node_interface.py b/ryven/ironflow/node_interface.py index a80984bd..9df6289d 100644 --- a/ryven/ironflow/node_interface.py +++ b/ryven/ironflow/node_interface.py @@ -56,7 +56,7 @@ def gui_object(self) -> Union[widgets.FloatSlider, widgets.Box]: def gui_object_change(self, change: Dict) -> None: self.node.set_state({"val": change["new"]}, 0) self.node.update_event() - self._central_gui.flow_canvas_widget.redraw() + self._central_gui.flow_canvas.redraw() def input_widgets(self) -> None: self._input = [] @@ -115,7 +115,7 @@ def input_change_i(self, i_c) -> Callable: def input_change(change: Dict) -> None: self.node.inputs[i_c].val = change["new"] self.node.update_event() - self._central_gui.flow_canvas_widget.redraw() + self._central_gui.flow_canvas.redraw() return input_change def draw(self) -> widgets.HBox: diff --git a/tests/unit/test_flow_canvas.py b/tests/unit/test_flow_canvas.py index 92d1006c..674600fb 100644 --- a/tests/unit/test_flow_canvas.py +++ b/tests/unit/test_flow_canvas.py @@ -11,7 +11,7 @@ class TestCanvasObect(TestCase): def setUp(self): self.gui = GUI('gui') - self.canvas = self.gui.flow_canvas_widget + self.canvas = self.gui.flow_canvas @classmethod def tearDownClass(cls): diff --git a/tests/unit/test_gui.py b/tests/unit/test_gui.py index 1b8ee0fd..59a67704 100644 --- a/tests/unit/test_gui.py +++ b/tests/unit/test_gui.py @@ -17,9 +17,9 @@ def tearDown(self) -> None: def test_multiple_scripts(self): gui = GUI('foo') - gui.flow_canvas_widget.add_node(0, 0, gui._nodes_dict['nodes']['val']) + gui.flow_canvas.add_node(0, 0, gui._nodes_dict['nodes']['val']) gui.create_script() - gui.flow_canvas_widget.add_node(1, 1, gui._nodes_dict['nodes']['result']) + gui.flow_canvas.add_node(1, 1, gui._nodes_dict['nodes']['result']) canonical_file_name = f"{gui.session_title}.json" gui.save(canonical_file_name) new_gui = GUI('something_random') @@ -45,7 +45,7 @@ def test_multiple_scripts(self): def test_saving_and_loading(self): title = 'foo' gui = GUI(title) - canvas = gui.flow_canvas_widget + canvas = gui.flow_canvas flow = gui._session.scripts[0].flow canvas.add_node(0, 0, gui._nodes_dict['nodes']['val']) # Need to create with canvas instead of flow @@ -99,7 +99,7 @@ def update_event(self, inp=-1): gui.register_user_node(MyNode) self.assertIn(MyNode, gui.session.nodes) - gui.flow_canvas_widget.add_node(0, 0, gui._nodes_dict["user"][MyNode.title]) + gui.flow_canvas.add_node(0, 0, gui._nodes_dict["user"][MyNode.title]) gui.flow.nodes[0].inputs[0].update(1) self.assertEqual(gui.flow.nodes[0].outputs[0].val, 43) @@ -120,7 +120,7 @@ def update_event(self, inp=-1): gui.flow.nodes[0].inputs[0].update(2) self.assertEqual(gui.flow.nodes[0].outputs[0].val, 44, msg="Expected to be using instance of old class") - gui.flow_canvas_widget.add_node(1, 1, gui._nodes_dict["user"][MyNode.title]) + gui.flow_canvas.add_node(1, 1, gui._nodes_dict["user"][MyNode.title]) gui.flow.nodes[1].inputs[0].update(2) self.assertEqual(gui.flow.nodes[1].outputs[0].val, -40, msg="New node instances should reflect updated class.") From 7c1751975c9485481b84f4260525d5cab9f58558 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:24:37 -0700 Subject: [PATCH 02/28] Move all mention of the canvas from model up to gui --- ryven/ironflow/gui.py | 12 ++++++++++++ ryven/ironflow/models.py | 11 +---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index f4ddbbcb..4f27334b 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -66,6 +66,18 @@ def flow_canvas(self): def new_node_class(self): return self._nodes_dict[self.modules_dropdown.value][self.node_selector.value] + def serialize(self) -> dict: + data = super().serialize() + currently_active = self.active_script_index + for i_script, script in enumerate(self.session.scripts): + all_data = data["scripts"][i_script]["flow"]["nodes"] + self.active_script_index = i_script + for i, node_widget in enumerate(self.flow_canvas.objects_to_draw): + all_data[i]["pos x"] = node_widget.x + all_data[i]["pos y"] = node_widget.y + self.active_script_index = currently_active + return data + def load_from_data(self, data: Dict) -> None: super().load_from_data(data) self._flow_canvases = [] diff --git a/ryven/ironflow/models.py b/ryven/ironflow/models.py index 2eebbbb3..816994f5 100644 --- a/ryven/ironflow/models.py +++ b/ryven/ironflow/models.py @@ -121,16 +121,7 @@ def save(self, file_path: str) -> None: f.write(json.dumps(data, indent=4)) def serialize(self) -> Dict: - currently_active = self.active_script_index - data = self.session.serialize() - for i_script, script in enumerate(self.session.scripts): - all_data = data["scripts"][i_script]["flow"]["nodes"] - self.active_script_index = i_script - for i, node_widget in enumerate(self.flow_canvas.objects_to_draw): - all_data[i]["pos x"] = node_widget.x - all_data[i]["pos y"] = node_widget.y - self.active_script_index = currently_active - return data + return self.session.serialize() def load(self, file_path: str) -> None: with open(file_path, "r") as f: From 61c77080c24973f76848b83f589d9f1b54c28efb Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:28:36 -0700 Subject: [PATCH 03/28] Remove unused code --- ryven/ironflow/gui.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index 4f27334b..8b771043 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -156,8 +156,6 @@ def draw(self) -> widgets.VBox: self.node_selector = widgets.RadioButtons( options=nodes_options, value=list(nodes_options)[0], - # layout={'width': 'max-content'}, # If the items' names are long - # description='Nodes:', disabled=False, ) From 5f6a1e2ffb5cb479997851bc49ae277cee58b028 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:28:59 -0700 Subject: [PATCH 04/28] Fix comment typo --- ryven/ironflow/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index 8b771043..2f48ac7c 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -172,7 +172,7 @@ def draw(self) -> widgets.VBox: self.btn_rename_script.on_click(self.click_rename_script) self.btn_input_text_ok.on_click(self.click_input_text_ok) self.text_input_field.on_submit(self.click_input_text_ok) - # ^ Ignore the deprecation warning, 'observe' does function the way we actually want + # ^ Ignore the deprecation warning, 'observe' doesn't function the way we actually want # https://github.com/jupyter-widgets/ipywidgets/issues/2446 self.btn_input_text_cancel.on_click(self.click_input_text_cancel) self.btn_delete_script.on_click(self.click_delete_script) From 1d1ce94a0d4c4e752d40d79c4d598ab39ddc4d63 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:31:32 -0700 Subject: [PATCH 05/28] Refactor: introduce variable and slide buttons Makes the layout of the GUI easier to read in the return statement --- ryven/ironflow/gui.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index 2f48ac7c..fa9d36d8 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -138,6 +138,17 @@ def draw(self) -> widgets.VBox: icon="map-marker", # TODO: Use location-dot once this is available layout=button_layout ) + buttons = [ + self.btn_save, + self.btn_load, + self.btn_help_node, + self.btn_add_node, + self.btn_delete_node, + self.btn_create_script, + self.btn_rename_script, + self.btn_delete_script, + self.btn_zero_location, + ] self.text_input_panel = widgets.HBox([]) self.text_input_field = widgets.Text(value="INIT VALUE", description="DESCRIPTION") @@ -185,15 +196,7 @@ def draw(self) -> widgets.VBox: [ self.modules_dropdown, self.alg_mode_dropdown, - self.btn_save, - self.btn_load, - self.btn_help_node, - self.btn_add_node, - self.btn_delete_node, - self.btn_create_script, - self.btn_rename_script, - self.btn_delete_script, - self.btn_zero_location, + *buttons, ] ), self.text_input_panel, From 2b6537cee9384f45f219e4d2073bf3b049fa1725 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:42:46 -0700 Subject: [PATCH 06/28] Update type checking style From python 3.7 with the annotations import and from 3.9 always, you can just use the type directly without typing.type. https://stackoverflow.com/questions/37087457/difference-between-defining-typing-dict-and-dict --- ryven/ironflow/canvas_widgets.py | 20 ++++++++++---------- ryven/ironflow/flow_canvas.py | 6 +++--- ryven/ironflow/gui.py | 26 +++++++++++++------------- ryven/ironflow/models.py | 8 ++++---- ryven/ironflow/node_interface.py | 8 ++++---- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 257d65ce..2febfdc0 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -10,7 +10,7 @@ from abc import ABC, abstractmethod from ryvencore.NodePort import NodeInput, NodeOutput -from typing import TYPE_CHECKING, Optional, Union, List, Callable +from typing import TYPE_CHECKING, Optional, Union if TYPE_CHECKING: from .flow_canvas import FlowCanvas from ryven.ironflow.gui import GUI @@ -42,7 +42,7 @@ def __init__( self, x: Number, y: Number, - parent: Union[FlowCanvas, CanvasWidget], + parent: FlowCanvas | CanvasWidget, layout: Layout, selected: bool = False, title: Optional[str] = None, @@ -136,7 +136,7 @@ def _is_at_xy(self, x_in: Number, y_in: Number) -> bool: y_coord = self.y # - (self.height * 0.5) return x_coord < x_in < (x_coord + self.width) and y_coord < y_in < (y_coord + self.height) - def get_element_at_xy(self, x_in: Number, y_in: Number) -> Union[CanvasWidget, None]: + def get_element_at_xy(self, x_in: Number, y_in: Number) -> CanvasWidget | None: if self.is_here(x_in, y_in): for o in self.objects_to_draw: if o.is_here(x_in, y_in): @@ -165,7 +165,7 @@ def __init__( self, x: Number, y: Number, - parent: Union[FlowCanvas, CanvasWidget], + parent: FlowCanvas | CanvasWidget, layout: Layout, selected: bool = False, title: Optional[str] = None, @@ -222,7 +222,7 @@ def __init__( self, x: Number, y: Number, - parent: Union[FlowCanvas, CanvasWidget], + parent: FlowCanvas | CanvasWidget, layout: PortLayout, port: NodePort, selected: bool = False, @@ -277,7 +277,7 @@ def __init__( self, x: Number, y: Number, - parent: Union[FlowCanvas, CanvasWidget], + parent: FlowCanvas | CanvasWidget, layout: NodeLayout, node: Node, selected: bool = False, @@ -373,8 +373,8 @@ def draw_title(self) -> None: def _add_ports( self, radius: Number, - inputs: Optional[List[NodeInputBP]] = None, - outputs: Optional[List[NodeOutputBP]] = None, + inputs: Optional[list[NodeInputBP]] = None, + outputs: Optional[list[NodeOutputBP]] = None, border: Number = 1.4, ) -> None: if inputs is not None: @@ -464,7 +464,7 @@ def __init__( self, x: Number, y: Number, - parent: Union[FlowCanvas, CanvasWidget], + parent: FlowCanvas | CanvasWidget, layout: NodeLayout, node: Node, selected: bool = False, @@ -572,7 +572,7 @@ def __init__( self, x: Number, y: Number, - parent: Union[FlowCanvas, CanvasWidget], + parent: FlowCanvas | CanvasWidget, layout: NodeLayout, node: Node, selected: bool = False, diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index b0d8220c..b693003d 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -12,7 +12,7 @@ ) from .layouts import NodeLayout -from typing import TYPE_CHECKING, Optional, Union, List +from typing import TYPE_CHECKING, Optional, Union if TYPE_CHECKING: from gui import GUI from ryven.NENV import Node @@ -181,13 +181,13 @@ def handle_mouse_down(self, x: Number, y: Number): def handle_mouse_up(self, x: Number, y: Number): self._mouse_is_down = False - def get_element_at_xy(self, x_in: Number, y_in: Number) -> Union[CanvasWidget, None]: + def get_element_at_xy(self, x_in: Number, y_in: Number) -> CanvasWidget | None: for o in self.objects_to_draw: if o.is_here(x_in, y_in): return o.get_element_at_xy(x_in, y_in) return None - def get_selected_objects(self) -> List[CanvasWidget]: + def get_selected_objects(self) -> list[CanvasWidget]: return [o for o in self.objects_to_draw if o.selected] def handle_mouse_move(self, x: Number, y: Number) -> None: diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index fa9d36d8..303224ed 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -21,7 +21,7 @@ from ryven.ironflow.node_interface import NodeInterface from ryven.ironflow.flow_canvas import FlowCanvas -from typing import Optional, Dict +from typing import Optional from ryvencore import Session alg_modes = ["data", "exec"] @@ -49,7 +49,7 @@ def create_script( self, title: Optional[str] = None, create_default_logs: bool = True, - data: Optional[Dict] = None + data: Optional[dict] = None ) -> None: super().create_script(title=title, create_default_logs=create_default_logs, data=data) self._flow_canvases.append(FlowCanvas(gui=self)) @@ -78,7 +78,7 @@ def serialize(self) -> dict: self.active_script_index = currently_active return data - def load_from_data(self, data: Dict) -> None: + def load_from_data(self, data: dict) -> None: super().load_from_data(data) self._flow_canvases = [] for i_script, script in enumerate(self.session.scripts): @@ -214,19 +214,19 @@ def draw(self) -> widgets.VBox: def click_add_node(self, change: dict) -> None: self.flow_canvas.add_node(10, 10, self.new_node_class) - def click_delete_node(self, change: Dict) -> None: + def click_delete_node(self, change: dict) -> None: self.flow_canvas.delete_selected() - def change_modules_dropdown(self, change: Dict) -> None: + def change_modules_dropdown(self, change: dict) -> None: self.node_selector.options = sorted(self._nodes_dict[self.modules_dropdown.value].keys()) - def change_alg_mode_dropdown(self, change: Dict) -> None: + def change_alg_mode_dropdown(self, change: dict) -> None: # Current behaviour: Updates the flow mode for all scripts # TODO: Change only for the active script, and update the dropdown on tab (script) switching for script in self.session.scripts: script.flow.set_algorithm_mode(self.alg_mode_dropdown.value) - def change_script_tabs(self, change: Dict): + def change_script_tabs(self, change: dict): if change['name'] == 'selected_index' and change['new'] is not None: self._depopulate_text_input_panel() if self.script_tabs.selected_index == self.n_scripts: @@ -250,11 +250,11 @@ def _populate_text_input_panel(self, description, initial_value, description_too def _depopulate_text_input_panel(self) -> None: self.text_input_panel.children = [] - def click_input_text_ok(self, change: Dict) -> None: + def click_input_text_ok(self, change: dict) -> None: self._context_actions[self._context](self.text_input_field.value) self._depopulate_text_input_panel() - def click_input_text_cancel(self, change: Dict) -> None: + def click_input_text_cancel(self, change: dict) -> None: self._depopulate_text_input_panel() self._print("") @@ -277,7 +277,7 @@ def _pretty_docstring(node_class): with self.out_log: display(_pretty_docstring(self.new_node_class)) - def click_save(self, change: Dict) -> None: + def click_save(self, change: dict) -> None: self._depopulate_text_input_panel() self._populate_text_input_panel( "Save file", @@ -291,7 +291,7 @@ def _save_context_action(self, file_name): self.save(f"{file_name}.json") self._print(f"Session saved to {file_name}.json") - def click_load(self, change: Dict) -> None: + def click_load(self, change: dict) -> None: self._depopulate_text_input_panel() self._populate_text_input_panel( "Load file", @@ -315,7 +315,7 @@ def click_create_script(self, change: dict) -> None: self.active_script_index = self.script_tabs.selected_index self.flow_canvas.redraw() - def click_rename_script(self, change: Dict) -> None: + def click_rename_script(self, change: dict) -> None: self._depopulate_text_input_panel() self._populate_text_input_panel( "New name", @@ -334,7 +334,7 @@ def _rename_context_action(self, new_name): else: self._print(f"INVALID NAME: Failed to rename script '{self.script.title}' to '{new_name}'.") - def click_delete_script(self, change: Dict) -> None: + def click_delete_script(self, change: dict) -> None: self.delete_script() self._update_tabs_from_model() diff --git a/ryven/ironflow/models.py b/ryven/ironflow/models.py index 816994f5..1654f296 100644 --- a/ryven/ironflow/models.py +++ b/ryven/ironflow/models.py @@ -22,7 +22,7 @@ from ryvencore import Session, Script, Flow from ryven.main.utils import import_nodes_package, NodesPackage -from typing import Optional, Dict, Type +from typing import Optional, Type import ryven.NENV as NENV @@ -94,7 +94,7 @@ def create_script( self, title: Optional[str] = None, create_default_logs: bool = True, - data: Optional[Dict] = None + data: Optional[dict] = None ) -> None: self.session.create_script( title=title if title is not None else self.next_auto_script_name, @@ -120,7 +120,7 @@ def save(self, file_path: str) -> None: with open(file_path, "w") as f: f.write(json.dumps(data, indent=4)) - def serialize(self) -> Dict: + def serialize(self) -> dict: return self.session.serialize() def load(self, file_path: str) -> None: @@ -129,7 +129,7 @@ def load(self, file_path: str) -> None: self.load_from_data(data) - def load_from_data(self, data: Dict) -> None: + def load_from_data(self, data: dict) -> None: for script in self.session.scripts[::-1]: self.session.delete_script(script) self.session.load(data) diff --git a/ryven/ironflow/node_interface.py b/ryven/ironflow/node_interface.py index 9df6289d..848b9508 100644 --- a/ryven/ironflow/node_interface.py +++ b/ryven/ironflow/node_interface.py @@ -11,7 +11,7 @@ import pickle import base64 -from typing import TYPE_CHECKING, Dict, Union, Callable +from typing import TYPE_CHECKING, Callable if TYPE_CHECKING: from gui import GUI from ryven.NENV import Node @@ -42,7 +42,7 @@ def __init__(self, central_gui: GUI): self._central_gui = central_gui # self.input = [] - def gui_object(self) -> Union[widgets.FloatSlider, widgets.Box]: + def gui_object(self) -> widgets.FloatSlider | widgets.Box: if "slider" in self.node.title.lower(): self.gui = widgets.FloatSlider( value=self.node.val, min=0, max=10, continuous_update=False @@ -53,7 +53,7 @@ def gui_object(self) -> Union[widgets.FloatSlider, widgets.Box]: self.gui = widgets.Box() return self.gui - def gui_object_change(self, change: Dict) -> None: + def gui_object_change(self, change: dict) -> None: self.node.set_state({"val": change["new"]}, 0) self.node.update_event() self._central_gui.flow_canvas.redraw() @@ -112,7 +112,7 @@ def input_widgets(self) -> None: # inp_widget.value = dtype_state['default'] def input_change_i(self, i_c) -> Callable: - def input_change(change: Dict) -> None: + def input_change(change: dict) -> None: self.node.inputs[i_c].val = change["new"] self.node.update_event() self._central_gui.flow_canvas.redraw() From 0c6a01f10ab895d102c34875ce24a70154921e96 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:48:18 -0700 Subject: [PATCH 07/28] Refactor: slide to order functions the same as they are ordered graphically --- ryven/ironflow/gui.py | 115 +++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/ryven/ironflow/gui.py b/ryven/ironflow/gui.py index 303224ed..5774489a 100644 --- a/ryven/ironflow/gui.py +++ b/ryven/ironflow/gui.py @@ -211,12 +211,6 @@ def draw(self) -> widgets.VBox: # Type hinting for unused `change` argument in callbacks taken from ipywidgets docs: # https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html#Traitlet-events - def click_add_node(self, change: dict) -> None: - self.flow_canvas.add_node(10, 10, self.new_node_class) - - def click_delete_node(self, change: dict) -> None: - self.flow_canvas.delete_selected() - def change_modules_dropdown(self, change: dict) -> None: self.node_selector.options = sorted(self._nodes_dict[self.modules_dropdown.value].keys()) @@ -226,57 +220,6 @@ def change_alg_mode_dropdown(self, change: dict) -> None: for script in self.session.scripts: script.flow.set_algorithm_mode(self.alg_mode_dropdown.value) - def change_script_tabs(self, change: dict): - if change['name'] == 'selected_index' and change['new'] is not None: - self._depopulate_text_input_panel() - if self.script_tabs.selected_index == self.n_scripts: - self.create_script() - self._update_tabs_from_model() - else: - self.active_script_index = self.script_tabs.selected_index - self.flow_canvas.redraw() - - def _populate_text_input_panel(self, description, initial_value, description_tooltip=None): - self.text_input_panel.children = [ - self.text_input_field, - self.btn_input_text_ok, - self.btn_input_text_cancel - ] - self.text_input_field.description = description - description_tooltip = description_tooltip if description_tooltip is not None else description - self.text_input_field.description_tooltip = description_tooltip - self.text_input_field.value = initial_value - - def _depopulate_text_input_panel(self) -> None: - self.text_input_panel.children = [] - - def click_input_text_ok(self, change: dict) -> None: - self._context_actions[self._context](self.text_input_field.value) - self._depopulate_text_input_panel() - - def click_input_text_cancel(self, change: dict) -> None: - self._depopulate_text_input_panel() - self._print("") - - def _set_context(self, context): - if context not in self._context_actions.keys(): - raise KeyError(f"Expected a context action among {list(self._context_actions.keys())} but got {context}.") - self._context = context - - def click_node_help(self, change: dict) -> None: - def _pretty_docstring(node_class): - """ - If we just pass a string, `display` doesn't resolve newlines. - If we pass a `print`ed string, `display` also shows the `None` value returned by `print` - So we use this ugly hack. - """ - string = f"{node_class.__name__.replace('_Node', '')}:\n{node_class.__doc__}" - return HTML(string.replace("\n", "
").replace("\t", " ").replace(" ", " ")) - - self.out_log.clear_output() - with self.out_log: - display(_pretty_docstring(self.new_node_class)) - def click_save(self, change: dict) -> None: self._depopulate_text_input_panel() self._populate_text_input_panel( @@ -308,6 +251,26 @@ def _load_context_action(self, file_name): self.out_log.clear_output() self._print(f"Session loaded from {file_name}.json") + def click_node_help(self, change: dict) -> None: + def _pretty_docstring(node_class): + """ + If we just pass a string, `display` doesn't resolve newlines. + If we pass a `print`ed string, `display` also shows the `None` value returned by `print` + So we use this ugly hack. + """ + string = f"{node_class.__name__.replace('_Node', '')}:\n{node_class.__doc__}" + return HTML(string.replace("\n", "
").replace("\t", " ").replace(" ", " ")) + + self.out_log.clear_output() + with self.out_log: + display(_pretty_docstring(self.new_node_class)) + + def click_add_node(self, change: dict) -> None: + self.flow_canvas.add_node(10, 10, self.new_node_class) + + def click_delete_node(self, change: dict) -> None: + self.flow_canvas.delete_selected() + def click_create_script(self, change: dict) -> None: self.create_script() self._update_tabs_from_model() @@ -343,6 +306,43 @@ def click_zero_location(self, change: dict) -> None: self.flow_canvas.y = 0 self.flow_canvas.redraw() + def _populate_text_input_panel(self, description, initial_value, description_tooltip=None): + self.text_input_panel.children = [ + self.text_input_field, + self.btn_input_text_ok, + self.btn_input_text_cancel + ] + self.text_input_field.description = description + description_tooltip = description_tooltip if description_tooltip is not None else description + self.text_input_field.description_tooltip = description_tooltip + self.text_input_field.value = initial_value + + def _depopulate_text_input_panel(self) -> None: + self.text_input_panel.children = [] + + def click_input_text_ok(self, change: dict) -> None: + self._context_actions[self._context](self.text_input_field.value) + self._depopulate_text_input_panel() + + def click_input_text_cancel(self, change: dict) -> None: + self._depopulate_text_input_panel() + self._print("") + + def _set_context(self, context): + if context not in self._context_actions.keys(): + raise KeyError(f"Expected a context action among {list(self._context_actions.keys())} but got {context}.") + self._context = context + + def change_script_tabs(self, change: dict): + if change['name'] == 'selected_index' and change['new'] is not None: + self._depopulate_text_input_panel() + if self.script_tabs.selected_index == self.n_scripts: + self.create_script() + self._update_tabs_from_model() + else: + self.active_script_index = self.script_tabs.selected_index + self.flow_canvas.redraw() + def _update_tabs_from_model(self): self.script_tabs.selected_index = None # ^ To circumvent a bug where the index gets set to 0 on child changes @@ -365,5 +365,4 @@ def _add_new_script_tab(self): def _print(self, text: str) -> None: with self.out_log: self.out_log.clear_output() - print(text) From 4e57c158d9ac3e2827d87a48b9bf9ad4f667fad9 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:50:50 -0700 Subject: [PATCH 08/28] Remove unused code --- ryven/ironflow/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ryven/ironflow/models.py b/ryven/ironflow/models.py index 1654f296..3bfdff61 100644 --- a/ryven/ironflow/models.py +++ b/ryven/ironflow/models.py @@ -34,7 +34,7 @@ ("built_in",), ("std",), ("pyiron",), -]] # , ("mynodes",) +]] class HasSession(ABC): From 5e97ad0510576c9cf0b32847546b519df0f5e41c Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 10:54:53 -0700 Subject: [PATCH 09/28] Use absolute imports --- ryven/ironflow/__init__.py | 4 ++-- ryven/ironflow/canvas_widgets.py | 4 ++-- ryven/ironflow/flow_canvas.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ryven/ironflow/__init__.py b/ryven/ironflow/__init__.py index 490564e3..873a4993 100644 --- a/ryven/ironflow/__init__.py +++ b/ryven/ironflow/__init__.py @@ -1,4 +1,4 @@ -__all__ = ['gui', 'flow_canvas', 'canvas_widgets', 'node_interface.py'] +__all__ = ['gui', 'flow_canvas', 'canvas_widgets', 'layouts', 'models', 'node_interface'] -from .gui import GUI +from ryven.ironflow.gui import GUI from ryven.NENV import Node, NodeInputBP, NodeOutputBP, dtypes diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 2febfdc0..d369cdf8 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -6,13 +6,13 @@ import numpy as np from IPython.display import display -from .layouts import Layout, NodeLayout, PortLayout, DataPortLayout, ExecPortLayout, ButtonLayout +from ryven.ironflow.layouts import Layout, NodeLayout, PortLayout, DataPortLayout, ExecPortLayout, ButtonLayout from abc import ABC, abstractmethod from ryvencore.NodePort import NodeInput, NodeOutput from typing import TYPE_CHECKING, Optional, Union if TYPE_CHECKING: - from .flow_canvas import FlowCanvas + from ryven.ironflow.flow_canvas import FlowCanvas from ryven.ironflow.gui import GUI from ipycanvas import Canvas from ryven.NENV import Node, NodeInputBP, NodeOutputBP diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index b693003d..269457cc 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -7,10 +7,10 @@ from ipycanvas import Canvas, hold_canvas from time import time -from .canvas_widgets import ( +from ryven.ironflow.canvas_widgets import ( NodeWidget, PortWidget, CanvasWidget, ButtonNodeWidget, DisplayableNodeWidget, DisplayButtonWidget ) -from .layouts import NodeLayout +from ryven.ironflow.layouts import NodeLayout from typing import TYPE_CHECKING, Optional, Union if TYPE_CHECKING: From 70cbe7653a5f1cfbc5dffe4da4fdd2bbb879ff66 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:03:13 -0700 Subject: [PATCH 10/28] Remove unused code --- ryven/ironflow/flow_canvas.py | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index 269457cc..96dcbb47 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -56,12 +56,6 @@ def __init__(self, gui: GUI, flow: Optional[Flow] = None, width: int = 2000, hei self._width, self._height = width, height self._col_background = "black" # "#584f4e" - self._col_node_header = "blue" # "#38a8a4" - self._col_node_selected = "#9dcea6" - self._col_node_unselected = "#dee7bc" - - self._font_size = 30 - self._node_box_size = 160, 70 self._canvas = Canvas(width=width, height=height) self._canvas.fill_style = self._col_background @@ -88,9 +82,6 @@ def __init__(self, gui: GUI, flow: Optional[Flow] = None, width: int = 2000, hei self._last_mouse_down = time() self._double_click_speed = 0.25 # In seconds. TODO: Put this in a config somewhere - self._connection_in = None - self._node_widget = None - self._object_to_gui_dict = {} @property @@ -102,9 +93,6 @@ def gui(self): return self._gui def draw_connection(self, port_1: int, port_2: int) -> None: - # i_out, i_in = path - # out = self.objects_to_draw[i_out] - # inp = self.objects_to_draw[i_in] out = self._object_to_gui_dict[port_1] inp = self._object_to_gui_dict[port_2] @@ -131,21 +119,6 @@ def canvas_restart(self) -> None: def handle_keyboard_event(self, key: str, shift_key, ctrl_key, meta_key) -> None: pass # TODO - def set_connection(self, ind_node: int) -> None: - if self._connection_in is None: - self._connection_in = ind_node - else: - out = self.objects_to_draw[self._connection_in].node.outputs[0] - inp = self.objects_to_draw[ind_node].node.inputs[-1] - if self.flow.connect_nodes(inp, out) is None: - i_con = self.connections.index([self._connection_in, ind_node]) - del self.connections[i_con] - else: - self.connections.append([self._connection_in, ind_node]) - - self._connection_in = None - self.deselect_all() - def deselect_all(self) -> None: [o.deselect() for o in self.objects_to_draw] self.redraw() @@ -212,18 +185,14 @@ def redraw(self) -> None: self.draw_connection(c.inp, c.out) def load_node(self, x: Number, y: Number, node: Node) -> NodeWidget: - # print ('node: ', node.identifier, node.GLOBAL_ID) - layout = NodeLayout() if hasattr(node, "main_widget_class"): if node.main_widget_class is not None: - # node.title = str(node.main_widget_class) f = eval(node.main_widget_class) s = f(x, y, parent=self, layout=layout, node=node) else: s = NodeWidget(x, y, parent=self, layout=layout, node=node) - # print ('s: ', s) else: s = NodeWidget(x, y, parent=self, layout=layout, node=node) From db5bab4b49d241e781ae7b2cd7968739b256270c Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:05:13 -0700 Subject: [PATCH 11/28] Refactor: simplify logic --- ryven/ironflow/flow_canvas.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index 96dcbb47..79568688 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -187,12 +187,9 @@ def redraw(self) -> None: def load_node(self, x: Number, y: Number, node: Node) -> NodeWidget: layout = NodeLayout() - if hasattr(node, "main_widget_class"): - if node.main_widget_class is not None: - f = eval(node.main_widget_class) - s = f(x, y, parent=self, layout=layout, node=node) - else: - s = NodeWidget(x, y, parent=self, layout=layout, node=node) + if hasattr(node, "main_widget_class") and node.main_widget_class is not None: + f = eval(node.main_widget_class) + s = f(x, y, parent=self, layout=layout, node=node) else: s = NodeWidget(x, y, parent=self, layout=layout, node=node) From 158b9d01aeb429dca00140379b9b84e2df1d6fd0 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:08:19 -0700 Subject: [PATCH 12/28] Refactor: rename variable and shift call outside conditional --- ryven/ironflow/flow_canvas.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index 79568688..05115ed3 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -188,10 +188,10 @@ def load_node(self, x: Number, y: Number, node: Node) -> NodeWidget: layout = NodeLayout() if hasattr(node, "main_widget_class") and node.main_widget_class is not None: - f = eval(node.main_widget_class) - s = f(x, y, parent=self, layout=layout, node=node) + node_class = eval(node.main_widget_class) else: - s = NodeWidget(x, y, parent=self, layout=layout, node=node) + node_class = NodeWidget + s = node_class(x=x, y=y, parent=self, layout=layout, node=node) self.objects_to_draw.append(s) return s From 118df70d3b3f4a94c977e83f0c9e4567c59d4dbc Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:09:38 -0700 Subject: [PATCH 13/28] Refactor: slide so all the canvas stuff is sequential --- ryven/ironflow/flow_canvas.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index 05115ed3..b20a91d7 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -63,14 +63,14 @@ def __init__(self, gui: GUI, flow: Optional[Flow] = None, width: int = 2000, hei self._canvas.layout.width = "100%" self._canvas.layout.height = "auto" - self.objects_to_draw = [] - self.connections = [] - self._canvas.on_mouse_down(self.handle_mouse_down) self._canvas.on_mouse_up(self.handle_mouse_up) self._canvas.on_mouse_move(self.handle_mouse_move) self._canvas.on_key_down(self.handle_keyboard_event) + self.objects_to_draw = [] + self.connections = [] + self.x = 0 self.y = 0 self._x_move_anchor = None From fbb41cafe51f3965ae574aee998fedeb4cba882f Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:12:13 -0700 Subject: [PATCH 14/28] Refactor: group styling variables in init --- ryven/ironflow/flow_canvas.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index b20a91d7..52ad68ce 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -55,10 +55,12 @@ def __init__(self, gui: GUI, flow: Optional[Flow] = None, width: int = 2000, hei self.flow = flow if flow is not None else gui.flow self._width, self._height = width, height - self._col_background = "black" # "#584f4e" + self._canvas_color = "black" # "#584f4e" + self._connection_style = "white" + self._connection_width = 3 self._canvas = Canvas(width=width, height=height) - self._canvas.fill_style = self._col_background + self._canvas.fill_style = self._canvas_color self._canvas.fill_rect(0, 0, width, height) self._canvas.layout.width = "100%" self._canvas.layout.height = "auto" @@ -97,8 +99,8 @@ def draw_connection(self, port_1: int, port_2: int) -> None: inp = self._object_to_gui_dict[port_2] canvas = self._canvas - canvas.stroke_style = "white" - canvas.line_width = 3 + canvas.stroke_style = self._connection_style + canvas.line_width = self._connection_width canvas.move_to(out.x, out.y) canvas.line_to(inp.x, inp.y) canvas.stroke() @@ -113,7 +115,7 @@ def _built_object_to_gui_dict(self) -> None: def canvas_restart(self) -> None: self._canvas.clear() - self._canvas.fill_style = self._col_background + self._canvas.fill_style = self._canvas_color self._canvas.fill_rect(0, 0, self._width, self._height) def handle_keyboard_event(self, key: str, shift_key, ctrl_key, meta_key) -> None: From 65ff7220181bc89704ea17b0bf24c9f77da60d01 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:13:22 -0700 Subject: [PATCH 15/28] Add author --- ryven/ironflow/canvas_widgets.py | 2 +- ryven/nodes/pyiron/atomistics_nodes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index d369cdf8..2206754a 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -21,7 +21,7 @@ from ryvencore.Flow import Flow -__author__ = "Joerg Neugebauer" +__author__ = "Joerg Neugebauer, Liam Huber" __copyright__ = ( "Copyright 2020, Max-Planck-Institut für Eisenforschung GmbH - " "Computational Materials Design (CM) Department" diff --git a/ryven/nodes/pyiron/atomistics_nodes.py b/ryven/nodes/pyiron/atomistics_nodes.py index 5650964e..62ccd997 100644 --- a/ryven/nodes/pyiron/atomistics_nodes.py +++ b/ryven/nodes/pyiron/atomistics_nodes.py @@ -11,7 +11,7 @@ from ryven.nodes.std.special_nodes import DualNodeBase -__author__ = "Joerg Neugebauer" +__author__ = "Joerg Neugebauer, Liam Huber" __copyright__ = ( "Copyright 2020, Max-Planck-Institut für Eisenforschung GmbH - " "Computational Materials Design (CM) Department" From 14010b2f6af7bb592c8a5038bda205a62903f767 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:22:44 -0700 Subject: [PATCH 16/28] Remove unused code --- ryven/ironflow/canvas_widgets.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 2206754a..4939678a 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -79,11 +79,11 @@ def height(self) -> int: @property def x(self) -> Number: - return self.parent.x + self._x # - self.parent.width//2 + return self.parent.x + self._x @property def y(self) -> Number: - return self.parent.y + self._y # - self.parent.height//2 + return self.parent.y + self._y @property def canvas(self) -> Canvas: @@ -114,8 +114,8 @@ def add_x_y(self, dx_in: Number, dy_in: Number) -> None: def draw_shape(self) -> None: self.canvas.fill_style = self.layout.selected_color if self.selected else self.layout.background_color self.canvas.fill_rect( - self.x, # - (self.width * 0.5), - self.y, # - (self.height * 0.5), + self.x, + self.y, self.width, self.height, ) @@ -132,8 +132,8 @@ def draw(self) -> None: o.draw() def _is_at_xy(self, x_in: Number, y_in: Number) -> bool: - x_coord = self.x # - (self.width * 0.5) - y_coord = self.y # - (self.height * 0.5) + x_coord = self.x + y_coord = self.y return x_coord < x_in < (x_coord + self.width) and y_coord < y_in < (y_coord + self.height) def get_element_at_xy(self, x_in: Number, y_in: Number) -> CanvasWidget | None: @@ -446,7 +446,6 @@ def expand_io(self): self._height = self._expanded_height for o in self.port_widgets: o.show() - # self.collapse_button.on_click() # Why doesn't this do the same as the next two lines?? self.collapse_button.on_unpressed() self.collapse_button.pressed = False From d73659d0791f41c03ab4e34652da5c3a42cb07ff Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:25:04 -0700 Subject: [PATCH 17/28] Simplify button call --- ryven/ironflow/canvas_widgets.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 4939678a..8d9392d6 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -446,16 +446,13 @@ def expand_io(self): self._height = self._expanded_height for o in self.port_widgets: o.show() - self.collapse_button.on_unpressed() - self.collapse_button.pressed = False + self.collapse_button.unpress() def collapse_io(self): self._height = self._collapsed_height for o in self.port_widgets: o.hide() - # TODO: The expand and collapse buttons are effectively an XOR toggle...improve this awkward implementation - self.expand_button.on_unpressed() - self.expand_button.pressed = False + self.expand_button.unpress() class ButtonNodeWidget(NodeWidget): From 8beea3109d77aa486d151b0f8c7ccefb6a1d741a Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:28:15 -0700 Subject: [PATCH 18/28] Fix type hints --- ryven/ironflow/canvas_widgets.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 8d9392d6..8344aadc 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -60,10 +60,10 @@ def __init__( self._height = self.layout.height @abstractmethod - def on_click(self, last_selected_object: Optional[CanvasWidget]) -> Optional[CanvasWidget]: + def on_click(self, last_selected_object: Optional[CanvasWidget]) -> CanvasWidget | None: pass - def on_double_click(self) -> Optional[CanvasWidget]: + def on_double_click(self) -> CanvasWidget | None: return self def _init_after_parent_assignment(self): @@ -245,7 +245,7 @@ def __init__( self.radius = radius self.port = port - def on_click(self, last_selected_object: Optional[CanvasWidget]) -> Optional[CanvasWidget]: + def on_click(self, last_selected_object: Optional[CanvasWidget]) -> PortWidget | None: if last_selected_object == self: self.deselect() return None @@ -341,7 +341,7 @@ def __init__( ) self.add_widget(self.collapse_button) - def on_click(self, last_selected_object: Optional[CanvasWidget]) -> Optional[CanvasWidget]: + def on_click(self, last_selected_object: Optional[CanvasWidget]) -> NodeWidget | None: if last_selected_object == self: return self else: @@ -357,7 +357,7 @@ def on_click(self, last_selected_object: Optional[CanvasWidget]) -> Optional[Can self.deselect() return None - def on_double_click(self) -> Optional[CanvasWidget]: + def on_double_click(self) -> None: self.delete() return None @@ -497,7 +497,7 @@ def __init__( self.title = title self.pressed = pressed - def on_click(self, last_selected_object: Optional[CanvasWidget]) -> Optional[CanvasWidget]: + def on_click(self, last_selected_object: Optional[CanvasWidget]) -> CanvasWidget | None: if self.pressed: self.unpress() else: From 93b15b795e9682e66376477e3bbded82413b4460 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:28:43 -0700 Subject: [PATCH 19/28] Remove unused code --- ryven/ironflow/canvas_widgets.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 8344aadc..aa26abac 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -66,9 +66,6 @@ def on_click(self, last_selected_object: Optional[CanvasWidget]) -> CanvasWidget def on_double_click(self) -> CanvasWidget | None: return self - def _init_after_parent_assignment(self): - pass - @property def width(self) -> int: return self.layout.width From 85a25a5e378e4a8da2672fc23564b68f5605df90 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:32:12 -0700 Subject: [PATCH 20/28] Make title box height part of the layout --- ryven/ironflow/canvas_widgets.py | 3 +-- ryven/ironflow/layouts.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index aa26abac..0d2d90ba 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -300,8 +300,7 @@ def __init__( 'exec': ExecPortLayout() } - self._title_box_height = 30 - + self._title_box_height = self.layout.title_box_height n_ports_max = max(len(self.node.inputs), len(self.node.outputs)) n_ports_min = len([p for p in self.node.inputs if p.type_ == "exec"]) subwidget_size_and_buffer = 1.33 * 2 * self.port_radius diff --git a/ryven/ironflow/layouts.py b/ryven/ironflow/layouts.py index b1224bc2..23aa5bf9 100644 --- a/ryven/ironflow/layouts.py +++ b/ryven/ironflow/layouts.py @@ -38,6 +38,7 @@ class NodeLayout(Layout): width: int = 200 height: int = 100 font_size: int = 22 + title_box_height: int = 30 @dataclass From d96deb216e258807726e3358586e0f1a01791e0e Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:54:30 -0700 Subject: [PATCH 21/28] Resolve a small TODO Now we won't collapse over exec ports if they don't come first. It will still look dumb without them first, but that's easy for a dev to notice and fix. Good enough for now. --- ryven/ironflow/canvas_widgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 0d2d90ba..9b76459a 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -302,12 +302,11 @@ def __init__( self._title_box_height = self.layout.title_box_height n_ports_max = max(len(self.node.inputs), len(self.node.outputs)) - n_ports_min = len([p for p in self.node.inputs if p.type_ == "exec"]) + exec_port_i = np.where([p.type_ == "exec" for p in self.node.inputs])[0] + n_ports_min = exec_port_i[-1] + 1 if len(exec_port_i) > 0 else 1 subwidget_size_and_buffer = 1.33 * 2 * self.port_radius self._io_height = subwidget_size_and_buffer * n_ports_max self._exec_height = subwidget_size_and_buffer * n_ports_min - # TODO: Right now we're hard-coding in that all the exec ports (which come with buttons) appear first in input - # This isn't necessarily so, nor checked for anywhere. Do better. self._expand_collapse_height = subwidget_size_and_buffer self._height = self._expanded_height From ff8aba1c17c17699be37f6eabd9bd677267427bd Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 11:58:09 -0700 Subject: [PATCH 22/28] :bug: Compare against the right default value in ryven --- ryven/ironflow/canvas_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 9b76459a..f81c9f70 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -709,7 +709,7 @@ def __init__( parent=parent, layout=layout, selected=selected, - title=port.label_str if port.label_str is not None else title, + title=port.label_str if port.label_str != '' else title, pressed=pressed ) self.port = port From e2c16a6f4b759dc191d7bae65c84cbcfe9ef2950 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 12:03:44 -0700 Subject: [PATCH 23/28] Make color prettier --- ryven/ironflow/canvas_widgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index f81c9f70..f86ed0f4 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -638,9 +638,8 @@ def __init__( if size is not None: layout.width = size layout.height = size - dpl = DataPortLayout() - layout.background_color = dpl.background_color - layout.pressed_color = dpl.background_color + layout.background_color = parent.node.color + layout.pressed_color = parent.node.color ButtonWidget.__init__(self, x=x, y=y, parent=parent, layout=layout, selected=selected, title=title, pressed=pressed) From 7a26c11d4898c40d4c71e53535f22fcfa4375b3c Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 12:06:44 -0700 Subject: [PATCH 24/28] Give buttons better position and size --- ryven/ironflow/canvas_widgets.py | 2 +- ryven/ironflow/layouts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index f86ed0f4..9d7a4e6b 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -401,7 +401,7 @@ def _add_ports( button_layout = ButtonLayout() self.add_widget( ExecButtonWidget( - x=x + 0.3 * button_layout.width, + x=x + radius, y=self._port_y_locs[i_port] - 0.5 * button_layout.height, parent=self, layout=button_layout, diff --git a/ryven/ironflow/layouts.py b/ryven/ironflow/layouts.py index 23aa5bf9..37d8d02f 100644 --- a/ryven/ironflow/layouts.py +++ b/ryven/ironflow/layouts.py @@ -62,7 +62,7 @@ class ExecPortLayout(PortLayout): @dataclass class ButtonLayout(Layout): font_size: int = 16 - width: int = 50 + width: int = 60 height: int = 20 background_color: str = "darkgray" pressed_color: str = "dimgray" From ec183f07268d64afd8faf7b1c90d1c2da2860742 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 12:07:11 -0700 Subject: [PATCH 25/28] Rename display button --- ryven/ironflow/canvas_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 9d7a4e6b..1501bbad 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -541,7 +541,7 @@ def __init__( parent: DisplayableNodeWidget, layout: ButtonLayout, selected: bool = False, - title="PLOT", + title="SHOW", ): super().__init__(x, y, parent, layout, selected, title=title) From abef24f1b629278645c0b6c3839c6043b0df75df Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 12:16:49 -0700 Subject: [PATCH 26/28] Use slightly clearer math for node heights --- ryven/ironflow/canvas_widgets.py | 35 +++++++++++++------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/ryven/ironflow/canvas_widgets.py b/ryven/ironflow/canvas_widgets.py index 1501bbad..4336c3ef 100644 --- a/ryven/ironflow/canvas_widgets.py +++ b/ryven/ironflow/canvas_widgets.py @@ -300,24 +300,25 @@ def __init__( 'exec': ExecPortLayout() } - self._title_box_height = self.layout.title_box_height - n_ports_max = max(len(self.node.inputs), len(self.node.outputs)) + n_ports_max = max(len(self.node.inputs), len(self.node.outputs)) + 1 # Includes the expand/collapse button exec_port_i = np.where([p.type_ == "exec" for p in self.node.inputs])[0] n_ports_min = exec_port_i[-1] + 1 if len(exec_port_i) > 0 else 1 subwidget_size_and_buffer = 1.33 * 2 * self.port_radius - self._io_height = subwidget_size_and_buffer * n_ports_max - self._exec_height = subwidget_size_and_buffer * n_ports_min - self._expand_collapse_height = subwidget_size_and_buffer + self._title_box_height = self.layout.title_box_height + self._max_body_height = subwidget_size_and_buffer * n_ports_max + self._min_body_height = subwidget_size_and_buffer * n_ports_min + self._expanded_height = self._title_box_height + self._max_body_height + self._collapsed_height = self._title_box_height + self._min_body_height self._height = self._expanded_height - y_step = (self._io_height + self._expand_collapse_height) / (n_ports_max + 1) - self._port_y_locs = (np.arange(n_ports_max + 1) + 0.5) * y_step + self._title_box_height + y_step = self._max_body_height / n_ports_max + self._subwidget_y_locs = (np.arange(n_ports_max) + 0.5) * y_step + self._title_box_height self.add_inputs() self.add_outputs() self.expand_button = ExpandButtonWidget( x=0.5 * self.width - self.port_radius, - y=self._port_y_locs[0] - self.port_radius, + y=self._subwidget_y_locs[0] - self.port_radius, parent=self, layout=ButtonLayout(), pressed=True, @@ -327,7 +328,7 @@ def __init__( self.add_widget(self.expand_button) self.collapse_button = CollapseButtonWidget( x=0.5 * self.width - self.port_radius, - y=self._port_y_locs[-1] - self.port_radius, + y=self._subwidget_y_locs[-1] - self.port_radius, parent=self, layout=ButtonLayout(), pressed=False, @@ -388,12 +389,12 @@ def _add_ports( self.add_widget( PortWidget( x=x, - y=self._port_y_locs[i_port], + y=self._subwidget_y_locs[i_port], parent=self, layout=self.port_layouts[data_or_exec], port=port, hidden_x=x, - hidden_y=self._port_y_locs[0], + hidden_y=self._subwidget_y_locs[0], radius=radius, ) ) @@ -402,7 +403,7 @@ def _add_ports( self.add_widget( ExecButtonWidget( x=x + radius, - y=self._port_y_locs[i_port] - 0.5 * button_layout.height, + y=self._subwidget_y_locs[i_port] - 0.5 * button_layout.height, parent=self, layout=button_layout, port=port @@ -429,14 +430,6 @@ def delete(self) -> None: def port_widgets(self) -> list[PortWidget]: return [o for o in self.objects_to_draw if isinstance(o, PortWidget)] - @property - def _expanded_height(self) -> Number: - return self._title_box_height + self._io_height + self._expand_collapse_height - - @property - def _collapsed_height(self) -> Number: - return self._title_box_height + max(self._expand_collapse_height, self._exec_height) - def expand_io(self): self._height = self._expanded_height for o in self.port_widgets: @@ -469,7 +462,7 @@ def __init__( button_layout = ButtonLayout() self.exec_button = ExecButtonWidget( x=0.8 * (self.width - button_layout.width), - y=self._port_y_locs[0] - 0.5 * button_layout.height, + y=self._subwidget_y_locs[0] - 0.5 * button_layout.height, parent=self, layout=button_layout, port=self.node.outputs[0], From 7f025aeed51a7aa822df4ea605fe951280aa6e3c Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 12:19:58 -0700 Subject: [PATCH 27/28] Remove unused code --- ryven/nodes/pyiron/atomistics_nodes.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ryven/nodes/pyiron/atomistics_nodes.py b/ryven/nodes/pyiron/atomistics_nodes.py index 62ccd997..867f3d3b 100644 --- a/ryven/nodes/pyiron/atomistics_nodes.py +++ b/ryven/nodes/pyiron/atomistics_nodes.py @@ -220,7 +220,6 @@ class GenericOutput_Node(NodeWithDisplay): """Select Generic Output item""" version = "v0.1" - title = "GenericOutput" init_inputs = [ NodeInputBP(dtype=dtypes.Data(size="m"), label="job"), @@ -234,9 +233,6 @@ class GenericOutput_Node(NodeWithDisplay): init_outputs = [ NodeOutputBP(), ] - - # main_widget_class = widgets.Result_Node_MainWidget - # main_widget_pos = 'between ports' color = "#c69a15" def __init__(self, params): @@ -390,8 +386,6 @@ class Result_Node(NodeBase): init_inputs = [ NodeInputBP(type_="data"), ] - # main_widget_class = widgets.Result_Node_MainWidget - # main_widget_pos = 'between ports' color = "#c69a15" def __init__(self, params): @@ -438,12 +432,7 @@ def update_event(self, inp=-1): self.exec_output(2) elif inp > 0: self._count = 0 - self.val = self._count - # for e in self.input(0): - # self.set_output_val(1, e) - # self.exec_output(0) - - # self.exec_output(2) + self.val = self._count class ExecCounter_Node(DualNodeBase): @@ -473,7 +462,6 @@ class Click_Node(NodeBase): title = "Click" version = "v0.1" main_widget_class = "ButtonNodeWidget" - main_widget_pos = "between ports" init_inputs = [] init_outputs = [NodeOutputBP(type_="exec")] color = "#99dd55" From 39584bb8b0e942f9e048c2f3c3b51ce75dfdb671 Mon Sep 17 00:00:00 2001 From: Liam Huber Date: Fri, 9 Sep 2022 12:24:13 -0700 Subject: [PATCH 28/28] Use objects instead of strings whenever you can --- ryven/ironflow/flow_canvas.py | 7 ++++++- ryven/nodes/pyiron/atomistics_nodes.py | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ryven/ironflow/flow_canvas.py b/ryven/ironflow/flow_canvas.py index 52ad68ce..6e738cf4 100644 --- a/ryven/ironflow/flow_canvas.py +++ b/ryven/ironflow/flow_canvas.py @@ -190,7 +190,12 @@ def load_node(self, x: Number, y: Number, node: Node) -> NodeWidget: layout = NodeLayout() if hasattr(node, "main_widget_class") and node.main_widget_class is not None: - node_class = eval(node.main_widget_class) + if isinstance(node.main_widget_class, str): + node_class = eval(node.main_widget_class) + elif issubclass(node.main_widget_class, NodeWidget): + node_class = node.main_widget_class + else: + raise TypeError(f"main_widget_class {node.main_widget_class} not recognized") else: node_class = NodeWidget s = node_class(x=x, y=y, parent=self, layout=layout, node=node) diff --git a/ryven/nodes/pyiron/atomistics_nodes.py b/ryven/nodes/pyiron/atomistics_nodes.py index 867f3d3b..fa2b97f1 100644 --- a/ryven/nodes/pyiron/atomistics_nodes.py +++ b/ryven/nodes/pyiron/atomistics_nodes.py @@ -6,6 +6,7 @@ import numpy as np from pyiron_atomistics import Project from ryven.NENV import Node, NodeInputBP, NodeOutputBP, dtypes +from ryven.ironflow.canvas_widgets import DisplayableNodeWidget, ButtonNodeWidget from abc import ABC, abstractmethod from ryven.nodes.std.special_nodes import DualNodeBase @@ -33,7 +34,7 @@ def __init__(self, params): class NodeWithDisplay(NodeBase, ABC): - main_widget_class = "DisplayableNodeWidget" + main_widget_class = DisplayableNodeWidget def __init__(self, params): super().__init__(params) @@ -461,7 +462,7 @@ def update_event(self, inp=-1): class Click_Node(NodeBase): title = "Click" version = "v0.1" - main_widget_class = "ButtonNodeWidget" + main_widget_class = ButtonNodeWidget init_inputs = [] init_outputs = [NodeOutputBP(type_="exec")] color = "#99dd55"