custom joint support

This commit is contained in:
2026-05-04 16:43:32 -07:00
parent 67243649ef
commit 17789a7615
2 changed files with 93 additions and 5 deletions
+4 -2
View File
@@ -1,6 +1,6 @@
{ {
"individual": [ "individual": [
{ "name": "Start Node", "type": "begin" }, { "name": "New Event", "type": "begin" },
{ {
"name": "Average", "name": "Average",
"type": "middle", "type": "middle",
@@ -53,6 +53,8 @@
{ "name": "Joint 14", "type": "logic" }, { "name": "Joint 14", "type": "logic" },
{ "name": "Joint 15", "type": "logic" }, { "name": "Joint 15", "type": "logic" },
{ "name": "Joint 16", "type": "logic" }, { "name": "Joint 16", "type": "logic" },
{ "name": "Joint 17", "type": "logic" } { "name": "Joint 17", "type": "logic" },
{ "name": "New Calculated Joint", "type": "begin" }
] ]
} }
+89 -3
View File
@@ -37,7 +37,7 @@ import PySide6
from PySide6.QtWidgets import (QApplication, QDoubleSpinBox, QFormLayout, QGraphicsItem, QGraphicsProxyWidget, QGraphicsTextItem, QLineEdit, QListWidget, QListWidgetItem, QMainWindow, QProgressDialog, QSizePolicy, QStyleOptionGraphicsItem, QTabBar, QWidget, QVBoxLayout, QGraphicsView, QGraphicsScene, from PySide6.QtWidgets import (QApplication, QDoubleSpinBox, QFormLayout, QGraphicsItem, QGraphicsProxyWidget, QGraphicsTextItem, QLineEdit, QListWidget, QListWidgetItem, QMainWindow, QProgressDialog, QSizePolicy, QStyleOptionGraphicsItem, QTabBar, QWidget, QVBoxLayout, QGraphicsView, QGraphicsScene,
QHBoxLayout, QSplitter, QLabel, QPushButton, QComboBox, QInputDialog, QGraphicsRectItem, QHBoxLayout, QSplitter, QLabel, QPushButton, QComboBox, QInputDialog, QGraphicsRectItem,
QFileDialog, QScrollArea, QMessageBox, QSlider, QTextEdit, QGroupBox, QGridLayout, QCheckBox, QTabWidget, QProgressBar) QFileDialog, QScrollArea, QMessageBox, QSlider, QTextEdit, QGroupBox, QGridLayout, QCheckBox, QTabWidget, QProgressBar)
from PySide6.QtCore import QEvent, Qt, QThread, Signal, QUrl, QRectF, QPointF, QRect, QSizeF, QTimer from PySide6.QtCore import QEvent, QObject, Qt, QThread, Signal, QUrl, QRectF, QPointF, QRect, QSizeF, QTimer
from PySide6.QtGui import QCursor, QDoubleValidator, QGuiApplication, QPainter, QColor, QFont, QPen, QBrush, QAction, QKeySequence, QIcon, QTextOption, QImage, QPixmap, QTransform from PySide6.QtGui import QCursor, QDoubleValidator, QGuiApplication, QPainter, QColor, QFont, QPen, QBrush, QAction, QKeySequence, QIcon, QTextOption, QImage, QPixmap, QTransform
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
from PySide6.QtMultimediaWidgets import QGraphicsVideoItem from PySide6.QtMultimediaWidgets import QGraphicsVideoItem
@@ -3162,10 +3162,17 @@ from PySide6.QtWidgets import QGraphicsPathItem, QGraphicsSimpleTextItem
from PySide6.QtWidgets import QGraphicsItem, QGraphicsSimpleTextItem, QInputDialog from PySide6.QtWidgets import QGraphicsItem, QGraphicsSimpleTextItem, QInputDialog
class BlockSignalProxy(QObject):
nameChanged = Signal(str, str)
class PuzzleBlock(QGraphicsPathItem): class PuzzleBlock(QGraphicsPathItem):
def __init__(self, b_type, label, parent_item=None, fields=None): def __init__(self, b_type, label, parent_item=None, fields=None):
super().__init__(parent_item) super().__init__(parent_item)
self.signals = BlockSignalProxy()
self.b_type = b_type # "begin", "middle", "end" self.b_type = b_type # "begin", "middle", "end"
self.label_text = label self.label_text = label
self.width = 160 self.width = 160
@@ -3322,8 +3329,7 @@ class PuzzleBlock(QGraphicsPathItem):
if self.b_type == "begin": if self.b_type == "begin":
new_name, ok = QInputDialog.getText(None, "Rename Step", "Enter name:", text=self.label_text) new_name, ok = QInputDialog.getText(None, "Rename Step", "Enter name:", text=self.label_text)
if ok and new_name: if ok and new_name:
self.label_text = new_name self.finish_rename(new_name)
self.label_item.setText(new_name)
super().mouseDoubleClickEvent(event) super().mouseDoubleClickEvent(event)
@@ -3383,6 +3389,20 @@ class PuzzleBlock(QGraphicsPathItem):
return super().itemChange(change, value) return super().itemChange(change, value)
def finish_rename(self, new_text):
old_text = self.label_text
print(f"[DEBUG - Block] Renaming '{old_text}' to '{new_text}'")
self.label_text = new_text
if hasattr(self, 'label_item'):
self.label_item.setText(new_text)
self.update()
if self.b_type == "begin":
print(f"[DEBUG - Block] Emitting nameChanged signal...")
self.signals.nameChanged.emit(old_text, new_text)
class BlockLibrary(QListWidget): class BlockLibrary(QListWidget):
def __init__(self, parent_tab, stringy): def __init__(self, parent_tab, stringy):
@@ -3447,8 +3467,39 @@ class BlockLibrary(QListWidget):
print(f"DEBUG: Drag finished with result: {result}") print(f"DEBUG: Drag finished with result: {result}")
def add_dynamic_logic_item(self, block_item):
"""
Triggered when a 'begin' block is dropped on the canvas.
Adds a corresponding 'logic' block to this library.
"""
# 1. Check for duplicates
# We don't want to add "Joint 1" five times if they drag five 'begin' blocks
label = block_item.label_text
items = self.findItems(label, Qt.MatchExactly)
for item in items:
if item.data(Qt.UserRole) == "logic":
print(f"DEBUG: {label} (logic) already exists in library. Skipping.")
return
# 2. Add the item as a 'logic' type
# In your system, logic blocks have no fields (they are just variables)
print(f"DEBUG: Dynamically adding '{label}' as a logic block.")
self.add_item(label, "logic", fields=[])
def update_item_name(self, old_name, new_name):
print(f"[DEBUG - Library] Searching for items matching: '{old_name}'")
items = self.findItems(old_name, Qt.MatchExactly)
if not items:
print(f"[DEBUG - Library] No items found matching '{old_name}'")
for item in items:
# Ensure we only rename the logic-type items
if item.data(Qt.UserRole) == "logic":
item.setText(new_name)
print(f"[DEBUG - Library] Successfully updated sidebar item to '{new_name}'")
from PySide6.QtWidgets import QGraphicsLineItem, QGraphicsRectItem, QGraphicsSimpleTextItem from PySide6.QtWidgets import QGraphicsLineItem, QGraphicsRectItem, QGraphicsSimpleTextItem
@@ -3456,12 +3507,40 @@ from PySide6.QtGui import QPen, QColor, QBrush
class TopologyCanvas(QGraphicsView): class TopologyCanvas(QGraphicsView):
beginBlockDropped = Signal(object)
def __init__(self): def __init__(self):
self.scene = QGraphicsScene() self.scene = QGraphicsScene()
super().__init__(self.scene) super().__init__(self.scene)
self.setAcceptDrops(True) self.setAcceptDrops(True)
self.block_library_ref = None
self.scene.setSceneRect(0, 0, 2000, 2000) self.scene.setSceneRect(0, 0, 2000, 2000)
self.beginBlockDropped.connect(self.on_new_definition_created)
def on_new_definition_created(self, block_item):
# Connect the block's rename signal to our canvas sync method
print(f"[DEBUG - Canvas] Connecting signal for new block: {block_item.label_text}")
block_item.signals.nameChanged.connect(self.sync_logic_blocks)
def sync_logic_blocks(self, old_name, new_name):
print(f"[DEBUG - Canvas] Syncing. Old: {old_name}, New: {new_name}")
# 1. Update existing blocks on canvas (This is already working!)
for item in self.scene.items():
if isinstance(item, PuzzleBlock) and item.b_type == "logic":
if item.label_text == old_name:
item.label_text = new_name
if hasattr(item, 'label_item'):
item.label_item.setText(new_name)
item.update()
# 2. Update Sidebar using the direct reference
if self.block_library_ref:
print("[DEBUG - Canvas] Found block_library_ref. Calling update...")
self.block_library_ref.update_item_name(old_name, new_name)
else:
print("[DEBUG - Canvas] ERROR: block_library_ref is None. Ensure it is linked in main.py.")
def mousePressEvent(self, event): def mousePressEvent(self, event):
@@ -3546,6 +3625,9 @@ class TopologyCanvas(QGraphicsView):
if not self.perform_snap(new_block, self.mapToScene(event.position().toPoint())): if not self.perform_snap(new_block, self.mapToScene(event.position().toPoint())):
new_block.setPos(self.mapToScene(event.position().toPoint())) new_block.setPos(self.mapToScene(event.position().toPoint()))
if block_type == "begin":
self.beginBlockDropped.emit(new_block)
event.acceptProposedAction() event.acceptProposedAction()
@@ -3958,6 +4040,10 @@ class ModelParameterConfigurationTab(QWidget):
sidebar_layout = QVBoxLayout(sidebar_container) sidebar_layout = QVBoxLayout(sidebar_container)
self.block_library = BlockLibrary(parent_tab=self, stringy=self.mode) self.block_library = BlockLibrary(parent_tab=self, stringy=self.mode)
self.canvas.block_library_ref = self.block_library
self.canvas.beginBlockDropped.connect(self.block_library.add_dynamic_logic_item)
# Reuse your existing Inspector logic # Reuse your existing Inspector logic
self.inspector_scroll = QScrollArea() self.inspector_scroll = QScrollArea()