Text fields and saving Icons to file

This commit is contained in:
2026-07-27 14:30:25 +02:00
parent 13d3825f9b
commit c68e693359
12 changed files with 510 additions and 35 deletions

View File

@@ -4,14 +4,18 @@ from copy import deepcopy
from PySide6.QtCore import QEvent, QObject, Qt, Signal
from PySide6.QtGui import QKeySequence, QPainter, QShortcut, QUndoCommand, QUndoStack, QWheelEvent, QShowEvent
from PySide6.QtWidgets import QDialog, QGraphicsView, QMainWindow, QWidget
from PySide6.QtWidgets import QDialog, QFileDialog, QGraphicsView, QMainWindow, QMessageBox, QWidget
from bedit_core.models import Port, PortID, SignalDirection
from bedit_gui.models import Icon, Shape, ShapeID
from bedit_gui.services import icon_files
from bedit_gui.services.application_logging import get_logger
from bedit_gui.ui.generated.ui_icon_editor_window import Ui_iconEditor
from bedit_gui.views.icon_graphics_scene import IconGraphicsScene, RectangleCreationTool
from bedit_gui.views.icon_graphics_scene import IconGraphicsScene, RectangleCreationTool, TextCreationTool
from bedit_gui.views.shape_options_dialog import ShapeOptionsDialog
logger = get_logger(__name__)
class ChangeIconDraftCommand(QUndoCommand):
def __init__(self, editor: IconEditorWindow, icon: Icon, text: str) -> None:
@@ -102,10 +106,11 @@ class IconEditorWindow(QMainWindow):
self.setWindowTitle("Icon Editor")
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
self._icon = deepcopy(icon)
self._ports = deepcopy(ports)
self.scene = IconGraphicsScene(self)
self.scene.set_ports(ports)
self._ensure_port_positions(ports)
self.scene.set_ports(self._ports)
self._ensure_port_positions(self._ports)
self.scene.set_icon(self._icon)
self.scene.port_moved.connect(self._port_moved)
self.scene.shape_created.connect(self._shape_created)
@@ -118,6 +123,8 @@ class IconEditorWindow(QMainWindow):
self.ui.graphicsView.viewport().installEventFilter(self)
self.ui.actionAdd_Rectangle.setCheckable(True)
self.ui.actionAdd_Rectangle.triggered.connect(self._start_rectangle_tool)
self.ui.actionAdd_Text.setCheckable(True)
self.ui.actionAdd_Text.triggered.connect(self._start_text_tool)
self.cancel_tool_shortcut = QShortcut(QKeySequence(Qt.Key.Key_Escape), self)
self.cancel_tool_shortcut.activated.connect(self.scene.cancel_creation_tool)
self.delete_shortcut = QShortcut(QKeySequence(Qt.Key.Key_Delete), self)
@@ -135,6 +142,8 @@ class IconEditorWindow(QMainWindow):
self.ui.actionUndo.triggered.connect(self.undo_stack.undo)
self.ui.actionRedo.triggered.connect(self.undo_stack.redo)
self.ui.actionSave.triggered.connect(self.save)
self.ui.actionSave_to_File.triggered.connect(self.save_to_file)
self.ui.actionOpen_from_File.triggered.connect(self.open_from_file)
self.ui.actionCancel.triggered.connect(self.close)
self.undo_stack.canUndoChanged.connect(self.ui.actionUndo.setEnabled)
self.undo_stack.canRedoChanged.connect(self.ui.actionRedo.setEnabled)
@@ -157,6 +166,32 @@ class IconEditorWindow(QMainWindow):
self.saved.emit(self.icon())
self.close()
def save_to_file(self) -> None:
file_name, _ = QFileDialog.getSaveFileName(self, "Save Icon", "", "JSON files (*.json)")
if not file_name:
return
path = file_name if file_name.lower().endswith(".json") else f"{file_name}.json"
try:
icon_files.save(self._icon, path)
except (OSError, TypeError, ValueError) as exc:
logger.exception("Could not save icon to %s", path)
QMessageBox.critical(self, "Could not save icon", str(exc))
return
logger.info("Saved icon to: %s", path)
def open_from_file(self) -> None:
file_name, _ = QFileDialog.getOpenFileName(self, "Open Icon", "", "JSON files (*.json)")
if not file_name:
return
try:
icon = icon_files.load(file_name)
except (OSError, TypeError, ValueError) as exc:
logger.exception("Could not open icon from %s", file_name)
QMessageBox.critical(self, "Could not open icon", str(exc))
return
self.apply_change(icon, "Load icon from file")
logger.info("Loaded icon from: %s", file_name)
def zoom_in(self) -> None:
self._zoom(self.zoom_step)
@@ -187,6 +222,7 @@ class IconEditorWindow(QMainWindow):
def _set_icon(self, icon: Icon) -> None:
self._icon = deepcopy(icon)
self._ensure_port_positions(self._ports)
self.scene.set_icon(self._icon)
self.icon_changed.emit(self.icon())
@@ -268,10 +304,18 @@ class IconEditorWindow(QMainWindow):
def _start_rectangle_tool(self) -> None:
layer = max((shape.layer for shape in self._icon.shapes.values()), default=-1) + 1
self.ui.actionAdd_Text.setChecked(False)
self.scene.set_creation_tool(RectangleCreationTool(self.scene, layer))
def _start_text_tool(self) -> None:
layer = max((shape.layer for shape in self._icon.shapes.values()), default=-1) + 1
self.ui.actionAdd_Rectangle.setChecked(False)
self.scene.set_creation_tool(TextCreationTool(self.scene, layer))
def _tool_active_changed(self, active: bool) -> None:
self.ui.actionAdd_Rectangle.setChecked(active)
if not active:
self.ui.actionAdd_Rectangle.setChecked(False)
self.ui.actionAdd_Text.setChecked(False)
cursor = Qt.CursorShape.CrossCursor if active else Qt.CursorShape.ArrowCursor
self.ui.graphicsView.viewport().setCursor(cursor)