Skip to content

Commit

Permalink
Add preview pictures to rewrites (#309)
Browse files Browse the repository at this point in the history
* Add tooltip pictures
* Add bialgebra preview
* Made viewing rewrite previews a toggleable option in the Settings
* Render preview of custom rules
* Made the rendering better
* Add tooltip pictures to MANIFEST
* Made preview setting a Bool
  • Loading branch information
jvdwetering authored Jul 16, 2024
1 parent 5b229b4 commit 6d99563
Show file tree
Hide file tree
Showing 15 changed files with 88 additions and 15 deletions.
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include zxlive/icons/*.svg
include zxlive/icons/*.svg
include zxlive/tooltips/*.png
1 change: 0 additions & 1 deletion zxlive/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
myappid = 'zxcalc.zxlive.zxlive.1.0.0' # arbitrary string
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) # type: ignore


class ZXLive(QApplication):
"""The main ZXLive application
Expand Down
4 changes: 3 additions & 1 deletion zxlive/custom_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ def from_json(cls, json_str: str) -> "CustomRule":
def to_rewrite_data(self) -> "RewriteData":
from .rewrite_data import MATCHES_VERTICES
return {"text": self.name, "matcher": self.matcher, "rule": self, "type": MATCHES_VERTICES,
"tooltip": self.description, 'copy_first': False, 'returns_new_graph': False}
"tooltip": self.description, 'copy_first': False, 'returns_new_graph': False,
"custom_rule": True, "lhs": self.lhs_graph, "rhs": self.rhs_graph}



def is_rewrite_unfusable(lhs_graph: GraphT) -> bool:
Expand Down
4 changes: 3 additions & 1 deletion zxlive/graphview.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ def __init__(self, start: QPointF, shift: bool = False) -> None:
class GraphView(QGraphicsView):
"""QtWidget containing a graph
This widget is view associated with a graph. However, most of the
This widget is the view associated with a graph. However, most of the
interesting stuff happens in `GraphScene`.
"""

wand_trace_finished = Signal(object)
draw_background_lines = True

def __init__(self, graph_scene: GraphScene) -> None:
self.graph_scene = graph_scene
Expand Down Expand Up @@ -267,6 +268,7 @@ def drawBackground(self, painter: QPainter, rect: QRectF | QRect) -> None:
painter.setBrush(QColor(255, 255, 255, 255))
painter.setPen(QPen(Qt.PenStyle.NoPen))
painter.drawRect(rect)
if not self.draw_background_lines: return

# Calculate grid lines
lines, thick_lines = [], []
Expand Down
61 changes: 57 additions & 4 deletions zxlive/rewrite_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@
import copy
from dataclasses import dataclass, field
from typing import Callable, TYPE_CHECKING, Any, cast, Union
from concurrent.futures import ThreadPoolExecutor

import pyzx
from PySide6.QtCore import Qt, QAbstractItemModel, QModelIndex, QPersistentModelIndex, Signal, QObject, QMetaObject
from concurrent.futures import ThreadPoolExecutor

from PySide6.QtCore import (Qt, QAbstractItemModel, QModelIndex, QPersistentModelIndex,
Signal, QObject, QMetaObject, QIODevice, QBuffer, QPoint, QPointF, QLineF)
from PySide6.QtGui import QPixmap, QColor, QPen
from PySide6.QtWidgets import QGraphicsView, QGraphicsScene


from .animations import make_animation
from .commands import AddRewriteStep
from .common import ET, GraphT, VT
from .common import ET, GraphT, VT, get_data
from .dialogs import show_error_msg
from .rewrite_data import is_rewrite_data, RewriteData, MatchType, MATCHES_VERTICES
from .settings import display_setting
from .graphscene import GraphScene
from .graphview import GraphView

if TYPE_CHECKING:
from .proof_panel import ProofPanel
Expand All @@ -36,12 +44,57 @@ class RewriteAction:

@classmethod
def from_rewrite_data(cls, d: RewriteData) -> RewriteAction:
if display_setting.PREVIEWS_SHOW and ('picture' in d or 'custom_rule' in d):
if 'custom_rule' in d:
# We will create a custom tooltip picture representing the custom rewrite
graph_scene_left = GraphScene()
graph_scene_right = GraphScene()
graph_view_left = GraphView(graph_scene_left)
graph_view_left.draw_background_lines = False
graph_view_left.set_graph(d['lhs'])
graph_view_right = GraphView(graph_scene_right)
graph_view_right.draw_background_lines = False
graph_view_right.set_graph(d['rhs'])
graph_view_left.fit_view()
graph_view_right.fit_view()
graph_view_left.setSceneRect(graph_scene_left.itemsBoundingRect())
graph_view_right.setSceneRect(graph_scene_right.itemsBoundingRect())
lhs_size = graph_view_left.viewport().size()
rhs_size = graph_view_right.viewport().size()
# The picture needs to be wide enough to fit both of them and have some space for the = sign
pixmap = QPixmap(lhs_size.width()+rhs_size.width()+160,max(lhs_size.height(),rhs_size.height()))
pixmap.fill(QColor("#ffffff"))
graph_view_left.viewport().render(pixmap)
graph_view_right.viewport().render(pixmap,QPoint(lhs_size.width()+160,0))
# We create a new scene to render the = sign
new_scene = GraphScene()
new_view = GraphView(new_scene)
new_view.draw_background_lines = False
new_scene.addLine(QLineF(QPointF(10,40),QPointF(80,40)),QPen(QColor("#000000"),8))
new_scene.addLine(QLineF(QPointF(10,10),QPointF(80,10)),QPen(QColor("#000000"),8))
new_view.setSceneRect(new_scene.itemsBoundingRect())
new_view.viewport().render(pixmap,QPoint(lhs_size.width(),max(lhs_size.height(),rhs_size.height())/2-20))

buffer = QBuffer()
buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, "PNG", quality=100)
image = bytes(buffer.data().toBase64()).decode()
else:
pixmap = QPixmap()
pixmap.load(get_data("tooltips/"+d['picture']))
buffer = QBuffer()
buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, "PNG", quality=100)
image = bytes(buffer.data().toBase64()).decode()
tooltip = '<img src="data:image/png;base64,{}" width="500">'.format(image) + d['tooltip']
else:
tooltip = d['tooltip']
return cls(
name=d['text'],
matcher=d['matcher'],
rule=d['rule'],
match_type=d['type'],
tooltip=d['tooltip'],
tooltip=tooltip,
copy_first=d.get('copy_first', False),
returns_new_graph=d.get('returns_new_graph', False),
)
Expand Down
22 changes: 16 additions & 6 deletions zxlive/rewrite_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class RewriteData(TypedDict):
tooltip: str
copy_first: NotRequired[bool]
returns_new_graph: NotRequired[bool]
picture: NotRequired[str]
custom_rule: NotRequired[bool]
lhs: NotRequired[GraphT]
rhs: NotRequired[GraphT]


def is_rewrite_data(d: dict) -> bool:
Expand All @@ -46,7 +50,6 @@ def read_custom_rules() -> list[RewriteData]:
custom_rules.append(rule)
return custom_rules


# We want additional actions that are not part of the original PyZX editor
# So we add them to operations

Expand All @@ -57,15 +60,17 @@ def read_custom_rules() -> list[RewriteData]:
"matcher": pyzx.rules.match_lcomp_parallel,
"rule": pyzx.rules.lcomp,
"type": MATCHES_VERTICES,
"copy_first": True
"copy_first": True,
"picture": "lcomp.png"
},
"pivot": {
"text": "pivot",
"tooltip": "Deletes a pair of spiders with 0/pi phases by performing a pivot",
"matcher": lambda g, matchf: pyzx.rules.match_pivot_parallel(g, matchf, check_edge_types=True),
"rule": pyzx.rules.pivot,
"type": MATCHES_EDGES,
"copy_first": True
"copy_first": True,
"picture": "pivot_regular.png"
},
"pivot_boundary": {
"text": "boundary pivot",
Expand All @@ -81,15 +86,17 @@ def read_custom_rules() -> list[RewriteData]:
"matcher": pyzx.rules.match_pivot_gadget,
"rule": pyzx.rules.pivot,
"type": MATCHES_EDGES,
"copy_first": True
"copy_first": True,
"picture": "pivot_gadget.png"
},
"phase_gadget_fuse": {
"text": "Fuse phase gadgets",
"tooltip": "Fuses two phase gadgets with the same connectivity.",
"matcher": pyzx.rules.match_phase_gadgets,
"rule": pyzx.rules.merge_phase_gadgets,
"type": MATCHES_VERTICES,
"copy_first": True
"copy_first": True,
"picture": "gadget_fuse.png"
},
"supplementarity": {
"text": "Supplementarity",
Expand Down Expand Up @@ -127,7 +134,7 @@ def ocm_rule(_graph: GraphT, _matches: list) -> pyzx.rules.RewriteOutputType[VT,

ocm_action: RewriteData = {
"text": "OCM",
"tooltip": "Saves the graph with the current vertex positions",
"tooltip": "Only Connectivity Matters. Saves the graph with the current vertex positions",
"matcher": const_true,
"rule": ocm_rule,
"type": MATCHES_VERTICES,
Expand Down Expand Up @@ -271,6 +278,9 @@ def ocm_rule(_graph: GraphT, _matches: list) -> pyzx.rules.RewriteOutputType[VT,
}

rules_basic = {"spider", "to_z", "to_x", "rem_id", "copy", "pauli", "bialgebra", "euler"}
operations["pauli"]["picture"] = "push_pauli.png"
operations["copy"]["picture"] = "copy_pi.png"
operations["bialgebra"]["picture"] = "bialgebra.png"

rules_zxw = {"spider", "fuse_w", "z_to_z_box"}

Expand Down
4 changes: 4 additions & 0 deletions zxlive/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ColorScheme(TypedDict):
"tab-bar-location": QTabWidget.TabPosition.North,
"snap-granularity": '4',
"input-circuit-format": 'openqasm',
"previews-show": True,
'sound-effects': False,
}

Expand Down Expand Up @@ -185,6 +186,7 @@ def _get_synonyms(key: str, default: list[str]) -> list[str]:

class DisplaySettings:
SNAP_DIVISION = 4 # Should be an integer dividing SCALE
PREVIEWS_SHOW = True

def __init__(self) -> None:
self.colors = color_schemes[str(settings.value("color-scheme"))]
Expand All @@ -200,6 +202,8 @@ def update(self) -> None:
get_settings_value("font/size", int)
)
self.SNAP = SCALE / self.SNAP_DIVISION
self.PREVIEWS_SHOW = get_settings_value("previews-show",bool)
self.PREVIEWS_SHOW = True


# Initialise settings
Expand Down
4 changes: 3 additions & 1 deletion zxlive/settings_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ class SettingsData(TypedDict):
'sqasm-no-simplification': "Spider QASM (no simplification)",
}


general_settings: list[SettingsData] = [
{"id": "path/custom-rules", "label": "Custom rules path", "type": FormInputType.Folder},
{"id": "color-scheme", "label": "Color scheme", "type": FormInputType.Combo, "data": color_scheme_data},
{"id": "tab-bar-location", "label": "Tab bar location", "type": FormInputType.Combo, "data": tab_positioning_data},
{"id": "snap-granularity", "label": "Snap-to-grid granularity", "type": FormInputType.Combo, "data": snap_to_grid_data},
{"id": "input-circuit-format", "label": "Input Circuit as", "type": FormInputType.Combo, "data": input_circuit_formats},
{"id": "previews-show", "label": "Show rewrite previews","type": FormInputType.Bool},
{"id": "sound-effects", "label": "Sound Effects", "type": FormInputType.Bool},
]

Expand Down Expand Up @@ -277,6 +277,8 @@ def update_global_settings(self) -> None:
self.settings.setValue(name, widget.value())
elif isinstance(widget, QComboBox):
self.settings.setValue(name, widget.currentData())
elif isinstance(widget, QCheckBox):
self.settings.setValue(name, widget.isChecked())
display_setting.update()

def apply_global_settings(self) -> None:
Expand Down
Binary file added zxlive/tooltips/bialgebra.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added zxlive/tooltips/copy_pi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added zxlive/tooltips/gadget_fuse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added zxlive/tooltips/lcomp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added zxlive/tooltips/pivot_gadget.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added zxlive/tooltips/pivot_regular.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added zxlive/tooltips/push_pauli.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6d99563

Please sign in to comment.