"""Render patient-level AI report layout regressions with the offscreen Qt backend.""" from __future__ import annotations import os from pathlib import Path os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtGui import QFontDatabase from PySide6.QtWidgets import QApplication, QLabel, QScrollArea from doctor_workstation.ui.pages.reception import _ReceptionAiAnalysisDialog def _report_payload(model_key: str) -> dict[str, object]: model_label = "OpenAI" if model_key == "openai" else "千问" return { "model_key": model_key, "model_label": model_label, "generated_at": "2026-08-17 10:20:00", "diagnosis_advice": [ "2型糖尿病,HbA1c 7.5%,近期空腹血糖仍有波动。", "建议:1. 监测空腹血糖 2. 记录餐后2小时血糖 3. 复核低血糖症状", r"保留患者原始报告结构。\n结合复诊记录动态调整随访频率。", ], "risk_assessment": [ {"label": "低血糖", "level": "high"}, {"label": "依从性风险", "level": "medium"}, {"label": "并发症筛查延误风险", "level": "low"}, {"label": "复诊中断风险", "level": "medium"}, { "label": "肾功能变化可能影响二甲双胍方案,需要结合复查结果持续评估。", "level": "high", }, {"label": "饮食波动风险", "level": "low"}, ], "treatment_advice": [ r"二甲双胍 0.5g,每日2次。\n复查肾功能后再评估剂量。", "继续糖尿病饮食教育,并记录运动后的血糖变化。", "如出现心悸、出汗或意识异常,及时复测血糖并按流程处置。", ], } def _render( app: QApplication, output: Path, *, width: int, height: int, ) -> None: histories = { "qwen": [_report_payload("qwen")], "openai": [_report_payload("openai")], } dialog = _ReceptionAiAnalysisDialog(histories, preferred_model="qwen") dialog.resize(width, height) dialog.show() for _index in range(3): app.processEvents() pixmap = dialog.grab() if pixmap.width() != width or pixmap.height() != height: raise RuntimeError( f"unexpected render size: {pixmap.width()}x{pixmap.height()} " f"(expected {width}x{height})" ) output.parent.mkdir(parents=True, exist_ok=True) if not pixmap.save(str(output), "PNG"): raise RuntimeError(f"failed to save {output}") scrolls = dialog.findChildren(QScrollArea) risks = [ label for label in dialog.findChildren(QLabel) if label.property("dialogAiRisk") ] print( "PATIENT_AI_LAYOUT", f"{width}x{height}", f"scrolls={len(scrolls)}", f"horizontal_max={dialog.scroll_area.horizontalScrollBar().maximum()}", f"risk_rows={len({label.y() for label in risks})}", f"body_height={dialog.scroll_area.widget().height()}", ) print(output) dialog.close() app.processEvents() def main() -> int: app = QApplication.instance() or QApplication([]) font_path = Path(r"C:\Windows\Fonts\msyh.ttc") if font_path.is_file(): QFontDatabase.addApplicationFont(str(font_path)) output_dir = ( Path(__file__).resolve().parents[1] / "artifacts" / "patient_ai_report_layout" ) _render( app, output_dir / "patient_ai_report_920x760.png", width=920, height=760, ) _render( app, output_dir / "patient_ai_report_720x560.png", width=720, height=560, ) return 0 if __name__ == "__main__": raise SystemExit(main())