from __future__ import annotations import os import sys from pathlib import Path from typing import Any os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "src" if str(SRC) not in sys.path: sys.path.insert(0, str(SRC)) from PySide6.QtCore import Qt # noqa: E402 from PySide6.QtGui import QFont, QFontDatabase # noqa: E402 from PySide6.QtWidgets import QApplication, QFrame, QLabel, QVBoxLayout # noqa: E402 from doctor_workstation.ui.diagnosis_drawer import DIAGNOSIS_QSS # noqa: E402 from doctor_workstation.ui.diagnosis_media import ( # noqa: E402 InlineRecordingPlayer, RecordingPlaybackCell, ) from doctor_workstation.ui.dialogs.diagnosis import DiagnosisDialog # noqa: E402 class RenderRepository: def get_prescription_order(self, order_id: int) -> dict[str, Any]: return {"id": order_id} def set_revisit_slot_start_offset(self, diagnosis_id: int, offset: int) -> dict[str, Any]: return {"diagnosis_id": diagnosis_id, "revisit_slot_start_offset": offset} def upload_call_recording( self, path: str, diagnosis_id: int, *, call_record_id: int | None = None, ) -> dict[str, Any]: return { "path": path, "diagnosis_id": diagnosis_id, "call_record_id": call_record_id, } def order_detail() -> dict[str, Any]: return { "id": 801, "order_no": "RX-20260811-0801", "gancao_reciperl_order_no": "GC-260811-4728", "diagnosis_id": 501, "amount": 428.5, "linked_pay_paid_total": 300, "refund_amount": 20, "agency_collect_amount": 128.5, "fulfillment_status": 5, "prescription_audit_status": 1, "payment_slip_audit_status": 1, "doctor_name": "陈医生", "creator_name": "赵医助", "creator_account": "assistant.zhao", "create_time": "2026-08-11 09:26", "recipient_name": "林晓岚", "recipient_phone": "18600004218", "shipping_province": "河南省", "shipping_city": "洛阳市", "shipping_district": "洛龙区", "shipping_address": "开元大道 88 号", "is_follow_up": 1, "medication_days": 14, "service_channel": "线上复诊", "service_package": ["调理服务", "复诊随访"], "fee_type": 3, "tracking_number": "SF164208110801", "express_company": "sf", "remark_assistant": "工作日下午送达", "prescription_audit_remark": "辨证与用量已复核", "payment_slip_audit_remark": "收款凭证已核验", "prescription": { "id": 601, "sn": "RX601", "patient_name": "林晓岚", "gender_desc": "女", "age": 34, "phone": "18600004218", "prescription_date": "2026-08-11", "doctor_name": "陈医生", "prescription_type": "饮片", "clinical_diagnosis": "气阴两虚", "dose_count": 14, "dose_unit": "剂", "usage_instruction": "水煎服", "amount": 428.5, "audit_status": 1, "dosage_amount": 180, "dosage_unit": "g", "need_decoction": 1, "times_per_day": 2, "usage_days": 14, "dietary_taboo": ["辛辣", "生冷"], "void_status": 0, }, "linked_pay_orders": [ { "id": 9101, "order_no": "PAY-9101", "order_type_desc": "药品费用", "amount": 300, "status_desc": "已支付", "creator_name": "赵医助", "create_time": "2026-08-11 09:32", } ], "unlinked_pay_orders": [], "logistics_trace": { "state_text": "运输中", "carrier_label": "顺丰速运", "traces": [ { "time": "2026-08-11 16:10", "status": "运输中", "context": "快件已离开洛阳集散中心", }, { "time": "2026-08-11 13:06", "status": "已揽收", "context": "顺丰速运已收取快件", }, ], }, "logs": [ { "admin_name": "赵医助", "action": "ship", "summary": "确认发货并填写顺丰运单", "create_time": "2026-08-11 13:08", } ], } def render_order(app: QApplication, output: Path) -> None: host = DiagnosisDialog(RenderRepository(), permissions=["*"]) host.resize(1440, 900) host.show() drawer = host._build_order_detail_dialog(order_detail(), 801) drawer.show() app.processEvents() image = drawer.grab() if not image.save(str(output), "PNG"): raise RuntimeError(f"failed to save {output}") host.resize(1024, 640) drawer.sync_to_owner() app.processEvents() compact = output.parent / "diagnosis_state_order_detail_drawer_1024x640.png" if not drawer.grab().save(str(compact), "PNG"): raise RuntimeError(f"failed to save {compact}") legacy = output.parent / "diagnosis_state_order_detail_640x540.png" if not drawer.grab().save(str(legacy), "PNG"): raise RuntimeError(f"failed to save {legacy}") drawer.close() host.close() app.processEvents() def render_video(app: QApplication, output: Path) -> None: dialog = DiagnosisDialog(RenderRepository(), permissions=["*"]) dialog._editable = True dialog._can_video_upload = True dialog._diagnosis_id = 501 dialog._tab_generations["video"] = 3 dialog._fill_video( [ { "id": 48, "recording_urls_list": [ "https://media.example.invalid/consultation-48.mp4", "https://bucket.cos.ap-shanghai.myqcloud.com/consultation-48/index.m3u8", "https://media.example.invalid/consultation-48.webm", ], "start_time_text": "2026-08-11 10:02:18", "end_time_text": "2026-08-11 10:26:43", "call_type": 2, "room_id": "room-501-20260811", "duration_text": "24分25秒", "status": 2, "recording_status_text": "录制完成", }, { "id": 47, "recording_urls_list": [], "start_time_text": "2026-08-04 09:18:05", "end_time_text": "2026-08-04 09:23:17", "call_type": 1, "room_id": "room-501-20260804", "duration_text": "5分12秒", "status": 3, "recording_status_text": "暂无录制", }, ] ) preview = QFrame() preview.setObjectName("DiagnosisVideoRender") preview.setStyleSheet( DIAGNOSIS_QSS + "QFrame#DiagnosisVideoRender{background:#F6F8FB;}" + "QLabel#DiagnosisVideoRenderTitle{color:#1F2937;font-size:20px;font-weight:650;}" ) layout = QVBoxLayout(preview) layout.setContentsMargins(22, 18, 22, 22) layout.setSpacing(12) title = QLabel("视频录制回放") title.setObjectName("DiagnosisVideoRenderTitle") title.setFixedHeight(32) title.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) layout.addWidget(title) page = dialog._tab_pages["video"] page.setParent(preview) page.show() dialog.video_upload_button.show() layout.addWidget(page, 1) preview.resize(1200, 560) preview.show() app.processEvents() table = dialog._table_registry["video"][1] playback = table.cellWidget(0, 0) if not isinstance(playback, RecordingPlaybackCell): raise RuntimeError("video render is missing the inline playback cell") player = playback.findChild(InlineRecordingPlayer) if ( player is None or playback.height() < playback.minimumSizeHint().height() or not 158 <= player.height() <= 180 or table.rowHeight(0) < playback.required_table_row_height() ): raise RuntimeError("video render clipped the inline playback geometry") image = preview.grab() if not image.save(str(output), "PNG"): raise RuntimeError(f"failed to save {output}") preview.resize(1024, 640) app.processEvents() replay_state = output.parent / "diagnosis_state_video_replay_1024x640.png" if not preview.grab().save(str(replay_state), "PNG"): raise RuntimeError(f"failed to save {replay_state}") table.horizontalScrollBar().setValue(table.horizontalScrollBar().maximum()) app.processEvents() upload_state = output.parent / "diagnosis_state_video_upload_action_1024x640.png" if not preview.grab().save(str(upload_state), "PNG"): raise RuntimeError(f"failed to save {upload_state}") preview.close() dialog.close() app.processEvents() def render_order_offset(app: QApplication, output: Path) -> None: dialog = DiagnosisDialog(RenderRepository(), permissions=["*"]) dialog._editable = True dialog._can_offset = True dialog._can_order_detail = True dialog._diagnosis_id = 501 dialog._saved_order_offset = 0 dialog.order_offset.setVisible(True) dialog.order_offset_save.setVisible(True) dialog.order_offset.setValue(2) dialog._fill_orders( [ { "id": 801, "order_no": "RX-20260811-0801", "global_visit_seq": 3, "counts_for_revisit_rate": 1, "amount": 428.5, "doctor_name": "陈医生", "assistant_name": "赵医助", "fulfillment_status": 5, "create_time": "2026-08-11 09:26", }, { "id": 794, "order_no": "RX-20260724-0794", "global_visit_seq": 2, "counts_for_revisit_rate": 1, "amount": 386, "doctor_name": "陈医生", "assistant_name": "赵医助", "fulfillment_status": 6, "create_time": "2026-07-24 10:18", }, ] ) dialog._orders_total = 2 dialog._update_orders_pager() preview = QFrame() preview.setObjectName("DiagnosisOrderRender") preview.setStyleSheet( DIAGNOSIS_QSS + "QFrame#DiagnosisOrderRender{background:#F6F8FB;}" + "QLabel#DiagnosisOrderRenderTitle{color:#1F2937;font-size:20px;font-weight:650;}" ) layout = QVBoxLayout(preview) layout.setContentsMargins(22, 18, 22, 22) layout.setSpacing(12) title = QLabel("业务订单") title.setObjectName("DiagnosisOrderRenderTitle") title.setFixedHeight(32) layout.addWidget(title) page = dialog._tab_pages["orders"] page.setParent(preview) page.show() layout.addWidget(page, 1) preview.resize(1024, 640) preview.show() app.processEvents() if not preview.grab().save(str(output), "PNG"): raise RuntimeError(f"failed to save {output}") preview.close() dialog.close() app.processEvents() def main() -> int: output_dir = ROOT / "artifacts" / "diagnosis_visual" output_dir.mkdir(parents=True, exist_ok=True) app = QApplication.instance() or QApplication([]) font_path = Path("C:/Windows/Fonts/msyh.ttc") if font_path.is_file(): font_id = QFontDatabase.addApplicationFont(str(font_path)) families = QFontDatabase.applicationFontFamilies(font_id) if families: app.setFont(QFont(families[0], 9)) order_output = output_dir / "diagnosis_order_detail_drawer_1440x900.png" video_output = output_dir / "diagnosis_video_inline_player_1200x560.png" offset_output = output_dir / "diagnosis_state_order_offset_1024x640.png" render_order(app, order_output) render_video(app, video_output) render_order_offset(app, offset_output) print(order_output) print(video_output) print(offset_output) return 0 if __name__ == "__main__": raise SystemExit(main())