重构添加统一异常处理
This commit is contained in:
@@ -72,30 +72,26 @@ def check_com_connections(self):
|
|||||||
try:
|
try:
|
||||||
# 检测TV连接
|
# 检测TV连接
|
||||||
ucd_connected = self.check_port_connection(is_ucd=True)
|
ucd_connected = self.check_port_connection(is_ucd=True)
|
||||||
self.root.after(
|
self._dispatch_ui(
|
||||||
0,
|
self.update_connection_indicator,
|
||||||
lambda: self.update_connection_indicator(
|
self.ucd_status_indicator, ucd_connected,
|
||||||
self.ucd_status_indicator, ucd_connected
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 检测CA连接
|
# 检测CA连接
|
||||||
ca_connected = self.check_port_connection(is_ucd=False)
|
ca_connected = self.check_port_connection(is_ucd=False)
|
||||||
self.root.after(
|
self._dispatch_ui(
|
||||||
0,
|
self.update_connection_indicator,
|
||||||
lambda: self.update_connection_indicator(
|
self.ca_status_indicator, ca_connected,
|
||||||
self.ca_status_indicator, ca_connected
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 更新状态栏
|
# 更新状态栏
|
||||||
self.root.after(0, lambda: self.status_var.set("连接检测完成"))
|
self._dispatch_ui(self.status_var.set, "连接检测完成")
|
||||||
|
|
||||||
# 重新启用所有控件
|
# 重新启用所有控件
|
||||||
self.root.after(0, self.enable_com_widgets)
|
self._dispatch_ui(self.enable_com_widgets)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.root.after(0, lambda: self.log_gui.log(f"连接检测出错: {e}"))
|
self._dispatch_ui(self.log_gui.log, f"连接检测出错: {e}")
|
||||||
self.root.after(0, self.enable_com_widgets)
|
self._dispatch_ui(self.enable_com_widgets)
|
||||||
|
|
||||||
# 启动线程
|
# 启动线程
|
||||||
threading.Thread(target=check_connections, daemon=True).start()
|
threading.Thread(target=check_connections, daemon=True).start()
|
||||||
|
|||||||
@@ -55,13 +55,13 @@ def run_test(self, test_type, test_items):
|
|||||||
|
|
||||||
# 测试完成后更新UI状态
|
# 测试完成后更新UI状态
|
||||||
if self.testing: # 如果没有被中途停止
|
if self.testing: # 如果没有被中途停止
|
||||||
self.root.after(0, self.on_test_completed)
|
self._dispatch_ui(self.on_test_completed)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log_gui.log(f"测试过程中发生错误: {str(e)}")
|
self.log_gui.log(f"测试过程中发生错误: {str(e)}")
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
self.log_gui.log(traceback.format_exc())
|
self.log_gui.log(traceback.format_exc())
|
||||||
self.root.after(0, self.on_test_error)
|
self._dispatch_ui(self.on_test_error)
|
||||||
|
|
||||||
|
|
||||||
def run_screen_module_test(self, test_items):
|
def run_screen_module_test(self, test_items):
|
||||||
@@ -154,7 +154,7 @@ def run_custom_sdr_test(self, test_items):
|
|||||||
self.test_custom_sdr()
|
self.test_custom_sdr()
|
||||||
|
|
||||||
if self.testing:
|
if self.testing:
|
||||||
self.root.after(0, self.on_custom_template_test_completed)
|
self._dispatch_ui(self.on_custom_template_test_completed)
|
||||||
|
|
||||||
|
|
||||||
def run_sdr_movie_test(self, test_items):
|
def run_sdr_movie_test(self, test_items):
|
||||||
|
|||||||
@@ -154,10 +154,10 @@ def start_local_dimming_test(self):
|
|||||||
log("=" * 60)
|
log("=" * 60)
|
||||||
|
|
||||||
self.ld_test_results = results
|
self.ld_test_results = results
|
||||||
self.root.after(0, lambda: self.update_ld_results(results))
|
self._dispatch_ui(self.update_ld_results, results)
|
||||||
self.root.after(0, lambda: self.ld_start_btn.config(state=tk.NORMAL))
|
self._dispatch_ui(self.ld_start_btn.config, state=tk.NORMAL)
|
||||||
self.root.after(0, lambda: self.ld_stop_btn.config(state=tk.DISABLED))
|
self._dispatch_ui(self.ld_stop_btn.config, state=tk.DISABLED)
|
||||||
self.root.after(0, lambda: self.ld_save_btn.config(state=tk.NORMAL))
|
self._dispatch_ui(self.ld_save_btn.config, state=tk.NORMAL)
|
||||||
|
|
||||||
threading.Thread(target=worker, daemon=True).start()
|
threading.Thread(target=worker, daemon=True).start()
|
||||||
|
|
||||||
@@ -192,14 +192,14 @@ def send_ld_window(self, percentage):
|
|||||||
try:
|
try:
|
||||||
image_path = _ensure_window_image(width, height, percentage)
|
image_path = _ensure_window_image(width, height, percentage)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.root.after(0, lambda: self.log_gui.log(f"❌ 图像生成失败: {e}"))
|
self._dispatch_ui(self.log_gui.log, f"❌ 图像生成失败: {e}")
|
||||||
return
|
return
|
||||||
ok = send_image_pattern(self.ucd, image_path)
|
ok = send_image_pattern(self.ucd, image_path)
|
||||||
msg = (
|
msg = (
|
||||||
f"✅ {percentage}% 窗口已发送" if ok
|
f"✅ {percentage}% 窗口已发送" if ok
|
||||||
else f"❌ {percentage}% 窗口发送失败"
|
else f"❌ {percentage}% 窗口发送失败"
|
||||||
)
|
)
|
||||||
self.root.after(0, lambda: self.log_gui.log(msg))
|
self._dispatch_ui(self.log_gui.log, msg)
|
||||||
|
|
||||||
threading.Thread(target=send, daemon=True).start()
|
threading.Thread(target=send, daemon=True).start()
|
||||||
|
|
||||||
@@ -219,23 +219,24 @@ def measure_ld_luminance(self):
|
|||||||
try:
|
try:
|
||||||
x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay()
|
x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.root.after(0, lambda: self.log_gui.log(f"❌ 采集异常: {str(e)}"))
|
self._dispatch_ui(self.log_gui.log, f"❌ 采集异常: {str(e)}")
|
||||||
return
|
return
|
||||||
if lv is None:
|
if lv is None:
|
||||||
self.root.after(0, lambda: self.log_gui.log("❌ 采集失败"))
|
self._dispatch_ui(self.log_gui.log, "❌ 采集失败")
|
||||||
return
|
return
|
||||||
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
|
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
|
||||||
self.root.after(0, lambda: self.ld_result_label.config(
|
self._dispatch_ui(
|
||||||
text=f"亮度: {lv:.2f} cd/m² | x: {x:.4f} | y: {y:.4f}"
|
self.ld_result_label.config,
|
||||||
))
|
text=f"亮度: {lv:.2f} cd/m² | x: {x:.4f} | y: {y:.4f}",
|
||||||
self.root.after(0, lambda: self.ld_tree.insert(
|
)
|
||||||
"", tk.END,
|
self._dispatch_ui(
|
||||||
|
self.ld_tree.insert, "", tk.END,
|
||||||
values=(
|
values=(
|
||||||
f"{self.current_ld_percentage}%",
|
f"{self.current_ld_percentage}%",
|
||||||
f"{lv:.2f}", f"{x:.4f}", f"{y:.4f}", timestamp,
|
f"{lv:.2f}", f"{x:.4f}", f"{y:.4f}", timestamp,
|
||||||
),
|
),
|
||||||
))
|
)
|
||||||
self.root.after(0, lambda: self.log_gui.log(f"✅ 采集完成: {lv:.2f} cd/m²"))
|
self._dispatch_ui(self.log_gui.log, f"✅ 采集完成: {lv:.2f} cd/m²")
|
||||||
|
|
||||||
threading.Thread(target=measure, daemon=True).start()
|
threading.Thread(target=measure, daemon=True).start()
|
||||||
|
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ def _clear_custom_result_row(self, item_id, row_no):
|
|||||||
def _run_custom_row_single_step(self, item_id, row_no):
|
def _run_custom_row_single_step(self, item_id, row_no):
|
||||||
"""后台执行客户模板单步测试"""
|
"""后台执行客户模板单步测试"""
|
||||||
try:
|
try:
|
||||||
self.root.after(0, lambda: self.status_var.set(f"单步测试第 {row_no} 行..."))
|
self._dispatch_ui(self.status_var.set, f"单步测试第 {row_no} 行...")
|
||||||
self.log_gui.log(f"开始单步测试第 {row_no} 行")
|
self.log_gui.log(f"开始单步测试第 {row_no} 行")
|
||||||
|
|
||||||
self.config.set_current_pattern("custom")
|
self.config.set_current_pattern("custom")
|
||||||
@@ -296,7 +296,7 @@ def _run_custom_row_single_step(self, item_id, row_no):
|
|||||||
|
|
||||||
if row_no > len(converted_params):
|
if row_no > len(converted_params):
|
||||||
self.log_gui.log(f"❌ 行号超出 pattern 范围: {row_no}/{len(converted_params)}")
|
self.log_gui.log(f"❌ 行号超出 pattern 范围: {row_no}/{len(converted_params)}")
|
||||||
self.root.after(0, lambda: self.status_var.set("单步测试失败:行号超范围"))
|
self._dispatch_ui(self.status_var.set, "单步测试失败:行号超范围")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.ucd.set_ucd_params(temp_config)
|
self.ucd.set_ucd_params(temp_config)
|
||||||
@@ -331,17 +331,16 @@ def _run_custom_row_single_step(self, item_id, row_no):
|
|||||||
"Pe": pe,
|
"Pe": pe,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.root.after(
|
self._dispatch_ui(
|
||||||
0,
|
self._update_custom_result_row, item_id, row_no, row_data
|
||||||
lambda: self._update_custom_result_row(item_id, row_no, row_data),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.log_gui.log(f"✓ 第 {row_no} 行单步测试完成并已覆盖")
|
self.log_gui.log(f"✓ 第 {row_no} 行单步测试完成并已覆盖")
|
||||||
self.root.after(0, lambda: self.status_var.set(f"第 {row_no} 行单步测试完成"))
|
self._dispatch_ui(self.status_var.set, f"第 {row_no} 行单步测试完成")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log_gui.log(f"❌ 单步测试失败: {str(e)}")
|
self.log_gui.log(f"❌ 单步测试失败: {str(e)}")
|
||||||
self.root.after(0, lambda: self.status_var.set("单步测试失败"))
|
self._dispatch_ui(self.status_var.set, "单步测试失败")
|
||||||
|
|
||||||
|
|
||||||
def _update_custom_result_row(self, item_id, row_no, result_data):
|
def _update_custom_result_row(self, item_id, row_no, result_data):
|
||||||
|
|||||||
@@ -1064,37 +1064,26 @@ class PQDebugPanel:
|
|||||||
|
|
||||||
def _enable_test_button(self, test_type, test_item):
|
def _enable_test_button(self, test_type, test_item):
|
||||||
"""启用测试按钮"""
|
"""启用测试按钮"""
|
||||||
|
dispatch = self.app._dispatch_ui
|
||||||
if test_type == "screen_module":
|
if test_type == "screen_module":
|
||||||
if test_item == "gamma":
|
if test_item == "gamma":
|
||||||
self.app.root.after(0, lambda: self.screen_test_btn.config(state=tk.NORMAL))
|
dispatch(self.screen_test_btn.config, state=tk.NORMAL)
|
||||||
elif test_item == "rgb":
|
elif test_item == "rgb":
|
||||||
self.app.root.after(0, lambda: self.screen_rgb_test_btn.config(state=tk.NORMAL))
|
dispatch(self.screen_rgb_test_btn.config, state=tk.NORMAL)
|
||||||
elif test_type == "sdr_movie":
|
elif test_type == "sdr_movie":
|
||||||
if test_item == "gamma":
|
if test_item == "gamma":
|
||||||
self.app.root.after(
|
dispatch(self.sdr_gamma_test_btn.config, state=tk.NORMAL)
|
||||||
0, lambda: self.sdr_gamma_test_btn.config(state=tk.NORMAL)
|
|
||||||
)
|
|
||||||
elif test_item == "accuracy":
|
elif test_item == "accuracy":
|
||||||
self.app.root.after(
|
dispatch(self.sdr_accuracy_test_btn.config, state=tk.NORMAL)
|
||||||
0, lambda: self.sdr_accuracy_test_btn.config(state=tk.NORMAL)
|
|
||||||
)
|
|
||||||
elif test_item == "rgb":
|
elif test_item == "rgb":
|
||||||
self.app.root.after(
|
dispatch(self.sdr_rgb_test_btn.config, state=tk.NORMAL)
|
||||||
0, lambda: self.sdr_rgb_test_btn.config(state=tk.NORMAL)
|
|
||||||
)
|
|
||||||
elif test_type == "hdr_movie":
|
elif test_type == "hdr_movie":
|
||||||
if test_item == "eotf":
|
if test_item == "eotf":
|
||||||
self.app.root.after(
|
dispatch(self.hdr_eotf_test_btn.config, state=tk.NORMAL)
|
||||||
0, lambda: self.hdr_eotf_test_btn.config(state=tk.NORMAL)
|
|
||||||
)
|
|
||||||
elif test_item == "accuracy":
|
elif test_item == "accuracy":
|
||||||
self.app.root.after(
|
dispatch(self.hdr_accuracy_test_btn.config, state=tk.NORMAL)
|
||||||
0, lambda: self.hdr_accuracy_test_btn.config(state=tk.NORMAL)
|
|
||||||
)
|
|
||||||
elif test_item == "rgb":
|
elif test_item == "rgb":
|
||||||
self.app.root.after(
|
dispatch(self.hdr_rgb_test_btn.config, state=tk.NORMAL)
|
||||||
0, lambda: self.hdr_rgb_test_btn.config(state=tk.NORMAL)
|
|
||||||
)
|
|
||||||
|
|
||||||
# ==================== 辅助方法 ====================
|
# ==================== 辅助方法 ====================
|
||||||
|
|
||||||
|
|||||||
@@ -283,6 +283,29 @@ class PQAutomationApp:
|
|||||||
)
|
)
|
||||||
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
|
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||||
|
|
||||||
|
def _dispatch_ui(self, fn, *args, **kwargs):
|
||||||
|
"""把 fn(*args, **kwargs) 调度到 Tk 主线程执行。
|
||||||
|
|
||||||
|
统一替代散落各处的 self.root.after(0, lambda: ...) 写法:
|
||||||
|
- 自动捕获异常并记录日志,避免工作线程静默丢失 UI 更新失败;
|
||||||
|
- 参数用位置/关键字传入,绕开 lambda 闭包捕获变量的常见坑;
|
||||||
|
- 允许在 UI 销毁(如关闭窗口)后安全失败。
|
||||||
|
"""
|
||||||
|
def _runner():
|
||||||
|
try:
|
||||||
|
fn(*args, **kwargs)
|
||||||
|
except Exception as exc:
|
||||||
|
log = getattr(self, "log_gui", None)
|
||||||
|
if log is not None:
|
||||||
|
try:
|
||||||
|
log.log(f"UI 调度异常: {exc}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
self.root.after(0, _runner)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
get_config_path = _cfg_get_config_path
|
get_config_path = _cfg_get_config_path
|
||||||
def initialize_default_test_type(self):
|
def initialize_default_test_type(self):
|
||||||
"""初始化默认测试类型(在所有控件创建完成后调用)"""
|
"""初始化默认测试类型(在所有控件创建完成后调用)"""
|
||||||
|
|||||||
@@ -70,7 +70,52 @@ a = Analysis(
|
|||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=[('assets', 'assets'), ('UniTAP', 'UniTAP')],
|
datas=[('assets', 'assets'), ('UniTAP', 'UniTAP')],
|
||||||
hiddenimports=[],
|
hiddenimports=[
|
||||||
|
# app 包的子模块在主文件里是静态 import 的,但惰性调用 / 属性绑定
|
||||||
|
# 场景较多,显式列出可避免 PyInstaller 漏打包。
|
||||||
|
'app',
|
||||||
|
'app.config_io',
|
||||||
|
'app.data_range_converter',
|
||||||
|
'app.resources',
|
||||||
|
'app.device',
|
||||||
|
'app.device.connection',
|
||||||
|
'app.pq',
|
||||||
|
'app.pq.pq_config',
|
||||||
|
'app.pq.pq_result',
|
||||||
|
'app.plots',
|
||||||
|
'app.plots.plot_accuracy',
|
||||||
|
'app.plots.plot_cct',
|
||||||
|
'app.plots.plot_contrast',
|
||||||
|
'app.plots.plot_eotf',
|
||||||
|
'app.plots.plot_gamma',
|
||||||
|
'app.plots.plot_gamut',
|
||||||
|
'app.runner',
|
||||||
|
'app.runner.test_runner',
|
||||||
|
'app.tests',
|
||||||
|
'app.tests.color_accuracy',
|
||||||
|
'app.tests.eotf',
|
||||||
|
'app.tests.gamma',
|
||||||
|
'app.tests.gamut',
|
||||||
|
'app.tests.local_dimming',
|
||||||
|
'app.views',
|
||||||
|
'app.views.chart_frame',
|
||||||
|
'app.views.collapsing_frame',
|
||||||
|
'app.views.panel_manager',
|
||||||
|
'app.views.pq_debug_panel',
|
||||||
|
'app.views.pq_log_gui',
|
||||||
|
'app.views.panels',
|
||||||
|
'app.views.panels.cct_panel',
|
||||||
|
'app.views.panels.custom_template_panel',
|
||||||
|
'app.views.panels.main_layout',
|
||||||
|
'app.views.panels.side_panels',
|
||||||
|
'drivers',
|
||||||
|
'drivers.baseSerail',
|
||||||
|
'drivers.caSerail',
|
||||||
|
'drivers.tvSerail',
|
||||||
|
'drivers.UCD323_Enum',
|
||||||
|
'drivers.UCD323_Function',
|
||||||
|
'drivers.ucd_helpers',
|
||||||
|
],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
hooksconfig={},
|
hooksconfig={},
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"current_test_type": "screen_module",
|
"current_test_type": "sdr_movie",
|
||||||
"test_types": {
|
"test_types": {
|
||||||
"screen_module": {
|
"screen_module": {
|
||||||
"name": "屏模组性能测试",
|
"name": "屏模组性能测试",
|
||||||
|
|||||||
Reference in New Issue
Block a user