Skip to content

Commit

Permalink
Merge pull request #35 from pyiron/global_workflow_controls
Browse files Browse the repository at this point in the history
Global workflow controls
  • Loading branch information
Tara-Lakshmipathy authored Dec 31, 2024
2 parents d5248a6 + 2bbe51c commit 5f12732
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 11 deletions.
107 changes: 105 additions & 2 deletions js/widget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ const render = createRender(() => {

const [macroName, setMacroName] = useState('custom_macro');

const [currentDateTime, setCurrentDateTime] = useState(() => {
const currentTime = new Date();
return currentTime.toLocaleString();
});

useEffect(() => {
const intervalId = setInterval(() => {
const currentTime = new Date();
setCurrentDateTime(currentTime.toLocaleString());
}, 1000); // update every second

return () => {
clearInterval(intervalId);
};
}, []);


const updateData = (nodeLabel, handleIndex, newValue) => {
setNodes(prevNodes =>
Expand Down Expand Up @@ -250,6 +266,29 @@ const render = createRender(() => {
),
);

function getOS() {
var userAgent = window.navigator.userAgent;
if (/Mac/.test(userAgent)) {
return 'Mac OS';
} else if (/Win/.test(userAgent)) {
return 'Windows';
} else if (/Linux/.test(userAgent)) {
return 'Linux';
}
return 'Unknown OS';
}

var os = getOS();
var macrobuttonStyle = {position: "absolute", zIndex: "4"};

if (os === "Windows") {
macrobuttonStyle = { ...macrobuttonStyle, right: "80px", top: "50px" }
} else if (os === "Linux") {
macrobuttonStyle = { ...macrobuttonStyle, right: "100px", top: "50px" }
} else if (os === "Mac OS") {
macrobuttonStyle = { ...macrobuttonStyle, right: "100px", top: "50px" }
}

const macroFunction = (userInput) => {
console.log('macro: ', userInput);
if (model) {
Expand All @@ -260,8 +299,48 @@ const render = createRender(() => {
}
}

const runFunction = (dateTime) => {
console.log('run executed at ', dateTime);
if (model) {
model.set("commands", `run executed at ${dateTime}`);
model.save_changes();
} else {
console.error('model is undefined');
}
}

const saveFunction = (dateTime) => {
console.log('save executed at ', dateTime);
if (model) {
model.set("commands", `save executed at ${dateTime}`);
model.save_changes();
} else {
console.error('model is undefined');
}
}

const loadFunction = (dateTime) => {
console.log('load executed at ', dateTime);
if (model) {
model.set("commands", `load executed at ${dateTime}`);
model.save_changes();
} else {
console.error('model is undefined');
}
}

const deleteFunction = (dateTime) => {
console.log('delete executed at ', dateTime);
if (model) {
model.set("commands", `delete executed at ${dateTime}`);
model.save_changes();
} else {
console.error('model is undefined');
}
}

return (
<div style={{ position: "relative", height: "800px", width: "100%" }}>
<div style={{ position: "relative", height: "80vh", width: "100%" }}>
<UpdateDataContext.Provider value={updateData}>
<ReactFlow
nodes={nodes}
Expand All @@ -286,11 +365,35 @@ const render = createRender(() => {
<MiniMap />
<Controls />
<button
style={{position: "absolute", right: "100px", top: "50px", zIndex: "4"}}
style={macrobuttonStyle}
onClick={() => macroFunction(macroName)}
>
Create Macro
</button>
<button
style={{position: "absolute", left: "10px", top: "10px", zIndex: "4"}}
onClick={() => runFunction(currentDateTime)}
>
Run Workflow
</button>
<button
style={{position: "absolute", left: "120px", top: "10px", zIndex: "4"}}
onClick={() => saveFunction(currentDateTime)}
>
Save Workflow
</button>
<button
style={{position: "absolute", left: "230px", top: "10px", zIndex: "4"}}
onClick={() => loadFunction(currentDateTime)}
>
Load Workflow
</button>
<button
style={{position: "absolute", left: "340px", top: "10px", zIndex: "4"}}
onClick={() => deleteFunction(currentDateTime)}
>
Delete Save File
</button>
</ReactFlow>
</UpdateDataContext.Provider>
</div>
Expand Down
42 changes: 37 additions & 5 deletions pyironflow/pyironflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,56 @@
__status__ = "development"
__date__ = "Aug 1, 2024"

def get_screen_resolution():
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
try:
root = tk.Tk()
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
return screen_width, screen_height
except:
screen_width = 1920
screen_height = 930
return screen_width, screen_height

class GUILayout:
screen_width, screen_height = get_screen_resolution()
flow_widget_width = screen_width / 1.75
flow_widget_height = screen_height / 1.2
output_widget_width = 400


class PyironFlow:
def __init__(self, wf_list=None, root_path='../pyiron_nodes/pyiron_nodes'):
def __init__(self, wf_list=None, root_path='../pyiron_nodes/pyiron_nodes', gui_layout: GUILayout = GUILayout()):
# generate empty default workflow if workflow list is empty
if wf_list is None:
wf_list = []
if len(wf_list) == 0:
wf_list = [Workflow('workflow')]

self._gui_layout = gui_layout
self.workflows = wf_list

self.out_log = widgets.Output(layout={'border': '1px solid black', 'width': '800px'})
self.out_widget = widgets.Output(layout={'border': '1px solid black', 'min_width': '400px'})
self.out_log = widgets.Output(layout={'border': '1px solid black',
'width': f'{gui_layout.output_widget_width}px',
'max_height': f'{self._gui_layout.flow_widget_height}px',
'overflow': 'auto', })
self.out_widget = widgets.Output(layout={'border': '1px solid black',
'max_width': f'{gui_layout.output_widget_width}px',
'overflow': 'auto', })
self.wf_widgets = [PyironFlowWidget(wf=wf, root_path=root_path, log=self.out_log, out_widget=self.out_widget)
for wf in self.workflows]
self.view_flows = self.view_flows()
self.tree_view = TreeView(root_path=root_path, flow_widget=self.wf_widgets[0], log=self.out_log)
self.accordion = widgets.Accordion(children=[self.tree_view.gui, self.out_widget, self.out_log],
titles=['Node Library', 'Output', 'Logging Info'],
layout={'border': '1px solid black', 'min_width': '400px'})
layout={'border': '1px solid black',
'min_width': f'{gui_layout.output_widget_width}px',
'max_height': f'{self._gui_layout.flow_widget_height}px',
'overflow': 'auto', })
for widget in self.wf_widgets:
widget.accordion_widget = self.accordion

Expand All @@ -59,7 +89,9 @@ def display_workflow(self, index: int, out_flow=None):
w = self.wf_widgets[index]

if out_flow is None:
out_flow = widgets.Output(layout={'border': '1px solid black', 'width': '1200px', 'height': '802px'})
out_flow = widgets.Output(layout={'border': '1px solid black',
'width': f'{self._gui_layout.flow_widget_width}px',
'max_height': f'{self._gui_layout.flow_widget_height+2}px'})

with out_flow:
display(w.gui)
Expand Down
56 changes: 52 additions & 4 deletions pyironflow/reactflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,17 @@ def on_value_change(self, change):
warnings.simplefilter("ignore")

print('command: ', change['new'])
command, node_name = change['new'].split(':')
node_name = node_name.split('-')[0].strip()
command = ''
node_name = ''
global_command = ''
if 'executed at' not in change['new']:
command, node_name = change['new'].split(':')
node_name = node_name.split('-')[0].strip()
else:
global_command_string = change['new'].split(' ')
global_command = global_command_string[0]
# print (f'node {node_name} not in wf {self.wf._children.keys()}: ', node_name not in self.wf._children)
if command != 'macro':
if command != '' and command != 'macro' and node_name != '':
node_name = node_name.split('-')[0].strip()
if node_name not in self.wf._children:
return
Expand Down Expand Up @@ -252,11 +259,52 @@ def on_value_change(self, change):
elif command == 'delete_node':
self.wf.remove_child(node_name)

elif command == 'macro':
elif command == 'macro' and node_name != '':
if self.accordion_widget is not None:
self.accordion_widget.selected_index = 1
create_macro(self.get_selected_workflow(), node_name, self.root_path)

elif global_command == 'run':
if self.accordion_widget is not None:
self.accordion_widget.selected_index = 1
self.out_widget.clear_output()
out = self.wf.run()
display(out)

elif global_command == 'save':
if self.accordion_widget is not None:
self.accordion_widget.selected_index = 1
temp_label = self.wf.label
self.wf.label = temp_label + "-save"
self.wf.save()
self.wf.label = temp_label
print("Successfully saved in " + temp_label + "-save")

elif global_command == 'load':
if self.accordion_widget is not None:
self.accordion_widget.selected_index = 1
temp_label = self.wf.label
self.wf.label = temp_label + "-save"
try:
self.wf.load()
self.wf.label = temp_label
self.update()
print("Successfully loaded from " + temp_label + "-save")
except:
self.wf.label = temp_label
self.update()
print("Save file " + temp_label + "-save" + " not found!")

elif global_command == 'delete':
if self.accordion_widget is not None:
self.accordion_widget.selected_index = 1
temp_label = self.wf.label
self.wf.label = temp_label + "-save"
self.wf.delete_storage()
self.wf.label = temp_label
print("Deleted " + temp_label + "-save")


def update(self):
nodes = get_nodes(self.wf)
edges = get_edges(self.wf)
Expand Down

0 comments on commit 5f12732

Please sign in to comment.