From cf145d6ed290ac33b05243d9172bf38449b4889a Mon Sep 17 00:00:00 2001 From: rob Date: Fri, 9 Jan 2026 10:52:57 -0400 Subject: [PATCH] Convert from PyQt6 to PySide6 for ecosystem unification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace PyQt6 imports with PySide6 across all GUI modules - Update pyqtSignal to Signal throughout codebase - Change dependency from PyQt6>=6.4.0 to PySide6>=6.4.0 - Unifies Qt library with development-hub and ramble (all LGPL) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pyproject.toml | 8 +++--- src/artifact_editor/dialogs.py | 6 ++-- src/artifact_editor/gui.py | 40 +++++++++++++------------- src/artifact_editor/svg_interactive.py | 30 +++++++++---------- src/artifact_editor/svg_scene.py | 14 ++++----- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eb36ddc..b42042d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ "PyYAML>=6.0", "cmdforge @ git+https://gitea.brrd.tech/rob/CmdForge.git", # GUI is required for the editor - include by default - "PyQt6>=6.4.0", + "PySide6>=6.4.0", "QScintilla>=2.14.0", ] @@ -39,7 +39,7 @@ tui = [ "urwid>=2.1.0", ] gui = [ - "PyQt6>=6.4.0", + "PySide6>=6.4.0", "QScintilla>=2.14.0", ] mermaid = [ @@ -50,7 +50,7 @@ openscad = [ ] all = [ "urwid>=2.1.0", - "PyQt6>=6.4.0", + "PySide6>=6.4.0", "QScintilla>=2.14.0", "solidpython2>=2.0.0", ] @@ -58,7 +58,7 @@ dev = [ "pytest>=7.0", "pytest-cov>=4.0", "urwid>=2.1.0", - "PyQt6>=6.4.0", + "PySide6>=6.4.0", "QScintilla>=2.14.0", ] diff --git a/src/artifact_editor/dialogs.py b/src/artifact_editor/dialogs.py index 64c8031..c674e5a 100644 --- a/src/artifact_editor/dialogs.py +++ b/src/artifact_editor/dialogs.py @@ -3,8 +3,8 @@ from typing import Optional, List, Dict, Any from dataclasses import dataclass, field -from PyQt6.QtCore import Qt -from PyQt6.QtWidgets import ( +from PySide6.QtCore import Qt +from PySide6.QtWidgets import ( QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QGridLayout, QLineEdit, QComboBox, QListWidget, QPushButton, QLabel, QDialogButtonBox, QGroupBox, QTextEdit, @@ -1966,7 +1966,7 @@ class ColorButton(QPushButton): self.setText("") def _pick_color(self): - from PyQt6.QtGui import QColor + from PySide6.QtGui import QColor initial = QColor(self._color) if self._color != 'transparent' else QColor("#ffffff") color = QColorDialog.getColor(initial, self, "Select Color") if color.isValid(): diff --git a/src/artifact_editor/gui.py b/src/artifact_editor/gui.py index fda6981..1a986a2 100644 --- a/src/artifact_editor/gui.py +++ b/src/artifact_editor/gui.py @@ -15,12 +15,12 @@ from enum import Enum, auto from pathlib import Path from typing import Optional -from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QThread, QRectF, QPointF, QSizeF -from PyQt6.QtGui import ( +from PySide6.QtCore import Qt, QTimer, Signal, QThread, QRectF, QPointF, QSizeF +from PySide6.QtGui import ( QFont, QColor, QAction, QKeySequence, QSyntaxHighlighter, QTextCharFormat, QPainter, QPen, QBrush, QTextCursor ) -from PyQt6.QtWidgets import ( +from PySide6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QLineEdit, QPushButton, QLabel, QFileDialog, QMessageBox, QStatusBar, QToolBar, QComboBox, @@ -28,7 +28,7 @@ from PyQt6.QtWidgets import ( QGraphicsTextItem, QGraphicsLineItem, QGraphicsItem, QListWidget, QListWidgetItem, QGroupBox, QSpinBox, QCheckBox, QDialog ) -from PyQt6.QtSvgWidgets import QSvgWidget +from PySide6.QtSvgWidgets import QSvgWidget from .renderers import get_renderer, list_renderers from .renderers.code import get_languages, get_themes, is_available as code_renderer_available @@ -150,7 +150,7 @@ class PlantUMLHighlighter(QSyntaxHighlighter): class RenderThread(QThread): """Background thread for rendering artifacts.""" - finished = pyqtSignal(bool, str) # success, message/path + finished = Signal(bool, str) # success, message/path def __init__(self, renderer, source: str, output_path: Path, camera_angles: tuple[int, int, int] | None = None, @@ -186,8 +186,8 @@ class RenderThread(QThread): class DictateThread(QThread): """Background thread for voice dictation.""" - finished = pyqtSignal(str) # transcribed text - tick = pyqtSignal(int) # seconds remaining + finished = Signal(str) # transcribed text + tick = Signal(int) # seconds remaining def __init__(self, duration: int = 10): super().__init__() @@ -235,8 +235,8 @@ class DictateThread(QThread): class AIThread(QThread): """Background thread for AI generation with validation and retry.""" - finished = pyqtSignal(bool, str) # success, result/error - status_update = pyqtSignal(str) # status message for UI + finished = Signal(bool, str) # success, result/error + status_update = Signal(str) # status message for UI MAX_RETRIES = 2 @@ -452,11 +452,11 @@ class CodeEditor(QPlainTextEdit): class DiagramCanvas(QGraphicsView): """Interactive canvas for diagram editing using QGraphicsScene.""" - shape_changed = pyqtSignal() # Emitted when shapes are modified - rotation_changed = pyqtSignal(int, int) # Emitted for 3D rotation (delta_x, delta_y) - zoom_changed = pyqtSignal(int) # Emitted for zoom change (delta) - element_clicked = pyqtSignal(str) # Emitted when an element is clicked (element name) - view_transform_changed = pyqtSignal() # Emitted when view transform changes (zoom) + shape_changed = Signal() # Emitted when shapes are modified + rotation_changed = Signal(int, int) # Emitted for 3D rotation (delta_x, delta_y) + zoom_changed = Signal(int) # Emitted for zoom change (delta) + element_clicked = Signal(str) # Emitted when an element is clicked (element name) + view_transform_changed = Signal() # Emitted when view transform changes (zoom) def __init__(self, parent=None): super().__init__(parent) @@ -501,7 +501,7 @@ class DiagramCanvas(QGraphicsView): self._svg_path = None try: - from PyQt6.QtGui import QPixmap, QImage + from PySide6.QtGui import QPixmap, QImage path = Path(image_path) @@ -511,7 +511,7 @@ class DiagramCanvas(QGraphicsView): self._parse_svg_elements(image_path) # Load SVG using QSvgRenderer - from PyQt6.QtSvg import QSvgRenderer + from PySide6.QtSvg import QSvgRenderer renderer = QSvgRenderer(image_path) if renderer.isValid(): @@ -728,8 +728,8 @@ class DiagramCanvas(QGraphicsView): class AIPanel(QWidget): """Bottom panel for AI interaction and dictation.""" - ai_submitted = pyqtSignal(str) # AI instruction submitted - dictate_requested = pyqtSignal() + ai_submitted = Signal(str) # AI instruction submitted + dictate_requested = Signal() def __init__(self, parent=None): super().__init__(parent) @@ -1149,7 +1149,7 @@ class ArtifactEditorWindow(QMainWindow): rotation_layout = QHBoxLayout(self.rotation_panel) rotation_layout.setContentsMargins(4, 4, 4, 4) - from PyQt6.QtWidgets import QSlider + from PySide6.QtWidgets import QSlider self.rot_x_slider = QSlider(Qt.Orientation.Horizontal) self.rot_x_slider.setRange(0, 360) @@ -2632,7 +2632,7 @@ class ArtifactEditorWindow(QMainWindow): def _on_svg_create_group(self, element): """Create a new group containing the SVG element.""" - from PyQt6.QtWidgets import QInputDialog + from PySide6.QtWidgets import QInputDialog group_id, ok = QInputDialog.getText( self, diff --git a/src/artifact_editor/svg_interactive.py b/src/artifact_editor/svg_interactive.py index a95c122..1e95cbc 100644 --- a/src/artifact_editor/svg_interactive.py +++ b/src/artifact_editor/svg_interactive.py @@ -8,9 +8,9 @@ from dataclasses import dataclass from enum import Enum from typing import Optional, List -from PyQt6.QtCore import Qt, QRectF, QPointF, pyqtSignal, QSizeF, QObject -from PyQt6.QtGui import QPen, QBrush, QColor, QCursor -from PyQt6.QtWidgets import ( +from PySide6.QtCore import Qt, QRectF, QPointF, Signal, QSizeF, QObject +from PySide6.QtGui import QPen, QBrush, QColor, QCursor +from PySide6.QtWidgets import ( QGraphicsScene, QGraphicsView, QGraphicsRectItem, QGraphicsItem, QMenu, QGraphicsSceneMouseEvent ) @@ -111,19 +111,19 @@ class SVGSelectionManager(QObject): """ # Signals - element_selected = pyqtSignal(object) # SVGElement or None - element_moved = pyqtSignal(object, float, float) # element, new_x, new_y - element_resized = pyqtSignal(object, float, float, float, float) # element, x, y, w, h + element_selected = Signal(object) # SVGElement or None + element_moved = Signal(object, float, float) # element, new_x, new_y + element_resized = Signal(object, float, float, float, float) # element, x, y, w, h # Context menu signals - bring_to_front = pyqtSignal(object) - send_to_back = pyqtSignal(object) - bring_forward = pyqtSignal(object) - send_backward = pyqtSignal(object) - add_to_group = pyqtSignal(object, str) - remove_from_group = pyqtSignal(object) - create_group = pyqtSignal(object) - delete_element = pyqtSignal(object) + bring_to_front = Signal(object) + send_to_back = Signal(object) + bring_forward = Signal(object) + send_backward = Signal(object) + add_to_group = Signal(object, str) + remove_from_group = Signal(object) + create_group = Signal(object) + delete_element = Signal(object) def __init__(self, scene: QGraphicsScene, view: QGraphicsView): super().__init__() @@ -327,7 +327,7 @@ class SVGSelectionManager(QObject): def eventFilter(self, obj, event): """Handle mouse events on the viewport.""" - from PyQt6.QtCore import QEvent + from PySide6.QtCore import QEvent if obj != self.view.viewport(): return False diff --git a/src/artifact_editor/svg_scene.py b/src/artifact_editor/svg_scene.py index 536cbdc..6b6ed92 100644 --- a/src/artifact_editor/svg_scene.py +++ b/src/artifact_editor/svg_scene.py @@ -8,10 +8,10 @@ matching the actual SVG rendering. """ from typing import Optional, List, Dict -from PyQt6.QtCore import Qt, QPointF, QRectF, QByteArray, pyqtSignal, QObject -from PyQt6.QtGui import QPainter, QPen, QBrush, QColor, QFont, QPolygonF, QFontMetricsF -from PyQt6.QtSvg import QSvgRenderer -from PyQt6.QtWidgets import ( +from PySide6.QtCore import Qt, QPointF, QRectF, QByteArray, Signal, QObject +from PySide6.QtGui import QPainter, QPen, QBrush, QColor, QFont, QPolygonF, QFontMetricsF +from PySide6.QtSvg import QSvgRenderer +from PySide6.QtWidgets import ( QGraphicsItem, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QGraphicsSceneMouseEvent, QStyleOptionGraphicsItem, QWidget @@ -595,9 +595,9 @@ class SVGSceneManager(QObject): scene_changed: Emitted when content changes (need to regenerate SVG) """ - element_selected = pyqtSignal(object) - element_moved = pyqtSignal(object, float, float) - scene_changed = pyqtSignal() + element_selected = Signal(object) + element_moved = Signal(object, float, float) + scene_changed = Signal() def __init__(self, scene: QGraphicsScene, view: QGraphicsView): super().__init__()