"""Runnable check that the editor builds and its edits reach project state. Runs offscreen, so it verifies wiring rather than appearance: that the widgets construct, that an edit changes the model, and that autosave and export work. """ from __future__ import annotations import os import sys from pathlib import Path os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") import numpy as np # noqa: E402 import pymupdf # noqa: E402 sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from PySide6.QtWidgets import QApplication # noqa: E402 from noteman_slicer.detect import detect_page # noqa: E402 from noteman_slicer.editor import Editor # noqa: E402 from noteman_slicer.pdf import open_source, page_raster # noqa: E402 from noteman_slicer.project import Cut, Project, default_path # noqa: E402 W, H = 1200, 1600 def _scan_pdf(path: Path) -> None: art = np.full((H, W), 255, np.uint8) for top in (300, 800): art[top : top + 200, 100:104] = 0 for staff in (top, top + 140): for i in range(5): art[staff + i * 15 : staff + i * 15 + 2, 110:1100] = 0 pix = pymupdf.Pixmap(pymupdf.csGRAY, W, H, bytearray(art.tobytes()), False) doc = pymupdf.open() doc.new_page(width=595, height=842).insert_image(pymupdf.Rect(0, 0, 595, 842), pixmap=pix) doc.save(path) def main() -> int: tmp = Path(__file__).with_name("_tmp") tmp.mkdir(exist_ok=True) pdf = tmp / "scan.pdf" _scan_pdf(pdf) app = QApplication.instance() or QApplication(sys.argv[:1]) source = open_source(pdf) gray = page_raster(source, 0) project = Project.from_detection(pdf, [detect_page(gray)], [gray.shape[0]]) editor = Editor(source, project) page = project.pages[0] # Cuts. before = page.slice_count editor.view.selected_cut = page.add_cut(Cut.straight(0.5)) editor.view.redraw() assert page.slice_count == before + 1 # A vertex turns a straight cut into a stepped one. cut = page.cuts[editor.view.selected_cut] cut.points.insert(1, (0.4, cut.y_at(0.4))) cut.points[1] = (0.4, cut.points[1][1] + 0.03) assert cut.straight_y is None, "the cut should no longer be straight" editor.view.redraw() # Discard. editor.view.selected_slice = 1 was = page.discards[1] editor.view.toggle_discard() assert page.discards[1] != was # Skew and levels reach the model and re-render without raising. editor.skew.setValue(-1.4) assert abs(page.skew + 1.4) < 1e-6 editor.black.setValue(40) editor.white.setValue(210) assert project.page_levels(0) == (40, 210) # Metadata. editor.metadata["title"].setText("Ketun joululaulu") editor.metadata["composer"].setText("trad.") assert project.metadata["title"] == "Ketun joululaulu" # Content rectangle edits and reset. page.content_rect = (0.05, 0.02, 0.95, 0.98) assert project.page_content_rect(0) == (0.05, 0.02, 0.95, 0.98) editor._reset_rect() assert project.page_content_rect(0) == project.content_rect # Autosave target, then a round-trip through disk. editor._save() saved = default_path(pdf) assert saved.exists() reloaded = Project.load(saved) assert reloaded.metadata["title"] == "Ketun joululaulu" assert reloaded.pages[0].skew == -1.4 assert reloaded.pages[0].levels == (40, 210) assert [c.points for c in reloaded.pages[0].cuts] == [c.points for c in page.cuts] editor.close() source.close() for f in (pdf, saved): f.unlink() tmp.rmdir() print("ok") return 0 if __name__ == "__main__": sys.exit(main())