更新
This commit is contained in:
@@ -9,7 +9,7 @@ import time
|
||||
from contextlib import suppress
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QObject, Qt, QTimer
|
||||
from PySide6.QtCore import QLibraryInfo, QLocale, QObject, Qt, QTimer, QTranslator
|
||||
from PySide6.QtGui import QGuiApplication, QIcon
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
@@ -43,7 +43,71 @@ from doctor_workstation.ui.widgets import (
|
||||
from doctor_workstation.video import BackendMode, launch_video_call
|
||||
from doctor_workstation.video.window import WEBENGINE_AVAILABLE
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _ChineseQtTranslator(QTranslator):
|
||||
"""Guarantee Chinese labels for common Qt standard buttons.
|
||||
|
||||
Qt's packaged ``qtbase_zh_CN`` catalog remains the primary source for
|
||||
framework text. This small fallback also keeps release builds localized
|
||||
when a packager omits the optional ``.qm`` files.
|
||||
"""
|
||||
|
||||
_BUTTON_TEXT = {
|
||||
"OK": "确定",
|
||||
"Open": "打开",
|
||||
"Save": "保存",
|
||||
"Save All": "全部保存",
|
||||
"Cancel": "取消",
|
||||
"Close": "关闭",
|
||||
"Yes": "是",
|
||||
"Yes to All": "全部确认",
|
||||
"No": "否",
|
||||
"No to All": "全部否定",
|
||||
"Abort": "中止",
|
||||
"Retry": "重试",
|
||||
"Ignore": "忽略",
|
||||
"Discard": "放弃",
|
||||
"Help": "帮助",
|
||||
"Apply": "应用",
|
||||
"Reset": "重置",
|
||||
"Restore Defaults": "恢复默认设置",
|
||||
"Don't Save": "不保存",
|
||||
}
|
||||
|
||||
def translate(
|
||||
self,
|
||||
context: str,
|
||||
source_text: str,
|
||||
disambiguation: str | None = None,
|
||||
n: int = -1,
|
||||
) -> str:
|
||||
del context, disambiguation, n
|
||||
return self._BUTTON_TEXT.get(source_text.replace("&", ""), "")
|
||||
|
||||
|
||||
def _install_chinese_translations(application: QApplication) -> None:
|
||||
"""Install Simplified Chinese Qt catalogs once for the whole process."""
|
||||
|
||||
if getattr(application, "_doctor_workstation_chinese_translators", None):
|
||||
return
|
||||
|
||||
QLocale.setDefault(QLocale("zh_CN"))
|
||||
translators: list[QTranslator] = []
|
||||
translations_path = QLibraryInfo.path(
|
||||
QLibraryInfo.LibraryPath.TranslationsPath
|
||||
)
|
||||
for catalog in ("qt_zh_CN", "qtbase_zh_CN"):
|
||||
translator = QTranslator(application)
|
||||
if translator.load(catalog, translations_path):
|
||||
application.installTranslator(translator)
|
||||
translators.append(translator)
|
||||
|
||||
fallback = _ChineseQtTranslator(application)
|
||||
application.installTranslator(fallback)
|
||||
translators.append(fallback)
|
||||
application._doctor_workstation_chinese_translators = translators # type: ignore[attr-defined]
|
||||
|
||||
|
||||
class _UnconfiguredRepository:
|
||||
@@ -200,11 +264,12 @@ class ApplicationController(QObject):
|
||||
|
||||
def _show_login(self) -> None:
|
||||
if self.login_window is None:
|
||||
self.login_window = LoginWindow(
|
||||
self._base_repository(),
|
||||
self.config,
|
||||
self.demo_repository,
|
||||
)
|
||||
self.login_window = LoginWindow(
|
||||
self._base_repository(),
|
||||
self.config,
|
||||
self.demo_repository,
|
||||
credential_store=self.token_store,
|
||||
)
|
||||
self.login_window.login_succeeded.connect(self._on_login_succeeded)
|
||||
self.login_window.config_changed.connect(self._on_config_changed)
|
||||
self.login_window.demo_mode_changed.connect(self._on_demo_mode_changed)
|
||||
@@ -212,8 +277,9 @@ class ApplicationController(QObject):
|
||||
else:
|
||||
self.login_window.repository = self._base_repository()
|
||||
self.login_window.config = self.config
|
||||
if not self.login_window.demo_check.isChecked():
|
||||
self.login_window.active_repository = self._base_repository()
|
||||
if not self.login_window.demo_check.isChecked():
|
||||
self.login_window.active_repository = self._base_repository()
|
||||
self.login_window.restore_remembered_credentials()
|
||||
self.login_window.show()
|
||||
self.login_window.raise_()
|
||||
self.login_window.activateWindow()
|
||||
@@ -498,20 +564,40 @@ class ApplicationController(QObject):
|
||||
if parent is None or self.current_repository is None:
|
||||
return
|
||||
patient_id = payload.get("patient_id")
|
||||
diagnosis_id = payload.get("diagnosis_id")
|
||||
patient_name = str(payload.get("patient_name") or "患者")
|
||||
diagnosis_id = payload.get("diagnosis_id")
|
||||
patient_name = str(payload.get("patient_name") or "患者")
|
||||
open_im = str(payload.get("mode") or "video").lower() == "im"
|
||||
if patient_id in (None, "") or diagnosis_id in (None, ""):
|
||||
show_toast(parent, "患者或诊单信息不完整,无法发起视频。", "danger", 4200)
|
||||
return
|
||||
|
||||
call_key = str(diagnosis_id)
|
||||
if (
|
||||
call_key in self.video_pending
|
||||
or call_key in self.video_calls
|
||||
or call_key in self.demo_video_dialogs
|
||||
):
|
||||
show_toast(parent, "该问诊的视频正在准备或通话中。", "info", 3600)
|
||||
return
|
||||
call_key = str(diagnosis_id)
|
||||
existing_call = self.video_calls.get(call_key)
|
||||
if open_im and existing_call is not None and getattr(existing_call, "open_im", False):
|
||||
qt_window = getattr(existing_call, "qt_window", None)
|
||||
if qt_window is not None:
|
||||
qt_window.show()
|
||||
qt_window.raise_()
|
||||
qt_window.activateWindow()
|
||||
show_toast(parent, "该患者的 IM 会话已经打开。", "info", 3200)
|
||||
return
|
||||
if (
|
||||
call_key in self.video_pending
|
||||
or existing_call is not None
|
||||
or call_key in self.demo_video_dialogs
|
||||
):
|
||||
show_toast(parent, "该问诊的视频正在准备或通话中。", "info", 3600)
|
||||
return
|
||||
|
||||
closed_previous_im = False
|
||||
if open_im:
|
||||
for key, call in tuple(self.video_calls.items()):
|
||||
if key == call_key or not getattr(call, "open_im", False):
|
||||
continue
|
||||
closed_previous_im = True
|
||||
self.video_calls.pop(key, None)
|
||||
with suppress(Exception):
|
||||
call.close()
|
||||
|
||||
if self.current_demo_mode:
|
||||
dialog = DemoVideoDialog(patient_name, parent)
|
||||
@@ -525,7 +611,11 @@ class ApplicationController(QObject):
|
||||
dialog.show()
|
||||
return
|
||||
|
||||
show_toast(parent, "正在获取安全通话凭证…", "info")
|
||||
show_toast(
|
||||
parent,
|
||||
"正在打开患者 IM 会话…" if open_im else "正在获取安全通话凭证…",
|
||||
"info",
|
||||
)
|
||||
repository = self.current_repository
|
||||
marker = object()
|
||||
self.video_pending[call_key] = marker
|
||||
@@ -536,23 +626,35 @@ class ApplicationController(QObject):
|
||||
diagnosis_id=int(diagnosis_id),
|
||||
)
|
||||
|
||||
run_async(
|
||||
get_ticket,
|
||||
on_success=lambda ticket: self._launch_video(
|
||||
ticket,
|
||||
diagnosis_id=diagnosis_id,
|
||||
patient_id=patient_id,
|
||||
repository=repository,
|
||||
call_key=call_key,
|
||||
marker=marker,
|
||||
),
|
||||
on_error=lambda error: self._video_ticket_error(
|
||||
call_key,
|
||||
marker,
|
||||
parent,
|
||||
error,
|
||||
),
|
||||
)
|
||||
def request_ticket() -> None:
|
||||
if self.video_pending.get(call_key) is not marker:
|
||||
return
|
||||
run_async(
|
||||
get_ticket,
|
||||
on_success=lambda ticket: self._launch_video(
|
||||
ticket,
|
||||
diagnosis_id=diagnosis_id,
|
||||
patient_id=patient_id,
|
||||
repository=repository,
|
||||
call_key=call_key,
|
||||
marker=marker,
|
||||
open_im=open_im,
|
||||
patient_name=patient_name,
|
||||
),
|
||||
on_error=lambda error: self._video_ticket_error(
|
||||
call_key,
|
||||
marker,
|
||||
parent,
|
||||
error,
|
||||
),
|
||||
)
|
||||
|
||||
# Tencent IM may take a brief moment to release the previous browser
|
||||
# connection. The admin version also has only one ChatDialog instance.
|
||||
if closed_previous_im:
|
||||
QTimer.singleShot(400, request_ticket)
|
||||
else:
|
||||
request_ticket()
|
||||
|
||||
def _video_ticket_error(
|
||||
self,
|
||||
@@ -579,9 +681,11 @@ class ApplicationController(QObject):
|
||||
diagnosis_id: Any,
|
||||
patient_id: Any,
|
||||
repository: Any,
|
||||
call_key: str,
|
||||
marker: object,
|
||||
) -> None:
|
||||
call_key: str,
|
||||
marker: object,
|
||||
open_im: bool = False,
|
||||
patient_name: str = "患者",
|
||||
) -> None:
|
||||
if self.video_pending.get(call_key) is not marker:
|
||||
return
|
||||
self.video_pending.pop(call_key, None)
|
||||
@@ -604,9 +708,11 @@ class ApplicationController(QObject):
|
||||
patient_id=patient_id,
|
||||
backend_mode=mode,
|
||||
local_dist=video_dist_path(),
|
||||
remote_url=self.config.video_web_url or None,
|
||||
logger=logging.getLogger("doctor_workstation.video"),
|
||||
)
|
||||
remote_url=self.config.video_web_url or None,
|
||||
logger=logging.getLogger("doctor_workstation.video"),
|
||||
open_im=open_im,
|
||||
patient_name=patient_name,
|
||||
)
|
||||
except Exception as error:
|
||||
LOGGER.exception("video call could not be launched")
|
||||
show_toast(
|
||||
@@ -682,7 +788,8 @@ def _create_application(argv: list[str]) -> QApplication:
|
||||
QGuiApplication.setHighDpiScaleFactorRoundingPolicy(
|
||||
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
|
||||
)
|
||||
application = QApplication(argv)
|
||||
application = QApplication(argv)
|
||||
_install_chinese_translations(application)
|
||||
application.setApplicationName("甄养堂医生工作站")
|
||||
application.setApplicationDisplayName("甄养堂医生工作站")
|
||||
application.setOrganizationName("ZhenYangTang")
|
||||
|
||||
Reference in New Issue
Block a user