Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving proof step renaming #290

Merged
merged 5 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions zxlive/proof.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -82,6 +82,11 @@ def data(self, index: Union[QModelIndex, QPersistentModelIndex], role: int=Qt.It
elif role == Qt.ItemDataRole.FontRole:
return QFont("monospace", 12)

def flags(self, index: Union[QModelIndex, QPersistentModelIndex]) -> Qt.ItemFlag:
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`.
Expand Down Expand Up @@ -205,7 +210,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)
Expand All @@ -216,6 +221,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.selectionModel().selectionChanged.connect(self.proof_step_selected)

# overriding this method to change the return type and stop mypy from complaining
def model(self) -> ProofModel:
Expand All @@ -239,20 +245,36 @@ 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.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

action = context_menu.exec_(self.mapToGlobal(position))
if action in action_function_map:
action_function_map[action]()

def rename_proof_step(self, new_name: str, index: int) -> None:
from .commands import UndoableChange
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:
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
Expand Down
48 changes: 18 additions & 30 deletions zxlive/proof_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
from typing import Iterator, Union, cast

import pyzx
from PySide6.QtCore import (QItemSelection, 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, QStyle,
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,
set_z_box_label)

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
Expand Down Expand Up @@ -55,33 +55,14 @@ 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.selectionModel().selectionChanged.connect(self._proof_step_selected)
self.step_view.doubleClicked.connect(self._double_click_handler)
self.step_view.setItemDelegate(ProofStepItemDelegate(self.step_view))

self.splitter.addWidget(self.step_view)

@property
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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -489,3 +464,16 @@ 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: QWidget, option: QStyleOptionViewItem, index: Union[QModelIndex, QPersistentModelIndex]) -> QLineEdit:
return QLineEdit(parent)

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: QWidget, model: QAbstractItemModel, index: Union[QModelIndex, QPersistentModelIndex]) -> None:
step_view = self.parent()
assert isinstance(step_view, ProofStepView)
assert isinstance(editor, QLineEdit)
step_view.rename_proof_step(editor.text(), index.row() - 1)
Loading