From d4588fd4c513d26f459ab8c633c9ba8a0dc0ba8e Mon Sep 17 00:00:00 2001 From: Boldi Date: Sun, 7 Jul 2024 15:03:35 +0100 Subject: [PATCH 1/9] Refactoring settings --- zxlive/common.py | 175 +------------------ zxlive/settings.py | 180 +++++++++++++++++++ zxlive/settings_dialog.py | 359 ++++++++++++++++++++------------------ zxlive/tikz.py | 7 +- 4 files changed, 372 insertions(+), 349 deletions(-) create mode 100644 zxlive/settings.py diff --git a/zxlive/common.py b/zxlive/common.py index beb0ff43..5bdaadf3 100644 --- a/zxlive/common.py +++ b/zxlive/common.py @@ -1,13 +1,11 @@ import os from enum import IntEnum -from typing import Final, Dict, Any, Optional +from typing import Final, Optional from pyzx import EdgeType from typing_extensions import TypeAlias from PySide6.QtCore import QSettings -from PySide6.QtGui import QColor -from PySide6.QtWidgets import QTabWidget import pyzx @@ -25,6 +23,7 @@ def get_custom_rules_path() -> str: VT: TypeAlias = int ET: TypeAlias = tuple[int, int, EdgeType] GraphT: TypeAlias = pyzx.graph.multigraph.Multigraph + def new_graph() -> GraphT: g = GraphT() g.set_auto_simplify(False) @@ -38,72 +37,6 @@ class ToolType(IntEnum): SCALE: Final = 64.0 -defaults: Dict[str,Any] = { - "path/custom-rules": "lemmas/", - "color-scheme": "modern-red-green", - "tab-bar-location": QTabWidget.TabPosition.North, - "snap-granularity": '4', - "input-circuit-format": 'openqasm', - - "tikz/boundary-export": pyzx.settings.tikz_classes['boundary'], - "tikz/Z-spider-export": pyzx.settings.tikz_classes['Z'], - "tikz/X-spider-export": pyzx.settings.tikz_classes['X'], - "tikz/Z-phase-export": pyzx.settings.tikz_classes['Z phase'], - "tikz/X-phase-export": pyzx.settings.tikz_classes['X phase'], - "tikz/z-box-export": pyzx.settings.tikz_classes['Z box'], - "tikz/Hadamard-export": pyzx.settings.tikz_classes['H'], - "tikz/w-output-export": pyzx.settings.tikz_classes['W'], - "tikz/w-input-export": pyzx.settings.tikz_classes['W input'], - "tikz/edge-export": pyzx.settings.tikz_classes['edge'], - "tikz/edge-H-export": pyzx.settings.tikz_classes['H-edge'], - "tikz/edge-W-export": pyzx.settings.tikz_classes['W-io-edge'], - - "tikz/boundary-import": ", ".join(pyzx.tikz.synonyms_boundary), - "tikz/Z-spider-import": ", ".join(pyzx.tikz.synonyms_z), - "tikz/X-spider-import": ", ".join(pyzx.tikz.synonyms_x), - "tikz/Hadamard-import": ", ".join(pyzx.tikz.synonyms_hadamard), - "tikz/w-input-import": ", ".join(pyzx.tikz.synonyms_w_input), - "tikz/w-output-import": ", ".join(pyzx.tikz.synonyms_w_output), - "tikz/z-box-import": ", ".join(pyzx.tikz.synonyms_z_box), - "tikz/edge-import": ", ".join(pyzx.tikz.synonyms_edge), - "tikz/edge-H-import": ", ".join(pyzx.tikz.synonyms_hedge), - "tikz/edge-W-import": ", ".join(pyzx.tikz.synonyms_wedge), - - "tikz/layout/hspace": 2.0, - "tikz/layout/vspace": 2.0, - "tikz/layout/max-width": 10.0, - - "tikz/names/fuse spiders": "f", - "tikz/names/bialgebra": "b", - "tikz/names/change color to Z": "cc", - "tikz/names/change color to X": "cc", - "tikz/names/remove identity": "id", - "tikz/names/Add Z identity": "id", - "tikz/names/copy 0/pi spider": "cp", - "tikz/names/push Pauli": "pi", - "tikz/names/decompose hadamard": "eu", -} - -color_schemes = { - 'modern-red-green': "Modern Red & Green", - 'classic-red-green': "Classic Red & Green", - 'white-grey': "Dodo book White & Grey", - 'gidney': "Gidney's Black & White", -} - -input_circuit_formats = { - 'openqasm': "standard OpenQASM", - 'sqasm': "Spider QASM", - 'sqasm-no-simplification': "Spider QASM (no simplification)", -} - -# Initialise settings -settings = QSettings("zxlive", "zxlive") -for key, value in defaults.items(): - if not settings.contains(key): - settings.setValue(key, value) - - class Settings(object): SNAP_DIVISION = 4 # Should be an integer dividing SCALE @@ -111,8 +44,7 @@ def __init__(self) -> None: self.update() def update(self) -> None: - settings = QSettings("zxlive", "zxlive") - self.SNAP_DIVISION = int(settings.value("snap-granularity")) + self.SNAP_DIVISION = int(type_safe_settings_value("snap-granularity")) self.SNAP = SCALE / self.SNAP_DIVISION setting = Settings() @@ -144,107 +76,6 @@ def pos_from_view_int(x:float,y: float) -> tuple[int, int]: def view_to_length(width:float,height:float)-> tuple[float, float]: return (width / SCALE, height / SCALE) - -class Colors(object): - z_spider: QColor = QColor("#ccffcc") - z_spider_pressed: QColor = QColor("#64BC90") - x_spider: QColor = QColor("#ff8888") - x_spider_pressed: QColor = QColor("#bb0f0f") - hadamard: QColor = QColor("#ffff00") - hadamard_pressed: QColor = QColor("#f1c232") - boundary: QColor = QColor("#000000") - boundary_pressed: QColor = QColor("#444444") - w_input: QColor = QColor("#000000") - w_input_pressed: QColor = QColor("#444444") - w_output: QColor = QColor("#000000") - w_output_pressed: QColor = QColor("#444444") - outline: QColor = QColor("#000000") - - def __init__(self, color_scheme:str): - self.set_color_scheme(color_scheme) - - def set_color_scheme(self, color_scheme: str) -> None: - if color_scheme == 'modern-red-green': - self.z_spider = QColor("#ccffcc") - self.z_spider_pressed = QColor("#64BC90") - self.x_spider = QColor("#ff8888") - self.x_spider_pressed = QColor("#bb0f0f") - self.hadamard = QColor("#ffff00") - self.hadamard_pressed = QColor("#f1c232") - self.boundary = QColor("#000000") - elif color_scheme == 'classic-red-green': - self.z_spider = QColor("#00ff00") - self.z_spider_pressed = QColor("#00dd00") - self.x_spider = QColor("#ff0d00") - self.x_spider_pressed = QColor("#dd0b00") - self.hadamard = QColor("#ffff00") - self.hadamard_pressed = QColor("#f1c232") - self.boundary = QColor("#000000") - elif color_scheme == 'white-grey': - self.z_spider = QColor("#ffffff") - self.z_spider_pressed = QColor("#eeeeee") - self.x_spider = QColor("#b4b4b4") - self.x_spider_pressed = QColor("#a0a0a0") - self.hadamard = QColor("#ffffff") - self.hadamard_pressed = QColor("#dddddd") - self.boundary = QColor("#000000") - elif color_scheme == 'gidney': - self.z_spider = QColor("#000000") - self.z_spider_pressed = QColor("#222222") - self.x_spider = QColor("#ffffff") - self.x_spider_pressed = QColor("#dddddd") - self.hadamard = QColor("#ffffff") - self.hadamard_pressed = QColor("#dddddd") - self.boundary = QColor("#000000") - else: - raise ValueError(f"Unknown colour scheme {color_scheme}") - - -settings = QSettings("zxlive", "zxlive") -color_scheme = settings.value("color-scheme") -if color_scheme is None: color_scheme = str(defaults["color-scheme"]) -else: color_scheme = str(color_scheme) -colors = Colors(color_scheme) - - -def set_pyzx_tikz_settings() -> None: - tikz_classes = { - 'boundary': str(settings.value('tikz/boundary-export') or pyzx.settings.tikz_classes['boundary']), - 'Z': str(settings.value('tikz/Z-spider-export') or pyzx.settings.tikz_classes['Z']), - 'X': str(settings.value('tikz/X-spider-export') or pyzx.settings.tikz_classes['X']), - 'Z phase': str(settings.value('tikz/Z-phase-export') or pyzx.settings.tikz_classes['Z phase']), - 'X phase': str(settings.value('tikz/X-phase-export') or pyzx.settings.tikz_classes['X phase']), - 'Z box': str(settings.value('tikz/Z-box-export') or pyzx.settings.tikz_classes['Z box']), - 'H': str(settings.value('tikz/Hadamard-export') or pyzx.settings.tikz_classes['H']), - 'W': str(settings.value('tikz/W-output-export') or pyzx.settings.tikz_classes['W']), - 'W input': str(settings.value('tikz/W-input-export') or pyzx.settings.tikz_classes['W input']), - 'edge': str(settings.value('tikz/edge-export') or pyzx.settings.tikz_classes['edge']), - 'H-edge': str(settings.value('tikz/edge-H-export') or pyzx.settings.tikz_classes['H-edge']), - 'W-io-edge': str(settings.value('tikz/edge-W-export') or pyzx.settings.tikz_classes['W-io-edge']), - } - - def _get_synonyms(key: str, default: list[str]) -> list[str]: - val: object = settings.value(key) - if not val: - return default - return [s.strip().lower() for s in str(val).split(',')] - - pyzx.settings.tikz_classes = tikz_classes - pyzx.tikz.synonyms_boundary = _get_synonyms('tikz/boundary-import', pyzx.tikz.synonyms_boundary) - pyzx.tikz.synonyms_z = _get_synonyms('tikz/Z-spider-import', pyzx.tikz.synonyms_z) - pyzx.tikz.synonyms_x = _get_synonyms('tikz/X-spider-import', pyzx.tikz.synonyms_x) - pyzx.tikz.synonyms_hadamard = _get_synonyms('tikz/Hadamard-import', pyzx.tikz.synonyms_hadamard) - pyzx.tikz.synonyms_w_input = _get_synonyms('tikz/W-input-import', pyzx.tikz.synonyms_w_input) - pyzx.tikz.synonyms_w_output = _get_synonyms('tikz/W-output-import', pyzx.tikz.synonyms_w_output) - pyzx.tikz.synonyms_z_box = _get_synonyms('tikz/Z-box-import', pyzx.tikz.synonyms_z_box) - pyzx.tikz.synonyms_edge = _get_synonyms('tikz/edge-import', pyzx.tikz.synonyms_edge) - pyzx.tikz.synonyms_hedge = _get_synonyms('tikz/edge-H-import', pyzx.tikz.synonyms_hedge) - pyzx.tikz.synonyms_wedge = _get_synonyms('tikz/edge-W-import', pyzx.tikz.synonyms_wedge) - - -set_pyzx_tikz_settings() # Call it once on startup - - def to_tikz(g: GraphT) -> str: return pyzx.tikz.to_tikz(g) # type: ignore diff --git a/zxlive/settings.py b/zxlive/settings.py new file mode 100644 index 00000000..b07ceb06 --- /dev/null +++ b/zxlive/settings.py @@ -0,0 +1,180 @@ +from __future__ import annotations + +from typing import Dict, Any, TypedDict + +import pyzx +from PySide6.QtCore import QSettings +from PySide6.QtGui import QColor +from PySide6.QtWidgets import QTabWidget + +general_defaults: Dict[str, Any] = { + "path/custom-rules": "lemmas/", + "color-scheme": "modern-red-green", + "tab-bar-location": QTabWidget.TabPosition.North, + "snap-granularity": 4, + "input-circuit-format": 'openqasm', +} + +tikz_defaults: dict[str, str | float] = { + "tikz/boundary-export": pyzx.settings.tikz_classes['boundary'], + "tikz/Z-spider-export": pyzx.settings.tikz_classes['Z'], + "tikz/X-spider-export": pyzx.settings.tikz_classes['X'], + "tikz/Z-phase-export": pyzx.settings.tikz_classes['Z phase'], + "tikz/X-phase-export": pyzx.settings.tikz_classes['X phase'], + "tikz/z-box-export": pyzx.settings.tikz_classes['Z box'], + "tikz/Hadamard-export": pyzx.settings.tikz_classes['H'], + "tikz/w-output-export": pyzx.settings.tikz_classes['W'], + "tikz/w-input-export": pyzx.settings.tikz_classes['W input'], + "tikz/edge-export": pyzx.settings.tikz_classes['edge'], + "tikz/edge-H-export": pyzx.settings.tikz_classes['H-edge'], + "tikz/edge-W-export": pyzx.settings.tikz_classes['W-io-edge'], + + "tikz/boundary-import": ", ".join(pyzx.tikz.synonyms_boundary), + "tikz/Z-spider-import": ", ".join(pyzx.tikz.synonyms_z), + "tikz/X-spider-import": ", ".join(pyzx.tikz.synonyms_x), + "tikz/Hadamard-import": ", ".join(pyzx.tikz.synonyms_hadamard), + "tikz/w-input-import": ", ".join(pyzx.tikz.synonyms_w_input), + "tikz/w-output-import": ", ".join(pyzx.tikz.synonyms_w_output), + "tikz/z-box-import": ", ".join(pyzx.tikz.synonyms_z_box), + "tikz/edge-import": ", ".join(pyzx.tikz.synonyms_edge), + "tikz/edge-H-import": ", ".join(pyzx.tikz.synonyms_hedge), + "tikz/edge-W-import": ", ".join(pyzx.tikz.synonyms_wedge), + + "tikz/layout/hspace": 2.0, + "tikz/layout/vspace": 2.0, + "tikz/layout/max-width": 10.0, + + "tikz/names/fuse spiders": "f", + "tikz/names/bialgebra": "b", + "tikz/names/change color to Z": "cc", + "tikz/names/change color to X": "cc", + "tikz/names/remove identity": "id", + "tikz/names/Add Z identity": "id", + "tikz/names/copy 0/pi spider": "cp", + "tikz/names/push Pauli": "pi", + "tikz/names/decompose hadamard": "eu", +} + +input_circuit_formats = { + 'openqasm': "standard OpenQASM", + 'sqasm': "Spider QASM", + 'sqasm-no-simplification': "Spider QASM (no simplification)", +} + + +class ColorsScheme(TypedDict): + id: str + name: str + z_spider: QColor + z_spider_pressed: QColor + x_spider: QColor + x_spider_pressed: QColor + hadamard: QColor + hadamard_pressed: QColor + boundary: QColor + boundary_pressed: QColor + w_input: QColor + w_input_pressed: QColor + w_output: QColor + w_output_pressed: QColor + outline: QColor + + +moder_red_green: ColorsScheme = { + "id": 'modern-red-green', + "name": "Modern Red & Green", + "z_spider": "#ccffcc", + "z_spider_pressed": "#64BC90", + "x_spider": "#ff8888", + "x_spider_pressed": "#bb0f0f", + "hadamard": "#ffff00", + "hadamard_pressed": "#f1c232", + "boundary": "#000000", + "boundary_pressed": "#444444", + "w_input": "#000000", + "w_input_pressed": "#444444", + "w_output": "#000000", + "w_output_pressed": "#444444", + "outline": "#000000", +} + +classic_red_green: ColorsScheme = moder_red_green | { + "id": "classic-red-green", + "name": "Classic Red & Green", + "z_spider": "#00ff00", + "z_spider_pressed": "#00dd00", + "x_spider": "#ff0d00", + "x_spider_pressed": "#dd0b00", +} + +white_gray: ColorsScheme = moder_red_green | { + "id": 'white-grey', + "name": "Dodo book White & Grey", + "z_spider": "#ffffff", + "z_spider_pressed": "#eeeeee", + "x_spider": "#b4b4b4", + "x_spider_pressed": "#a0a0a0", + "hadamard": "#ffffff", + "hadamard_pressed": "#dddddd", +} + +gidney: ColorsScheme = white_gray | { + "id": 'gidney', + "name": "Gidney's Black & White", + "z_spider": "#000000", + "z_spider_pressed": "#222222", + "x_spider": "#ffffff", + "x_spider_pressed": "#dddddd", +} + +color_schemes = { + scheme["id"]: scheme for scheme in [moder_red_green, classic_red_green, white_gray, gidney] +} + + +def load_tikz_classes(): + return { + 'boundary': str(settings.value('tikz/boundary-export', pyzx.settings.tikz_classes['boundary'])), + 'Z': str(settings.value('tikz/Z-spider-export', pyzx.settings.tikz_classes['Z'])), + 'X': str(settings.value('tikz/X-spider-export', pyzx.settings.tikz_classes['X'])), + 'Z phase': str(settings.value('tikz/Z-phase-export', pyzx.settings.tikz_classes['Z phase'])), + 'X phase': str(settings.value('tikz/X-phase-export', pyzx.settings.tikz_classes['X phase'])), + 'Z box': str(settings.value('tikz/Z-box-export', pyzx.settings.tikz_classes['Z box'])), + 'H': str(settings.value('tikz/Hadamard-export', pyzx.settings.tikz_classes['H'])), + 'W': str(settings.value('tikz/W-output-export', pyzx.settings.tikz_classes['W'])), + 'W input': str(settings.value('tikz/W-input-export', pyzx.settings.tikz_classes['W input'])), + 'edge': str(settings.value('tikz/edge-export', pyzx.settings.tikz_classes['edge'])), + 'H-edge': str(settings.value('tikz/edge-H-export', pyzx.settings.tikz_classes['H-edge'])), + 'W-io-edge': str(settings.value('tikz/edge-W-export', pyzx.settings.tikz_classes['W-io-edge'])), + } + + +def refresh_pyzx_tikz_settings() -> None: + def _get_synonyms(key: str, default: list[str]) -> list[str]: + val: object = settings.value(key) + if not val: + return default + return [s.strip().lower() for s in str(val).split(',')] + + pyzx.settings.tikz_classes = load_tikz_classes() + pyzx.tikz.synonyms_boundary = _get_synonyms('tikz/boundary-import', pyzx.tikz.synonyms_boundary) + pyzx.tikz.synonyms_z = _get_synonyms('tikz/Z-spider-import', pyzx.tikz.synonyms_z) + pyzx.tikz.synonyms_x = _get_synonyms('tikz/X-spider-import', pyzx.tikz.synonyms_x) + pyzx.tikz.synonyms_hadamard = _get_synonyms('tikz/Hadamard-import', pyzx.tikz.synonyms_hadamard) + pyzx.tikz.synonyms_w_input = _get_synonyms('tikz/W-input-import', pyzx.tikz.synonyms_w_input) + pyzx.tikz.synonyms_w_output = _get_synonyms('tikz/W-output-import', pyzx.tikz.synonyms_w_output) + pyzx.tikz.synonyms_z_box = _get_synonyms('tikz/Z-box-import', pyzx.tikz.synonyms_z_box) + pyzx.tikz.synonyms_edge = _get_synonyms('tikz/edge-import', pyzx.tikz.synonyms_edge) + pyzx.tikz.synonyms_hedge = _get_synonyms('tikz/edge-H-import', pyzx.tikz.synonyms_hedge) + pyzx.tikz.synonyms_wedge = _get_synonyms('tikz/edge-W-import', pyzx.tikz.synonyms_wedge) + + +# Initialise settings +settings = QSettings("zxlive", "zxlive") +for key, value in (general_defaults | tikz_defaults).items(): + if not settings.contains(key): + settings.setValue(key, value) + +colors = str(settings.value("color-scheme")) + +refresh_pyzx_tikz_settings() # Call it once on startup diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index 76d62357..41316e34 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -15,167 +15,150 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Dict, Any, Optional, Union +from enum import IntEnum +from typing import TYPE_CHECKING, Dict, Any, TypeVar, Type +from typing_extensions import TypedDict, NotRequired from PySide6.QtCore import QSettings -from PySide6.QtWidgets import (QDialog, QFileDialog, - QFormLayout, QLineEdit, - QPushButton, QWidget, - QVBoxLayout, QSpinBox, QDoubleSpinBox, - QLabel, QHBoxLayout, QTabWidget, - QComboBox) +from PySide6.QtWidgets import ( + QDialog, QFileDialog, QFormLayout, QLineEdit, QPushButton, QWidget, + QVBoxLayout, QSpinBox, QDoubleSpinBox, QLabel, QHBoxLayout, QTabWidget, + QComboBox +) -import pyzx - -from .common import set_pyzx_tikz_settings, colors, setting, color_schemes, input_circuit_formats, defaults +from .settings import (set_pyzx_tikz_settings, colors, setting, color_schemes, + input_circuit_formats, defaults) if TYPE_CHECKING: from .mainwindow import MainWindow +T = TypeVar('T') + + +class FormInputType(IntEnum): + Str = 0 + Int = 1 + Float = 2 + Folder = 3 + Combo = 4 + + +class SettingsData(TypedDict): + id: str + label: str + type: FormInputType + data: NotRequired[dict[Any, str]] + + +tab_positioning_data = { + QTabWidget.TabPosition.North: "Top", + QTabWidget.TabPosition.South: "Bottom", + QTabWidget.TabPosition.West: "Left", + QTabWidget.TabPosition.East: "Right" +} + +snap_to_grpid_data = {'2': "2", '4': "4", '8': "8", '16': "16"} + +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_schemes}, + {"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_grpid_data}, + {"id": "input-circuit-format", "label": "Input Circuit as", "type": FormInputType.Combo, "data": input_circuit_formats}, +] + +tikz_export_settings: list[SettingsData] = [ + {"id": "tikz/Z-spider-export", "label": "Z-spider", "type": FormInputType.Str}, + {"id": "tikz/Z-phase-export", "label": "Z-spider with phase", "type": FormInputType.Str}, + {"id": "tikz/X-spider-export", "label": "X-spider", "type": FormInputType.Str}, + {"id": "tikz/X-phase-export", "label": "X-spider with phase", "type": FormInputType.Str}, + {"id": "tikz/Hadamard-export", "label": "Hadamard", "type": FormInputType.Str}, + {"id": "tikz/w-input-export", "label": "W input", "type": FormInputType.Str}, + {"id": "tikz/w-output-export", "label": "W output", "type": FormInputType.Str}, + {"id": "tikz/z-box-export", "label": "Z box", "type": FormInputType.Str}, + {"id": "tikz/edge-W-export", "label": "W io edge", "type": FormInputType.Str}, +] + +tikz_import_settings: list[SettingsData] = [ + {"id": "tikz/Z-spider-import", "label": "Z-spider", "type": FormInputType.Str}, + {"id": "tikz/w-input-import", "label": "W input", "type": FormInputType.Str}, + {"id": "tikz/w-output-import", "label": "W output", "type": FormInputType.Str}, + {"id": "tikz/z-box-import", "label": "Z box", "type": FormInputType.Str}, + {"id": "tikz/edge-W-import", "label": "W io edge", "type": FormInputType.Str}, +] + +tikz_layout_settings: list[SettingsData] = [ + {"id": "tikz/layout/hspace", "label": "Horizontal spacing", "type": FormInputType.Float}, + {"id": "tikz/layout/vspace", "label": "Vertical spacing", "type": FormInputType.Float}, + {"id": "tikz/layout/max-width", "label": "Maximum width", "type": FormInputType.Float}, +] + +tikz_rule_name_settings: list[SettingsData] = [ + {"id": "tikz/names/fuse spiders", "label": "fuse spiders", "type": FormInputType.Str}, + {"id": "tikz/names/bialgebra", "label": "bialgebra", "type": FormInputType.Str}, + {"id": "tikz/names/change color to Z", "label": "change color to Z", "type": FormInputType.Str}, + {"id": "tikz/names/change color to X", "label": "change color to X", "type": FormInputType.Str}, + {"id": "tikz/names/remove identity", "label": "remove identity", "type": FormInputType.Str}, + {"id": "tikz/names/Add Z identity", "label": "add Z identity", "type": FormInputType.Str}, + {"id": "tikz/names/copy 0/pi spider", "label": "copy 0/pi spider", "type": FormInputType.Str}, + {"id": "tikz/names/push Pauli", "label": "push Pauli", "type": FormInputType.Str}, + {"id": "tikz/names/decompose hadamard", "label": "decompose hadamard", "type": FormInputType.Str}, +] + + class SettingsDialog(QDialog): def __init__(self, main_window: MainWindow) -> None: super().__init__(main_window) self.main_window = main_window self.setWindowTitle("Settings") + self.settings = QSettings("zxlive", "zxlive") - self.value_dict: Dict[str,QWidget] = {} + self.value_dict: Dict[str, QWidget] = {} + self.prev_color_scheme = self.get_settings_value("color-scheme", str) + self.prev_tab_bar_location = self.get_settings_value("tab-bar-location", QTabWidget.TabPosition) layout = QVBoxLayout() - self.setLayout(layout) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) + self.setLayout(layout) tab_widget = QTabWidget() layout.addWidget(tab_widget) - ##### General settings ##### - panel_base_settings = QWidget() - vlayout = QVBoxLayout() - panel_base_settings.setLayout(vlayout) - tab_widget.addTab(panel_base_settings, "General") - vlayout.addWidget(QLabel("General ZXLive settings")) - form_general = QFormLayout() - w = QWidget() - w.setLayout(form_general) - vlayout.addWidget(w) - self.add_setting(form_general, "path/custom-rules", "Custom rules path", 'folder') - self.add_setting(form_general, "color-scheme", "Color scheme", 'combo',data=color_schemes) - self.add_setting(form_general, "tab-bar-location", "Tab bar location", 'combo', data={ - QTabWidget.TabPosition.North: "Top", QTabWidget.TabPosition.South: "Bottom", - QTabWidget.TabPosition.West: "Left", QTabWidget.TabPosition.East: "Right"}) - self.add_setting(form_general, "snap-granularity", "Snap-to-grid granularity", 'combo', - data = {'2': "2", '4': "4", '8': "8", '16': "16"}) - self.add_setting(form_general, "input-circuit-format", "Input Circuit as", 'combo', data=input_circuit_formats) - self.prev_color_scheme = self.settings.value("color-scheme") - self.prev_tab_bar_location = self.settings.value("tab-bar-location") - vlayout.addStretch() - - ##### Tikz Export settings ##### - panel_tikz_export = QWidget() - vlayout = QVBoxLayout() - panel_tikz_export.setLayout(vlayout) - tab_widget.addTab(panel_tikz_export, "Tikz export") - - vlayout.addWidget(QLabel("Tikz export settings")) - vlayout.addWidget(QLabel("These are the class names that will be used when exporting to tikz.")) - - form_export = QFormLayout() - w = QWidget() - w.setLayout(form_export) - vlayout.addWidget(w) - vlayout.addStretch() - - self.add_setting(form_export, "tikz/Z-spider-export", "Z-spider", 'str') - self.add_setting(form_export, "tikz/Z-phase-export", "Z-spider with phase", 'str') - self.add_setting(form_export, "tikz/X-spider-export", "X-spider", 'str') - self.add_setting(form_export, "tikz/X-phase-export", "X-spider with phase", 'str') - self.add_setting(form_export, "tikz/Hadamard-export", "Hadamard", 'str') - self.add_setting(form_export, "tikz/boundary-export", "Boundary", 'str') - self.add_setting(form_export, "tikz/edge-export", "Regular Edge", 'str') - self.add_setting(form_export, "tikz/edge-H-export", "Hadamard edge", 'str') - - self.add_setting(form_export, "tikz/w-input-export", "W input", 'str') - self.add_setting(form_export, "tikz/w-output-export", "W output", 'str') - self.add_setting(form_export, "tikz/z-box-export", "Z box", 'str') - self.add_setting(form_export, "tikz/edge-W-export", "W io edge", 'str') - - - ##### Tikz Import settings ##### - panel_tikz_import = QWidget() - vlayout = QVBoxLayout() - panel_tikz_import.setLayout(vlayout) - tab_widget.addTab(panel_tikz_import, "Tikz import") - - vlayout.addWidget(QLabel("Tikz import settings")) - vlayout.addWidget(QLabel("These are the class names that are understood when importing from tikz.")) - - form_import = QFormLayout() - w = QWidget() - w.setLayout(form_import) - vlayout.addWidget(w) - vlayout.addStretch() + self.add_settings_tab(tab_widget, "General", "Tikz rule name settings", general_settings) - self.add_setting(form_import, "tikz/Z-spider-import", "Z-spider", 'str') - self.add_setting(form_import, "tikz/X-spider-import", "X-spider", 'str') - self.add_setting(form_import, "tikz/Hadamard-import", "Hadamard", 'str') - self.add_setting(form_import, "tikz/boundary-import", "Boundary", 'str') - self.add_setting(form_import, "tikz/edge-import", "Regular Edge", 'str') - self.add_setting(form_import, "tikz/edge-H-import", "Hadamard edge", 'str') + self.add_settings_tab(tab_widget, "Tikz rule names", "General ZXLive settings", tikz_rule_name_settings) - self.add_setting(form_import, "tikz/w-input-import", "W input", 'str') - self.add_setting(form_import, "tikz/w-output-import", "W output", 'str') - self.add_setting(form_import, "tikz/z-box-import", "Z box", 'str') - self.add_setting(form_import, "tikz/edge-W-import", "W io edge", 'str') + self.add_settings_tab(tab_widget, "Tikz export", "These are the class names that will be used when exporting to tikz.", tikz_export_settings) - ##### Tikz Layout settings ##### - panel_tikz_layout = QWidget() - vlayout = QVBoxLayout() - panel_tikz_layout.setLayout(vlayout) - tab_widget.addTab(panel_tikz_layout, "Tikz layout") - - vlayout.addWidget(QLabel("Tikz layout settings")) + self.add_settings_tab(tab_widget, "Tikz import", "These are the class names that are understood when importing from tikz.", tikz_import_settings) - form_layout = QFormLayout() - w = QWidget() - w.setLayout(form_layout) - vlayout.addWidget(w) - vlayout.addStretch() + self.add_settings_tab(tab_widget, "Tikz layout", "Tikz layout settings", tikz_layout_settings) - self.add_setting(form_layout, "tikz/layout/hspace", "Horizontal spacing", "float") - self.add_setting(form_layout, "tikz/layout/vspace", "Vertical spacing", "float") - self.add_setting(form_layout, "tikz/layout/max-width", "Maximum width", 'float') + self.init_okay_cancle_buttons(layout) + def get_settings_value(self, arg: str, _type: Type[T], default: T | None = None) -> T: + if not isinstance(val := self.settings.value(arg, default), _type) and default is None: + raise ValueError(f"Unexpected _type for {val}: expected {_type}, got {type(val)}") + return val - ##### Tikz rule name settings ##### + def add_settings_tab(self, tab_widget: QTabWidget, tab_name: str, label: str, data: list[SettingsData]) -> None: panel_tikz_names = QWidget() vlayout = QVBoxLayout() panel_tikz_names.setLayout(vlayout) - tab_widget.addTab(panel_tikz_names, "Tikz rule names") - - vlayout.addWidget(QLabel("Tikz rule name settings")) - vlayout.addWidget(QLabel("Mapping of pyzx rule names to tikz display strings")) - + tab_widget.addTab(panel_tikz_names, tab_name) + vlayout.addWidget(QLabel(label)) form_names = QFormLayout() w = QWidget() w.setLayout(form_names) vlayout.addWidget(w) vlayout.addStretch() + for d in data: + self.add_setting_to_form(form_names, d) - self.add_setting(form_names, "tikz/names/fuse spiders", "fuse spiders", "str") - self.add_setting(form_names, "tikz/names/bialgebra", "bialgebra", "str") - self.add_setting(form_names, "tikz/names/change color to Z", "change color to Z", "str") - self.add_setting(form_names, "tikz/names/change color to X", "change color to X", "str") - self.add_setting(form_names, "tikz/names/remove identity", "remove identity", "str") - self.add_setting(form_names, "tikz/names/Add Z identity", "add Z identity", "str") - self.add_setting(form_names, "tikz/names/copy 0/pi spider", "copy 0/pi spider", "str") - self.add_setting(form_names, "tikz/names/push Pauli", "push Pauli", "str") - self.add_setting(form_names, "tikz/names/decompose hadamard", "decompose hadamard", "str") - - - - ##### Okay/Cancel Buttons ##### - w= QWidget() + def init_okay_cancle_buttons(self, layout: QVBoxLayout) -> None: + w = QWidget() hlayout = QHBoxLayout() hlayout.addStretch() w.setLayout(hlayout) @@ -187,49 +170,80 @@ def __init__(self, main_window: MainWindow) -> None: cancel_button.clicked.connect(self.cancel) hlayout.addWidget(cancel_button) - - def add_setting(self,form:QFormLayout, name:str, label:str, ty:str, data: Optional[dict[Any, str]] = None) -> None: - val = self.settings.value(name) - widget: Union[QWidget, QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox] - if val is None: val = defaults[name] - if ty == 'str': - widget = QLineEdit() - val = str(val) - widget.setText(val) - elif ty == 'int': - widget = QSpinBox() - widget.setValue(int(val)) # type: ignore - elif ty == 'float': - widget = QDoubleSpinBox() - widget.setValue(float(val)) # type: ignore - elif ty == 'folder': - widget = QWidget() - hlayout = QHBoxLayout() - widget.setLayout(hlayout) - widget_line = QLineEdit() - widget_line.setText(str(val)) - def browse() -> None: - directory = QFileDialog.getExistingDirectory(self,"Pick folder",options=QFileDialog.Option.ShowDirsOnly) - if directory: - widget_line.setText(directory) - setattr(widget, "text_value", directory) - hlayout.addWidget(widget_line) - button = QPushButton("Browse") - button.clicked.connect(browse) - hlayout.addWidget(button) - elif ty == 'combo': - widget = QComboBox() - assert data is not None - widget.addItems(list(data.values())) - widget.setCurrentText(data[val]) - setattr(widget, "data", data) - - - form.addRow(label, widget) - self.value_dict[name] = widget + def make_str_form_input(self, data: SettingsData) -> QLineEdit: + name = data["id"] + value = self.get_settings_value(name, str, defaults[name]) + widget = QLineEdit() + widget.setText(value) + return widget + + def make_int_form_input(self, data: SettingsData) -> QSpinBox: + name = data["id"] + value = self.get_settings_value(name, int, defaults[name]) + widget = QSpinBox() + widget.setValue(value) + return widget + + def make_float_form_input(self, data: SettingsData) -> QDoubleSpinBox: + name = data["id"] + value = self.get_settings_value(name, float, defaults[name]) + widget = QDoubleSpinBox() + widget.setValue(value) + return widget + + def make_folder_form_input(self, data: SettingsData) -> QWidget: + name = data["id"] + value = self.get_settings_value(name, str, defaults[name]) + widget = QWidget() + hlayout = QHBoxLayout() + widget.setLayout(hlayout) + widget_line = QLineEdit() + widget_line.setText(value) + + def browse() -> None: + directory = QFileDialog.getExistingDirectory( + self,"Pick folder", options=QFileDialog.Option.ShowDirsOnly + ) + if directory: + widget_line.setText(directory) + setattr(widget, "text_value", directory) + + hlayout.addWidget(widget_line) + button = QPushButton("Browse") + button.clicked.connect(browse) + hlayout.addWidget(button) + return widget + + def make_combo_form_input(self, data: SettingsData) -> QComboBox: + name, _data = data["id"], data["data"] + value = self.get_settings_value(name, str, defaults[name]) + widget = QComboBox() + widget.addItems(list(_data.values())) + widget.setCurrentText(_data[value]) + setattr(widget, "data", _data) + return widget + + def add_setting_to_form(self, form: QFormLayout, settings_data: SettingsData) -> None: + setting_maker = { + FormInputType.Str: self.make_str_form_input, + FormInputType.Int: self.make_int_form_input, + FormInputType.Float: self.make_float_form_input, + FormInputType.Folder: self.make_folder_form_input, + FormInputType.Combo: self.make_combo_form_input, + } + setting_widget_maker = setting_maker[settings_data["type"]] + widget: QWidget = setting_widget_maker(settings_data) + + form.addRow(settings_data["label"], widget) + self.value_dict[settings_data["id"]] = widget def okay(self) -> None: - for name,widget in self.value_dict.items(): + self.update_global_settings() + self.apply_global_settings() + self.accept() + + def update_global_settings(self) -> None: + for name, widget in self.value_dict.items(): if isinstance(widget, QLineEdit): self.settings.setValue(name, widget.text()) elif isinstance(widget, QSpinBox): @@ -243,25 +257,22 @@ def okay(self) -> None: self.settings.setValue(name, val) elif isinstance(widget, QWidget) and hasattr(widget, "text_value"): self.settings.setValue(name, widget.text_value) - set_pyzx_tikz_settings() setting.update() - if self.settings.value("color-scheme") != self.prev_color_scheme: - theme = self.settings.value("color-scheme") - assert isinstance(theme, str) + + def apply_global_settings(self) -> None: + set_pyzx_tikz_settings() + theme = self.get_settings_value("color-scheme", str) + if theme != self.prev_color_scheme: colors.set_color_scheme(theme) self.main_window.update_colors() - if self.settings.value("tab-bar-location") != self.prev_tab_bar_location: - pos = self.settings.value("tab-bar-location") - assert isinstance(pos, QTabWidget.TabPosition) + pos = self.get_settings_value("tab-bar-location", QTabWidget.TabPosition) + if pos != self.prev_tab_bar_location: self.main_window.tab_widget.setTabPosition(pos) - self.accept() def cancel(self) -> None: self.reject() - - def open_settings_dialog(parent: MainWindow) -> None: dialog = SettingsDialog(parent) dialog.exec() diff --git a/zxlive/tikz.py b/zxlive/tikz.py index c9b3e40e..22e6bad7 100644 --- a/zxlive/tikz.py +++ b/zxlive/tikz.py @@ -1,14 +1,15 @@ from PySide6.QtCore import QSettings from pyzx.tikz import TIKZ_BASE, _to_tikz +from .common import type_safe_settings_value from zxlive.proof import ProofModel def proof_to_tikz(proof: ProofModel) -> str: settings = QSettings("zxlive", "zxlive") - vspace = float(settings.value("tikz/layout/vspace")) - hspace = float(settings.value("tikz/layout/hspace")) - max_width = float(settings.value("tikz/layout/max-width")) + vspace = type_safe_settings_value("tikz/layout/vspace", float) + hspace = type_safe_settings_value("tikz/layout/hspace", float) + max_width = type_safe_settings_value("tikz/layout/max-width", float) draw_scalar = False xoffset = -max_width From 10c265bd1a4032ce43213aa79097c9316ad10c9c Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 11:55:24 +0100 Subject: [PATCH 2/9] Further refactoring --- zxlive/commands.py | 11 +-- zxlive/common.py | 29 ++++---- zxlive/edit_panel.py | 3 +- zxlive/editor_base_panel.py | 15 ++-- zxlive/proof.py | 5 +- zxlive/settings.py | 142 ++++++++++++++++++++++-------------- zxlive/settings_dialog.py | 42 ++++++----- zxlive/tikz.py | 8 +- zxlive/vitem.py | 37 +++++----- 9 files changed, 167 insertions(+), 125 deletions(-) diff --git a/zxlive/commands.py b/zxlive/commands.py index 3c69590a..7ca9dc59 100644 --- a/zxlive/commands.py +++ b/zxlive/commands.py @@ -14,7 +14,8 @@ from pyzx.symbolic import Poly from pyzx.utils import EdgeType, VertexType, get_w_partner, vertex_is_w, get_w_io, get_z_box_label, set_z_box_label -from .common import ET, VT, W_INPUT_OFFSET, GraphT, setting +from .common import ET, VT, W_INPUT_OFFSET, GraphT +from settings import display_setting from .eitem import EItem from .graphview import GraphView from .proof import ProofModel, ProofStepView, Rewrite @@ -202,8 +203,8 @@ def undo(self) -> None: self.update_graph_view() def redo(self) -> None: - y = round(self.y * setting.SNAP_DIVISION) / setting.SNAP_DIVISION - x = round(self.x * setting.SNAP_DIVISION) / setting.SNAP_DIVISION + y = round(self.y * display_setting.SNAP_DIVISION) / display_setting.SNAP_DIVISION + x = round(self.x * display_setting.SNAP_DIVISION) / display_setting.SNAP_DIVISION self._added_vert = self.g.add_vertex(self.vty, y,x) self.update_graph_view() @@ -224,8 +225,8 @@ def undo(self) -> None: self.update_graph_view() def redo(self) -> None: - y = round(self.y * setting.SNAP_DIVISION) / setting.SNAP_DIVISION - x = round(self.x * setting.SNAP_DIVISION) / setting.SNAP_DIVISION + y = round(self.y * display_setting.SNAP_DIVISION) / display_setting.SNAP_DIVISION + x = round(self.x * display_setting.SNAP_DIVISION) / display_setting.SNAP_DIVISION self._added_input_vert = self.g.add_vertex(VertexType.W_INPUT, y - W_INPUT_OFFSET, self.x) self._added_output_vert = self.g.add_vertex(VertexType.W_OUTPUT, y, x) self.g.add_edge((self._added_input_vert, self._added_output_vert), EdgeType.W_IO) diff --git a/zxlive/common.py b/zxlive/common.py index 5bdaadf3..2dc8fedf 100644 --- a/zxlive/common.py +++ b/zxlive/common.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import os from enum import IntEnum -from typing import Final, Optional +from typing import Final, Optional, TypeVar, Type from pyzx import EdgeType from typing_extensions import TypeAlias @@ -12,9 +14,21 @@ _ROOT = os.path.abspath(os.path.dirname(__file__)) +T = TypeVar('T') + + def get_data(path: str) -> str: return os.path.join(os.environ.get("_MEIPASS", _ROOT), path) + +def get_settings_value(arg: str, _type: Type[T], default: T | None = None, settings: QSettings | None = None) -> T: + _settings = settings or QSettings("zxlive", "zxlive") + if not isinstance(val := _settings.value(arg, default), _type): + if default is not None: + return default + raise ValueError(f"Unexpected type for {val}: expected {_type}, got {type(val)}") + return val + def get_custom_rules_path() -> str: settings = QSettings("zxlive", "zxlive") return str(settings.value('path/custom-rules')) @@ -36,19 +50,6 @@ class ToolType(IntEnum): SCALE: Final = 64.0 - -class Settings(object): - SNAP_DIVISION = 4 # Should be an integer dividing SCALE - - def __init__(self) -> None: - self.update() - - def update(self) -> None: - self.SNAP_DIVISION = int(type_safe_settings_value("snap-granularity")) - self.SNAP = SCALE / self.SNAP_DIVISION - -setting = Settings() - # Offsets should be a multiple of SCALE for grid snapping to work properly OFFSET_X: Final = 300 * SCALE OFFSET_Y: Final = 300 * SCALE diff --git a/zxlive/edit_panel.py b/zxlive/edit_panel.py index d0e24807..3a711478 100644 --- a/zxlive/edit_panel.py +++ b/zxlive/edit_panel.py @@ -12,11 +12,12 @@ from .base_panel import ToolbarSection from .commands import UpdateGraph -from .common import GraphT, input_circuit_formats +from .common import GraphT from .dialogs import show_error_msg, create_circuit_dialog from .editor_base_panel import EditorBasePanel from .graphscene import EditGraphScene from .graphview import GraphView +from .settings import input_circuit_formats class GraphEditPanel(EditorBasePanel): diff --git a/zxlive/editor_base_panel.py b/zxlive/editor_base_panel.py index ad06122f..ff7ffe6f 100644 --- a/zxlive/editor_base_panel.py +++ b/zxlive/editor_base_panel.py @@ -19,10 +19,11 @@ from .commands import (AddEdge, AddNode, AddWNode, ChangeEdgeColor, ChangeNodeType, ChangePhase, MoveNode, SetGraph, UpdateGraph) -from .common import VT, GraphT, ToolType, get_data, colors +from .common import VT, GraphT, ToolType, get_data from .dialogs import show_error_msg from .eitem import HAD_EDGE_BLUE from .graphscene import EditGraphScene +from .settings import display_setting from .vitem import BLACK @@ -40,12 +41,12 @@ class DrawPanelNodeType(TypedDict): def vertices_data() -> dict[VertexType, DrawPanelNodeType]: return { - VertexType.Z: {"text": "Z spider", "icon": (ShapeType.CIRCLE, colors.z_spider)}, - VertexType.X: {"text": "X spider", "icon": (ShapeType.CIRCLE, colors.x_spider)}, - VertexType.H_BOX: {"text": "H box", "icon": (ShapeType.SQUARE, colors.hadamard)}, - VertexType.Z_BOX: {"text": "Z box", "icon": (ShapeType.SQUARE, colors.z_spider)}, - VertexType.W_OUTPUT: {"text": "W node", "icon": (ShapeType.TRIANGLE, colors.w_output)}, - VertexType.BOUNDARY: {"text": "boundary", "icon": (ShapeType.CIRCLE, colors.w_input)}, + VertexType.Z: {"text": "Z spider", "icon": (ShapeType.CIRCLE, display_setting.colors["z_spider"])}, + VertexType.X: {"text": "X spider", "icon": (ShapeType.CIRCLE, display_setting.colors["x_spider"])}, + VertexType.H_BOX: {"text": "H box", "icon": (ShapeType.SQUARE, display_setting.colors["hadamard"])}, + VertexType.Z_BOX: {"text": "Z box", "icon": (ShapeType.SQUARE, display_setting.colors["z_spider"])}, + VertexType.W_OUTPUT: {"text": "W node", "icon": (ShapeType.TRIANGLE, display_setting.colors["w_output"])}, + VertexType.BOUNDARY: {"text": "boundary", "icon": (ShapeType.CIRCLE, display_setting.colors["w_input"])}, } def edges_data() -> dict[EdgeType, DrawPanelNodeType]: diff --git a/zxlive/proof.py b/zxlive/proof.py index fc57cb9f..64db6caf 100644 --- a/zxlive/proof.py +++ b/zxlive/proof.py @@ -12,7 +12,8 @@ QStyle, QStyledItemDelegate, QStyleOptionViewItem, QWidget) -from .common import GraphT, colors +from .common import GraphT +from .settings import display_setting class Rewrite(NamedTuple): @@ -354,7 +355,7 @@ def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: Union[QM # Draw circle painter.setPen(QPen(Qt.GlobalColor.black, self.circle_outline_width)) - painter.setBrush(colors.z_spider) + painter.setBrush(display_setting.colors["z_spider"]) circle_radius = self.circle_radius_selected if option.state & QStyle.StateFlag.State_Selected else self.circle_radius painter.drawEllipse( QPointF(self.line_padding + self.line_width / 2, option.rect.y() + option.rect.height() / 2), diff --git a/zxlive/settings.py b/zxlive/settings.py index b07ceb06..1da3977f 100644 --- a/zxlive/settings.py +++ b/zxlive/settings.py @@ -7,15 +7,36 @@ from PySide6.QtGui import QColor from PySide6.QtWidgets import QTabWidget -general_defaults: Dict[str, Any] = { +from common import get_settings_value, SCALE + + +class ColorsScheme(TypedDict): + id: str + label: str + z_spider: QColor + z_spider_pressed: QColor + x_spider: QColor + x_spider_pressed: QColor + hadamard: QColor + hadamard_pressed: QColor + boundary: QColor + boundary_pressed: QColor + w_input: QColor + w_input_pressed: QColor + w_output: QColor + w_output_pressed: QColor + outline: QColor + + +general_defaults: Dict[str, str | QTabWidget.TabPosition | int] = { "path/custom-rules": "lemmas/", "color-scheme": "modern-red-green", "tab-bar-location": QTabWidget.TabPosition.North, - "snap-granularity": 4, + "snap-granularity": '4', "input-circuit-format": 'openqasm', } -tikz_defaults: dict[str, str | float] = { +tikz_export_defaults: dict[str, str] = { "tikz/boundary-export": pyzx.settings.tikz_classes['boundary'], "tikz/Z-spider-export": pyzx.settings.tikz_classes['Z'], "tikz/X-spider-export": pyzx.settings.tikz_classes['X'], @@ -28,7 +49,9 @@ "tikz/edge-export": pyzx.settings.tikz_classes['edge'], "tikz/edge-H-export": pyzx.settings.tikz_classes['H-edge'], "tikz/edge-W-export": pyzx.settings.tikz_classes['W-io-edge'], +} +tikz_import_defaults: dict[str, str] = { "tikz/boundary-import": ", ".join(pyzx.tikz.synonyms_boundary), "tikz/Z-spider-import": ", ".join(pyzx.tikz.synonyms_z), "tikz/X-spider-import": ", ".join(pyzx.tikz.synonyms_x), @@ -39,11 +62,15 @@ "tikz/edge-import": ", ".join(pyzx.tikz.synonyms_edge), "tikz/edge-H-import": ", ".join(pyzx.tikz.synonyms_hedge), "tikz/edge-W-import": ", ".join(pyzx.tikz.synonyms_wedge), +} +tikz_layout_defaults: dict[str, float] = { "tikz/layout/hspace": 2.0, "tikz/layout/vspace": 2.0, "tikz/layout/max-width": 10.0, +} +tikz_names_defaults: dict[str, str] = { "tikz/names/fuse spiders": "f", "tikz/names/bialgebra": "b", "tikz/names/change color to Z": "cc", @@ -55,6 +82,9 @@ "tikz/names/decompose hadamard": "eu", } +defaults = general_defaults | tikz_export_defaults | tikz_import_defaults \ + | tikz_layout_defaults | tikz_names_defaults + input_circuit_formats = { 'openqasm': "standard OpenQASM", 'sqasm': "Spider QASM", @@ -62,77 +92,61 @@ } -class ColorsScheme(TypedDict): - id: str - name: str - z_spider: QColor - z_spider_pressed: QColor - x_spider: QColor - x_spider_pressed: QColor - hadamard: QColor - hadamard_pressed: QColor - boundary: QColor - boundary_pressed: QColor - w_input: QColor - w_input_pressed: QColor - w_output: QColor - w_output_pressed: QColor - outline: QColor - - moder_red_green: ColorsScheme = { "id": 'modern-red-green', - "name": "Modern Red & Green", - "z_spider": "#ccffcc", - "z_spider_pressed": "#64BC90", - "x_spider": "#ff8888", - "x_spider_pressed": "#bb0f0f", - "hadamard": "#ffff00", - "hadamard_pressed": "#f1c232", - "boundary": "#000000", - "boundary_pressed": "#444444", - "w_input": "#000000", - "w_input_pressed": "#444444", - "w_output": "#000000", - "w_output_pressed": "#444444", - "outline": "#000000", + "label": "Modern Red & Green", + "z_spider": QColor("#ccffcc"), + "z_spider_pressed": QColor("#64BC90"), + "x_spider": QColor("#ff8888"), + "x_spider_pressed": QColor("#bb0f0f"), + "hadamard": QColor("#ffff00"), + "hadamard_pressed": QColor("#f1c232"), + "boundary": QColor("#000000"), + "boundary_pressed": QColor("#444444"), + "w_input": QColor("#000000"), + "w_input_pressed": QColor("#444444"), + "w_output": QColor("#000000"), + "w_output_pressed": QColor("#444444"), + "outline": QColor("#000000"), } classic_red_green: ColorsScheme = moder_red_green | { "id": "classic-red-green", - "name": "Classic Red & Green", - "z_spider": "#00ff00", - "z_spider_pressed": "#00dd00", - "x_spider": "#ff0d00", - "x_spider_pressed": "#dd0b00", + "label": "Classic Red & Green", + "z_spider": QColor("#00ff00"), + "z_spider_pressed": QColor("#00dd00"), + "x_spider": QColor("#ff0d00"), + "x_spider_pressed": QColor("#dd0b00"), } white_gray: ColorsScheme = moder_red_green | { "id": 'white-grey', - "name": "Dodo book White & Grey", - "z_spider": "#ffffff", - "z_spider_pressed": "#eeeeee", - "x_spider": "#b4b4b4", - "x_spider_pressed": "#a0a0a0", - "hadamard": "#ffffff", - "hadamard_pressed": "#dddddd", + "label": "Dodo book White & Grey", + "z_spider": QColor("#ffffff"), + "z_spider_pressed": QColor("#eeeeee"), + "x_spider": QColor("#b4b4b4"), + "x_spider_pressed": QColor("#a0a0a0"), + "hadamard": QColor("#ffffff"), + "hadamard_pressed": QColor("#dddddd"), } gidney: ColorsScheme = white_gray | { "id": 'gidney', - "name": "Gidney's Black & White", - "z_spider": "#000000", - "z_spider_pressed": "#222222", - "x_spider": "#ffffff", - "x_spider_pressed": "#dddddd", + "label": "Gidney's Black & White", + "z_spider": QColor("#000000"), + "z_spider_pressed": QColor("#222222"), + "x_spider": QColor("#ffffff"), + "x_spider_pressed": QColor("#dddddd"), } color_schemes = { scheme["id"]: scheme for scheme in [moder_red_green, classic_red_green, white_gray, gidney] } +color_scheme_ids = list(color_schemes.keys()) -def load_tikz_classes(): + +def load_tikz_classes() -> dict[str, str]: return { 'boundary': str(settings.value('tikz/boundary-export', pyzx.settings.tikz_classes['boundary'])), 'Z': str(settings.value('tikz/Z-spider-export', pyzx.settings.tikz_classes['Z'])), @@ -169,12 +183,28 @@ def _get_synonyms(key: str, default: list[str]) -> list[str]: pyzx.tikz.synonyms_wedge = _get_synonyms('tikz/edge-W-import', pyzx.tikz.synonyms_wedge) +class DisplaySettings(object): + SNAP_DIVISION = 4 # Should be an integer dividing SCALE + + def __init__(self, scheme_id: str) -> None: + self.colors = color_schemes[scheme_id] + self.update() + + def set_color_scheme(self, scheme_id: str) -> None: + self.colors = color_schemes[scheme_id] + + def update(self) -> None: + self.SNAP_DIVISION = int(get_settings_value("snap-granularity", str)) + self.SNAP = SCALE / self.SNAP_DIVISION + + # Initialise settings settings = QSettings("zxlive", "zxlive") -for key, value in (general_defaults | tikz_defaults).items(): +for key, value in defaults.items(): if not settings.contains(key): settings.setValue(key, value) -colors = str(settings.value("color-scheme")) - refresh_pyzx_tikz_settings() # Call it once on startup + + +display_setting = DisplaySettings(str(settings.value("color-scheme"))) diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index 41316e34..c575e080 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -26,16 +26,16 @@ QComboBox ) -from .settings import (set_pyzx_tikz_settings, colors, setting, color_schemes, - input_circuit_formats, defaults) +from .common import get_settings_value, T +from .settings import ( + refresh_pyzx_tikz_settings, input_circuit_formats, defaults, + display_setting, color_schemes +) if TYPE_CHECKING: from .mainwindow import MainWindow -T = TypeVar('T') - - class FormInputType(IntEnum): Str = 0 Int = 1 @@ -48,9 +48,13 @@ class SettingsData(TypedDict): id: str label: str type: FormInputType - data: NotRequired[dict[Any, str]] + data: NotRequired[Any] +color_scheme_data = { + id: scheme["label"] for id, scheme in color_schemes.items() +} + tab_positioning_data = { QTabWidget.TabPosition.North: "Top", QTabWidget.TabPosition.South: "Bottom", @@ -62,7 +66,7 @@ class SettingsData(TypedDict): 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_schemes}, + {"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_grpid_data}, {"id": "input-circuit-format", "label": "Input Circuit as", "type": FormInputType.Combo, "data": input_circuit_formats}, @@ -139,9 +143,7 @@ def __init__(self, main_window: MainWindow) -> None: self.init_okay_cancle_buttons(layout) def get_settings_value(self, arg: str, _type: Type[T], default: T | None = None) -> T: - if not isinstance(val := self.settings.value(arg, default), _type) and default is None: - raise ValueError(f"Unexpected _type for {val}: expected {_type}, got {type(val)}") - return val + return get_settings_value(arg, _type, default, self.settings) def add_settings_tab(self, tab_widget: QTabWidget, tab_name: str, label: str, data: list[SettingsData]) -> None: panel_tikz_names = QWidget() @@ -172,28 +174,32 @@ def init_okay_cancle_buttons(self, layout: QVBoxLayout) -> None: def make_str_form_input(self, data: SettingsData) -> QLineEdit: name = data["id"] - value = self.get_settings_value(name, str, defaults[name]) + assert isinstance(default := defaults[name], str) + value: str = self.get_settings_value(name, str, default) widget = QLineEdit() widget.setText(value) return widget def make_int_form_input(self, data: SettingsData) -> QSpinBox: name = data["id"] - value = self.get_settings_value(name, int, defaults[name]) + assert isinstance(default := defaults[name], int) + value: int = self.get_settings_value(name, int, default) widget = QSpinBox() widget.setValue(value) return widget def make_float_form_input(self, data: SettingsData) -> QDoubleSpinBox: name = data["id"] - value = self.get_settings_value(name, float, defaults[name]) + assert isinstance(default := defaults[name], float) + value = self.get_settings_value(name, float, default) widget = QDoubleSpinBox() widget.setValue(value) return widget def make_folder_form_input(self, data: SettingsData) -> QWidget: name = data["id"] - value = self.get_settings_value(name, str, defaults[name]) + assert isinstance(default := defaults[name], str) + value = self.get_settings_value(name, str, default) widget = QWidget() hlayout = QHBoxLayout() widget.setLayout(hlayout) @@ -216,7 +222,7 @@ def browse() -> None: def make_combo_form_input(self, data: SettingsData) -> QComboBox: name, _data = data["id"], data["data"] - value = self.get_settings_value(name, str, defaults[name]) + value = self.settings.value(name, defaults[name]) widget = QComboBox() widget.addItems(list(_data.values())) widget.setCurrentText(_data[value]) @@ -257,13 +263,13 @@ def update_global_settings(self) -> None: self.settings.setValue(name, val) elif isinstance(widget, QWidget) and hasattr(widget, "text_value"): self.settings.setValue(name, widget.text_value) - setting.update() + display_setting.update() def apply_global_settings(self) -> None: - set_pyzx_tikz_settings() + refresh_pyzx_tikz_settings() theme = self.get_settings_value("color-scheme", str) if theme != self.prev_color_scheme: - colors.set_color_scheme(theme) + display_setting.set_color_scheme(theme) self.main_window.update_colors() pos = self.get_settings_value("tab-bar-location", QTabWidget.TabPosition) if pos != self.prev_tab_bar_location: diff --git a/zxlive/tikz.py b/zxlive/tikz.py index 22e6bad7..538d65da 100644 --- a/zxlive/tikz.py +++ b/zxlive/tikz.py @@ -1,15 +1,15 @@ from PySide6.QtCore import QSettings from pyzx.tikz import TIKZ_BASE, _to_tikz -from .common import type_safe_settings_value +from .common import get_settings_value from zxlive.proof import ProofModel def proof_to_tikz(proof: ProofModel) -> str: settings = QSettings("zxlive", "zxlive") - vspace = type_safe_settings_value("tikz/layout/vspace", float) - hspace = type_safe_settings_value("tikz/layout/hspace", float) - max_width = type_safe_settings_value("tikz/layout/max-width", float) + vspace = get_settings_value("tikz/layout/vspace", float, settings=settings) + hspace = get_settings_value("tikz/layout/hspace", float, settings=settings) + max_width = get_settings_value("tikz/layout/max-width", float, settings=settings) draw_scalar = False xoffset = -max_width diff --git a/zxlive/vitem.py b/zxlive/vitem.py index 68f71f81..6e4fb23e 100644 --- a/zxlive/vitem.py +++ b/zxlive/vitem.py @@ -28,7 +28,8 @@ from pyzx.utils import VertexType, phase_to_s, get_w_partner, vertex_is_w, get_z_box_label -from .common import VT, W_INPUT_OFFSET, GraphT, SCALE, setting, pos_to_view, pos_from_view, colors +from .common import VT, W_INPUT_OFFSET, GraphT, SCALE, pos_to_view, pos_from_view +from .settings import display_setting if TYPE_CHECKING: from .eitem import EItem @@ -123,20 +124,20 @@ def refresh(self) -> None: if not self.isSelected(): t = self.ty if t == VertexType.Z or t == VertexType.Z_BOX: - self.setBrush(QBrush(colors.z_spider)) + self.setBrush(QBrush(display_setting.colors["z_spider"])) elif t == VertexType.X: - self.setBrush(QBrush(colors.x_spider)) + self.setBrush(QBrush(display_setting.colors["x_spider"])) elif t == VertexType.H_BOX: - self.setBrush(QBrush(colors.hadamard)) + self.setBrush(QBrush(display_setting.colors["hadamard"])) elif t == VertexType.W_INPUT: - self.setBrush(QBrush(colors.w_input)) + self.setBrush(QBrush(display_setting.colors["w_input"])) elif t == VertexType.W_OUTPUT: - self.setBrush(QBrush(colors.w_output)) + self.setBrush(QBrush(display_setting.colors["w_output"])) else: - self.setBrush(QBrush(colors.boundary)) + self.setBrush(QBrush(display_setting.colors["boundary"])) pen = QPen() pen.setWidthF(3) - pen.setColor(colors.outline) + pen.setColor(display_setting.colors["outline"]) self.setPen(pen) if self.isSelected(): @@ -144,30 +145,30 @@ def refresh(self) -> None: pen.setWidthF(5) t = self.ty if t == VertexType.Z or t == VertexType.Z_BOX: - brush = QBrush(colors.z_spider_pressed) + brush = QBrush(display_setting.colors["z_spider_pressed"]) brush.setStyle(Qt.BrushStyle.Dense1Pattern) self.setBrush(brush) elif t == VertexType.X: - brush = QBrush(colors.x_spider_pressed) + brush = QBrush(display_setting.colors["x_spider_pressed"]) brush.setStyle(Qt.BrushStyle.Dense1Pattern) self.setBrush(brush) elif t == VertexType.H_BOX: - brush = QBrush(colors.hadamard_pressed) + brush = QBrush(display_setting.colors["hadamard_pressed"]) brush.setStyle(Qt.BrushStyle.Dense1Pattern) self.setBrush(brush) elif t == VertexType.W_INPUT: - brush = QBrush(colors.w_input_pressed) + brush = QBrush(display_setting.colors["w_input_pressed"]) brush.setStyle(Qt.BrushStyle.Dense1Pattern) self.setBrush(brush) elif t == VertexType.W_OUTPUT: - brush = QBrush(colors.w_output_pressed) + brush = QBrush(display_setting.colors["w_output_pressed"]) brush.setStyle(Qt.BrushStyle.Dense1Pattern) self.setBrush(brush) else: - brush = QBrush(colors.boundary_pressed) + brush = QBrush(display_setting.colors["boundary_pressed"]) brush.setStyle(Qt.BrushStyle.Dense1Pattern) self.setBrush(brush) - pen.setColor(colors.boundary_pressed) + pen.setColor(display_setting.colors["boundary_pressed"]) self.prepareGeometryChange() self.setPen(pen) @@ -184,7 +185,7 @@ def refresh(self) -> None: def update_shape(self) -> None: pen = QPen() pen.setWidthF(3) - pen.setColor(colors.outline) + pen.setColor(display_setting.colors["outline"]) self.setPen(pen) path = QPainterPath() @@ -236,8 +237,8 @@ def itemChange(self, change: QGraphicsItem.GraphicsItemChange, value: Any) -> An x = value.x() y = value.y() else: - x = round(value.x() / setting.SNAP) * setting.SNAP - y = round(value.y() / setting.SNAP) * setting.SNAP + x = round(value.x() / display_setting.SNAP) * display_setting.SNAP + y = round(value.y() / display_setting.SNAP) * display_setting.SNAP return QPointF(x, y) # When selecting/deselecting items, we move them to the front/back From bbb3ac53c3b191f84cee6c2d9ededfe975695760 Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 12:09:36 +0100 Subject: [PATCH 3/9] Making code cleaner --- zxlive/commands.py | 2 +- zxlive/edit_panel.py | 2 +- zxlive/settings.py | 8 +------- zxlive/settings_dialog.py | 33 ++++++++++++++++++--------------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/zxlive/commands.py b/zxlive/commands.py index 7ca9dc59..68659712 100644 --- a/zxlive/commands.py +++ b/zxlive/commands.py @@ -15,7 +15,7 @@ from pyzx.utils import EdgeType, VertexType, get_w_partner, vertex_is_w, get_w_io, get_z_box_label, set_z_box_label from .common import ET, VT, W_INPUT_OFFSET, GraphT -from settings import display_setting +from .settings import display_setting from .eitem import EItem from .graphview import GraphView from .proof import ProofModel, ProofStepView, Rewrite diff --git a/zxlive/edit_panel.py b/zxlive/edit_panel.py index 3a711478..9f281fd2 100644 --- a/zxlive/edit_panel.py +++ b/zxlive/edit_panel.py @@ -17,7 +17,7 @@ from .editor_base_panel import EditorBasePanel from .graphscene import EditGraphScene from .graphview import GraphView -from .settings import input_circuit_formats +from .settings_dialog import input_circuit_formats class GraphEditPanel(EditorBasePanel): diff --git a/zxlive/settings.py b/zxlive/settings.py index 1da3977f..3f322b07 100644 --- a/zxlive/settings.py +++ b/zxlive/settings.py @@ -85,12 +85,6 @@ class ColorsScheme(TypedDict): defaults = general_defaults | tikz_export_defaults | tikz_import_defaults \ | tikz_layout_defaults | tikz_names_defaults -input_circuit_formats = { - 'openqasm': "standard OpenQASM", - 'sqasm': "Spider QASM", - 'sqasm-no-simplification': "Spider QASM (no simplification)", -} - moder_red_green: ColorsScheme = { "id": 'modern-red-green', @@ -183,7 +177,7 @@ def _get_synonyms(key: str, default: list[str]) -> list[str]: pyzx.tikz.synonyms_wedge = _get_synonyms('tikz/edge-W-import', pyzx.tikz.synonyms_wedge) -class DisplaySettings(object): +class DisplaySettings: SNAP_DIVISION = 4 # Should be an integer dividing SCALE def __init__(self, scheme_id: str) -> None: diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index c575e080..35fd397a 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -16,7 +16,7 @@ from __future__ import annotations from enum import IntEnum -from typing import TYPE_CHECKING, Dict, Any, TypeVar, Type +from typing import TYPE_CHECKING, Dict, Any, TypeVar, Type, overload from typing_extensions import TypedDict, NotRequired from PySide6.QtCore import QSettings @@ -28,8 +28,7 @@ from .common import get_settings_value, T from .settings import ( - refresh_pyzx_tikz_settings, input_circuit_formats, defaults, - display_setting, color_schemes + refresh_pyzx_tikz_settings, defaults, display_setting, color_schemes ) if TYPE_CHECKING: @@ -64,6 +63,13 @@ class SettingsData(TypedDict): snap_to_grpid_data = {'2': "2", '4': "4", '8': "8", '16': "16"} +input_circuit_formats = { + 'openqasm': "standard OpenQASM", + 'sqasm': "Spider QASM", + '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}, @@ -145,6 +151,12 @@ def __init__(self, main_window: MainWindow) -> None: def get_settings_value(self, arg: str, _type: Type[T], default: T | None = None) -> T: return get_settings_value(arg, _type, default, self.settings) + def get_settings_from_data(self, data: SettingsData, _type: Type[T]) -> T: + name = data["id"] + assert isinstance(default := defaults[name], _type) + return self.get_settings_value(name, _type, default) + + def add_settings_tab(self, tab_widget: QTabWidget, tab_name: str, label: str, data: list[SettingsData]) -> None: panel_tikz_names = QWidget() vlayout = QVBoxLayout() @@ -173,27 +185,18 @@ def init_okay_cancle_buttons(self, layout: QVBoxLayout) -> None: hlayout.addWidget(cancel_button) def make_str_form_input(self, data: SettingsData) -> QLineEdit: - name = data["id"] - assert isinstance(default := defaults[name], str) - value: str = self.get_settings_value(name, str, default) widget = QLineEdit() - widget.setText(value) + widget.setText(self.get_settings_from_data(data, str)) return widget def make_int_form_input(self, data: SettingsData) -> QSpinBox: - name = data["id"] - assert isinstance(default := defaults[name], int) - value: int = self.get_settings_value(name, int, default) widget = QSpinBox() - widget.setValue(value) + widget.setValue(self.get_settings_from_data(data, int)) return widget def make_float_form_input(self, data: SettingsData) -> QDoubleSpinBox: - name = data["id"] - assert isinstance(default := defaults[name], float) - value = self.get_settings_value(name, float, default) widget = QDoubleSpinBox() - widget.setValue(value) + widget.setValue(self.get_settings_from_data(data, float)) return widget def make_folder_form_input(self, data: SettingsData) -> QWidget: From d8b9deb5101b16ed957aeab414774a4bdc42b954 Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 12:10:36 +0100 Subject: [PATCH 4/9] Fix pytest --- zxlive/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zxlive/settings.py b/zxlive/settings.py index 3f322b07..f2c3a29d 100644 --- a/zxlive/settings.py +++ b/zxlive/settings.py @@ -7,7 +7,7 @@ from PySide6.QtGui import QColor from PySide6.QtWidgets import QTabWidget -from common import get_settings_value, SCALE +from .common import get_settings_value, SCALE class ColorsScheme(TypedDict): From 871a299b07566630c9985a9cb4b0345ff9accef7 Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 17:14:33 +0100 Subject: [PATCH 5/9] Fix typos --- zxlive/settings.py | 10 +++++----- zxlive/settings_dialog.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/zxlive/settings.py b/zxlive/settings.py index f2c3a29d..d4e6c8b9 100644 --- a/zxlive/settings.py +++ b/zxlive/settings.py @@ -10,7 +10,7 @@ from .common import get_settings_value, SCALE -class ColorsScheme(TypedDict): +class ColorScheme(TypedDict): id: str label: str z_spider: QColor @@ -86,7 +86,7 @@ class ColorsScheme(TypedDict): | tikz_layout_defaults | tikz_names_defaults -moder_red_green: ColorsScheme = { +moder_red_green: ColorScheme = { "id": 'modern-red-green', "label": "Modern Red & Green", "z_spider": QColor("#ccffcc"), @@ -104,7 +104,7 @@ class ColorsScheme(TypedDict): "outline": QColor("#000000"), } -classic_red_green: ColorsScheme = moder_red_green | { +classic_red_green: ColorScheme = moder_red_green | { "id": "classic-red-green", "label": "Classic Red & Green", "z_spider": QColor("#00ff00"), @@ -113,7 +113,7 @@ class ColorsScheme(TypedDict): "x_spider_pressed": QColor("#dd0b00"), } -white_gray: ColorsScheme = moder_red_green | { +white_gray: ColorScheme = moder_red_green | { "id": 'white-grey', "label": "Dodo book White & Grey", "z_spider": QColor("#ffffff"), @@ -124,7 +124,7 @@ class ColorsScheme(TypedDict): "hadamard_pressed": QColor("#dddddd"), } -gidney: ColorsScheme = white_gray | { +gidney: ColorScheme = white_gray | { "id": 'gidney', "label": "Gidney's Black & White", "z_spider": QColor("#000000"), diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index 35fd397a..e55c7274 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -61,7 +61,7 @@ class SettingsData(TypedDict): QTabWidget.TabPosition.East: "Right" } -snap_to_grpid_data = {'2': "2", '4': "4", '8': "8", '16': "16"} +snap_to_grid_data = {'2': "2", '4': "4", '8': "8", '16': "16"} input_circuit_formats = { 'openqasm': "standard OpenQASM", @@ -146,7 +146,7 @@ def __init__(self, main_window: MainWindow) -> None: self.add_settings_tab(tab_widget, "Tikz layout", "Tikz layout settings", tikz_layout_settings) - self.init_okay_cancle_buttons(layout) + self.init_okay_cancel_buttons(layout) def get_settings_value(self, arg: str, _type: Type[T], default: T | None = None) -> T: return get_settings_value(arg, _type, default, self.settings) @@ -171,7 +171,7 @@ def add_settings_tab(self, tab_widget: QTabWidget, tab_name: str, label: str, da for d in data: self.add_setting_to_form(form_names, d) - def init_okay_cancle_buttons(self, layout: QVBoxLayout) -> None: + def init_okay_cancel_buttons(self, layout: QVBoxLayout) -> None: w = QWidget() hlayout = QHBoxLayout() hlayout.addStretch() From 74f82e6540390cf3cdfb71d04b85f4f249957932 Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 17:16:12 +0100 Subject: [PATCH 6/9] Fix typos --- zxlive/settings.py | 8 ++++---- zxlive/settings_dialog.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zxlive/settings.py b/zxlive/settings.py index d4e6c8b9..003d4504 100644 --- a/zxlive/settings.py +++ b/zxlive/settings.py @@ -86,7 +86,7 @@ class ColorScheme(TypedDict): | tikz_layout_defaults | tikz_names_defaults -moder_red_green: ColorScheme = { +modern_red_green: ColorScheme = { "id": 'modern-red-green', "label": "Modern Red & Green", "z_spider": QColor("#ccffcc"), @@ -104,7 +104,7 @@ class ColorScheme(TypedDict): "outline": QColor("#000000"), } -classic_red_green: ColorScheme = moder_red_green | { +classic_red_green: ColorScheme = modern_red_green | { "id": "classic-red-green", "label": "Classic Red & Green", "z_spider": QColor("#00ff00"), @@ -113,7 +113,7 @@ class ColorScheme(TypedDict): "x_spider_pressed": QColor("#dd0b00"), } -white_gray: ColorScheme = moder_red_green | { +white_gray: ColorScheme = modern_red_green | { "id": 'white-grey', "label": "Dodo book White & Grey", "z_spider": QColor("#ffffff"), @@ -134,7 +134,7 @@ class ColorScheme(TypedDict): } color_schemes = { - scheme["id"]: scheme for scheme in [moder_red_green, classic_red_green, white_gray, gidney] + scheme["id"]: scheme for scheme in [modern_red_green, classic_red_green, white_gray, gidney] } color_scheme_ids = list(color_schemes.keys()) diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index e55c7274..88d99acd 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -16,7 +16,7 @@ from __future__ import annotations from enum import IntEnum -from typing import TYPE_CHECKING, Dict, Any, TypeVar, Type, overload +from typing import TYPE_CHECKING, Dict, Any, Type from typing_extensions import TypedDict, NotRequired from PySide6.QtCore import QSettings @@ -74,7 +74,7 @@ class SettingsData(TypedDict): {"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_grpid_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}, ] From 0a985c0c5a41f5f44e30c6c9a0f5d39ea8a00b61 Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 22:04:41 +0100 Subject: [PATCH 7/9] Fix typing issues --- zxlive/icons/folder.svg | 28 +++++++++++++++++++ zxlive/settings_dialog.py | 58 +++++++++++++++------------------------ 2 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 zxlive/icons/folder.svg diff --git a/zxlive/icons/folder.svg b/zxlive/icons/folder.svg new file mode 100644 index 00000000..758ad189 --- /dev/null +++ b/zxlive/icons/folder.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index 88d99acd..b9e2c630 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -16,7 +16,9 @@ from __future__ import annotations from enum import IntEnum -from typing import TYPE_CHECKING, Dict, Any, Type +from typing import TYPE_CHECKING, Dict, Any + +from PySide6.QtGui import QIcon from typing_extensions import TypedDict, NotRequired from PySide6.QtCore import QSettings @@ -26,7 +28,7 @@ QComboBox ) -from .common import get_settings_value, T +from .common import get_settings_value, T, get_data from .settings import ( refresh_pyzx_tikz_settings, defaults, display_setting, color_schemes ) @@ -137,34 +139,28 @@ def __init__(self, main_window: MainWindow) -> None: layout.addWidget(tab_widget) self.add_settings_tab(tab_widget, "General", "Tikz rule name settings", general_settings) - self.add_settings_tab(tab_widget, "Tikz rule names", "General ZXLive settings", tikz_rule_name_settings) - self.add_settings_tab(tab_widget, "Tikz export", "These are the class names that will be used when exporting to tikz.", tikz_export_settings) - self.add_settings_tab(tab_widget, "Tikz import", "These are the class names that are understood when importing from tikz.", tikz_import_settings) - self.add_settings_tab(tab_widget, "Tikz layout", "Tikz layout settings", tikz_layout_settings) self.init_okay_cancel_buttons(layout) - def get_settings_value(self, arg: str, _type: Type[T], default: T | None = None) -> T: + def get_settings_value(self, arg: str, _type: type[T], default: T | None = None) -> T: return get_settings_value(arg, _type, default, self.settings) - def get_settings_from_data(self, data: SettingsData, _type: Type[T]) -> T: + def get_settings_from_data(self, data: SettingsData, _type: type[T]) -> T: name = data["id"] assert isinstance(default := defaults[name], _type) return self.get_settings_value(name, _type, default) def add_settings_tab(self, tab_widget: QTabWidget, tab_name: str, label: str, data: list[SettingsData]) -> None: - panel_tikz_names = QWidget() - vlayout = QVBoxLayout() + panel_tikz_names, vlayout = QWidget(), QVBoxLayout() panel_tikz_names.setLayout(vlayout) tab_widget.addTab(panel_tikz_names, tab_name) vlayout.addWidget(QLabel(label)) - form_names = QFormLayout() - w = QWidget() + form_names, w = QFormLayout(), QWidget() w.setLayout(form_names) vlayout.addWidget(w) vlayout.addStretch() @@ -172,8 +168,7 @@ def add_settings_tab(self, tab_widget: QTabWidget, tab_name: str, label: str, da self.add_setting_to_form(form_names, d) def init_okay_cancel_buttons(self, layout: QVBoxLayout) -> None: - w = QWidget() - hlayout = QHBoxLayout() + w, hlayout = QWidget(), QHBoxLayout() hlayout.addStretch() w.setLayout(hlayout) layout.addWidget(w) @@ -199,15 +194,9 @@ def make_float_form_input(self, data: SettingsData) -> QDoubleSpinBox: widget.setValue(self.get_settings_from_data(data, float)) return widget - def make_folder_form_input(self, data: SettingsData) -> QWidget: - name = data["id"] - assert isinstance(default := defaults[name], str) - value = self.get_settings_value(name, str, default) - widget = QWidget() - hlayout = QHBoxLayout() - widget.setLayout(hlayout) + def make_folder_form_input(self, data: SettingsData) -> QLineEdit: widget_line = QLineEdit() - widget_line.setText(value) + widget_line.setText(self.get_settings_from_data(data, str)) def browse() -> None: directory = QFileDialog.getExistingDirectory( @@ -215,21 +204,23 @@ def browse() -> None: ) if directory: widget_line.setText(directory) - setattr(widget, "text_value", directory) - hlayout.addWidget(widget_line) - button = QPushButton("Browse") - button.clicked.connect(browse) - hlayout.addWidget(button) - return widget + action = widget_line.addAction( + QIcon(get_data("icons/folder.svg")),QLineEdit.ActionPosition.TrailingPosition + ) + action.setToolTip("Browse...") + action.triggered.connect(browse) + # It would be nice to highlight the icon on hover + + return widget_line def make_combo_form_input(self, data: SettingsData) -> QComboBox: name, _data = data["id"], data["data"] value = self.settings.value(name, defaults[name]) widget = QComboBox() - widget.addItems(list(_data.values())) + for k, v in _data.items(): + widget.addItem(v, userData=k) widget.setCurrentText(_data[value]) - setattr(widget, "data", _data) return widget def add_setting_to_form(self, form: QFormLayout, settings_data: SettingsData) -> None: @@ -260,12 +251,7 @@ def update_global_settings(self) -> None: elif isinstance(widget, QDoubleSpinBox): self.settings.setValue(name, widget.value()) elif isinstance(widget, QComboBox): - s = widget.currentText() - assert hasattr(widget, "data") - val = next(k for k in widget.data if widget.data[k] == s) - self.settings.setValue(name, val) - elif isinstance(widget, QWidget) and hasattr(widget, "text_value"): - self.settings.setValue(name, widget.text_value) + self.settings.setValue(name, widget.currentData()) display_setting.update() def apply_global_settings(self) -> None: From 5f1bacf8db01f77bd6d96b32f13b8bb8814817f4 Mon Sep 17 00:00:00 2001 From: Boldi Date: Mon, 8 Jul 2024 22:12:21 +0100 Subject: [PATCH 8/9] Check for "data" key in SettingsData for ComboBox --- zxlive/settings_dialog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index b9e2c630..c3798c9b 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -49,7 +49,7 @@ class SettingsData(TypedDict): id: str label: str type: FormInputType - data: NotRequired[Any] + data: NotRequired[dict[Any, str]] color_scheme_data = { @@ -215,6 +215,7 @@ def browse() -> None: return widget_line def make_combo_form_input(self, data: SettingsData) -> QComboBox: + assert "data" in data name, _data = data["id"], data["data"] value = self.settings.value(name, defaults[name]) widget = QComboBox() From 4b723209497b4ac1eed504a3c5217c497ee9a832 Mon Sep 17 00:00:00 2001 From: Razin Shaikh Date: Mon, 8 Jul 2024 23:04:58 +0100 Subject: [PATCH 9/9] fixing the name/description of settings tabs --- zxlive/settings_dialog.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index c3798c9b..63064df9 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -138,8 +138,8 @@ def __init__(self, main_window: MainWindow) -> None: tab_widget = QTabWidget() layout.addWidget(tab_widget) - self.add_settings_tab(tab_widget, "General", "Tikz rule name settings", general_settings) - self.add_settings_tab(tab_widget, "Tikz rule names", "General ZXLive settings", tikz_rule_name_settings) + self.add_settings_tab(tab_widget, "General", "General ZXLive settings", general_settings) + self.add_settings_tab(tab_widget, "Tikz rule names", "Tikz rule name settings", tikz_rule_name_settings) self.add_settings_tab(tab_widget, "Tikz export", "These are the class names that will be used when exporting to tikz.", tikz_export_settings) self.add_settings_tab(tab_widget, "Tikz import", "These are the class names that are understood when importing from tikz.", tikz_import_settings) self.add_settings_tab(tab_widget, "Tikz layout", "Tikz layout settings", tikz_layout_settings) @@ -206,7 +206,7 @@ def browse() -> None: widget_line.setText(directory) action = widget_line.addAction( - QIcon(get_data("icons/folder.svg")),QLineEdit.ActionPosition.TrailingPosition + QIcon(get_data("icons/folder.svg")), QLineEdit.ActionPosition.TrailingPosition ) action.setToolTip("Browse...") action.triggered.connect(browse)