161 lines
6.3 KiB
Python
161 lines
6.3 KiB
Python
import sys
|
|
import pkgutil
|
|
import importlib
|
|
import inspect
|
|
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):
|
|
"""
|
|
Custom scene that draws a blueprint-style transparent grid with gradient shading.
|
|
"""
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setBackgroundBrush(QtCore.Qt.transparent)
|
|
self.grid_color = QtGui.QColor(100, 160, 160, 160) # Blueprint grid color (10% more transparent)
|
|
self.grid_size = 115
|
|
|
|
def drawBackground(self, painter, rect):
|
|
"""
|
|
Custom draw function to render a blueprint-style grid with gradient shading.
|
|
"""
|
|
painter.save()
|
|
painter.setRenderHint(QPainter.Antialiasing, False)
|
|
painter.setBrush(QtCore.Qt.NoBrush) # No background fill
|
|
pen = QPen(self.grid_color, 0.5)
|
|
|
|
left = int(rect.left()) - (int(rect.left()) % self.grid_size)
|
|
top = int(rect.top()) - (int(rect.top()) % self.grid_size)
|
|
|
|
# Draw vertical lines
|
|
lines = []
|
|
for x in range(left, int(rect.right()), self.grid_size):
|
|
lines.append(QtCore.QLineF(x, rect.top(), x, rect.bottom()))
|
|
|
|
# Draw horizontal lines
|
|
for y in range(top, int(rect.bottom()), self.grid_size):
|
|
lines.append(QtCore.QLineF(rect.left(), y, rect.right(), y))
|
|
|
|
painter.setPen(pen)
|
|
painter.drawLines(lines)
|
|
|
|
# Draw gradient shading (top and bottom)
|
|
gradient = QLinearGradient(QPointF(rect.left(), rect.top()), QPointF(rect.left(), rect.bottom()))
|
|
gradient.setColorAt(0.0, QColor(0, 40, 100, 220)) # Darker blue at the top
|
|
gradient.setColorAt(0.5, QColor(0, 0, 0, 0)) # Transparent in the middle
|
|
gradient.setColorAt(1.0, QColor(0, 40, 100, 220)) # Darker blue at the bottom
|
|
painter.fillRect(rect, QBrush(gradient))
|
|
|
|
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 and handles right-click context menu.
|
|
"""
|
|
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):
|
|
"""A frameless, transparent overlay with a custom graph."""
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
# Full-screen overlay
|
|
app = QApplication.instance()
|
|
screen_geo = app.primaryScreen().geometry()
|
|
self.setGeometry(screen_geo)
|
|
|
|
# Frameless, top-most, fully transparent
|
|
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
|
self.setAttribute(Qt.WA_TranslucentBackground, True)
|
|
|
|
# Transparent central widget
|
|
central = QWidget(self)
|
|
central.setAttribute(Qt.WA_TranslucentBackground, True)
|
|
layout = QVBoxLayout(central)
|
|
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.graph, self)
|
|
layout.addWidget(self.view)
|
|
|
|
# Global update timer
|
|
self.timer = QTimer(self)
|
|
self.timer.timeout.connect(self.global_update)
|
|
self.timer.start(500)
|
|
|
|
def global_update(self):
|
|
"""Update all nodes periodically."""
|
|
for node in self.graph.all_nodes():
|
|
if hasattr(node, "process_input"):
|
|
node.process_input()
|
|
|
|
# --- Entry Point ---
|
|
if __name__ == '__main__':
|
|
app = QApplication(sys.argv)
|
|
window = MainWindow()
|
|
window.show()
|
|
sys.exit(app.exec_())
|