Files
2026-02-08 04:38:10 +03:30

165 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import sys
from PySide6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QTextEdit,
QPushButton, QLabel, QSplitter
)
from PySide6.QtCore import Qt
from PySide6 import QtGui
from NodeGraphQt import NodeGraph, BaseNode
from core.workflow import Graph
from nodes.image_reader import ImageReader
from nodes.feature_extractor import FeatureExtractor
from nodes.feature_writer import FeatureWriter
# GUI node wrappers around core nodes
class ImageReaderNode(BaseNode):
__identifier__ = "radiuma.gui"
NODE_NAME = "ImageReader"
def __init__(self):
super().__init__()
self.add_output("image")
self.core_node = ImageReader()
class FeatureExtractorNode(BaseNode):
__identifier__ = "radiuma.gui"
NODE_NAME = "FeatureExtractor"
def __init__(self):
super().__init__()
self.add_input("image")
self.add_output("features")
self.core_node = FeatureExtractor()
class FeatureWriterNode(BaseNode):
__identifier__ = "radiuma.gui"
NODE_NAME = "FeatureWriter"
def __init__(self):
super().__init__()
self.add_input("features")
self.core_node = FeatureWriter()
class VisualEditor(QMainWindow):
# Visual workflow editor with dynamic semantic validation
def __init__(self):
super().__init__()
self.setWindowTitle("Radiuma R&D Semantic Workflow Editor")
self.resize(1200, 800)
self.graph_engine = Graph()
self.graph = NodeGraph()
self.viewer = self.graph.widget
self.graph.register_node(ImageReaderNode)
self.graph.register_node(FeatureExtractorNode)
self.graph.register_node(FeatureWriterNode)
self.log_panel = QTextEdit()
self.log_panel.setReadOnly(True)
btn_reader = QPushButton("Add ImageReader")
btn_extractor = QPushButton("Add FeatureExtractor")
btn_writer = QPushButton("Add FeatureWriter")
btn_reader.clicked.connect(
lambda: self.graph.create_node("radiuma.gui.ImageReaderNode")
)
btn_extractor.clicked.connect(
lambda: self.graph.create_node("radiuma.gui.FeatureExtractorNode")
)
btn_writer.clicked.connect(
lambda: self.graph.create_node("radiuma.gui.FeatureWriterNode")
)
left_panel = QWidget()
left_layout = QVBoxLayout(left_panel)
left_layout.addWidget(QLabel("Nodes"))
left_layout.addWidget(btn_reader)
left_layout.addWidget(btn_extractor)
left_layout.addWidget(btn_writer)
left_layout.addStretch()
top_splitter = QSplitter(Qt.Horizontal)
top_splitter.addWidget(left_panel)
top_splitter.addWidget(self.viewer)
main_splitter = QSplitter(Qt.Vertical)
main_splitter.addWidget(top_splitter)
main_splitter.addWidget(self.log_panel)
main_splitter.setSizes([600, 200])
self.setCentralWidget(main_splitter)
self.graph.port_connected.connect(self._on_port_connected)
self.graph.port_disconnected.connect(self._on_port_disconnected)
def log(self, msg, level="INFO"):
self.log_panel.append(f"[{level}] {msg}")
def _on_port_connected(self, port_a, port_b):
node_a = port_a.node()
node_b = port_b.node()
core_a = getattr(node_a, "core_node", None)
core_b = getattr(node_b, "core_node", None)
if not core_a or not core_b:
self._color_connection(port_a, port_b, (200, 200, 200))
self.log("Missing core node mapping", "ERROR")
return
# Normalize out → in
if port_a.type_() == "out":
out_node, out_port = core_a, port_a.name()
in_node, in_port = core_b, port_b.name()
gui_out, gui_in = port_a, port_b
else:
out_node, out_port = core_b, port_b.name()
in_node, in_port = core_a, port_a.name()
gui_out, gui_in = port_b, port_a
ok, msg = self.graph_engine.can_connect(
out_node, out_port, in_node, in_port
)
if ok:
self._color_connection(gui_out, gui_in, (46, 204, 113))
self.log(msg, "INFO")
else:
self._color_connection(gui_out, gui_in, (231, 76, 60))
self.log(msg, "ERROR")
def _on_port_disconnected(self, port_a, port_b):
self._color_connection(port_a, port_b, (180, 180, 180))
self.log("Disconnected ports", "INFO")
def _color_connection(self, port_a, port_b, color):
# Patch connection paint dynamically
scene = self.graph._viewer.scene()
for item in scene.items():
if hasattr(item, "port1") and hasattr(item, "port2"):
if {item.port1, item.port2} == {port_a, port_b}:
setattr(item, "_custom_color", color)
if not hasattr(item, "_patched"):
original_paint = item.paint
def patched_paint(painter, option, widget=None,
_orig=original_paint, _item=item):
pen = painter.pen()
pen.setColor(QtGui.QColor(*_item._custom_color))
painter.setPen(pen)
_orig(painter, option, widget)
item.paint = patched_paint
item._patched = True
item.update()
return