From 499ed20d8db00be8ab5e233268e87e47c85e74a0 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 4 Jul 2024 21:40:15 +0100 Subject: [PATCH 1/5] moved the handlers for step view inside the step view class --- zxlive/proof.py | 27 +++++++++++++++++++++++++-- zxlive/proof_panel.py | 29 ++--------------------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/zxlive/proof.py b/zxlive/proof.py index 597e398..d41e5cd 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -4,10 +4,10 @@ if TYPE_CHECKING: from .proof_panel import ProofPanel -from PySide6.QtCore import (QAbstractListModel, QModelIndex, +from PySide6.QtCore import (QAbstractListModel, QItemSelection, QModelIndex, QPersistentModelIndex, Qt, QPoint) from PySide6.QtGui import QColor, QFont -from PySide6.QtWidgets import QAbstractItemView, QListView, QMenu +from PySide6.QtWidgets import QAbstractItemView, QInputDialog, QListView, QMenu from .common import GraphT @@ -216,6 +216,8 @@ def __init__(self, parent: 'ProofPanel'): self.viewport().setAttribute(Qt.WidgetAttribute.WA_Hover) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) + self.doubleClicked.connect(self.double_click_handler) + self.selectionModel().selectionChanged.connect(self.proof_step_selected) # overriding this method to change the return type and stop mypy from complaining def model(self) -> ProofModel: @@ -253,6 +255,27 @@ def show_context_menu(self, position: QPoint) -> None: if action in action_function_map: action_function_map[action]() + def double_click_handler(self, index: Union[QModelIndex, QPersistentModelIndex]) -> None: + from .commands import UndoableChange + # The first row in the item list is the START step, which is not interactive + if index.row() == 0: + return + new_name, ok = QInputDialog.getText(self, "Rename proof step", "Enter new name") + if ok: + # Subtract 1 from index since the START step isn't part of the model + old_name = self.model().steps[index.row()-1].display_name + cmd = UndoableChange(self.graph_view, + lambda: self.model().rename_step(index.row()-1, old_name), + lambda: self.model().rename_step(index.row()-1, new_name) + ) + self.undo_stack.push(cmd) + + def proof_step_selected(self, selected: QItemSelection, deselected: QItemSelection) -> None: + if not selected or not deselected: + return + step_index = selected.first().topLeft().row() + self.move_to_step(step_index) + def group_selected_steps(self) -> None: from .commands import GroupRewriteSteps from .dialogs import show_error_msg diff --git a/zxlive/proof_panel.py b/zxlive/proof_panel.py index c3f51a9..a91a408 100644 --- a/zxlive/proof_panel.py +++ b/zxlive/proof_panel.py @@ -4,7 +4,7 @@ from typing import Iterator, Union, cast import pyzx -from PySide6.QtCore import (QItemSelection, QModelIndex, QPersistentModelIndex, +from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, QPointF, QRect, QSize, Qt) from PySide6.QtGui import (QAction, QColor, QFont, QFontInfo, QFontMetrics, QIcon, QPainter, QPen, QVector2D) @@ -18,7 +18,7 @@ from . import animations as anims from .base_panel import BasePanel, ToolbarSection -from .commands import AddRewriteStep, MoveNodeProofMode, UndoableChange +from .commands import AddRewriteStep, MoveNodeProofMode from .common import (ET, VT, GraphT, colors, get_data, pos_from_view, pos_to_view) from .dialogs import show_error_msg @@ -56,8 +56,6 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None: self.step_view = ProofStepView(self) self.step_view.setItemDelegate(ProofStepItemDelegate()) - self.step_view.selectionModel().selectionChanged.connect(self._proof_step_selected) - self.step_view.doubleClicked.connect(self._double_click_handler) self.splitter.addWidget(self.step_view) @@ -65,23 +63,6 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None: def proof_model(self) -> ProofModel: return self.step_view.model() - def _double_click_handler(self, index: QModelIndex | QPersistentModelIndex) -> None: - # The first row in the item list is the START step, which is not interactive - if index.row() == 0: - return - - new_name, ok = QInputDialog.getText(self, "Rename proof step", "Enter new name") - - if ok: - # Subtract 1 from index since the START step isn't part of the model - old_name = self.proof_model.steps[index.row()-1].display_name - cmd = UndoableChange(self.graph_view, - lambda: self.proof_model.rename_step(index.row()-1, old_name), - lambda: self.proof_model.rename_step(index.row()-1, new_name) - ) - - self.undo_stack.push(cmd) - def _toolbar_sections(self) -> Iterator[ToolbarSection]: icon_size = QSize(32, 32) self.selection = QToolButton(self) @@ -404,12 +385,6 @@ def _vert_double_clicked(self, v: VT) -> None: cmd = AddRewriteStep(self.graph_view, new_g, self.step_view, "color change") self.undo_stack.push(cmd) - def _proof_step_selected(self, selected: QItemSelection, deselected: QItemSelection) -> None: - if not selected or not deselected: - return - step_index = selected.first().topLeft().row() - self.step_view.move_to_step(step_index) - def _refresh_rewrites_model(self) -> None: refresh_custom_rules() model = RewriteActionTreeModel.from_dict(action_groups, self) From 8073550b680c9fed818c3df92cedc0fba39aa18b Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 4 Jul 2024 21:52:25 +0100 Subject: [PATCH 2/5] add rename to the context menu --- zxlive/proof.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/zxlive/proof.py b/zxlive/proof.py index d41e5cd..5108d2c 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -241,13 +241,14 @@ def show_context_menu(self, position: QPoint) -> None: context_menu = QMenu(self) action_function_map = {} + index = selected_indexes[0].row() if len(selected_indexes) > 1: group_action = context_menu.addAction("Group Steps") action_function_map[group_action] = self.group_selected_steps - - if len(selected_indexes) == 1: - index = selected_indexes[0].row() - if index != 0 and self.model().steps[index - 1].grouped_rewrites is not None: + elif index != 0: + rename_action = context_menu.addAction("Rename Step") + action_function_map[rename_action] = lambda: self.rename_proof_step(index - 1) + if self.model().steps[index - 1].grouped_rewrites is not None: ungroup_action = context_menu.addAction("Ungroup Steps") action_function_map[ungroup_action] = self.ungroup_selected_step @@ -256,17 +257,19 @@ def show_context_menu(self, position: QPoint) -> None: action_function_map[action]() def double_click_handler(self, index: Union[QModelIndex, QPersistentModelIndex]) -> None: - from .commands import UndoableChange # The first row in the item list is the START step, which is not interactive if index.row() == 0: return + self.rename_proof_step(index.row()-1) + + def rename_proof_step(self, index: int) -> None: + from .commands import UndoableChange new_name, ok = QInputDialog.getText(self, "Rename proof step", "Enter new name") if ok: - # Subtract 1 from index since the START step isn't part of the model - old_name = self.model().steps[index.row()-1].display_name + old_name = self.model().steps[index].display_name cmd = UndoableChange(self.graph_view, - lambda: self.model().rename_step(index.row()-1, old_name), - lambda: self.model().rename_step(index.row()-1, new_name) + lambda: self.model().rename_step(index, old_name), + lambda: self.model().rename_step(index, new_name) ) self.undo_stack.push(cmd) From 527ba14680c2b4115509895bf00a60ac202c12c7 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 4 Jul 2024 22:20:58 +0100 Subject: [PATCH 3/5] trying to use the built in editor to rename --- zxlive/proof.py | 25 ++++++++++++++++++------- zxlive/proof_panel.py | 11 ++++++++++- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/zxlive/proof.py b/zxlive/proof.py index 5108d2c..48a9dff 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -82,6 +82,17 @@ def data(self, index: Union[QModelIndex, QPersistentModelIndex], role: int=Qt.It elif role == Qt.ItemDataRole.FontRole: return QFont("monospace", 12) + def setData(self, index: Union[QModelIndex, QPersistentModelIndex], value: Any, role: int=Qt.ItemDataRole.EditRole) -> bool: + if role == Qt.EditRole: + self.rename_step(index.row()-1, value) + return True + return False + + def flags(self, index): + if index.row() == 0: + return super().flags(index) + return super().flags(index) | Qt.ItemFlag.ItemIsEditable + def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.ItemDataRole.DisplayRole) -> Any: """Overrides `QAbstractItemModel.headerData`. @@ -205,7 +216,7 @@ def __init__(self, parent: 'ProofPanel'): self.undo_stack = parent.undo_stack self.setModel(ProofModel(self.graph_view.graph_scene.g)) self.setCurrentIndex(self.model().index(0, 0)) - self.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + self.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked) self.setPalette(QColor(255, 255, 255)) self.setSpacing(0) self.setSelectionMode(QAbstractItemView.SelectionMode.ContiguousSelection) @@ -216,7 +227,7 @@ def __init__(self, parent: 'ProofPanel'): self.viewport().setAttribute(Qt.WidgetAttribute.WA_Hover) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) - self.doubleClicked.connect(self.double_click_handler) + # self.doubleClicked.connect(self.double_click_handler) self.selectionModel().selectionChanged.connect(self.proof_step_selected) # overriding this method to change the return type and stop mypy from complaining @@ -256,11 +267,11 @@ def show_context_menu(self, position: QPoint) -> None: if action in action_function_map: action_function_map[action]() - def double_click_handler(self, index: Union[QModelIndex, QPersistentModelIndex]) -> None: - # The first row in the item list is the START step, which is not interactive - if index.row() == 0: - return - self.rename_proof_step(index.row()-1) + # def double_click_handler(self, index: Union[QModelIndex, QPersistentModelIndex]) -> None: + # # The first row in the item list is the START step, which is not interactive + # if index.row() == 0: + # return + # self.rename_proof_step(index.row()-1) def rename_proof_step(self, index: int) -> None: from .commands import UndoableChange diff --git a/zxlive/proof_panel.py b/zxlive/proof_panel.py index a91a408..a425d56 100644 --- a/zxlive/proof_panel.py +++ b/zxlive/proof_panel.py @@ -8,7 +8,7 @@ QPointF, QRect, QSize, Qt) from PySide6.QtGui import (QAction, QColor, QFont, QFontInfo, QFontMetrics, QIcon, QPainter, QPen, QVector2D) -from PySide6.QtWidgets import (QAbstractItemView, QInputDialog, QStyle, +from PySide6.QtWidgets import (QAbstractItemView, QInputDialog, QLineEdit, QStyle, QStyledItemDelegate, QStyleOptionViewItem, QToolButton, QTreeView) from pyzx import VertexType, basicrules @@ -464,3 +464,12 @@ def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex | QPersisten size = super().sizeHint(option, index) return QSize(size.width(), size.height() + 2 * self.vert_padding) + def createEditor(self, parent, option, index): + return QLineEdit(parent) + + def setEditorData(self, editor, index): + value = index.model().data(index, Qt.DisplayRole) + editor.setText(str(value)) + + def setModelData(self, editor, model, index): + model.setData(index, editor.text(), Qt.EditRole) From 138ff485c9be248ee5b2610b8b672a6970ffa856 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 4 Jul 2024 22:32:51 +0100 Subject: [PATCH 4/5] typing --- zxlive/proof.py | 4 ++-- zxlive/proof_panel.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/zxlive/proof.py b/zxlive/proof.py index 48a9dff..bcdcf48 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -83,12 +83,12 @@ def data(self, index: Union[QModelIndex, QPersistentModelIndex], role: int=Qt.It return QFont("monospace", 12) def setData(self, index: Union[QModelIndex, QPersistentModelIndex], value: Any, role: int=Qt.ItemDataRole.EditRole) -> bool: - if role == Qt.EditRole: + if role == Qt.ItemDataRole.EditRole: self.rename_step(index.row()-1, value) return True return False - def flags(self, index): + def flags(self, index: Union[QModelIndex, QPersistentModelIndex]) -> Qt.ItemFlag: if index.row() == 0: return super().flags(index) return super().flags(index) | Qt.ItemFlag.ItemIsEditable diff --git a/zxlive/proof_panel.py b/zxlive/proof_panel.py index a425d56..d4fe766 100644 --- a/zxlive/proof_panel.py +++ b/zxlive/proof_panel.py @@ -4,13 +4,13 @@ from typing import Iterator, Union, cast import pyzx -from PySide6.QtCore import (QModelIndex, QPersistentModelIndex, +from PySide6.QtCore import (QAbstractItemModel, QModelIndex, QPersistentModelIndex, QPointF, QRect, QSize, Qt) from PySide6.QtGui import (QAction, QColor, QFont, QFontInfo, QFontMetrics, QIcon, QPainter, QPen, QVector2D) from PySide6.QtWidgets import (QAbstractItemView, QInputDialog, QLineEdit, QStyle, QStyledItemDelegate, QStyleOptionViewItem, - QToolButton, QTreeView) + QToolButton, QTreeView, QWidget) from pyzx import VertexType, basicrules from pyzx.graph.jsonparser import string_to_phase from pyzx.utils import (EdgeType, FractionLike, get_w_partner, get_z_box_label, @@ -464,12 +464,14 @@ def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex | QPersisten size = super().sizeHint(option, index) return QSize(size.width(), size.height() + 2 * self.vert_padding) - def createEditor(self, parent, option, index): + def createEditor(self, parent: QWidget, option: QStyleOptionViewItem, index: Union[QModelIndex, QPersistentModelIndex]) -> QLineEdit: return QLineEdit(parent) - def setEditorData(self, editor, index): - value = index.model().data(index, Qt.DisplayRole) + def setEditorData(self, editor: QWidget, index: Union[QModelIndex, QPersistentModelIndex]) -> None: + assert isinstance(editor, QLineEdit) + value = index.model().data(index, Qt.ItemDataRole.DisplayRole) editor.setText(str(value)) - def setModelData(self, editor, model, index): - model.setData(index, editor.text(), Qt.EditRole) + def setModelData(self, editor: QWidget, model: QAbstractItemModel, index: Union[QModelIndex, QPersistentModelIndex]) -> None: + assert isinstance(editor, QLineEdit) + model.setData(index, editor.text(), Qt.ItemDataRole.EditRole) From ec13b1d3ec416aca502edb5c2ed0c6c59920d5cc Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Thu, 4 Jul 2024 23:02:09 +0100 Subject: [PATCH 5/5] making renaming undoable --- zxlive/proof.py | 31 ++++++++----------------------- zxlive/proof_panel.py | 6 ++++-- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/zxlive/proof.py b/zxlive/proof.py index bcdcf48..5844cb7 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -82,12 +82,6 @@ def data(self, index: Union[QModelIndex, QPersistentModelIndex], role: int=Qt.It elif role == Qt.ItemDataRole.FontRole: return QFont("monospace", 12) - def setData(self, index: Union[QModelIndex, QPersistentModelIndex], value: Any, role: int=Qt.ItemDataRole.EditRole) -> bool: - if role == Qt.ItemDataRole.EditRole: - self.rename_step(index.row()-1, value) - return True - return False - def flags(self, index: Union[QModelIndex, QPersistentModelIndex]) -> Qt.ItemFlag: if index.row() == 0: return super().flags(index) @@ -227,7 +221,6 @@ def __init__(self, parent: 'ProofPanel'): self.viewport().setAttribute(Qt.WidgetAttribute.WA_Hover) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) - # self.doubleClicked.connect(self.double_click_handler) self.selectionModel().selectionChanged.connect(self.proof_step_selected) # overriding this method to change the return type and stop mypy from complaining @@ -258,7 +251,7 @@ def show_context_menu(self, position: QPoint) -> None: action_function_map[group_action] = self.group_selected_steps elif index != 0: rename_action = context_menu.addAction("Rename Step") - action_function_map[rename_action] = lambda: self.rename_proof_step(index - 1) + action_function_map[rename_action] = lambda: self.edit(selected_indexes[0]) if self.model().steps[index - 1].grouped_rewrites is not None: ungroup_action = context_menu.addAction("Ungroup Steps") action_function_map[ungroup_action] = self.ungroup_selected_step @@ -267,22 +260,14 @@ def show_context_menu(self, position: QPoint) -> None: if action in action_function_map: action_function_map[action]() - # def double_click_handler(self, index: Union[QModelIndex, QPersistentModelIndex]) -> None: - # # The first row in the item list is the START step, which is not interactive - # if index.row() == 0: - # return - # self.rename_proof_step(index.row()-1) - - def rename_proof_step(self, index: int) -> None: + def rename_proof_step(self, new_name: str, index: int) -> None: from .commands import UndoableChange - new_name, ok = QInputDialog.getText(self, "Rename proof step", "Enter new name") - if ok: - old_name = self.model().steps[index].display_name - cmd = UndoableChange(self.graph_view, - lambda: self.model().rename_step(index, old_name), - lambda: self.model().rename_step(index, new_name) - ) - self.undo_stack.push(cmd) + old_name = self.model().steps[index].display_name + cmd = UndoableChange(self.graph_view, + lambda: self.model().rename_step(index, old_name), + lambda: self.model().rename_step(index, new_name) + ) + self.undo_stack.push(cmd) def proof_step_selected(self, selected: QItemSelection, deselected: QItemSelection) -> None: if not selected or not deselected: diff --git a/zxlive/proof_panel.py b/zxlive/proof_panel.py index d4fe766..26600cb 100644 --- a/zxlive/proof_panel.py +++ b/zxlive/proof_panel.py @@ -55,7 +55,7 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None: self.graph_scene.edge_dragged.connect(self.change_edge_curves) self.step_view = ProofStepView(self) - self.step_view.setItemDelegate(ProofStepItemDelegate()) + self.step_view.setItemDelegate(ProofStepItemDelegate(self.step_view)) self.splitter.addWidget(self.step_view) @@ -473,5 +473,7 @@ def setEditorData(self, editor: QWidget, index: Union[QModelIndex, QPersistentMo editor.setText(str(value)) def setModelData(self, editor: QWidget, model: QAbstractItemModel, index: Union[QModelIndex, QPersistentModelIndex]) -> None: + step_view = self.parent() + assert isinstance(step_view, ProofStepView) assert isinstance(editor, QLineEdit) - model.setData(index, editor.text(), Qt.ItemDataRole.EditRole) + step_view.rename_proof_step(editor.text(), index.row() - 1)