fix: 放大任务错误提示并自动关闭 (#84)

This commit is contained in:
chengma
2026-08-10 09:59:34 +08:00
parent f0715ee273
commit 74198bd70d
3 changed files with 131 additions and 10 deletions
+65 -6
View File
@@ -95,6 +95,12 @@ TASK_STATUS_TEXT = {
TaskStatus.CANCELLED: "已取消",
}
ERROR_FEEDBACK_DURATION_MS = 5_000
ERROR_FEEDBACK_MIN_WIDTH = 520
ERROR_FEEDBACK_MIN_HEIGHT = 112
ERROR_FEEDBACK_BUTTON_MIN_WIDTH = 120
ERROR_FEEDBACK_BUTTON_MIN_HEIGHT = 40
class ClaimTaskWorker(QObject):
"""在后台补交、领取或执行至多一条任务。"""
@@ -233,6 +239,7 @@ class PDDTaskPageEvent(QObject):
self._claim_worker: Optional[ClaimTaskWorker] = None
self._rerun_cancel_requested = False
self._rerun_feedback: Optional[InfoBar] = None
self._claim_feedback: Optional[InfoBar] = None
self._device_feedback: Optional[InfoBar] = None
self._auto_fetch_running = False
self._stop_requested = False
@@ -384,6 +391,7 @@ class PDDTaskPageEvent(QObject):
assert self._claim_gateway is not None
self._close_rerun_feedback()
self._close_claim_feedback()
self._close_device_feedback()
self._rerun_cancel_requested = False
self._claim_busy = True
@@ -498,13 +506,13 @@ class PDDTaskPageEvent(QObject):
self._replace_rerun_feedback(bar)
def _show_rerun_error(self, title: str, content: str) -> None:
"""显示一条可明确关闭的重新采集错误。"""
"""显示一条较大、可明确关闭且会自动消失的重新采集错误。"""
bar = InfoBar.error(
title=title,
content=content,
isClosable=True,
duration=-1,
duration=ERROR_FEEDBACK_DURATION_MS,
position=InfoBarPosition.TOP_RIGHT,
parent=self._page,
)
@@ -515,7 +523,9 @@ class PDDTaskPageEvent(QObject):
self._close_rerun_feedback()
self._rerun_feedback = bar
self._set_large_error_feedback_size(bar)
close_button = PushButton("关闭提示", bar)
self._set_large_feedback_button_size(close_button)
close_button.setAccessibleName("关闭重新采集提示")
close_button.clicked.connect(
lambda _checked=False, current=bar: self._close_rerun_feedback(current)
@@ -783,16 +793,43 @@ class PDDTaskPageEvent(QObject):
)
def _show_claim_error(self, title: str, content: str) -> None:
"""显示不会自动消失的可恢复错误,同时保留底部状态文字。"""
"""显示较大的临时错误,同时在底部保留完整状态。"""
InfoBar.error(
self._close_claim_feedback()
bar = InfoBar.error(
title=title,
content=content,
isClosable=True,
duration=-1,
duration=ERROR_FEEDBACK_DURATION_MS,
position=InfoBarPosition.TOP_RIGHT,
parent=self._page,
)
self._claim_feedback = bar
self._set_large_error_feedback_size(bar)
close_button = PushButton("关闭提示", bar)
self._set_large_feedback_button_size(close_button)
close_button.setAccessibleName("关闭采集采购错误提示")
close_button.clicked.connect(
lambda _checked=False, current=bar: self._close_claim_feedback(current)
)
bar.addWidget(close_button)
bar.destroyed.connect(
lambda _object=None, current=bar: self._forget_claim_feedback(current)
)
def _close_claim_feedback(self, expected: Optional[InfoBar] = None) -> None:
bar = self._claim_feedback
if bar is None or (expected is not None and bar is not expected):
return
self._claim_feedback = None
try:
bar.close()
except RuntimeError:
pass
def _forget_claim_feedback(self, bar: InfoBar) -> None:
if self._claim_feedback is bar:
self._claim_feedback = None
def _show_device_unavailable(self, content: str) -> None:
"""显示一条可进入设置且不会堆叠的设备错误。"""
@@ -802,17 +839,20 @@ class PDDTaskPageEvent(QObject):
title="Android 设备不可用",
content=content,
isClosable=True,
duration=-1,
duration=ERROR_FEEDBACK_DURATION_MS,
position=InfoBarPosition.TOP_RIGHT,
parent=self._page,
)
self._device_feedback = bar
self._set_large_error_feedback_size(bar)
settings_button = PushButton("打开设置", bar)
self._set_large_feedback_button_size(settings_button)
settings_button.setAccessibleName("打开 Android 设备设置")
settings_button.clicked.connect(
lambda _checked=False, current=bar: self._open_device_settings(current)
)
close_button = PushButton("关闭提示", bar)
self._set_large_feedback_button_size(close_button)
close_button.setAccessibleName("关闭 Android 设备提示")
close_button.clicked.connect(
lambda _checked=False, current=bar: self._close_device_feedback(current)
@@ -841,6 +881,24 @@ class PDDTaskPageEvent(QObject):
if self._device_feedback is bar:
self._device_feedback = None
@staticmethod
def _set_large_error_feedback_size(bar: InfoBar) -> None:
"""设置适合长中文错误和高缩放环境的最小尺寸。"""
bar.setMinimumSize(
ERROR_FEEDBACK_MIN_WIDTH,
ERROR_FEEDBACK_MIN_HEIGHT,
)
@staticmethod
def _set_large_feedback_button_size(button: PushButton) -> None:
"""扩大操作按钮,避免用户只能点击右上角的小关闭图标。"""
button.setMinimumSize(
ERROR_FEEDBACK_BUTTON_MIN_WIDTH,
ERROR_FEEDBACK_BUTTON_MIN_HEIGHT,
)
@pyqtSlot()
def _on_claim_thread_finished(self) -> None:
self._claim_worker = None
@@ -937,6 +995,7 @@ class PDDTaskPageEvent(QObject):
self._stop_requested = True
self._next_cycle_timer.stop()
self._close_rerun_feedback()
self._close_claim_feedback()
self._close_device_feedback()
for window in list(self._detail_windows.values()):
+62
View File
@@ -16,6 +16,11 @@ from src.android_device_service import AndroidDeviceSearchError
from src.pdd_ui import PDDTaskPage
from src.admin_gateway import AdminGatewayError, AdminTask, SubmissionReceipt
from src.pdd_ui_event import (
ERROR_FEEDBACK_BUTTON_MIN_HEIGHT,
ERROR_FEEDBACK_BUTTON_MIN_WIDTH,
ERROR_FEEDBACK_DURATION_MS,
ERROR_FEEDBACK_MIN_HEIGHT,
ERROR_FEEDBACK_MIN_WIDTH,
PDDTaskPageEvent,
admin_task_to_new_claimed_task,
summary_to_row,
@@ -463,6 +468,7 @@ class PDDTaskPageEventTest(unittest.TestCase):
events._show_rerun_error("重新采集失败", "测试错误")
second = events._rerun_feedback
self.assertIsNot(first, second)
self.assertEqual(second.duration, ERROR_FEEDBACK_DURATION_MS)
close_buttons = [
button
@@ -470,12 +476,55 @@ class PDDTaskPageEventTest(unittest.TestCase):
if button.text() == "关闭提示"
]
self.assertEqual(len(close_buttons), 1)
self.assertGreaterEqual(second.minimumWidth(), ERROR_FEEDBACK_MIN_WIDTH)
self.assertGreaterEqual(second.minimumHeight(), ERROR_FEEDBACK_MIN_HEIGHT)
self.assertGreaterEqual(
close_buttons[0].minimumWidth(),
ERROR_FEEDBACK_BUTTON_MIN_WIDTH,
)
self.assertGreaterEqual(
close_buttons[0].minimumHeight(),
ERROR_FEEDBACK_BUTTON_MIN_HEIGHT,
)
close_buttons[0].click()
self.app.processEvents()
self.assertIsNone(events._rerun_feedback)
events.shutdown()
page.deleteLater()
def test_claim_error_is_large_closable_and_replaces_previous(self):
page = PDDTaskPage()
events = PDDTaskPageEvent(page, self.repository)
events._show_claim_error("采集失败", "第一条错误")
first = events._claim_feedback
events._show_claim_error("采购失败", "第二条错误")
second = events._claim_feedback
self.assertIsNot(first, second)
self.assertEqual(second.duration, ERROR_FEEDBACK_DURATION_MS)
self.assertGreaterEqual(second.minimumWidth(), ERROR_FEEDBACK_MIN_WIDTH)
self.assertGreaterEqual(second.minimumHeight(), ERROR_FEEDBACK_MIN_HEIGHT)
close_buttons = [
button
for button in second.findChildren(type(page.rerunButton))
if button.text() == "关闭提示"
]
self.assertEqual(len(close_buttons), 1)
self.assertGreaterEqual(
close_buttons[0].minimumWidth(),
ERROR_FEEDBACK_BUTTON_MIN_WIDTH,
)
self.assertGreaterEqual(
close_buttons[0].minimumHeight(),
ERROR_FEEDBACK_BUTTON_MIN_HEIGHT,
)
close_buttons[0].click()
self.app.processEvents()
self.assertIsNone(events._claim_feedback)
events.shutdown()
page.deleteLater()
def test_late_rerun_success_does_not_override_cancel_status(self):
page = PDDTaskPage()
events = PDDTaskPageEvent(page, self.repository)
@@ -527,6 +576,7 @@ class PDDTaskPageEventTest(unittest.TestCase):
before = self.repository.get_task("PDD-001")
before_run = self.repository.latest_task_run("PDD-001")
page = PDDTaskPage()
def disconnected(_serial):
raise AndroidDeviceSearchError(
"USB Android 设备 USB-001 未连接"
@@ -566,12 +616,24 @@ class PDDTaskPageEventTest(unittest.TestCase):
events._show_device_unavailable("第二条")
second = events._device_feedback
self.assertIsNot(first, second)
self.assertEqual(second.duration, ERROR_FEEDBACK_DURATION_MS)
self.assertGreaterEqual(second.minimumWidth(), ERROR_FEEDBACK_MIN_WIDTH)
self.assertGreaterEqual(second.minimumHeight(), ERROR_FEEDBACK_MIN_HEIGHT)
buttons = {
button.text(): button
for button in second.findChildren(type(page.rerunButton))
}
self.assertIn("打开设置", buttons)
self.assertIn("关闭提示", buttons)
for button in buttons.values():
self.assertGreaterEqual(
button.minimumWidth(),
ERROR_FEEDBACK_BUTTON_MIN_WIDTH,
)
self.assertGreaterEqual(
button.minimumHeight(),
ERROR_FEEDBACK_BUTTON_MIN_HEIGHT,
)
buttons["打开设置"].click()
self.app.processEvents()
self.assertEqual(opened, [True])