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", "PyYAML>=6.0",
"cmdforge @ git+https://gitea.brrd.tech/rob/CmdForge.git", "cmdforge @ git+https://gitea.brrd.tech/rob/CmdForge.git",
# GUI is required for the editor - include by default # GUI is required for the editor - include by default
"PyQt6>=6.4.0", "PySide6>=6.4.0",
"QScintilla>=2.14.0", "QScintilla>=2.14.0",
] ]
@ -39,7 +39,7 @@ tui = [
"urwid>=2.1.0", "urwid>=2.1.0",
] ]
gui = [ gui = [
"PyQt6>=6.4.0", "PySide6>=6.4.0",
"QScintilla>=2.14.0", "QScintilla>=2.14.0",
] ]
mermaid = [ mermaid = [
@ -50,7 +50,7 @@ openscad = [
] ]
all = [ all = [
"urwid>=2.1.0", "urwid>=2.1.0",
"PyQt6>=6.4.0", "PySide6>=6.4.0",
"QScintilla>=2.14.0", "QScintilla>=2.14.0",
"solidpython2>=2.0.0", "solidpython2>=2.0.0",
] ]
@ -58,7 +58,7 @@ dev = [
"pytest>=7.0", "pytest>=7.0",
"pytest-cov>=4.0", "pytest-cov>=4.0",
"urwid>=2.1.0", "urwid>=2.1.0",
"PyQt6>=6.4.0", "PySide6>=6.4.0",
"QScintilla>=2.14.0", "QScintilla>=2.14.0",
] ]

View File

@ -3,8 +3,8 @@
from typing import Optional, List, Dict, Any from typing import Optional, List, Dict, Any
from dataclasses import dataclass, field from dataclasses import dataclass, field
from PyQt6.QtCore import Qt from PySide6.QtCore import Qt
from PyQt6.QtWidgets import ( from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QGridLayout, QDialog, QVBoxLayout, QHBoxLayout, QFormLayout, QGridLayout,
QLineEdit, QComboBox, QListWidget, QPushButton, QLineEdit, QComboBox, QListWidget, QPushButton,
QLabel, QDialogButtonBox, QGroupBox, QTextEdit, QLabel, QDialogButtonBox, QGroupBox, QTextEdit,
@ -1966,7 +1966,7 @@ class ColorButton(QPushButton):
self.setText("") self.setText("")
def _pick_color(self): 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") initial = QColor(self._color) if self._color != 'transparent' else QColor("#ffffff")
color = QColorDialog.getColor(initial, self, "Select Color") color = QColorDialog.getColor(initial, self, "Select Color")
if color.isValid(): if color.isValid():

View File

@ -15,12 +15,12 @@ from enum import Enum, auto
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QThread, QRectF, QPointF, QSizeF from PySide6.QtCore import Qt, QTimer, Signal, QThread, QRectF, QPointF, QSizeF
from PyQt6.QtGui import ( from PySide6.QtGui import (
QFont, QColor, QAction, QKeySequence, QSyntaxHighlighter, QFont, QColor, QAction, QKeySequence, QSyntaxHighlighter,
QTextCharFormat, QPainter, QPen, QBrush, QTextCursor QTextCharFormat, QPainter, QPen, QBrush, QTextCursor
) )
from PyQt6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QSplitter, QPlainTextEdit, QLineEdit, QPushButton, QLabel, QSplitter, QPlainTextEdit, QLineEdit, QPushButton, QLabel,
QFileDialog, QMessageBox, QStatusBar, QToolBar, QComboBox, QFileDialog, QMessageBox, QStatusBar, QToolBar, QComboBox,
@ -28,7 +28,7 @@ from PyQt6.QtWidgets import (
QGraphicsTextItem, QGraphicsLineItem, QGraphicsItem, QGraphicsTextItem, QGraphicsLineItem, QGraphicsItem,
QListWidget, QListWidgetItem, QGroupBox, QSpinBox, QCheckBox, QDialog 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 import get_renderer, list_renderers
from .renderers.code import get_languages, get_themes, is_available as code_renderer_available from .renderers.code import get_languages, get_themes, is_available as code_renderer_available
@ -150,7 +150,7 @@ class PlantUMLHighlighter(QSyntaxHighlighter):
class RenderThread(QThread): class RenderThread(QThread):
"""Background thread for rendering artifacts.""" """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, def __init__(self, renderer, source: str, output_path: Path,
camera_angles: tuple[int, int, int] | None = None, camera_angles: tuple[int, int, int] | None = None,
@ -186,8 +186,8 @@ class RenderThread(QThread):
class DictateThread(QThread): class DictateThread(QThread):
"""Background thread for voice dictation.""" """Background thread for voice dictation."""
finished = pyqtSignal(str) # transcribed text finished = Signal(str) # transcribed text
tick = pyqtSignal(int) # seconds remaining tick = Signal(int) # seconds remaining
def __init__(self, duration: int = 10): def __init__(self, duration: int = 10):
super().__init__() super().__init__()
@ -235,8 +235,8 @@ class DictateThread(QThread):
class AIThread(QThread): class AIThread(QThread):
"""Background thread for AI generation with validation and retry.""" """Background thread for AI generation with validation and retry."""
finished = pyqtSignal(bool, str) # success, result/error finished = Signal(bool, str) # success, result/error
status_update = pyqtSignal(str) # status message for UI status_update = Signal(str) # status message for UI
MAX_RETRIES = 2 MAX_RETRIES = 2
@ -452,11 +452,11 @@ class CodeEditor(QPlainTextEdit):
class DiagramCanvas(QGraphicsView): class DiagramCanvas(QGraphicsView):
"""Interactive canvas for diagram editing using QGraphicsScene.""" """Interactive canvas for diagram editing using QGraphicsScene."""
shape_changed = pyqtSignal() # Emitted when shapes are modified shape_changed = Signal() # Emitted when shapes are modified
rotation_changed = pyqtSignal(int, int) # Emitted for 3D rotation (delta_x, delta_y) rotation_changed = Signal(int, int) # Emitted for 3D rotation (delta_x, delta_y)
zoom_changed = pyqtSignal(int) # Emitted for zoom change (delta) zoom_changed = Signal(int) # Emitted for zoom change (delta)
element_clicked = pyqtSignal(str) # Emitted when an element is clicked (element name) element_clicked = Signal(str) # Emitted when an element is clicked (element name)
view_transform_changed = pyqtSignal() # Emitted when view transform changes (zoom) view_transform_changed = Signal() # Emitted when view transform changes (zoom)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -501,7 +501,7 @@ class DiagramCanvas(QGraphicsView):
self._svg_path = None self._svg_path = None
try: try:
from PyQt6.QtGui import QPixmap, QImage from PySide6.QtGui import QPixmap, QImage
path = Path(image_path) path = Path(image_path)
@ -511,7 +511,7 @@ class DiagramCanvas(QGraphicsView):
self._parse_svg_elements(image_path) self._parse_svg_elements(image_path)
# Load SVG using QSvgRenderer # Load SVG using QSvgRenderer
from PyQt6.QtSvg import QSvgRenderer from PySide6.QtSvg import QSvgRenderer
renderer = QSvgRenderer(image_path) renderer = QSvgRenderer(image_path)
if renderer.isValid(): if renderer.isValid():
@ -728,8 +728,8 @@ class DiagramCanvas(QGraphicsView):
class AIPanel(QWidget): class AIPanel(QWidget):
"""Bottom panel for AI interaction and dictation.""" """Bottom panel for AI interaction and dictation."""
ai_submitted = pyqtSignal(str) # AI instruction submitted ai_submitted = Signal(str) # AI instruction submitted
dictate_requested = pyqtSignal() dictate_requested = Signal()
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -1149,7 +1149,7 @@ class ArtifactEditorWindow(QMainWindow):
rotation_layout = QHBoxLayout(self.rotation_panel) rotation_layout = QHBoxLayout(self.rotation_panel)
rotation_layout.setContentsMargins(4, 4, 4, 4) 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 = QSlider(Qt.Orientation.Horizontal)
self.rot_x_slider.setRange(0, 360) self.rot_x_slider.setRange(0, 360)
@ -2632,7 +2632,7 @@ class ArtifactEditorWindow(QMainWindow):
def _on_svg_create_group(self, element): def _on_svg_create_group(self, element):
"""Create a new group containing the SVG element.""" """Create a new group containing the SVG element."""
from PyQt6.QtWidgets import QInputDialog from PySide6.QtWidgets import QInputDialog
group_id, ok = QInputDialog.getText( group_id, ok = QInputDialog.getText(
self, self,

View File

@ -8,9 +8,9 @@ from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Optional, List from typing import Optional, List
from PyQt6.QtCore import Qt, QRectF, QPointF, pyqtSignal, QSizeF, QObject from PySide6.QtCore import Qt, QRectF, QPointF, Signal, QSizeF, QObject
from PyQt6.QtGui import QPen, QBrush, QColor, QCursor from PySide6.QtGui import QPen, QBrush, QColor, QCursor
from PyQt6.QtWidgets import ( from PySide6.QtWidgets import (
QGraphicsScene, QGraphicsView, QGraphicsRectItem, QGraphicsScene, QGraphicsView, QGraphicsRectItem,
QGraphicsItem, QMenu, QGraphicsSceneMouseEvent QGraphicsItem, QMenu, QGraphicsSceneMouseEvent
) )
@ -111,19 +111,19 @@ class SVGSelectionManager(QObject):
""" """
# Signals # Signals
element_selected = pyqtSignal(object) # SVGElement or None element_selected = Signal(object) # SVGElement or None
element_moved = pyqtSignal(object, float, float) # element, new_x, new_y element_moved = Signal(object, float, float) # element, new_x, new_y
element_resized = pyqtSignal(object, float, float, float, float) # element, x, y, w, h element_resized = Signal(object, float, float, float, float) # element, x, y, w, h
# Context menu signals # Context menu signals
bring_to_front = pyqtSignal(object) bring_to_front = Signal(object)
send_to_back = pyqtSignal(object) send_to_back = Signal(object)
bring_forward = pyqtSignal(object) bring_forward = Signal(object)
send_backward = pyqtSignal(object) send_backward = Signal(object)
add_to_group = pyqtSignal(object, str) add_to_group = Signal(object, str)
remove_from_group = pyqtSignal(object) remove_from_group = Signal(object)
create_group = pyqtSignal(object) create_group = Signal(object)
delete_element = pyqtSignal(object) delete_element = Signal(object)
def __init__(self, scene: QGraphicsScene, view: QGraphicsView): def __init__(self, scene: QGraphicsScene, view: QGraphicsView):
super().__init__() super().__init__()
@ -327,7 +327,7 @@ class SVGSelectionManager(QObject):
def eventFilter(self, obj, event): def eventFilter(self, obj, event):
"""Handle mouse events on the viewport.""" """Handle mouse events on the viewport."""
from PyQt6.QtCore import QEvent from PySide6.QtCore import QEvent
if obj != self.view.viewport(): if obj != self.view.viewport():
return False return False

View File

@ -8,10 +8,10 @@ matching the actual SVG rendering.
""" """
from typing import Optional, List, Dict from typing import Optional, List, Dict
from PyQt6.QtCore import Qt, QPointF, QRectF, QByteArray, pyqtSignal, QObject from PySide6.QtCore import Qt, QPointF, QRectF, QByteArray, Signal, QObject
from PyQt6.QtGui import QPainter, QPen, QBrush, QColor, QFont, QPolygonF, QFontMetricsF from PySide6.QtGui import QPainter, QPen, QBrush, QColor, QFont, QPolygonF, QFontMetricsF
from PyQt6.QtSvg import QSvgRenderer from PySide6.QtSvg import QSvgRenderer
from PyQt6.QtWidgets import ( from PySide6.QtWidgets import (
QGraphicsItem, QGraphicsRectItem, QGraphicsScene, QGraphicsItem, QGraphicsRectItem, QGraphicsScene,
QGraphicsView, QGraphicsSceneMouseEvent, QGraphicsView, QGraphicsSceneMouseEvent,
QStyleOptionGraphicsItem, QWidget QStyleOptionGraphicsItem, QWidget
@ -595,9 +595,9 @@ class SVGSceneManager(QObject):
scene_changed: Emitted when content changes (need to regenerate SVG) scene_changed: Emitted when content changes (need to regenerate SVG)
""" """
element_selected = pyqtSignal(object) element_selected = Signal(object)
element_moved = pyqtSignal(object, float, float) element_moved = Signal(object, float, float)
scene_changed = pyqtSignal() scene_changed = Signal()
def __init__(self, scene: QGraphicsScene, view: QGraphicsView): def __init__(self, scene: QGraphicsScene, view: QGraphicsView):
super().__init__() super().__init__()