Added Status bar, menu bar, and made context menu blue.
This commit is contained in:
		
							
								
								
									
										98
									
								
								Experiments/gui_elements.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								Experiments/gui_elements.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| # example_qt_interface.py | ||||
| import sys | ||||
| from PySide6.QtCore import Qt | ||||
| from PySide6.QtGui import QAction, QIcon | ||||
| from PySide6.QtWidgets import ( | ||||
|     QApplication, QMainWindow, QWidget, QVBoxLayout, | ||||
|     QLabel, QMenuBar, QToolBar, QSplitter, QListWidget, | ||||
|     QTextEdit, QStatusBar, QFileDialog, QPushButton | ||||
| ) | ||||
|  | ||||
|  | ||||
| class MainWindow(QMainWindow): | ||||
|     def __init__(self): | ||||
|         super().__init__() | ||||
|  | ||||
|         self.setWindowTitle("Example Qt Interface") | ||||
|  | ||||
|         # Create and set up the menu bar. | ||||
|         menu_bar = QMenuBar(self) | ||||
|         self.setMenuBar(menu_bar) | ||||
|  | ||||
|         # File menu. | ||||
|         file_menu = menu_bar.addMenu("File") | ||||
|  | ||||
|         # Create some actions to populate the File menu. | ||||
|         open_action = QAction("Open", self) | ||||
|         open_action.triggered.connect(self.open_file) | ||||
|         file_menu.addAction(open_action) | ||||
|  | ||||
|         save_action = QAction("Save", self) | ||||
|         save_action.triggered.connect(self.save_file) | ||||
|         file_menu.addAction(save_action) | ||||
|  | ||||
|         exit_action = QAction("Exit", self) | ||||
|         exit_action.triggered.connect(self.close) | ||||
|         file_menu.addAction(exit_action) | ||||
|  | ||||
|         # Create a toolbar and add some actions. | ||||
|         tool_bar = QToolBar("Main Toolbar", self) | ||||
|         tool_bar.addAction(open_action) | ||||
|         tool_bar.addAction(save_action) | ||||
|         self.addToolBar(Qt.TopToolBarArea, tool_bar) | ||||
|  | ||||
|         # Set up a status bar at the bottom. | ||||
|         self.setStatusBar(QStatusBar(self)) | ||||
|         self.statusBar().showMessage("Ready") | ||||
|  | ||||
|         # Create your central widget area. | ||||
|         central_widget = QWidget() | ||||
|         self.setCentralWidget(central_widget) | ||||
|         layout = QVBoxLayout(central_widget) | ||||
|  | ||||
|         # A splitter as an example container that can hold multiple widgets side-by-side. | ||||
|         splitter = QSplitter() | ||||
|  | ||||
|         # Left side: a simple list widget. | ||||
|         self.list_widget = QListWidget() | ||||
|         self.list_widget.addItem("Item A") | ||||
|         self.list_widget.addItem("Item B") | ||||
|         self.list_widget.addItem("Item C") | ||||
|         splitter.addWidget(self.list_widget) | ||||
|  | ||||
|         # Right side: a text edit widget. | ||||
|         self.text_edit = QTextEdit() | ||||
|         self.text_edit.setPlainText("Type here...") | ||||
|         splitter.addWidget(self.text_edit) | ||||
|  | ||||
|         layout.addWidget(splitter) | ||||
|  | ||||
|         # Example button in the central widget area. | ||||
|         example_button = QPushButton("Click Me") | ||||
|         example_button.clicked.connect(self.on_button_clicked) | ||||
|         layout.addWidget(example_button) | ||||
|  | ||||
|     def open_file(self): | ||||
|         file_name, _ = QFileDialog.getOpenFileName(self, "Open File", "", "All Files (*.*)") | ||||
|         if file_name: | ||||
|             self.statusBar().showMessage(f"Opened: {file_name}") | ||||
|  | ||||
|     def save_file(self): | ||||
|         file_name, _ = QFileDialog.getSaveFileName(self, "Save File", "", "All Files (*.*)") | ||||
|         if file_name: | ||||
|             self.statusBar().showMessage(f"Saved: {file_name}") | ||||
|  | ||||
|     def on_button_clicked(self): | ||||
|         self.statusBar().showMessage("Button clicked!") | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     app = QApplication(sys.argv) | ||||
|     window = MainWindow() | ||||
|     window.resize(800, 600) | ||||
|     window.show() | ||||
|     sys.exit(app.exec()) | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
							
								
								
									
										399
									
								
								borealis.py
									
									
									
									
									
								
							
							
						
						
									
										399
									
								
								borealis.py
									
									
									
									
									
								
							| @@ -2,10 +2,9 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| import sys | ||||
| import pkgutil | ||||
| import importlib | ||||
| import inspect | ||||
| import os | ||||
| import inspect | ||||
| import importlib | ||||
|  | ||||
| from Qt import QtWidgets, QtCore, QtGui | ||||
|  | ||||
| @@ -21,7 +20,6 @@ except ImportError: | ||||
|  | ||||
| # ------------------------------------------------------------------ | ||||
| # MONKEY-PATCH to fix "'BackdropNodeItem' object has no attribute 'widgets'" | ||||
| # by giving BackdropNodeItem a trivial widgets dictionary. | ||||
| try: | ||||
|     from OdenGraphQt.nodes.backdrop_node import BackdropNodeItem | ||||
|     if not hasattr(BackdropNodeItem, "widgets"): | ||||
| @@ -32,6 +30,7 @@ except ImportError: | ||||
|  | ||||
| # Import your data_manager so we can start the Flask server | ||||
| from Modules import data_manager | ||||
| data_manager.start_api_server() | ||||
|  | ||||
| # --- BEGIN ROBUST PATCH FOR QGraphicsScene.setSelectionArea --- | ||||
| _original_setSelectionArea = QtWidgets.QGraphicsScene.setSelectionArea | ||||
| @@ -57,10 +56,68 @@ QtWidgets.QGraphicsScene.setSelectionArea = _patched_setSelectionArea | ||||
| from OdenGraphQt import NodeGraph, BaseNode | ||||
| 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. | ||||
|     """ | ||||
|     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) | ||||
|         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): | ||||
|     """ | ||||
|     Recursively import all modules from the given package. | ||||
|     Returns a dictionary where keys are subfolder names, and values are lists of BaseNode subclasses. | ||||
|     Returns a dictionary where keys are subfolder names, | ||||
|     and values are lists of BaseNode subclasses. | ||||
|     """ | ||||
|     nodes_by_category = {} | ||||
|     package = importlib.import_module(package_name) | ||||
| @@ -83,13 +140,14 @@ def import_nodes_from_folder(package_name): | ||||
|                             nodes_by_category[category_name].append(obj) | ||||
|                 except Exception as e: | ||||
|                     print(f"Failed to import {module_name}: {e}") | ||||
|      | ||||
|  | ||||
|     return nodes_by_category | ||||
|  | ||||
|  | ||||
| def make_node_command(graph, node_type_str): | ||||
|     """ | ||||
|     Return a function that creates a node of the given type at the current cursor position. | ||||
|     Ensures that only one FlyffCharacterStatusNode exists. | ||||
|     Ensures that only one FlyffCharacterStatusNode exists if that's the node being created. | ||||
|     """ | ||||
|     def real_create(): | ||||
|         if node_type_str.startswith("bunny-lab.io.flyff_character_status_node"): | ||||
| @@ -115,161 +173,188 @@ def make_node_command(graph, node_type_str): | ||||
|  | ||||
|     return command | ||||
|  | ||||
| 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() | ||||
| class BorealisWindow(QtWidgets.QMainWindow): | ||||
|     def __init__(self): | ||||
|         super().__init__() | ||||
|  | ||||
| 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 | ||||
|         self.setWindowTitle("Project Borealis - Workflow Automation System") | ||||
|  | ||||
|     if not file_path.lower().endswith(".json"): | ||||
|         file_path += ".json" | ||||
|         # Create NodeGraph and widget | ||||
|         self.graph = NodeGraph() | ||||
|  | ||||
|     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)) | ||||
|         # Grid styling changes | ||||
|         self.graph.set_background_color(20, 20, 20)  # Dark gray | ||||
|         self.graph.set_grid_color(60, 60, 60)        # Gray grid lines | ||||
|  | ||||
| def load_workflow(graph: NodeGraph): | ||||
|     """ | ||||
|     Loads a workflow (including node values, connections, positions, etc.) from a specified JSON file. | ||||
|     """ | ||||
|     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 | ||||
|         # Add gradient background | ||||
|         scene = self.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)) | ||||
|  | ||||
|         # 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)) | ||||
|         # Default status message | ||||
|         self.update_status_bar("Flask Server: http://0.0.0.0:5000/data") | ||||
|  | ||||
|         # Resize | ||||
|         self.resize(1200, 800) | ||||
|  | ||||
|     def _build_graph_context_menu(self, custom_nodes_by_category): | ||||
|         """ | ||||
|         Build context menu and re-apply the custom stylesheet for a  | ||||
|         'blue-ish' highlight, removing the pink/purple highlight. | ||||
|         """ | ||||
|         # Grab the node graph's context menu | ||||
|         graph_context_menu = self.graph.get_context_menu("graph") | ||||
|  | ||||
|         # We can define a custom style for the QMenu objects: | ||||
|         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; | ||||
|         } | ||||
|         """ | ||||
|  | ||||
|         # Apply the custom style | ||||
|         if graph_context_menu and graph_context_menu.qmenu: | ||||
|             graph_context_menu.qmenu.setStyleSheet(menu_stylesheet) | ||||
|  | ||||
|         # Top-level "Add Nodes" folder in the context menu | ||||
|         add_nodes_menu = graph_context_menu.add_menu("Add Nodes") | ||||
|  | ||||
|         # If you want the same style for "Add Nodes" submenus: | ||||
|         if add_nodes_menu and add_nodes_menu.qmenu: | ||||
|             add_nodes_menu.qmenu.setStyleSheet(menu_stylesheet) | ||||
|  | ||||
|         # For each category, build a submenu under "Add Nodes" | ||||
|         for category, node_classes in custom_nodes_by_category.items(): | ||||
|             category_menu = add_nodes_menu.add_menu(category) | ||||
|             # Also reapply style for each new sub-menu: | ||||
|             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) | ||||
|                 ) | ||||
|  | ||||
|         # Provide a way to remove selected nodes | ||||
|         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 | ||||
|         ) | ||||
|  | ||||
|         # No 'Workflows' portion here because we moved it into the top menubar. | ||||
|  | ||||
|     def _build_menubar(self): | ||||
|         menubar = self.menuBar() | ||||
|  | ||||
|         # 1) Workflows menu in menubar | ||||
|         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) | ||||
|  | ||||
|         # 2) About menu | ||||
|         about_menu = menubar.addMenu("About") | ||||
|  | ||||
|         # "Gitea Project" option | ||||
|         gitea_action = QtWidgets.QAction("Gitea Project", self) | ||||
|         gitea_action.triggered.connect(self._open_gitea_project) | ||||
|         about_menu.addAction(gitea_action) | ||||
|  | ||||
|         # "Credits" option | ||||
|         credits_action = QtWidgets.QAction("Credits", self) | ||||
|         credits_action.triggered.connect(self._show_credits_popup) | ||||
|         about_menu.addAction(credits_action) | ||||
|  | ||||
|         # "Check for Updates" option | ||||
|         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 update_status_bar(self, data: str): | ||||
|         """ | ||||
|         Flattens multi-line data into a single line | ||||
|         (using ' | ' to replace newlines) | ||||
|         and shows it in the status bar. | ||||
|         """ | ||||
|         flattened = data.replace("\n", " | ") | ||||
|         self.statusBar().showMessage(flattened) | ||||
|  | ||||
|  | ||||
| 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__": | ||||
|     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 root context menu | ||||
|     graph_context_menu = graph.get_context_menu("graph") | ||||
|  | ||||
|     # Create a top-level "Add Nodes" folder in the context menu | ||||
|     add_nodes_menu = graph_context_menu.add_menu("Add Nodes") | ||||
|  | ||||
|     # Insert each category as a submenu under "Add Nodes" | ||||
|     for category, node_classes in custom_nodes_by_category.items(): | ||||
|         category_menu = add_nodes_menu.add_menu(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( | ||||
|                 node_name, | ||||
|                 make_node_command(graph, node_type) | ||||
|             ) | ||||
|  | ||||
|     # Provide a way to remove selected nodes from the root menu (not in Add Nodes) | ||||
|     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 a top-level "Workflows" menu for saving/loading/closing | ||||
|     workflow_menu = graph_context_menu.add_menu("Workflows") | ||||
|     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)) | ||||
|  | ||||
|     # ------------------------------------------------------------------ | ||||
|     # Custom stylesheet to control highlight color & overall QMenu look: | ||||
|     # ------------------------------------------------------------------ | ||||
|     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: rgb(60, 120, 180); /* set your highlight color here */ | ||||
|     } | ||||
|     QMenu::separator { | ||||
|         height: 1px; | ||||
|         background: rgba(255,255,255,20); | ||||
|         margin: 4px 8px; | ||||
|     } | ||||
|     """ | ||||
|     # Apply to each top-level menu. Submenus inherit from the parent menu. | ||||
|     graph_context_menu.qmenu.setStyleSheet(menu_stylesheet) | ||||
|     add_nodes_menu.qmenu.setStyleSheet(menu_stylesheet) | ||||
|     workflow_menu.qmenu.setStyleSheet(menu_stylesheet) | ||||
|     # ------------------------------------------------------------------ | ||||
|  | ||||
|     # 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_()) | ||||
|     main() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user