144 lines
5.6 KiB
Python
144 lines
5.6 KiB
Python
"""Render the real native client with synthetic data for UI design review."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import html
|
||
import json
|
||
import os
|
||
from pathlib import Path
|
||
|
||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||
os.environ.setdefault("DOCTOR_MOTION", "off")
|
||
|
||
from PySide6.QtCore import QSettings, QThreadPool
|
||
from PySide6.QtGui import QImage
|
||
from PySide6.QtTest import QTest
|
||
from PySide6.QtWidgets import QApplication
|
||
|
||
from doctor_workstation.services import DemoDoctorRepository
|
||
from doctor_workstation.ui import ShellWindow, apply_theme
|
||
from doctor_workstation.ui.login import LoginWindow
|
||
|
||
ROUTES = {
|
||
"appointments": "挂号列表",
|
||
"consultations": "问诊列表",
|
||
"patients": "我的患者",
|
||
"prescription_library": "我的处方库",
|
||
"prescriptions": "已开处方",
|
||
"reception": "接诊台",
|
||
}
|
||
|
||
|
||
def settle(app: QApplication) -> None:
|
||
for _ in range(4):
|
||
QThreadPool.globalInstance().waitForDone(3000)
|
||
app.processEvents()
|
||
QTest.qWait(75)
|
||
|
||
|
||
def main() -> None:
|
||
parser = argparse.ArgumentParser(description=__doc__)
|
||
parser.add_argument("--output", type=Path, default=Path("artifacts/client-design"))
|
||
args = parser.parse_args()
|
||
output = args.output.resolve()
|
||
output.mkdir(parents=True, exist_ok=True)
|
||
app = QApplication.instance() or QApplication([])
|
||
apply_theme(app)
|
||
repo = DemoDoctorRepository()
|
||
records = []
|
||
|
||
def capture(window, name: str, title: str) -> None:
|
||
settle(app)
|
||
filename = f"{name}.png"
|
||
if not window.grab().save(str(output / filename)):
|
||
raise RuntimeError(f"Could not save {filename}")
|
||
records.append({
|
||
"title": title, "file": filename,
|
||
"width": window.width(), "height": window.height(),
|
||
})
|
||
|
||
# Isolate preview settings from the user's saved account and credentials.
|
||
settings = QSettings(str(output / "preview-login.ini"), QSettings.Format.IniFormat)
|
||
settings.clear()
|
||
login = LoginWindow(repo, settings=settings)
|
||
login.show()
|
||
for width, height in ((1280, 800), (860, 640)):
|
||
login.resize(width, height)
|
||
capture(login, f"login-{width}", "登录工作站")
|
||
login.close()
|
||
|
||
session = repo.login(repo.DEMO_ACCOUNT, repo.DEMO_PASSWORD)
|
||
shell = ShellWindow(
|
||
repo, {"session": session, "demo_mode": True}, permissions=session.permissions,
|
||
)
|
||
shell.show()
|
||
try:
|
||
for width, height in ((1440, 900), (1024, 640)):
|
||
shell.resize(width, height)
|
||
for route, title in ROUTES.items():
|
||
shell.navigate(route)
|
||
capture(shell, f"{route}-{width}", title)
|
||
finally:
|
||
settle(app)
|
||
shell.close()
|
||
|
||
# Include independently rendered dialog/media fixtures when available.
|
||
supplementary = [
|
||
("patient_orders_1366x768.png", "患者业务订单"),
|
||
("patient_progress_1366x768.png", "患者进度"),
|
||
("clinical-after/appointment_drawer_1440x900.png", "预约编辑"),
|
||
("clinical-after/diagnosis_edit_1440x900.png", "诊单编辑"),
|
||
("clinical-after/diagnosis_edit_1024x640.png", "诊单编辑 · 紧凑窗口"),
|
||
("clinical-after/diagnosis_index_1440x900.png", "诊单列表"),
|
||
("assistant-1440.png", "AI 问诊"),
|
||
("assistant-1080.png", "AI 问诊 · 紧凑窗口"),
|
||
("../subwindow_exact/prescription_editor_860x900.png", "处方编辑"),
|
||
("../subwindow_exact/prescription_ai_report_920x760.png", "处方报告"),
|
||
("video-call-1120x760.png", "视频面诊"),
|
||
("video-call-440x300.png", "视频面诊 · 小窗"),
|
||
("video-chat-440x300.png", "视频聊天 · 小窗"),
|
||
("video-preview-1120x760.png", "视频截图确认"),
|
||
]
|
||
for filename, title in supplementary:
|
||
preview = QImage(str(output / filename))
|
||
if not preview.isNull():
|
||
records.append({
|
||
"title": title, "file": filename,
|
||
"width": preview.width(), "height": preview.height(),
|
||
})
|
||
|
||
(output / "gallery.json").write_text(
|
||
json.dumps(records, ensure_ascii=False, indent=2), encoding="utf-8",
|
||
)
|
||
cards = []
|
||
for record in records:
|
||
filename = html.escape(record["file"])
|
||
title = html.escape(record["title"])
|
||
cards.append(
|
||
f'<section><h2>{title} <small>{record["width"]} × {record["height"]}</small>'
|
||
f'</h2><a href="{filename}"><img src="{filename}" alt="{title}" loading="lazy">'
|
||
'</a></section>'
|
||
)
|
||
(output / "index.html").write_text(
|
||
'<!doctype html><html lang="zh-CN"><meta charset="utf-8">'
|
||
'<meta name="viewport" content="width=device-width, initial-scale=1">'
|
||
'<title>医生工作站 · 客户端设计验收</title><style>'
|
||
'body{margin:0;padding:32px;background:#F5F7FA;color:#223047;'
|
||
'font-family:"Microsoft YaHei",sans-serif}main{max-width:1440px;margin:auto}'
|
||
'h1{font-size:28px}p,small{color:#647287}small{font-weight:400;font-size:14px}'
|
||
'section{margin:32px 0 48px}h2{font-size:20px;display:flex;gap:20px;align-items:baseline}'
|
||
'img{width:100%;display:block;border:1px solid #E6EAF0;border-radius:16px}'
|
||
'a:focus-visible{outline:3px solid #6D8CCB}footer{padding:24px 0}'
|
||
'</style><main><h1>医生工作站 · 客户端设计验收</h1>'
|
||
'<p>使用演示数据渲染的真实客户端。点击图片查看原始尺寸。</p>'
|
||
+ "".join(cards)
|
||
+ '<footer>主窗口与紧凑窗口均按实际控件排版生成。</footer></main></html>',
|
||
encoding="utf-8",
|
||
)
|
||
print(output / "index.html")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|