From d9623193d8047cb0a218a26085818bd9ab783bf1 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Fri, 14 Feb 2025 04:20:20 -0700 Subject: [PATCH] Added Functional Health Warning --- .../Transparent Nodes/QML}/blueprint_grid.qml | 0 .../Transparent Nodes/blueprint_grid.py | 0 .../Transparent Nodes/borealis_transparent.py | 63 ++++++- Modules/overlay_helpers.py | 80 -------- ...lyff_low_health_alert_node.cpython-312.pyc | Bin 1186 -> 9517 bytes Nodes/flyff_low_health_alert_node.py | 175 +++++++++++++++++- Project_Borealis.zip | Bin 72878 -> 0 bytes borealis.py | 2 +- 8 files changed, 223 insertions(+), 97 deletions(-) rename {QML => Experiments/Transparent Nodes/QML}/blueprint_grid.qml (100%) rename blueprint_grid.py => Experiments/Transparent Nodes/blueprint_grid.py (100%) rename borealis_transparent.py => Experiments/Transparent Nodes/borealis_transparent.py (61%) delete mode 100644 Modules/overlay_helpers.py delete mode 100644 Project_Borealis.zip diff --git a/QML/blueprint_grid.qml b/Experiments/Transparent Nodes/QML/blueprint_grid.qml similarity index 100% rename from QML/blueprint_grid.qml rename to Experiments/Transparent Nodes/QML/blueprint_grid.qml diff --git a/blueprint_grid.py b/Experiments/Transparent Nodes/blueprint_grid.py similarity index 100% rename from blueprint_grid.py rename to Experiments/Transparent Nodes/blueprint_grid.py diff --git a/borealis_transparent.py b/Experiments/Transparent Nodes/borealis_transparent.py similarity index 61% rename from borealis_transparent.py rename to Experiments/Transparent Nodes/borealis_transparent.py index 69ca9d1..0560967 100644 --- a/borealis_transparent.py +++ b/Experiments/Transparent Nodes/borealis_transparent.py @@ -2,10 +2,15 @@ import pkgutil import importlib import inspect -from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsItem +from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QGraphicsView, QGraphicsScene, QGraphicsItem, QMenu from PyQt5.QtCore import Qt, QTimer, QRectF, QPointF from PyQt5.QtGui import QColor, QPainter, QPen, QBrush, QGradient, QLinearGradient from PyQt5 import QtWidgets, QtCore, QtGui +from OdenGraphQt import NodeGraph, BaseNode + +# --- Fix Missing QUndoStack in QtGui --- +import OdenGraphQt.base.graph as base_graph +base_graph.QtGui.QUndoStack = QtWidgets.QUndoStack # Monkey-patch the missing QUndoStack # --- Custom Graph Scene --- class CustomGraphScene(QGraphicsScene): @@ -51,19 +56,55 @@ class CustomGraphScene(QGraphicsScene): painter.restore() +# --- Node Management --- +def import_nodes_from_folder(package_name): + imported_nodes = [] + package = importlib.import_module(package_name) + for loader, module_name, is_pkg in pkgutil.walk_packages(package.__path__, package.__name__ + "."): + module = importlib.import_module(module_name) + for name, obj in inspect.getmembers(module, inspect.isclass): + if issubclass(obj, BaseNode) and obj.__module__ == module.__name__: + imported_nodes.append(obj) + return imported_nodes + # --- Custom Graph View --- class CustomGraphView(QGraphicsView): """ - Custom view for the graph that applies full transparency. + Custom view for the graph that applies full transparency and handles right-click context menu. """ - def __init__(self, scene, parent=None): + def __init__(self, scene, graph, parent=None): super().__init__(scene, parent) + self.graph = graph # Reference to NodeGraph self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform) self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setStyleSheet("background: transparent; border: none;") self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True) + + # Enable context menu on right-click + self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.show_context_menu) + + def show_context_menu(self, position): + """ + Displays the node creation context menu with dynamically loaded nodes. + """ + menu = QMenu() + for node_class in self.graph.registered_nodes(): + node_name = getattr(node_class, "NODE_NAME", node_class.__name__) + menu.addAction(f"Create {node_name}", lambda nc=node_class: self.create_node(nc)) + menu.exec_(self.mapToGlobal(position)) + + def create_node(self, node_class): + """ + Creates a node instance of the given class in the NodeGraph. + """ + try: + node = self.graph.create_node(f"{node_class.__identifier__}.{node_class.__name__}") + print(f"Created node: {node_class.__name__}") + except Exception as e: + print(f"Error creating node: {e}") # --- Main Window --- class MainWindow(QMainWindow): @@ -87,9 +128,17 @@ class MainWindow(QMainWindow): layout.setContentsMargins(0, 0, 0, 0) self.setCentralWidget(central) + # Initialize NodeGraph + self.graph = NodeGraph() + + # Load custom nodes + custom_nodes = import_nodes_from_folder('Nodes') + for node_class in custom_nodes: + self.graph.register_node(node_class) + # Initialize Custom Graph Scene & View self.scene = CustomGraphScene() - self.view = CustomGraphView(self.scene) + self.view = CustomGraphView(self.scene, self.graph, self) layout.addWidget(self.view) # Global update timer @@ -98,8 +147,10 @@ class MainWindow(QMainWindow): self.timer.start(500) def global_update(self): - """Update all nodes periodically (to be implemented).""" - pass + """Update all nodes periodically.""" + for node in self.graph.all_nodes(): + if hasattr(node, "process_input"): + node.process_input() # --- Entry Point --- if __name__ == '__main__': diff --git a/Modules/overlay_helpers.py b/Modules/overlay_helpers.py deleted file mode 100644 index 4ce2665..0000000 --- a/Modules/overlay_helpers.py +++ /dev/null @@ -1,80 +0,0 @@ -import sys -from PyQt5.QtWidgets import QApplication, QWidget -from PyQt5.QtCore import Qt, QRect, QPoint -from PyQt5.QtGui import QPainter, QPen, QColor, QFont, QFontMetrics - -class OverlayCanvas(QWidget): - """ - UI overlay for drawing and interacting with on-screen elements. - """ - def __init__(self, parent=None): - super().__init__(parent) - - # **Full-screen overlay** - screen_geo = QApplication.primaryScreen().geometry() - self.setGeometry(screen_geo) # Set to full screen - - self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) - self.setAttribute(Qt.WA_TranslucentBackground, True) - self.setAttribute(Qt.WA_NoSystemBackground, True) - self.setAttribute(Qt.WA_OpaquePaintEvent, False) - self.setAttribute(Qt.WA_AlwaysStackOnTop, True) - - # **Helper Object: Low Health Alert** - self.helper_LowHealthAlert = QRect(250, 300, 900, 35) # Adjusted width and height - self.dragging = False - self.resizing = False - self.drag_offset = QPoint() - - def paintEvent(self, event): - """Draw the helper overlay objects.""" - painter = QPainter(self) - painter.setPen(Qt.NoPen) - painter.setBrush(QColor(255, 0, 0)) # Solid red rectangle - painter.drawRect(self.helper_LowHealthAlert) - - # Draw bold white text centered within the rectangle - font = QFont("Arial", 14, QFont.Bold) # Scaled text - painter.setFont(font) - painter.setPen(QColor(255, 255, 255)) - - text = "LOW HEALTH" - metrics = QFontMetrics(font) - text_width = metrics.horizontalAdvance(text) - text_height = metrics.height() - text_x = self.helper_LowHealthAlert.center().x() - text_width // 2 - text_y = self.helper_LowHealthAlert.center().y() + text_height // 4 - - painter.drawText(text_x, text_y, text) - - def mousePressEvent(self, event): - """Detect clicks for dragging and resizing the helper object.""" - if event.button() == Qt.LeftButton: - if self.helper_LowHealthAlert.contains(event.pos()): - if event.pos().x() > self.helper_LowHealthAlert.right() - 10 and event.pos().y() > self.helper_LowHealthAlert.bottom() - 10: - self.resizing = True - else: - self.dragging = True - self.drag_offset = event.pos() - self.helper_LowHealthAlert.topLeft() - - def mouseMoveEvent(self, event): - """Handle dragging and resizing movements.""" - if self.dragging: - self.helper_LowHealthAlert.moveTopLeft(event.pos() - self.drag_offset) - self.update() - elif self.resizing: - new_width = max(150, event.pos().x() - self.helper_LowHealthAlert.x()) - new_height = max(20, event.pos().y() - self.helper_LowHealthAlert.y()) - self.helper_LowHealthAlert.setSize(new_width, new_height) - self.update() - - def mouseReleaseEvent(self, event): - """End dragging or resizing event.""" - self.dragging = False - self.resizing = False - -if __name__ == '__main__': - app_gui = QApplication(sys.argv) - overlay_window = OverlayCanvas() - overlay_window.show() - sys.exit(app_gui.exec_()) diff --git a/Nodes/__pycache__/flyff_low_health_alert_node.cpython-312.pyc b/Nodes/__pycache__/flyff_low_health_alert_node.cpython-312.pyc index 35e9fd0033907470340f5032b3ab72b1a6af7270..cca471d7e0d9117ed8b6582f74b5e86d465fdabe 100644 GIT binary patch literal 9517 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 F{{cM;@D>07 delta 664 zcmYjO&ubGw82x5{WfQxlib*O}sXt*=a_T`uOQ|h2MN>sk*vq)?Ow+~9ZkX9%;w3rc z(1Qo<9u!Z-n@G?85uOCS4CXA}0#)qMnKc3D@V@tbk9pr?=3w)EarU$0lt7;DyMyL} z{(5%4zbv~qK!BhKTF664aV^qXx~Jz{kBpY-nFuu40AdV*m=t}~l?#Ap^(?pWQ&ATQ zd)4Q(kq|2R303d2r2RM~O`5L9otB)QQPaC&OxH=6KJugRBJtCN-QJshy! z<|KJh{(o<6YUoIb-;;1OfbX{RGJ3oA#lG^@zLxzsb))pnzH=xwa0^+pd@0+zu+Udt z!kH`yXcS@WB6dgd(=-=HcpRM5X&w6ID5 diff --git a/Nodes/flyff_low_health_alert_node.py b/Nodes/flyff_low_health_alert_node.py index 708c69e..2aa0b1d 100644 --- a/Nodes/flyff_low_health_alert_node.py +++ b/Nodes/flyff_low_health_alert_node.py @@ -1,22 +1,177 @@ +import time +import sys from OdenGraphQt import BaseNode -from OdenGraphQt.constants import NodePropWidgetEnum -from OdenGraphQt.widgets.node_widgets import NodeLineEditValidatorCheckBox +from Qt import QtWidgets, QtCore, QtGui -class CheckboxNode(BaseNode): +# Attempt to import winsound (Windows-only) +try: + import winsound + HAS_WINSOUND = True +except ImportError: + HAS_WINSOUND = False + + +class OverlayCanvas(QtWidgets.QWidget): + """ + UI overlay for displaying a red warning box, which can be repositioned by dragging. + """ + + def __init__(self, parent=None): + super().__init__(parent) + + # **Full-screen overlay** + screen_geo = QtWidgets.QApplication.primaryScreen().geometry() + self.setGeometry(screen_geo) # Set to full screen + + self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) + self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True) + self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True) + self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent, False) + self.setAttribute(QtCore.Qt.WA_AlwaysStackOnTop, True) + + # **Draggable Low Health Alert** + self.helper_LowHealthAlert = QtCore.QRect(250, 300, 900, 35) # Default Position + self.dragging = False + self.drag_offset = QtCore.QPoint() + + self.setVisible(False) # Initially hidden + + def paintEvent(self, event): + """Draw the helper overlay objects.""" + if not self.isVisible(): + return # Don't draw anything if invisible + + painter = QtGui.QPainter(self) + painter.setPen(QtCore.Qt.NoPen) + painter.setBrush(QtGui.QColor(255, 0, 0)) # Solid red rectangle + painter.drawRect(self.helper_LowHealthAlert) + + # Draw bold white text centered within the rectangle + font = QtGui.QFont("Arial", 14, QtGui.QFont.Bold) + painter.setFont(font) + painter.setPen(QtGui.QColor(255, 255, 255)) + + text = "LOW HEALTH" + metrics = QtGui.QFontMetrics(font) + text_width = metrics.horizontalAdvance(text) + text_height = metrics.height() + text_x = self.helper_LowHealthAlert.center().x() - text_width // 2 + text_y = self.helper_LowHealthAlert.center().y() + text_height // 4 + + painter.drawText(text_x, text_y, text) + + def toggle_alert(self, state): + """ + Show or hide the overlay based on the state (1 = show, 0 = hide). + """ + self.setVisible(state == 1) + self.update() + + def mousePressEvent(self, event): + """Detect clicks inside the red box and allow dragging.""" + if event.button() == QtCore.Qt.LeftButton and self.helper_LowHealthAlert.contains(event.pos()): + self.dragging = True + self.drag_offset = event.pos() - self.helper_LowHealthAlert.topLeft() + + def mouseMoveEvent(self, event): + """Handle dragging movement.""" + if self.dragging: + new_x = event.pos().x() - self.drag_offset.x() + new_y = event.pos().y() - self.drag_offset.y() + self.helper_LowHealthAlert.moveTopLeft(QtCore.QPoint(new_x, new_y)) + self.update() + + def mouseReleaseEvent(self, event): + """Stop dragging when the mouse button is released.""" + self.dragging = False + + +class FlyffLowHealthAlertNode(BaseNode): + """ + Custom OdenGraphQt node that toggles a visual alert overlay and plays a beep when health is low. + """ - # set a unique node identifier. __identifier__ = 'bunny-lab.io.flyff_low_health_alert_node' - - # set the initial default node name. NODE_NAME = 'Flyff - Low Health Alert' - def __init__(self): - super(CheckboxNode, self).__init__() + overlay_instance = None # Shared overlay instance + last_beep_time = 0 # Time tracking for beep interval + BEEP_INTERVAL_SECONDS = 2 # Beep every 2 seconds - # Create checkboxes to decide which kind of alert(s) to utilize. + def __init__(self): + super(FlyffLowHealthAlertNode, self).__init__() + + # Create checkboxes to decide which kind of alert(s) to utilize self.add_checkbox('cb_1', '', 'Sound Alert', True) self.add_checkbox('cb_2', '', 'Visual Alert', True) # Create Input Port self.add_input('Toggle (1 = On | 0 = Off)', color=(200, 100, 0)) - #self.add_output('out', color=(0, 100, 200)) \ No newline at end of file + + # Add text input widget to display received value + self.add_text_input('value', 'Current Value', text='0') + + # Ensure only one overlay instance exists + if not FlyffLowHealthAlertNode.overlay_instance: + FlyffLowHealthAlertNode.overlay_instance = OverlayCanvas() + FlyffLowHealthAlertNode.overlay_instance.show() + + def process_input(self): + """ + This function runs every 500ms (via the global update loop). + It updates the displayed value and toggles the alert if needed. + """ + input_port = self.input(0) + + # If there is a connected node, fetch its output value + if input_port.connected_ports(): + connected_node = input_port.connected_ports()[0].node() + if hasattr(connected_node, 'get_property'): + value = connected_node.get_property('value') + else: + value = "0" + else: + value = "0" # Default to zero if nothing is connected + + try: + input_value = int(value) # Ensure we interpret input as an integer (0 or 1) + except ValueError: + input_value = 0 # Default to off if the input is not valid + + # Update the value display box + self.set_property('value', str(input_value)) + + # Check if the "Visual Alert" checkbox is enabled + visual_alert_enabled = self.get_property('cb_2') + + # Ensure that if "Visual Alert" is unchecked, the overlay is always hidden + if not visual_alert_enabled: + FlyffLowHealthAlertNode.overlay_instance.toggle_alert(0) + else: + FlyffLowHealthAlertNode.overlay_instance.toggle_alert(input_value) + + # Check if "Sound Alert" is enabled and beep if necessary + self.handle_beep(input_value) + + def handle_beep(self, input_value): + """ + Plays a beep sound every 2 seconds when the value is `1` and "Sound Alert" is enabled. + """ + sound_alert_enabled = self.get_property('cb_1') + current_time = time.time() + + if input_value == 1 and sound_alert_enabled: + if (current_time - FlyffLowHealthAlertNode.last_beep_time) >= FlyffLowHealthAlertNode.BEEP_INTERVAL_SECONDS: + FlyffLowHealthAlertNode.last_beep_time = current_time + self.play_beep() + else: + FlyffLowHealthAlertNode.last_beep_time = 0 # Reset when health is safe + + def play_beep(self): + """ + Plays a beep using `winsound.Beep` (Windows) or prints a terminal bell (`\a`). + """ + if HAS_WINSOUND: + winsound.Beep(376, 100) # 376 Hz, 100ms duration + else: + print('\a', end='') # Terminal bell for non-Windows systems diff --git a/Project_Borealis.zip b/Project_Borealis.zip deleted file mode 100644 index 22db142cfcd1394f453c64f8adca51c49f842ae6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72878 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