更新
This commit is contained in:
@@ -1004,6 +1004,8 @@ class ApplicationController(QObject):
|
||||
preview_height = min(380, max(320, round(preview_width * 0.66)))
|
||||
margin = 18
|
||||
|
||||
if callable(getattr(video_window, "setProperty", None)):
|
||||
video_window.setProperty("doctorSkipOpenGeometry", True)
|
||||
video_window.showNormal()
|
||||
video_window.setMinimumSize(440, 300)
|
||||
video_window.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True)
|
||||
@@ -1058,6 +1060,8 @@ class ApplicationController(QObject):
|
||||
window.raise_()
|
||||
if activate:
|
||||
window.activateWindow()
|
||||
if callable(getattr(window, "setProperty", None)):
|
||||
window.setProperty("doctorSkipOpenGeometry", False)
|
||||
except (AttributeError, RuntimeError):
|
||||
return
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from ..core.appointment_modes import APPOINTMENT_MODES
|
||||
from .theme import main_window_target_rect
|
||||
from .widgets import (
|
||||
display_text,
|
||||
first_value,
|
||||
@@ -1766,6 +1767,11 @@ class AppointmentDrawer(QDialog):
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def _sync_to_owner(self) -> None:
|
||||
target = main_window_target_rect(self)
|
||||
if target is not None:
|
||||
self.setGeometry(target)
|
||||
self._update_drawer_width()
|
||||
return
|
||||
if self._owner is None:
|
||||
self._update_drawer_width()
|
||||
return
|
||||
|
||||
@@ -176,8 +176,8 @@ QDialog#AiConsultDialog QTabBar#AiConsultTabs {
|
||||
}
|
||||
QDialog#AiConsultDialog QTabBar#AiConsultTabs::tab {
|
||||
min-height: 36px;
|
||||
padding: 0 16px;
|
||||
margin-right: 4px;
|
||||
padding: 0 7px;
|
||||
margin-right: 0;
|
||||
color: #606163;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
@@ -189,6 +189,18 @@ QDialog#AiConsultDialog QTabBar#AiConsultTabs::tab:selected {
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid #4F63D9;
|
||||
}
|
||||
QDialog#AiConsultDialog QPushButton#AiConsultChatFilter {
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
color: #4F63D9;
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E1E5F4;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
QDialog#AiConsultDialog QPushButton#AiConsultChatFilter:hover {
|
||||
background-color: #EEF1FA;
|
||||
}
|
||||
QDialog#AiConsultDialog QFrame#AiConsultChatPane,
|
||||
QDialog#AiConsultDialog QFrame#AiConsultSide,
|
||||
QDialog#AiConsultDialog QFrame#AiConsultRecordPane {
|
||||
@@ -637,6 +649,13 @@ QDialog#AiConsultDialog QLabel#AiConsultTag[level="warn"] {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
QDialog#AiConsultDialog QLabel#AiConsultTag[level="unknown"] {
|
||||
color: #6A6B6D;
|
||||
background-color: #F0F0F0;
|
||||
border-radius: 8px;
|
||||
padding: 2px 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
QDialog#AiConsultDialog QPushButton#AiConsultChipButton {
|
||||
min-height: 28px;
|
||||
max-height: 30px;
|
||||
@@ -3463,6 +3482,7 @@ class AiConsultDialog(QDialog):
|
||||
# done 事件是否到达。用于区分“模型没内容”和“连接直接断了”。
|
||||
self._stream_completed = False
|
||||
self._follow_chat = True
|
||||
self._chat_filter = "all"
|
||||
self._flush_timer = QTimer(self)
|
||||
self._flush_timer.setSingleShot(True)
|
||||
self._flush_timer.setInterval(40)
|
||||
@@ -3513,6 +3533,7 @@ class AiConsultDialog(QDialog):
|
||||
self._generation += 1
|
||||
self._image_generation += 1
|
||||
self._clear_chat()
|
||||
self._set_chat_filter("all")
|
||||
# The AI context envelope depends on workspace data that has not yet
|
||||
# been fetched, so reset its cache eagerly instead of letting it
|
||||
# linger from a previous patient session.
|
||||
@@ -3545,6 +3566,24 @@ class AiConsultDialog(QDialog):
|
||||
layout = QVBoxLayout(shell)
|
||||
layout.setContentsMargins(18, 14, 18, 14)
|
||||
layout.setSpacing(12)
|
||||
|
||||
splitter = QSplitter(Qt.Orientation.Horizontal)
|
||||
splitter.setChildrenCollapsible(False)
|
||||
splitter.addWidget(self._build_center())
|
||||
splitter.addWidget(self._build_right_pane())
|
||||
splitter.setStretchFactor(0, 1)
|
||||
splitter.setStretchFactor(1, 0)
|
||||
splitter.setSizes([920, 460])
|
||||
layout.addWidget(splitter, 1)
|
||||
|
||||
def _build_right_pane(self) -> QWidget:
|
||||
host = QWidget()
|
||||
host.setObjectName("AiConsultRightPane")
|
||||
host.setMinimumWidth(360)
|
||||
host.setMaximumWidth(480)
|
||||
layout = QVBoxLayout(host)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
layout.addLayout(self._build_crumb())
|
||||
layout.addWidget(self._build_hero())
|
||||
self.tabs = QTabBar()
|
||||
@@ -3553,15 +3592,31 @@ class AiConsultDialog(QDialog):
|
||||
self.tabs.addTab(title)
|
||||
self.tabs.currentChanged.connect(self._tab_changed)
|
||||
layout.addWidget(self.tabs)
|
||||
layout.addWidget(self._build_side(), 1)
|
||||
return host
|
||||
|
||||
splitter = QSplitter(Qt.Orientation.Horizontal)
|
||||
splitter.setChildrenCollapsible(False)
|
||||
splitter.addWidget(self._build_center())
|
||||
splitter.addWidget(self._build_side())
|
||||
splitter.setStretchFactor(0, 1)
|
||||
splitter.setStretchFactor(1, 0)
|
||||
splitter.setSizes([920, 400])
|
||||
layout.addWidget(splitter, 1)
|
||||
def _build_chat_filter(self) -> QPushButton:
|
||||
self.chat_filter_button = QPushButton("筛选")
|
||||
self.chat_filter_button.setObjectName("AiConsultChatFilter")
|
||||
self.chat_filter_button.setAccessibleName("筛选问诊对话")
|
||||
self.chat_filter_button.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.chat_filter_menu = QMenu(self.chat_filter_button)
|
||||
for value, label in (
|
||||
("all", "全部对话"),
|
||||
("patient", "患者消息"),
|
||||
("doctor", "医生消息"),
|
||||
("ai", "AI 助手消息"),
|
||||
("system", "系统事件"),
|
||||
):
|
||||
action = self.chat_filter_menu.addAction(label)
|
||||
action.setData(value)
|
||||
action.setCheckable(True)
|
||||
action.setChecked(value == "all")
|
||||
action.triggered.connect(
|
||||
lambda _checked=False, role=value: self._set_chat_filter(role)
|
||||
)
|
||||
self.chat_filter_button.setMenu(self.chat_filter_menu)
|
||||
return self.chat_filter_button
|
||||
|
||||
def _build_crumb(self) -> QHBoxLayout:
|
||||
row = QHBoxLayout()
|
||||
@@ -3577,6 +3632,7 @@ class AiConsultDialog(QDialog):
|
||||
current.setObjectName("AiConsultCrumbCurrent")
|
||||
row.addWidget(current)
|
||||
row.addStretch(1)
|
||||
row.addWidget(self._build_chat_filter())
|
||||
back = QPushButton("返回")
|
||||
back.setObjectName("AiConsultBack")
|
||||
back.clicked.connect(self.reject)
|
||||
@@ -3587,9 +3643,11 @@ class AiConsultDialog(QDialog):
|
||||
card = QFrame()
|
||||
card.setObjectName("AiConsultHero")
|
||||
_style_surface(card)
|
||||
row = QHBoxLayout(card)
|
||||
row.setContentsMargins(16, 14, 16, 14)
|
||||
row.setSpacing(14)
|
||||
body = QVBoxLayout(card)
|
||||
body.setContentsMargins(14, 12, 14, 12)
|
||||
body.setSpacing(8)
|
||||
row = QHBoxLayout()
|
||||
row.setSpacing(10)
|
||||
self.avatar = QLabel("患")
|
||||
self.avatar.setObjectName("AiConsultAvatar")
|
||||
self.avatar.setFixedSize(44, 44)
|
||||
@@ -3604,13 +3662,14 @@ class AiConsultDialog(QDialog):
|
||||
self.identity_label = QLabel("")
|
||||
self.identity_label.setObjectName("AiConsultMeta")
|
||||
info.addWidget(self.identity_label)
|
||||
self.chip_row = _FlowRow()
|
||||
info.addWidget(self.chip_row)
|
||||
row.addLayout(info, 1)
|
||||
full = QPushButton("查看完整资料")
|
||||
full.setObjectName("AiConsultFullRecord")
|
||||
full.clicked.connect(self._open_full_record)
|
||||
row.addWidget(full, 0, Qt.AlignmentFlag.AlignTop)
|
||||
body.addLayout(row)
|
||||
self.chip_row = _FlowRow()
|
||||
body.addWidget(self.chip_row)
|
||||
return card
|
||||
|
||||
def _build_center(self) -> QWidget:
|
||||
@@ -3815,7 +3874,6 @@ class AiConsultDialog(QDialog):
|
||||
side.setObjectName("AiConsultSide")
|
||||
_style_surface(side)
|
||||
side.setMinimumWidth(350)
|
||||
side.setMaximumWidth(420)
|
||||
outer = QVBoxLayout(side)
|
||||
outer.setContentsMargins(18, 16, 18, 16)
|
||||
outer.setSpacing(14)
|
||||
@@ -3918,6 +3976,32 @@ class AiConsultDialog(QDialog):
|
||||
|
||||
def _tab_changed(self, index: int) -> None:
|
||||
self.center_stack.setCurrentIndex(index)
|
||||
self.chat_filter_button.setVisible(index == 0)
|
||||
|
||||
def _set_chat_filter(self, role: str) -> None:
|
||||
self._chat_filter = role
|
||||
label = "全部对话"
|
||||
for action in self.chat_filter_menu.actions():
|
||||
selected = action.data() == role
|
||||
action.setChecked(selected)
|
||||
if selected:
|
||||
label = action.text()
|
||||
self.chat_filter_button.setText(
|
||||
"筛选" if role == "all" else f"筛选:{label}"
|
||||
)
|
||||
for index in range(self.chat_layout.count() - 1):
|
||||
widget = self.chat_layout.itemAt(index).widget()
|
||||
if widget is not None:
|
||||
widget.setVisible(self._chat_item_matches_filter(widget))
|
||||
|
||||
def _chat_item_matches_filter(self, widget: QWidget) -> bool:
|
||||
if self._chat_filter == "all":
|
||||
return True
|
||||
if isinstance(widget, _ChatEvent):
|
||||
return self._chat_filter == "system"
|
||||
if isinstance(widget, _ChatBubble):
|
||||
return widget._role == self._chat_filter
|
||||
return True
|
||||
|
||||
def _clear_chat(self) -> None:
|
||||
while self.chat_layout.count() > 1:
|
||||
@@ -3939,6 +4023,7 @@ class AiConsultDialog(QDialog):
|
||||
) -> _ChatBubble:
|
||||
bubble = _ChatBubble(role=role, text=text, time_text=time_text, extra=extra)
|
||||
self.chat_layout.insertWidget(self.chat_layout.count() - 1, bubble)
|
||||
bubble.setVisible(self._chat_item_matches_filter(bubble))
|
||||
if self._follow_chat:
|
||||
QTimer.singleShot(0, self._scroll_chat_to_bottom)
|
||||
return bubble
|
||||
@@ -3960,6 +4045,7 @@ class AiConsultDialog(QDialog):
|
||||
marker=marker,
|
||||
)
|
||||
self.chat_layout.insertWidget(self.chat_layout.count() - 1, event)
|
||||
event.setVisible(self._chat_item_matches_filter(event))
|
||||
if self._follow_chat:
|
||||
QTimer.singleShot(0, self._scroll_chat_to_bottom)
|
||||
return event
|
||||
@@ -4571,51 +4657,24 @@ class AiConsultDialog(QDialog):
|
||||
)
|
||||
top_risk = risk_rows[0] if risk_rows else {}
|
||||
risk_label = display_text(first_value(top_risk, "label"), "")
|
||||
risk_level = str(first_value(top_risk, "level", default="medium") or "medium")
|
||||
risk_level = str(first_value(top_risk, "level", default="unknown") or "unknown")
|
||||
if risk_level not in {"high", "medium", "low"}:
|
||||
risk_level = "medium"
|
||||
level_text = {"high": "高风险", "medium": "中风险", "low": "低风险"}[risk_level]
|
||||
diagnosis = _as_mapping(get_value(self._detail, "diagnosis", default=self._detail) or self._seed)
|
||||
appointment = _as_mapping(get_value(self._detail, "appointment", default={}))
|
||||
clinical = display_text(
|
||||
_pick(("clinical_diagnosis", "diagnosis_name"), diagnosis, appointment, self._seed),
|
||||
"",
|
||||
)
|
||||
complaint = display_text(_pick(("chief_complaint",), diagnosis, self._seed), "")
|
||||
glucose = _pick(("fasting_blood_sugar",), diagnosis, appointment, self._seed)
|
||||
glucose_number = _as_float(glucose)
|
||||
if not advice:
|
||||
advice = _join_meta(clinical, f"主诉 {complaint}" if complaint else "") or "结合现有病历评估证候与病情。"
|
||||
if not risk_label:
|
||||
if glucose_number is not None and glucose_number >= 7.0:
|
||||
risk_label, risk_level, level_text = "高血糖风险", "high", "高风险"
|
||||
elif glucose_number is not None and glucose_number >= 6.1:
|
||||
risk_label, risk_level, level_text = "血糖波动风险", "medium", "中风险"
|
||||
else:
|
||||
risk_label, risk_level, level_text = "需持续随访", "low", "低风险"
|
||||
if not treatment:
|
||||
treatment = "结合处方、依从性与最新检验结果评估是否需要调整用药。"
|
||||
markdown = _analysis_markdown(
|
||||
{
|
||||
"diagnosis_advice": advice,
|
||||
"treatment_advice": treatment,
|
||||
"risk_assessment": risk_rows
|
||||
or [{"label": risk_label, "level": risk_level}],
|
||||
}
|
||||
)
|
||||
risk_level = "unknown"
|
||||
level_text = {"high": "高风险", "medium": "中风险", "low": "低风险", "unknown": "未提供"}[risk_level]
|
||||
if not advice and not treatment and not risk_rows:
|
||||
self._set_insights(
|
||||
[("分析状态", "当前诊单暂无可用的 AI 分析报告,请重新分析后查看。", "未生成", "unknown")]
|
||||
)
|
||||
return
|
||||
markdown = _analysis_markdown(payload)
|
||||
if markdown:
|
||||
bubble = self._append_bubble("ai", markdown)
|
||||
bubble.finalize_clinical_analysis()
|
||||
glucose_tag, glucose_level = (
|
||||
("未达标", "warn")
|
||||
if "高血糖" in risk_label or risk_level == "high"
|
||||
else ("需关注", "low")
|
||||
)
|
||||
self._set_insights(
|
||||
[
|
||||
("血糖控制评估", advice, glucose_tag, glucose_level),
|
||||
("并发症风险评估", risk_label, level_text, risk_level),
|
||||
("用药合理性评估", treatment, "需关注", "low"),
|
||||
("血糖控制评估", advice or "报告未提供此项分析。", "已分析" if advice else "未提供", "low" if advice else "unknown"),
|
||||
("并发症风险评估", risk_label or "报告未列出风险项。", level_text, risk_level),
|
||||
("用药合理性评估", treatment or "报告未提供此项分析。", "已分析" if treatment else "未提供", "low" if treatment else "unknown"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ from ..diagnosis_media import (
|
||||
safe_image_sources,
|
||||
)
|
||||
from ..infinite_list import InfiniteList
|
||||
from ..theme import main_window_target_rect
|
||||
from ..widgets import (
|
||||
display_text,
|
||||
first_value,
|
||||
@@ -716,6 +717,11 @@ class OrderDetailDrawer(QDialog):
|
||||
QTimer.singleShot(0, self.sync_to_owner)
|
||||
|
||||
def sync_to_owner(self) -> None:
|
||||
target = main_window_target_rect(self)
|
||||
if target is not None:
|
||||
self.setGeometry(target)
|
||||
self.drawer_panel.setFixedWidth(max(1, round(target.width() * self.drawer_ratio)))
|
||||
return
|
||||
owner = self._owner
|
||||
if owner is None:
|
||||
self.resize(1152, 720)
|
||||
@@ -1836,6 +1842,10 @@ class DiagnosisDialog(QDialog):
|
||||
def _sync_host_geometry(self) -> None:
|
||||
if self._embedded:
|
||||
return
|
||||
target = main_window_target_rect(self)
|
||||
if target is not None:
|
||||
self.setGeometry(target)
|
||||
return
|
||||
owner = self._owner
|
||||
if owner is None:
|
||||
if self.width() < 760 or self.height() < 520:
|
||||
|
||||
@@ -93,6 +93,7 @@ from PySide6.QtWidgets import (
|
||||
from .. import icons
|
||||
from ..diagnosis_terms import shared_terms
|
||||
from ..infinite_list import InfiniteList
|
||||
from ..theme import main_window_target_rect
|
||||
from ..widgets import (
|
||||
MessageBanner,
|
||||
display_text,
|
||||
@@ -3306,6 +3307,17 @@ class PrescriptionEditorDialog(QDialog):
|
||||
self._fit_drawer_geometry()
|
||||
|
||||
def _fit_drawer_geometry(self) -> None:
|
||||
target = main_window_target_rect(self)
|
||||
if target is not None:
|
||||
width = target.width()
|
||||
self.drawer_surface.setMaximumWidth(max(self.DRAWER_WIDTH, width))
|
||||
self.setGeometry(target)
|
||||
if self.diagnosis_host.isVisible():
|
||||
editor_width = min(self.DRAWER_WIDTH, max(520, round(width * 0.55)))
|
||||
self.workspace_splitter.setSizes([max(300, width - editor_width - 1), editor_width])
|
||||
else:
|
||||
self.workspace_splitter.setSizes([0, width])
|
||||
return
|
||||
parent = self.parentWidget()
|
||||
if parent is None:
|
||||
return
|
||||
|
||||
@@ -885,6 +885,7 @@ class ShellWindow(QMainWindow):
|
||||
parent: QWidget | None = None,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
self.setProperty("doctorMainWindow", True)
|
||||
self.setWindowTitle("甄养堂 · 问诊中心")
|
||||
self.setWindowFlag(Qt.WindowType.FramelessWindowHint, True)
|
||||
# The ground paints its own rounded corner, so everything outside that
|
||||
|
||||
@@ -8,7 +8,7 @@ from pathlib import Path
|
||||
from string import Template
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QEvent, QObject, QPointF, Qt
|
||||
from PySide6.QtCore import QEvent, QObject, QPointF, QRect, Qt
|
||||
from PySide6.QtGui import (
|
||||
QColor,
|
||||
QFont,
|
||||
@@ -33,6 +33,7 @@ from PySide6.QtWidgets import (
|
||||
QScrollArea,
|
||||
QTextBrowser,
|
||||
QTextEdit,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from doctor_workstation.resources import resource_path
|
||||
@@ -1450,6 +1451,73 @@ def mark_business_dialog(dialog: QDialog, object_name: str | None = None) -> Non
|
||||
_refresh_widget_style(dialog)
|
||||
|
||||
|
||||
def main_window_target_rect(window: QWidget) -> QRect | None:
|
||||
"""Return a centered rectangle covering 85% of the visible workstation."""
|
||||
|
||||
app = QApplication.instance()
|
||||
if app is None:
|
||||
return None
|
||||
main = next(
|
||||
(
|
||||
candidate
|
||||
for candidate in app.topLevelWidgets()
|
||||
if candidate is not window
|
||||
and candidate.property("doctorMainWindow")
|
||||
and candidate.isVisible()
|
||||
),
|
||||
None,
|
||||
)
|
||||
if main is None:
|
||||
return None
|
||||
|
||||
main_frame = main.frameGeometry()
|
||||
target_width = max(1, round(main_frame.width() * 0.85))
|
||||
target_height = max(1, round(main_frame.height() * 0.85))
|
||||
return QRect(
|
||||
main_frame.center().x() - target_width // 2,
|
||||
main_frame.center().y() - target_height // 2,
|
||||
target_width,
|
||||
target_height,
|
||||
)
|
||||
|
||||
|
||||
def _size_new_window_to_main(window: QWidget) -> None:
|
||||
"""Open a business window at 85% of the visible workstation, centered."""
|
||||
|
||||
if not window.isWindow() or window.property("doctorMainWindow"):
|
||||
return
|
||||
if window.property("doctorSkipOpenGeometry"):
|
||||
return
|
||||
if (
|
||||
isinstance(window, (QMessageBox, QInputDialog, QFileDialog))
|
||||
or window.metaObject().className() == "QPrintDialog"
|
||||
):
|
||||
return
|
||||
if window.windowFlags() & Qt.WindowType.FramelessWindowHint:
|
||||
# Drawers have their own owner-sync routines and use the same target rect.
|
||||
return
|
||||
target = main_window_target_rect(window)
|
||||
if target is None:
|
||||
return
|
||||
|
||||
target_width = target.width()
|
||||
target_height = target.height()
|
||||
if window.minimumWidth() > target_width:
|
||||
window.setMinimumWidth(target_width)
|
||||
if window.minimumHeight() > target_height:
|
||||
window.setMinimumHeight(target_height)
|
||||
if window.maximumWidth() < target_width:
|
||||
window.setMaximumWidth(target_width)
|
||||
if window.maximumHeight() < target_height:
|
||||
window.setMaximumHeight(target_height)
|
||||
window.resize(target_width, target_height)
|
||||
frame = window.frameGeometry()
|
||||
window.move(
|
||||
target.center().x() - frame.width() // 2,
|
||||
target.center().y() - frame.height() // 2,
|
||||
)
|
||||
|
||||
|
||||
class _BusinessDialogStyleFilter(QObject):
|
||||
"""Style dynamically-created Qt dialogs just before they become visible."""
|
||||
|
||||
@@ -1490,6 +1558,7 @@ class _BusinessDialogStyleFilter(QObject):
|
||||
if not self._is_native_or_overlay(watched):
|
||||
allow_dialog_resize(watched)
|
||||
elif isinstance(watched, QDialog) and event_type == QEvent.Type.Show:
|
||||
_size_new_window_to_main(watched)
|
||||
for button_box in watched.findChildren(QDialogButtonBox):
|
||||
_polish_dialog_buttons(button_box)
|
||||
if not self._is_native_or_overlay(watched):
|
||||
@@ -1505,6 +1574,13 @@ class _BusinessDialogStyleFilter(QObject):
|
||||
watched.setMinimumWidth(max(watched.minimumWidth(), 460))
|
||||
elif isinstance(watched, QInputDialog):
|
||||
watched.setMinimumWidth(max(watched.minimumWidth(), 480))
|
||||
elif (
|
||||
isinstance(watched, QWidget)
|
||||
and event_type == QEvent.Type.Show
|
||||
and watched.isWindow()
|
||||
and watched.windowType() == Qt.WindowType.Window
|
||||
):
|
||||
_size_new_window_to_main(watched)
|
||||
return False
|
||||
|
||||
|
||||
@@ -1600,4 +1676,5 @@ __all__ = [
|
||||
"apply_theme",
|
||||
"crisp_pixmap",
|
||||
"mark_business_dialog",
|
||||
"main_window_target_rect",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user