diff --git a/zxlive/app.py b/zxlive/app.py index 22a06d7..8f678d5 100644 --- a/zxlive/app.py +++ b/zxlive/app.py @@ -24,6 +24,7 @@ from .mainwindow import MainWindow from .common import get_data, GraphT +from .settings import display_setting from typing import Optional, cast # The following hack is needed on windows in order to show the icon in the taskbar @@ -45,6 +46,7 @@ class ZXLive(QApplication): def __init__(self) -> None: super().__init__(sys.argv) + self.setFont(display_setting.font) self.setApplicationName('ZXLive') self.setDesktopFileName('ZXLive') self.setApplicationVersion('0.2.0') # TODO: read this from pyproject.toml if possible diff --git a/zxlive/base_panel.py b/zxlive/base_panel.py index 1651425..b0da58b 100644 --- a/zxlive/base_panel.py +++ b/zxlive/base_panel.py @@ -119,3 +119,6 @@ def set_splitter_size(self) -> None: def change_edge_curves(self, eitem: EItem, new_distance: float, old_distance: float) -> None: self.undo_stack.push(ChangeEdgeCurve(self.graph_view, eitem, new_distance, old_distance)) + + def update_font(self) -> None: + self.graph_view.update_font() diff --git a/zxlive/graphview.py b/zxlive/graphview.py index 998c3fd..6b0469f 100644 --- a/zxlive/graphview.py +++ b/zxlive/graphview.py @@ -23,13 +23,13 @@ from PySide6.QtWidgets import QGraphicsView, QGraphicsPathItem, QRubberBand, QGraphicsEllipseItem, QGraphicsItem, QLabel from PySide6.QtGui import QPen, QColor, QPainter, QPainterPath, QTransform, QMouseEvent, QWheelEvent, QBrush, QShortcut, QKeySequence -from .graphscene import GraphScene, VItem, EItem, EditGraphScene - from dataclasses import dataclass +from . import animations as anims from .common import GraphT, SCALE, OFFSET_X, OFFSET_Y, MIN_ZOOM, MAX_ZOOM +from .graphscene import GraphScene, VItem, EItem, EditGraphScene +from .settings import display_setting from .vitem import PHASE_ITEM_Z -from . import animations as anims if TYPE_CHECKING: from .rule_panel import RulePanel @@ -289,6 +289,10 @@ def drawBackground(self, painter: QPainter, rect: QRectF | QRect) -> None: painter.setPen(QPen(QColor(240, 240, 240), 2, Qt.PenStyle.SolidLine)) painter.drawLines(thick_lines) + def update_font(self) -> None: + for i in self.graph_scene.items(): + if isinstance(i, VItem): + i.update_font() class ProofGraphView(GraphView): def __init__(self, graph_scene: GraphScene) -> None: @@ -307,6 +311,7 @@ def update_graph(self, g: GraphT, select_new: bool = False) -> None: self.__update_scalar_label(g.scalar) def __update_scalar_label(self, scalar: Scalar) -> None: + self.scalar = scalar scalar_string = f" Scalar: {scalar.polar_str()}" if scalar.is_zero: colour = "red" @@ -318,6 +323,11 @@ def __update_scalar_label(self, scalar: Scalar) -> None: self.scalar_label.setText(f"{text}") self.scalar_label.setFixedWidth(self.scalar_label.fontMetrics().size(0, text, 0).width()) + def update_font(self) -> None: + self.scalar_label.setFont(display_setting.font) + self.__update_scalar_label(self.scalar) + super().update_font() + class RuleEditGraphView(GraphView): def __init__(self, parent_panel: RulePanel, graph_scene: GraphScene) -> None: diff --git a/zxlive/mainwindow.py b/zxlive/mainwindow.py index d100a62..1bd2bac 100644 --- a/zxlive/mainwindow.py +++ b/zxlive/mainwindow.py @@ -67,7 +67,7 @@ def __init__(self) -> None: self.restoreGeometry(geom) self.show() - tab_widget = QTabWidget() + tab_widget = QTabWidget(self) w.layout().addWidget(tab_widget) tab_widget.setTabsClosable(True) tab_widget.currentChanged.connect(self.tab_changed) @@ -575,3 +575,8 @@ def proof_as_lemma(self) -> None: def update_colors(self) -> None: if self.active_panel is not None: self.active_panel.update_colors() + + def update_font(self) -> None: + for i in range(self.tab_widget.count()): + w = cast(BasePanel, self.tab_widget.widget(i)) + w.update_font() diff --git a/zxlive/proof_panel.py b/zxlive/proof_panel.py index 523ec68..b097836 100644 --- a/zxlive/proof_panel.py +++ b/zxlive/proof_panel.py @@ -25,6 +25,7 @@ from .proof import ProofModel, ProofStepView from .rewrite_action import RewriteActionTreeModel from .rewrite_data import action_groups, refresh_custom_rules +from .settings import display_setting from .vitem import SCALE, W_INPUT_OFFSET, DragState, VItem @@ -96,25 +97,30 @@ def _toolbar_sections(self) -> Iterator[ToolbarSection]: yield ToolbarSection(self.refresh_rules) def init_rewrites_bar(self) -> None: + self.reset_rewrite_panel_style() + self._refresh_rewrites_model() + + def reset_rewrite_panel_style(self) -> None: self.rewrites_panel.setUniformRowHeights(True) self.rewrites_panel.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) fi = QFontInfo(self.font()) - self.rewrites_panel.setStyleSheet( f''' QTreeView::Item:hover {{ background-color: #e2f4ff; }} QTreeView::Item{{ - height:{fi.pixelSize() * 2}px; + height:{fi.pixelSize() * 2.2}px; }} QTreeView::Item:!enabled {{ color: #c0c0c0; }} ''') - # Set the models - self._refresh_rewrites_model() + def update_font(self) -> None: + self.rewrites_panel.setFont(display_setting.font) + self.reset_rewrite_panel_style() + super().update_font() def parse_selection(self) -> tuple[list[VT], list[ET]]: selection = list(self.graph_scene.selected_vertices) diff --git a/zxlive/settings.py b/zxlive/settings.py index 003d450..0d60725 100644 --- a/zxlive/settings.py +++ b/zxlive/settings.py @@ -4,7 +4,7 @@ import pyzx from PySide6.QtCore import QSettings -from PySide6.QtGui import QColor +from PySide6.QtGui import QColor, QFont from PySide6.QtWidgets import QTabWidget from .common import get_settings_value, SCALE @@ -28,7 +28,7 @@ class ColorScheme(TypedDict): outline: QColor -general_defaults: Dict[str, str | QTabWidget.TabPosition | int] = { +general_defaults: dict[str, str | QTabWidget.TabPosition | int] = { "path/custom-rules": "lemmas/", "color-scheme": "modern-red-green", "tab-bar-location": QTabWidget.TabPosition.North, @@ -36,6 +36,11 @@ class ColorScheme(TypedDict): "input-circuit-format": 'openqasm', } +font_defaults: dict[str, str | int | None] = { + "font/size": 13, + "font/family": "Ariel", +} + tikz_export_defaults: dict[str, str] = { "tikz/boundary-export": pyzx.settings.tikz_classes['boundary'], "tikz/Z-spider-export": pyzx.settings.tikz_classes['Z'], @@ -82,8 +87,8 @@ class ColorScheme(TypedDict): "tikz/names/decompose hadamard": "eu", } -defaults = general_defaults | tikz_export_defaults | tikz_import_defaults \ - | tikz_layout_defaults | tikz_names_defaults +defaults = general_defaults | font_defaults | tikz_export_defaults | \ + tikz_import_defaults | tikz_layout_defaults | tikz_names_defaults modern_red_green: ColorScheme = { @@ -180,8 +185,8 @@ def _get_synonyms(key: str, default: list[str]) -> list[str]: class DisplaySettings: SNAP_DIVISION = 4 # Should be an integer dividing SCALE - def __init__(self, scheme_id: str) -> None: - self.colors = color_schemes[scheme_id] + def __init__(self) -> None: + self.colors = color_schemes[str(settings.value("color-scheme"))] self.update() def set_color_scheme(self, scheme_id: str) -> None: @@ -189,6 +194,10 @@ def set_color_scheme(self, scheme_id: str) -> None: def update(self) -> None: self.SNAP_DIVISION = int(get_settings_value("snap-granularity", str)) + self.font = QFont( + get_settings_value("font/family", str), + get_settings_value("font/size", int) + ) self.SNAP = SCALE / self.SNAP_DIVISION @@ -200,5 +209,4 @@ def update(self) -> None: refresh_pyzx_tikz_settings() # Call it once on startup - -display_setting = DisplaySettings(str(settings.value("color-scheme"))) +display_setting = DisplaySettings() diff --git a/zxlive/settings_dialog.py b/zxlive/settings_dialog.py index 63064df..69131c6 100644 --- a/zxlive/settings_dialog.py +++ b/zxlive/settings_dialog.py @@ -18,14 +18,14 @@ from enum import IntEnum from typing import TYPE_CHECKING, Dict, Any -from PySide6.QtGui import QIcon +from PySide6.QtGui import QIcon, QFontDatabase 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 + QComboBox, QApplication ) from .common import get_settings_value, T, get_data @@ -80,6 +80,21 @@ class SettingsData(TypedDict): {"id": "input-circuit-format", "label": "Input Circuit as", "type": FormInputType.Combo, "data": input_circuit_formats}, ] + +font_settings: list[SettingsData] = [ + {"id": "font/size", "label": "Font size", "type": FormInputType.Int}, + # Font families can be loaded after a QGuiApplication is constructed. + # load_font_families needs to be called once a QGuiApplication is up. + {"id": "font/family", "label": "Font family", "type": FormInputType.Combo, "data": {"Ariel": "Ariel"}}, +] + + +def load_font_families() -> None: + # Index 0 is hard coded here. Just making sure that is correct. + index = next(i for i, d in enumerate(font_settings) if d["id"] == "font/family") + font_settings[index]["data"] |= {f: f for f in QFontDatabase.families()} + + 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}, @@ -129,6 +144,7 @@ def __init__(self, main_window: MainWindow) -> None: 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) + load_font_families() layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) @@ -139,6 +155,7 @@ def __init__(self, main_window: MainWindow) -> None: layout.addWidget(tab_widget) self.add_settings_tab(tab_widget, "General", "General ZXLive settings", general_settings) + self.add_settings_tab(tab_widget, "Font", "Font settings", font_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) @@ -264,6 +281,10 @@ def apply_global_settings(self) -> None: pos = self.get_settings_value("tab-bar-location", QTabWidget.TabPosition) if pos != self.prev_tab_bar_location: self.main_window.tab_widget.setTabPosition(pos) + app = QApplication.instance() + if isinstance(app, QApplication): + app.setFont(display_setting.font) + self.main_window.update_font() def cancel(self) -> None: self.reject() diff --git a/zxlive/vitem.py b/zxlive/vitem.py index 5e6391e..8e386be 100644 --- a/zxlive/vitem.py +++ b/zxlive/vitem.py @@ -354,6 +354,9 @@ def mouseReleaseEvent(self, e: QGraphicsSceneMouseEvent) -> None: else: e.ignore() + def update_font(self) -> None: + self.phase_item.setFont(display_setting.font) + class VItemAnimation(QVariantAnimation): """Animator for vertex graphics items. @@ -434,7 +437,6 @@ def __init__(self, v_item: VItem) -> None: self.setZValue(PHASE_ITEM_Z) self.setDefaultTextColor(QColor("#006bb3")) - self.setFont(QFont("monospace")) self.v_item = v_item self.refresh()