Reincorporated Old Code with Pipe Color Overrides
This commit is contained in:
parent
78ca945581
commit
4591178a4b
Binary file not shown.
101
Workflows/Basic_Data_Node_Connection.json
Normal file
101
Workflows/Basic_Data_Node_Connection.json
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"graph":{
|
||||||
|
"layout_direction":0,
|
||||||
|
"acyclic":true,
|
||||||
|
"pipe_collision":false,
|
||||||
|
"pipe_slicing":true,
|
||||||
|
"pipe_style":1,
|
||||||
|
"accept_connection_types":{},
|
||||||
|
"reject_connection_types":{}
|
||||||
|
},
|
||||||
|
"nodes":{
|
||||||
|
"0x1ad82a5c620":{
|
||||||
|
"type_":"bunny-lab.io.data_node.DataNode",
|
||||||
|
"icon":null,
|
||||||
|
"name":"Data Node",
|
||||||
|
"color":[
|
||||||
|
13,
|
||||||
|
18,
|
||||||
|
23,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"border_color":[
|
||||||
|
74,
|
||||||
|
84,
|
||||||
|
85,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"text_color":[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
180
|
||||||
|
],
|
||||||
|
"disabled":false,
|
||||||
|
"selected":false,
|
||||||
|
"visible":true,
|
||||||
|
"width":269.0,
|
||||||
|
"height":74.2,
|
||||||
|
"pos":[
|
||||||
|
-93.6890385514249,
|
||||||
|
181.13214119942148
|
||||||
|
],
|
||||||
|
"layout_direction":0,
|
||||||
|
"port_deletion_allowed":false,
|
||||||
|
"subgraph_session":{},
|
||||||
|
"custom":{
|
||||||
|
"value":"57"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0x1ad82a5cef0":{
|
||||||
|
"type_":"bunny-lab.io.data_node.DataNode",
|
||||||
|
"icon":null,
|
||||||
|
"name":"Data Node 1",
|
||||||
|
"color":[
|
||||||
|
13,
|
||||||
|
18,
|
||||||
|
23,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"border_color":[
|
||||||
|
74,
|
||||||
|
84,
|
||||||
|
85,
|
||||||
|
255
|
||||||
|
],
|
||||||
|
"text_color":[
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
180
|
||||||
|
],
|
||||||
|
"disabled":false,
|
||||||
|
"selected":false,
|
||||||
|
"visible":true,
|
||||||
|
"width":269.0,
|
||||||
|
"height":74.2,
|
||||||
|
"pos":[
|
||||||
|
361.37200584121035,
|
||||||
|
287.313051557703
|
||||||
|
],
|
||||||
|
"layout_direction":0,
|
||||||
|
"port_deletion_allowed":false,
|
||||||
|
"subgraph_session":{},
|
||||||
|
"custom":{
|
||||||
|
"value":"57"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connections":[
|
||||||
|
{
|
||||||
|
"out":[
|
||||||
|
"0x1ad82a5c620",
|
||||||
|
"Output"
|
||||||
|
],
|
||||||
|
"in":[
|
||||||
|
"0x1ad82a5cef0",
|
||||||
|
"Input"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
__pycache__/monkey_patches.cpython-312.pyc
Normal file
BIN
__pycache__/monkey_patches.cpython-312.pyc
Normal file
Binary file not shown.
383
borealis.py
383
borealis.py
@ -2,15 +2,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import pkgutil
|
||||||
import inspect
|
|
||||||
import importlib
|
import importlib
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
|
||||||
from Qt import QtWidgets, QtCore, QtGui
|
from Qt import QtWidgets, QtCore, QtGui
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# -------------------------------------------------------#
|
||||||
# MONKEY-PATCH to override all pens in PipeItem.__init__ so
|
# MONKEY PATCHES - MODIFICATIONS TO OdenGraphQT BEHAVIOR #
|
||||||
# idle, hover, selected, etc. all share the same color.
|
# -------------------------------------------------------#
|
||||||
|
|
||||||
|
# PATCH: Override the color of interconnection pipes between nodes
|
||||||
try:
|
try:
|
||||||
from OdenGraphQt.qgraphics.pipe import PipeItem
|
from OdenGraphQt.qgraphics.pipe import PipeItem
|
||||||
from qtpy.QtGui import QPen, QColor
|
from qtpy.QtGui import QPen, QColor
|
||||||
@ -55,33 +58,24 @@ except ImportError:
|
|||||||
print("WARNING: Could not patch PipeItem paint method.")
|
print("WARNING: Could not patch PipeItem paint method.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Patch for PipeItem.paint override failed: {e}")
|
print(f"Patch for PipeItem.paint override failed: {e}")
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# PATCH: Fix "module 'qtpy.QtGui' has no attribute 'QUndoStack'"
|
||||||
# MONKEY-PATCH to fix "module 'qtpy.QtGui' has no attribute 'QUndoStack'"
|
|
||||||
try:
|
try:
|
||||||
from qtpy.QtWidgets import QUndoStack
|
from qtpy.QtWidgets import QUndoStack
|
||||||
import qtpy
|
import qtpy
|
||||||
qtpy.QtGui.QUndoStack = QUndoStack
|
qtpy.QtGui.QUndoStack = QUndoStack
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("WARNING: Could not monkey-patch QUndoStack. You may see an error if OdenGraphQt needs it.")
|
print("WARNING: Could not monkey-patch QUndoStack. You may see an error if OdenGraphQt needs it.")
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# PATCH: Fix "'BackdropNodeItem' object has no attribute 'widgets'" by giving BackdropNodeItem a trivial widgets dictionary.
|
||||||
# MONKEY-PATCH to fix "'BackdropNodeItem' object has no attribute 'widgets'"
|
|
||||||
try:
|
try:
|
||||||
from OdenGraphQt.nodes.backdrop_node import BackdropNodeItem
|
from OdenGraphQt.nodes.backdrop_node import BackdropNodeItem
|
||||||
if not hasattr(BackdropNodeItem, "widgets"):
|
if not hasattr(BackdropNodeItem, "widgets"):
|
||||||
BackdropNodeItem.widgets = {}
|
BackdropNodeItem.widgets = {}
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("WARNING: Could not monkey-patch BackdropNodeItem to add `widgets`.")
|
print("WARNING: Could not monkey-patch BackdropNodeItem to add `widgets`.")
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Import your data_manager so we can start the Flask server
|
# PATCH: BEGIN ROBUST PATCH FOR QGraphicsScene.setSelectionArea
|
||||||
from Modules import data_manager
|
|
||||||
data_manager.start_api_server()
|
|
||||||
|
|
||||||
# --- BEGIN ROBUST PATCH FOR QGraphicsScene.setSelectionArea ---
|
|
||||||
_original_setSelectionArea = QtWidgets.QGraphicsScene.setSelectionArea
|
_original_setSelectionArea = QtWidgets.QGraphicsScene.setSelectionArea
|
||||||
|
|
||||||
def _patched_setSelectionArea(self, *args, **kwargs):
|
def _patched_setSelectionArea(self, *args, **kwargs):
|
||||||
@ -100,77 +94,17 @@ def _patched_setSelectionArea(self, *args, **kwargs):
|
|||||||
return _original_setSelectionArea(self, painterPath, selection_op, selection_mode, transform)
|
return _original_setSelectionArea(self, painterPath, selection_op, selection_mode, transform)
|
||||||
|
|
||||||
QtWidgets.QGraphicsScene.setSelectionArea = _patched_setSelectionArea
|
QtWidgets.QGraphicsScene.setSelectionArea = _patched_setSelectionArea
|
||||||
# --- END PATCH ---
|
|
||||||
|
# Import your data_manager so we can start the Flask server
|
||||||
|
from Modules import data_manager
|
||||||
|
|
||||||
from OdenGraphQt import NodeGraph, BaseNode
|
from OdenGraphQt import NodeGraph, BaseNode
|
||||||
from OdenGraphQt.widgets.dialogs import FileDialog
|
from OdenGraphQt.widgets.dialogs import FileDialog
|
||||||
|
|
||||||
|
|
||||||
def ensure_workflows_folder():
|
|
||||||
"""
|
|
||||||
Ensures a 'Workflows' subfolder exists.
|
|
||||||
"""
|
|
||||||
if not os.path.exists("Workflows"):
|
|
||||||
os.makedirs("Workflows")
|
|
||||||
|
|
||||||
|
|
||||||
def close_workflow(graph: NodeGraph):
|
|
||||||
"""
|
|
||||||
Closes the current workflow (removes all nodes and connections).
|
|
||||||
"""
|
|
||||||
graph.clear_session()
|
|
||||||
|
|
||||||
|
|
||||||
def save_workflow(graph: NodeGraph):
|
|
||||||
"""
|
|
||||||
Saves the current workflow (including custom names, positions, wires, etc.)
|
|
||||||
into a JSON file in the 'Workflows' subfolder.
|
|
||||||
"""
|
|
||||||
ensure_workflows_folder()
|
|
||||||
file_filter = "JSON Files (*.json);;All Files (*.*)"
|
|
||||||
dlg = FileDialog.getSaveFileName(None, "Save Workflow", os.path.join("Workflows", ""), file_filter)
|
|
||||||
file_path = dlg[0]
|
|
||||||
if not file_path:
|
|
||||||
return # User canceled
|
|
||||||
|
|
||||||
if not file_path.lower().endswith(".json"):
|
|
||||||
file_path += ".json"
|
|
||||||
|
|
||||||
try:
|
|
||||||
graph.save_session(file_path)
|
|
||||||
print(f"Workflow saved to {file_path}")
|
|
||||||
except Exception as e:
|
|
||||||
QtWidgets.QMessageBox.critical(None, "Error Saving Workflow", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
def load_workflow(graph: NodeGraph):
|
|
||||||
"""
|
|
||||||
Loads a workflow (including node values, connections, positions, etc.)
|
|
||||||
from a specified JSON file, then centers the view on all loaded nodes.
|
|
||||||
"""
|
|
||||||
ensure_workflows_folder()
|
|
||||||
file_filter = "JSON Files (*.json);;All Files (*.*)"
|
|
||||||
dlg = FileDialog.getOpenFileName(None, "Load Workflow", os.path.join("Workflows", ""), file_filter)
|
|
||||||
file_path = dlg[0]
|
|
||||||
if not file_path:
|
|
||||||
return # User canceled
|
|
||||||
|
|
||||||
try:
|
|
||||||
graph.load_session(file_path)
|
|
||||||
# After loading, center the viewer on all nodes.
|
|
||||||
all_nodes = graph.all_nodes()
|
|
||||||
if all_nodes:
|
|
||||||
graph.viewer().zoom_to_nodes([node.view for node in all_nodes])
|
|
||||||
print(f"Workflow loaded from {file_path}")
|
|
||||||
except Exception as e:
|
|
||||||
QtWidgets.QMessageBox.critical(None, "Error Loading Workflow", str(e))
|
|
||||||
|
|
||||||
|
|
||||||
def import_nodes_from_folder(package_name):
|
def import_nodes_from_folder(package_name):
|
||||||
"""
|
"""
|
||||||
Recursively import all modules from the given package.
|
Recursively import all modules from the given package.
|
||||||
Returns a dictionary where keys are subfolder names,
|
Returns a dictionary where keys are subfolder names, and values are lists of BaseNode subclasses.
|
||||||
and values are lists of BaseNode subclasses.
|
|
||||||
"""
|
"""
|
||||||
nodes_by_category = {}
|
nodes_by_category = {}
|
||||||
package = importlib.import_module(package_name)
|
package = importlib.import_module(package_name)
|
||||||
@ -193,14 +127,13 @@ def import_nodes_from_folder(package_name):
|
|||||||
nodes_by_category[category_name].append(obj)
|
nodes_by_category[category_name].append(obj)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to import {module_name}: {e}")
|
print(f"Failed to import {module_name}: {e}")
|
||||||
|
|
||||||
return nodes_by_category
|
return nodes_by_category
|
||||||
|
|
||||||
|
|
||||||
def make_node_command(graph, node_type_str):
|
def make_node_command(graph, node_type_str):
|
||||||
"""
|
"""
|
||||||
Return a function that creates a node of the given type at the current cursor position.
|
Return a function that creates a node of the given type at the current cursor position.
|
||||||
Ensures that only one FlyffCharacterStatusNode exists if that's the node being created.
|
Ensures that only one FlyffCharacterStatusNode exists.
|
||||||
"""
|
"""
|
||||||
def real_create():
|
def real_create():
|
||||||
if node_type_str.startswith("bunny-lab.io.flyff_character_status_node"):
|
if node_type_str.startswith("bunny-lab.io.flyff_character_status_node"):
|
||||||
@ -226,175 +159,129 @@ def make_node_command(graph, node_type_str):
|
|||||||
|
|
||||||
return command
|
return command
|
||||||
|
|
||||||
|
def ensure_workflows_folder():
|
||||||
|
"""
|
||||||
|
Ensures a 'Workflows' subfolder exists.
|
||||||
|
"""
|
||||||
|
if not os.path.exists("Workflows"):
|
||||||
|
os.makedirs("Workflows")
|
||||||
|
|
||||||
class BorealisWindow(QtWidgets.QMainWindow):
|
def close_workflow(graph: NodeGraph):
|
||||||
def __init__(self):
|
"""
|
||||||
super().__init__()
|
Closes the current workflow (removes all nodes and connections).
|
||||||
|
"""
|
||||||
|
graph.clear_session()
|
||||||
|
|
||||||
self.setWindowTitle("Borealis - Workflow Automation System")
|
def save_workflow(graph: NodeGraph):
|
||||||
|
"""
|
||||||
|
Saves the current workflow (including custom names, positions, wires, etc.) into a JSON file
|
||||||
|
in the 'Workflows' subfolder.
|
||||||
|
"""
|
||||||
|
ensure_workflows_folder()
|
||||||
|
file_filter = "JSON Files (*.json);;All Files (*.*)"
|
||||||
|
dlg = FileDialog.getSaveFileName(None, "Save Workflow", os.path.join("Workflows", ""), file_filter)
|
||||||
|
file_path = dlg[0]
|
||||||
|
if not file_path:
|
||||||
|
return # User canceled
|
||||||
|
|
||||||
# Create NodeGraph and widget
|
if not file_path.lower().endswith(".json"):
|
||||||
self.graph = NodeGraph()
|
file_path += ".json"
|
||||||
|
|
||||||
# Grid styling changes
|
try:
|
||||||
self.graph.set_background_color(20, 20, 20) # Dark gray
|
graph.save_session(file_path)
|
||||||
self.graph.set_grid_color(60, 60, 60) # Gray grid lines
|
print(f"Workflow saved to {file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
QtWidgets.QMessageBox.critical(None, "Error Saving Workflow", str(e))
|
||||||
|
|
||||||
# Add gradient background
|
def load_workflow(graph: NodeGraph):
|
||||||
scene = self.graph.scene()
|
"""
|
||||||
gradient = QtGui.QLinearGradient(0, 0, 0, 1)
|
Loads a workflow (including node values, connections, positions, etc.) from a specified JSON file.
|
||||||
gradient.setCoordinateMode(QtGui.QGradient.ObjectBoundingMode)
|
"""
|
||||||
gradient.setColorAt(0.0, QtGui.QColor(9, 44, 68))
|
ensure_workflows_folder()
|
||||||
gradient.setColorAt(0.3, QtGui.QColor(30, 30, 30))
|
file_filter = "JSON Files (*.json);;All Files (*.*)"
|
||||||
gradient.setColorAt(0.7, QtGui.QColor(30, 30, 30))
|
dlg = FileDialog.getOpenFileName(None, "Load Workflow", os.path.join("Workflows", ""), file_filter)
|
||||||
gradient.setColorAt(1.0, QtGui.QColor(9, 44, 68))
|
file_path = dlg[0]
|
||||||
scene.setBackgroundBrush(QtGui.QBrush(gradient))
|
if not file_path:
|
||||||
|
return # User canceled
|
||||||
# Load custom nodes from "Nodes" folder
|
|
||||||
custom_nodes_by_category = import_nodes_from_folder("Nodes")
|
|
||||||
for category, node_classes in custom_nodes_by_category.items():
|
|
||||||
for node_class in node_classes:
|
|
||||||
self.graph.register_node(node_class)
|
|
||||||
|
|
||||||
# Build the right-click context menu for the graph
|
|
||||||
self._build_graph_context_menu(custom_nodes_by_category)
|
|
||||||
|
|
||||||
# Setup the central widget to show the NodeGraph
|
|
||||||
self.setCentralWidget(self.graph.widget)
|
|
||||||
|
|
||||||
# Build the top menu bar
|
|
||||||
self._build_menubar()
|
|
||||||
|
|
||||||
# Create a status bar
|
|
||||||
self.setStatusBar(QtWidgets.QStatusBar(self))
|
|
||||||
# Remove the resize handle from the status bar:
|
|
||||||
self.statusBar().setSizeGripEnabled(False)
|
|
||||||
|
|
||||||
# Add a permanent clickable link to the right side of the status bar:
|
|
||||||
self.link_label = QtWidgets.QLabel()
|
|
||||||
self.link_label.setTextFormat(QtCore.Qt.RichText)
|
|
||||||
self.link_label.setOpenExternalLinks(True)
|
|
||||||
# Add a couple spaces after the URL using
|
|
||||||
# Also color style to match your "blue-ish" highlight:
|
|
||||||
self.link_label.setText(
|
|
||||||
"<a href='http://127.0.0.1:5000/data' "
|
|
||||||
"style='color: rgb(60,120,180); text-decoration: none;'>"
|
|
||||||
"Flask API Server: http://127.0.0.1:5000/data </a>"
|
|
||||||
)
|
|
||||||
self.statusBar().addPermanentWidget(self.link_label)
|
|
||||||
|
|
||||||
# Resize
|
|
||||||
self.resize(1200, 800)
|
|
||||||
|
|
||||||
def _build_graph_context_menu(self, custom_nodes_by_category):
|
|
||||||
"""
|
|
||||||
Build context menu and apply custom stylesheet for the 'blue-ish' highlight.
|
|
||||||
"""
|
|
||||||
graph_context_menu = self.graph.get_context_menu("graph")
|
|
||||||
menu_stylesheet = """
|
|
||||||
QMenu {
|
|
||||||
background-color: rgb(30, 30, 30);
|
|
||||||
border: 1px solid rgba(200, 200, 200, 60);
|
|
||||||
}
|
|
||||||
QMenu::item {
|
|
||||||
padding: 5px 18px 2px;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
QMenu::item:selected {
|
|
||||||
color: rgb(255, 255, 255);
|
|
||||||
background-color: rgba(60, 120, 180, 150);
|
|
||||||
}
|
|
||||||
QMenu::separator {
|
|
||||||
height: 1px;
|
|
||||||
background: rgba(255, 255, 255, 50);
|
|
||||||
margin: 4px 8px;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
if graph_context_menu and graph_context_menu.qmenu:
|
|
||||||
graph_context_menu.qmenu.setStyleSheet(menu_stylesheet)
|
|
||||||
|
|
||||||
add_nodes_menu = graph_context_menu.add_menu("Add Nodes")
|
|
||||||
if add_nodes_menu and add_nodes_menu.qmenu:
|
|
||||||
add_nodes_menu.qmenu.setStyleSheet(menu_stylesheet)
|
|
||||||
|
|
||||||
for category, node_classes in custom_nodes_by_category.items():
|
|
||||||
category_menu = add_nodes_menu.add_menu(category)
|
|
||||||
if category_menu and category_menu.qmenu:
|
|
||||||
category_menu.qmenu.setStyleSheet(menu_stylesheet)
|
|
||||||
|
|
||||||
for node_class in node_classes:
|
|
||||||
node_type = f"{node_class.__identifier__}.{node_class.__name__}"
|
|
||||||
node_name = node_class.NODE_NAME
|
|
||||||
category_menu.add_command(
|
|
||||||
node_name,
|
|
||||||
make_node_command(self.graph, node_type)
|
|
||||||
)
|
|
||||||
|
|
||||||
graph_context_menu.add_command(
|
|
||||||
"Remove Selected Node",
|
|
||||||
lambda: [self.graph.remove_node(node)
|
|
||||||
for node in self.graph.selected_nodes()]
|
|
||||||
if self.graph.selected_nodes() else None
|
|
||||||
)
|
|
||||||
|
|
||||||
def _build_menubar(self):
|
|
||||||
menubar = self.menuBar()
|
|
||||||
|
|
||||||
# Workflows menu
|
|
||||||
workflows_menu = menubar.addMenu("Workflows")
|
|
||||||
|
|
||||||
load_action = QtWidgets.QAction("Load Workflow", self)
|
|
||||||
load_action.triggered.connect(lambda: load_workflow(self.graph))
|
|
||||||
workflows_menu.addAction(load_action)
|
|
||||||
|
|
||||||
save_action = QtWidgets.QAction("Save Workflow", self)
|
|
||||||
save_action.triggered.connect(lambda: save_workflow(self.graph))
|
|
||||||
workflows_menu.addAction(save_action)
|
|
||||||
|
|
||||||
close_action = QtWidgets.QAction("Close Workflow", self)
|
|
||||||
close_action.triggered.connect(lambda: close_workflow(self.graph))
|
|
||||||
workflows_menu.addAction(close_action)
|
|
||||||
|
|
||||||
# About menu
|
|
||||||
about_menu = menubar.addMenu("About")
|
|
||||||
|
|
||||||
gitea_action = QtWidgets.QAction("Gitea Project", self)
|
|
||||||
gitea_action.triggered.connect(self._open_gitea_project)
|
|
||||||
about_menu.addAction(gitea_action)
|
|
||||||
|
|
||||||
credits_action = QtWidgets.QAction("Credits", self)
|
|
||||||
credits_action.triggered.connect(self._show_credits_popup)
|
|
||||||
about_menu.addAction(credits_action)
|
|
||||||
|
|
||||||
updates_action = QtWidgets.QAction("Check for Updates", self)
|
|
||||||
updates_action.triggered.connect(self._show_updates_popup)
|
|
||||||
about_menu.addAction(updates_action)
|
|
||||||
|
|
||||||
def _open_gitea_project(self):
|
|
||||||
url = QtCore.QUrl("https://git.bunny-lab.io/Scripts/Project_Borealis")
|
|
||||||
QtGui.QDesktopServices.openUrl(url)
|
|
||||||
|
|
||||||
def _show_credits_popup(self):
|
|
||||||
QtWidgets.QMessageBox.information(
|
|
||||||
self,
|
|
||||||
"Credits",
|
|
||||||
"Created by Nicole Rappe"
|
|
||||||
)
|
|
||||||
|
|
||||||
def _show_updates_popup(self):
|
|
||||||
QtWidgets.QMessageBox.information(
|
|
||||||
self,
|
|
||||||
"Check for Updates",
|
|
||||||
"Built-in update functionality has not been built yet, but it's on the roadmap. Stay tuned."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
app = QtWidgets.QApplication(sys.argv)
|
|
||||||
window = BorealisWindow()
|
|
||||||
window.show()
|
|
||||||
sys.exit(app.exec_())
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
graph.load_session(file_path)
|
||||||
|
print(f"Workflow loaded from {file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
QtWidgets.QMessageBox.critical(None, "Error Loading Workflow", str(e))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
app = QtWidgets.QApplication([])
|
||||||
|
|
||||||
|
# Start Flask API Server
|
||||||
|
data_manager.start_api_server()
|
||||||
|
|
||||||
|
# Create the NodeGraph
|
||||||
|
graph = NodeGraph()
|
||||||
|
graph.widget.setWindowTitle("Project Borealis - Workflow Automation System")
|
||||||
|
|
||||||
|
# Dynamically import custom node classes from the 'Nodes' package.
|
||||||
|
custom_nodes_by_category = import_nodes_from_folder("Nodes")
|
||||||
|
|
||||||
|
# Register each node in its category
|
||||||
|
for category, node_classes in custom_nodes_by_category.items():
|
||||||
|
for node_class in node_classes:
|
||||||
|
graph.register_node(node_class)
|
||||||
|
|
||||||
|
# Create categorized context menu
|
||||||
|
graph_context_menu = graph.get_context_menu("graph")
|
||||||
|
|
||||||
|
for category, node_classes in custom_nodes_by_category.items():
|
||||||
|
category_menu = graph_context_menu.add_menu(category) # Create submenu for category
|
||||||
|
for node_class in node_classes:
|
||||||
|
node_type = f"{node_class.__identifier__}.{node_class.__name__}"
|
||||||
|
node_name = node_class.NODE_NAME
|
||||||
|
category_menu.add_command(
|
||||||
|
f"Create: {node_name}",
|
||||||
|
make_node_command(graph, node_type)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add a "Remove Selected Node" command
|
||||||
|
graph_context_menu.add_command(
|
||||||
|
"Remove Selected Node",
|
||||||
|
lambda: [graph.remove_node(node) for node in graph.selected_nodes()] if graph.selected_nodes() else None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add workflow menu commands
|
||||||
|
workflow_menu = graph_context_menu.add_menu("Workflow")
|
||||||
|
workflow_menu.add_command("Load Workflow", lambda: load_workflow(graph))
|
||||||
|
workflow_menu.add_command("Save Workflow", lambda: save_workflow(graph))
|
||||||
|
workflow_menu.add_command("Close Workflow", lambda: close_workflow(graph))
|
||||||
|
|
||||||
|
# Grid styling changes
|
||||||
|
graph.set_background_color(20, 20, 20) # Dark gray
|
||||||
|
graph.set_grid_color(60, 60, 60) # Gray grid lines
|
||||||
|
|
||||||
|
# Add gradient background
|
||||||
|
scene = graph.scene()
|
||||||
|
gradient = QtGui.QLinearGradient(0, 0, 0, 1)
|
||||||
|
gradient.setCoordinateMode(QtGui.QGradient.ObjectBoundingMode)
|
||||||
|
gradient.setColorAt(0.0, QtGui.QColor(9, 44, 68))
|
||||||
|
gradient.setColorAt(0.3, QtGui.QColor(30, 30, 30))
|
||||||
|
gradient.setColorAt(0.7, QtGui.QColor(30, 30, 30))
|
||||||
|
gradient.setColorAt(1.0, QtGui.QColor(9, 44, 68))
|
||||||
|
scene.setBackgroundBrush(QtGui.QBrush(gradient))
|
||||||
|
|
||||||
|
# Resize and show the graph widget
|
||||||
|
graph.widget.resize(1600, 900)
|
||||||
|
graph.widget.show()
|
||||||
|
|
||||||
|
# Global update function
|
||||||
|
def global_update():
|
||||||
|
for node in graph.all_nodes():
|
||||||
|
if hasattr(node, "process_input"):
|
||||||
|
try:
|
||||||
|
node.process_input()
|
||||||
|
except Exception as e:
|
||||||
|
print("Error updating node", node, e)
|
||||||
|
|
||||||
|
timer = QtCore.QTimer()
|
||||||
|
timer.timeout.connect(global_update)
|
||||||
|
timer.start(500)
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user