Convert from PyQt6 to PySide6 for ecosystem unification

- 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 <noreply@anthropic.com>
This commit is contained in:
rob 2026-01-09 10:52:57 -04:00
parent 3fb46c2111
commit cf145d6ed2
5 changed files with 49 additions and 49 deletions

View File

@ -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",
]

View File

@ -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():

View File

@ -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,

View File

@ -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

View File

@ -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__()