diff --git a/blocks.json b/blocks.json index 1cd235f..d2b3845 100644 --- a/blocks.json +++ b/blocks.json @@ -1,6 +1,6 @@ { "individual": [ - { "name": "Start Node", "type": "begin" }, + { "name": "New Event", "type": "begin" }, { "name": "Average", "type": "middle", @@ -53,6 +53,8 @@ { "name": "Joint 14", "type": "logic" }, { "name": "Joint 15", "type": "logic" }, { "name": "Joint 16", "type": "logic" }, - { "name": "Joint 17", "type": "logic" } + { "name": "Joint 17", "type": "logic" }, + { "name": "New Calculated Joint", "type": "begin" } + ] } \ No newline at end of file diff --git a/main.py b/main.py index 545542c..d1e923c 100644 --- a/main.py +++ b/main.py @@ -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, QHBoxLayout, QSplitter, QLabel, QPushButton, QComboBox, QInputDialog, QGraphicsRectItem, 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.QtMultimedia import QMediaPlayer, QAudioOutput from PySide6.QtMultimediaWidgets import QGraphicsVideoItem @@ -3162,10 +3162,17 @@ from PySide6.QtWidgets import QGraphicsPathItem, QGraphicsSimpleTextItem from PySide6.QtWidgets import QGraphicsItem, QGraphicsSimpleTextItem, QInputDialog +class BlockSignalProxy(QObject): + nameChanged = Signal(str, str) class PuzzleBlock(QGraphicsPathItem): + + def __init__(self, b_type, label, parent_item=None, fields=None): super().__init__(parent_item) + + self.signals = BlockSignalProxy() + self.b_type = b_type # "begin", "middle", "end" self.label_text = label self.width = 160 @@ -3322,8 +3329,7 @@ class PuzzleBlock(QGraphicsPathItem): if self.b_type == "begin": new_name, ok = QInputDialog.getText(None, "Rename Step", "Enter name:", text=self.label_text) if ok and new_name: - self.label_text = new_name - self.label_item.setText(new_name) + self.finish_rename(new_name) super().mouseDoubleClickEvent(event) @@ -3383,6 +3389,20 @@ class PuzzleBlock(QGraphicsPathItem): 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): def __init__(self, parent_tab, stringy): @@ -3447,8 +3467,39 @@ class BlockLibrary(QListWidget): 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 @@ -3456,12 +3507,40 @@ from PySide6.QtGui import QPen, QColor, QBrush class TopologyCanvas(QGraphicsView): + + beginBlockDropped = Signal(object) + def __init__(self): self.scene = QGraphicsScene() super().__init__(self.scene) self.setAcceptDrops(True) + self.block_library_ref = None 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): @@ -3546,6 +3625,9 @@ class TopologyCanvas(QGraphicsView): if not self.perform_snap(new_block, self.mapToScene(event.position().toPoint())): new_block.setPos(self.mapToScene(event.position().toPoint())) + if block_type == "begin": + self.beginBlockDropped.emit(new_block) + event.acceptProposedAction() @@ -3958,6 +4040,10 @@ class ModelParameterConfigurationTab(QWidget): sidebar_layout = QVBoxLayout(sidebar_container) 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 self.inspector_scroll = QScrollArea()