The tab strip is drawn from the classroom's own palette, the one the panel, Parent Call and Presentations read: larger titles, an accent under the active tab, worksheets marked with a document icon. Its one corner widget is the lesson's size and Print — not a new-tab button. With one tab the strip stays hidden, as before; a change of size shows a moment's badge instead, and the right-click menu and keys still work. The window is named "Lesson — Wonder Ink" and carries its own icon. The print preview showed "Page 1 could not be shown" in the real session while printing correctly: poppler 25.03 does not write to stdout when asked for "-" — it writes a file called "-.png" into the current directory, which for Wonder Ink is the teacher's home. Pages are now rendered into a directory of their own, and the lab print check fails any preview that does not draw its page. The teacher's manual has lesson size and printing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
142 lines
7.5 KiB
Python
142 lines
7.5 KiB
Python
"""Drive Wonder Ink's print preview through one scenario, headless.
|
|
|
|
usage: wonderink-print-rig.py WONDERINK_COMMAND SCENARIO
|
|
|
|
Run by tools/lab/wonderink-print-check inside the lab, against the
|
|
command the package installed. It presses the preview's own buttons the
|
|
way a teacher would, and prints what happened; the check around it asks
|
|
CUPS what actually arrived.
|
|
"""
|
|
import importlib.machinery, importlib.util, sys, time, faulthandler, gc, os
|
|
faulthandler.enable()
|
|
path, scenario = sys.argv[1], sys.argv[2]
|
|
loader = importlib.machinery.SourceFileLoader("wonderink", path)
|
|
spec = importlib.util.spec_from_loader("wonderink", loader)
|
|
wi = importlib.util.module_from_spec(spec); loader.exec_module(wi)
|
|
|
|
from pathlib import Path
|
|
from PyQt6.QtCore import QTimer, QUrl
|
|
from PyQt6.QtGui import QPdfWriter, QPainter, QFont, QPageSize
|
|
from PyQt6.QtWidgets import QApplication
|
|
from PyQt6.QtWebEngineCore import QWebEngineProfile, QWebEngineSettings
|
|
|
|
notes = []
|
|
wi.notify = lambda s, b: (notes.append(s), print("NOTIFY", s, "|", b, flush=True))
|
|
app = QApplication(sys.argv)
|
|
profile = QWebEngineProfile("rig", app)
|
|
for a in ("PdfViewerEnabled", "PluginsEnabled"):
|
|
profile.settings().setAttribute(getattr(QWebEngineSettings.WebAttribute, a), True)
|
|
window = wi.WonderInk(profile)
|
|
profile.downloadRequested.connect(window.on_download)
|
|
window.resize(1280, 800); window.show()
|
|
t0 = time.monotonic()
|
|
def stamp(m): print(f"[{time.monotonic()-t0:6.2f}s] {m}", flush=True)
|
|
|
|
def lesson(view, title="Noahs Ark lesson"):
|
|
view.setHtml(f"<html><head><title>{title}</title></head><body>" + "".join(
|
|
f"<h1>{title}, part {i}</h1><p>{'Noah built an ark. ' * 80}</p>" for i in range(5)
|
|
) + "</body></html>", QUrl("https://app.wonderink.org/lesson"))
|
|
|
|
def worksheet(name="Colour the Ark"):
|
|
d = wi.DOWNLOADS; d.mkdir(parents=True, exist_ok=True)
|
|
p = d / f"{name}.pdf"
|
|
w = QPdfWriter(str(p)); w.setPageSize(QPageSize(QPageSize.PageSizeId.Letter)); w.setResolution(300)
|
|
pa = QPainter(w); pa.setFont(QFont("DejaVu Sans", 28))
|
|
pa.drawText(200, 400, f"WORKSHEET {name} page one"); pa.drawRect(200, 600, 2000, 1500)
|
|
w.newPage(); pa.drawText(200, 400, f"WORKSHEET {name} page two"); pa.end()
|
|
return p
|
|
|
|
def when(cond, then, label, limit=30):
|
|
start = time.monotonic()
|
|
def poll():
|
|
try: ok = cond()
|
|
except Exception as e: ok = False
|
|
if ok: return then()
|
|
if time.monotonic() - start > limit:
|
|
stamp(f"GAVE UP waiting for {label}"); app.exit(4); return
|
|
QTimer.singleShot(100, poll)
|
|
QTimer.singleShot(100, poll)
|
|
|
|
def preview(): return window._preview
|
|
def ready(): return preview() is not None and preview()._pdf is not None
|
|
def describe(d):
|
|
return f"shown={d._page in d._images} pages={d._pages} printers={[d._printer.itemText(i) for i in range(d._printer.count())]} paper={d.paper()} colour_offered={d._colour.isVisible()} status={d._status.text()!r}"
|
|
|
|
def finish(code=0):
|
|
stamp(f"RESULT notes={notes} preview_open={preview() is not None} tabs={window._tabs.count()}")
|
|
QTimer.singleShot(1500, lambda: app.exit(code))
|
|
|
|
def press_print(then=None):
|
|
d = preview(); stamp("PREVIEW " + describe(d))
|
|
d._print.click()
|
|
stamp(f"after Print: open={preview() is not None} status={d._status.text() if preview() else ''!r}")
|
|
(then or finish)()
|
|
|
|
actions = {
|
|
"web": lambda view: when(ready, press_print, "preview"),
|
|
"cancel": lambda view: when(ready, lambda: (stamp("PREVIEW " + describe(preview())), preview()._cancel.click(), finish()), "preview"),
|
|
"options": lambda view: when(ready, lambda: (
|
|
preview()._which.setCurrentIndex(1), preview()._range.setText("2-3"),
|
|
preview()._copies.setValue(2), preview()._colour.setCurrentIndex(1), press_print()), "preview"),
|
|
"bad-range": lambda view: when(ready, lambda: (
|
|
preview()._which.setCurrentIndex(1), preview()._range.setText("7"), preview()._print.click(),
|
|
stamp(f"bad range: open={preview() is not None} status={preview()._status.text()!r}"),
|
|
preview()._range.setText("1"), press_print()), "preview"),
|
|
"broken": lambda view: when(ready, lambda: (
|
|
preview()._printer.setCurrentText("Refusing"),
|
|
stamp(f"switched to Refusing: paper={preview().paper()} status={preview()._status.text()!r}"),
|
|
when(ready, lambda: (
|
|
stamp(f"re-rendered on {preview().paper()}: pages={preview()._pages}"),
|
|
preview()._print.click(),
|
|
stamp(f"broken: open={preview() is not None} status={preview()._status.text()!r}"),
|
|
preview()._cancel.click(), finish()), "re-render")), "preview"),
|
|
"page-closes-itself": lambda view: when(ready, lambda: (
|
|
view.page().runJavaScript("window.close()"),
|
|
when(lambda: preview() and "closed" in preview()._status.text(), lambda: (
|
|
stamp(f"after page closed: status={preview()._status.text()!r} print_enabled={preview()._print.isEnabled()}"),
|
|
preview()._cancel.click(), finish()), "closed message")), "preview"),
|
|
"close-window": lambda view: when(ready, lambda: (stamp("closing window with preview open"), window.close()), "preview"),
|
|
"page-prints-again": lambda view: when(ready, lambda: (
|
|
view.page().runJavaScript("window.print()"),
|
|
QTimer.singleShot(800, lambda: (stamp(f"second request while open: still one preview={preview() is not None}"), press_print()))), "preview"),
|
|
}
|
|
|
|
if scenario == "repeat":
|
|
view = window.open_tab(); lesson(view); n = [0]
|
|
def once():
|
|
if n[0] == 5: return finish()
|
|
n[0] += 1; QTimer.singleShot(0, lambda: window.print_view(view))
|
|
when(ready, lambda: press_print(then=lambda: QTimer.singleShot(200, once)), "preview")
|
|
view.loadFinished.connect(lambda ok: once() if n[0] == 0 else None)
|
|
elif scenario in ("pdf", "pdf-options"):
|
|
window.open_pdf(worksheet()); view = window.current()
|
|
if scenario == "pdf-options":
|
|
actions["pdf-options"] = lambda view: when(ready, lambda: (
|
|
preview()._which.setCurrentIndex(1), preview()._range.setText("2"),
|
|
preview()._copies.setValue(3), preview()._colour.setCurrentIndex(1), press_print()), "preview")
|
|
QTimer.singleShot(2500, lambda: (QTimer.singleShot(0, lambda: window.print_view(view)),
|
|
actions[scenario if scenario == "pdf-options" else "web"](view)))
|
|
elif scenario == "pdf-viewer-button":
|
|
# The viewer's own print button reaches the page as a print request.
|
|
window.open_pdf(worksheet()); view = window.current()
|
|
QTimer.singleShot(2500, lambda: (actions["web"](view), view.page().printRequested.emit()))
|
|
elif scenario == "inline":
|
|
view = window.open_tab(); view.load(QUrl("http://127.0.0.1:8088/ark.pdf"))
|
|
QTimer.singleShot(4000, lambda: (stamp(f"inline mime={window._mime.get(view)!r} shows_pdf={window._shows_pdf(view)}"),
|
|
QTimer.singleShot(0, lambda: window.print_view(view)),
|
|
when(ready, lambda: (stamp(f"worksheet tab opened: tabs={window._tabs.count()} current_is_pdf={window.current() in window._pdf_views}"), press_print()), "inline preview")))
|
|
else:
|
|
if scenario == "page-closes-itself":
|
|
lesson(window.open_tab(), "The other lesson")
|
|
view = window.open_tab(); lesson(view)
|
|
fired = []
|
|
def go(ok):
|
|
if fired: return
|
|
fired.append(1); QTimer.singleShot(0, lambda: window.print_view(view)); actions[scenario](view)
|
|
view.loadFinished.connect(go)
|
|
|
|
QTimer.singleShot(90000, lambda: (stamp("TIMEOUT"), app.exit(3)))
|
|
rc = app.exec()
|
|
stamp(f"EXIT {rc} scratch_left={sorted(p.name for p in Path(os.environ.get('XDG_RUNTIME_DIR','/tmp')).glob('wonderink-print-*'))}")
|
|
sys.exit(rc)
|