@@ -6,9 +6,7 @@ import threading
import time
import os
import datetime
import re
import traceback
import numpy as np
import matplotlib . pyplot as plt
from app_version import APP_NAME , APP_VERSION , get_app_title
from drivers . UCD323_Function import UCDController
@@ -24,6 +22,7 @@ from app.views.panels import custom_template_panel as _ctp
from app . views . panels import side_panels as _sp
from app . views . panels import cct_panel as _ccp
from app . views . panels import main_layout as _main
from app . views import panel_manager as PM
# Step 0/1 重构:资源工具和纯算法已迁移到 app/ 包,这里重新导入以保持
# 对原函数名/方法名的向后兼容(老代码内部仍用 self.calculate_* 调用)。
@@ -107,7 +106,6 @@ from app.runner.test_runner import (
plt . rcParams [ " font.family " ] = [ " sans-serif " ]
plt . rcParams [ " font.sans-serif " ] = [ " Microsoft YaHei " ]
class PQAutomationApp :
def __init__ ( self , root ) :
self . root = root
@@ -133,9 +131,7 @@ class PQAutomationApp:
# 创建主框架
self . main_frame = ttk . Frame ( root )
self . main_frame . pack ( fill = tk . BOTH , expand = True , padx = 10 , pady = 10 )
backgroud_style_set ( )
# 创建配置对象
@@ -242,13 +238,9 @@ class PQAutomationApp:
pass
def initialize_default_test_type ( self ) :
""" 初始化默认测试类型(在所有控件创建完成后调用) """
try :
# 强制切换到 屏模组
# 初始设置为 屏模组
self . change_test_type ( " screen_module " )
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 默认测试类型已设置为屏模组 " )
except Exception as e :
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( f " 初始化默认测试类型失败: { str ( e ) } " )
@@ -257,6 +249,10 @@ class PQAutomationApp:
load_pq_config = _cfg_load_pq_config
save_pq_config = _cfg_save_pq_config
register_panel = PM . register_panel
show_panel = PM . show_panel
hide_all_panels = PM . hide_all_panels
init_gamut_chart = _cf_init_gamut_chart
init_gamma_chart = _cf_init_gamma_chart
init_eotf_chart = _cf_init_eotf_chart
@@ -275,6 +271,8 @@ class PQAutomationApp:
create_test_type_frame = _main . create_test_type_frame
update_config_info_display = _main . update_config_info_display
on_screen_module_timing_changed = _main . on_screen_module_timing_changed
update_test_items = _main . update_test_items
on_test_type_change = _main . on_test_type_change
create_cct_params_frame = _ccp . create_cct_params_frame
_get_cct_var_dict = _ccp . _get_cct_var_dict
@@ -340,185 +338,112 @@ class PQAutomationApp:
clear_ld_records = _ld_clear_ld_records
save_local_dimming_results = _ld_save_local_dimming_results
def update_test_items ( self ) :
""" 根据当前测试类型更新测试项目复选框 """
# 先隐藏所有测试项目框架
for config in self . test_items . values ( ) :
config [ " frame " ] . pack_forget ( )
current_test_type = self . config . current_test_type
self . test_vars = { }
if current_test_type in self . test_items :
config = self . test_items [ current_test_type ]
frame = config [ " frame " ]
frame . pack ( fill = tk . X , padx = 5 , pady = 5 )
# 添加测试类型标签
type_label = ttk . Label (
frame ,
text = self . get_test_type_display_name ( current_test_type ) ,
style = " primary.TLabel " ,
)
type_label . grid ( row = 0 , column = 0 , columnspan = 2 , sticky = tk . W , padx = 5 , pady = 3 )
# 从配置中读取保存的选择状态
saved_test_items = self . config . current_test_types [ current_test_type ] . get (
" test_items " , [ ]
)
# 添加复选框
for i , ( text , var_name ) in enumerate ( config [ " items " ] ) :
# 修改:根据配置决定是否勾选
# 如果配置中有该测试项,则勾选;否则不勾选
is_checked = var_name in saved_test_items
var = tk . BooleanVar ( value = is_checked )
self . test_vars [ f " { current_test_type } _ { var_name } " ] = var
ttk . Checkbutton (
frame ,
text = text ,
variable = var ,
bootstyle = " round-toggle " ,
command = self . update_config_and_tabs ,
) . grid ( row = i / / 2 + 1 , column = i % 2 , sticky = tk . W , padx = 10 , pady = 5 )
# 只有在 chart_notebook 已创建后才更新状态
if hasattr ( self , " chart_notebook " ) :
self . update_chart_tabs_state ( )
# 更新色度参数框的显示状态
if hasattr ( self , " cct_params_frame " ) :
self . toggle_cct_params_frame ( )
def get_test_type_display_name ( self , test_type ) :
""" 获取测试类型的显示名称 """
display_names = {
" screen_module " : " 屏模组性能测试 " ,
" sdr_movie " : " SDR Movie测试 " ,
" hdr_movie " : " HDR Movie测试 " ,
}
return display_names . get ( test_type , test_type )
def register_panel ( self , panel_name , frame , button , visible_attr ) :
""" 注册一个面板到管理系统 """
self . panels [ panel_name ] = {
" frame " : frame ,
" button " : button ,
" visible_attr " : visible_attr ,
}
def show_panel ( self , panel_name ) :
""" 显示指定面板,隐藏其他所有面板 """
if panel_name not in self . panels :
return
# 如果当前面板就是要显示的面板,则隐藏它
if self . current_panel == panel_name :
self . hide_all_panels ( )
return
# 隐藏所有面板
self . hide_all_panels ( )
# 显示指定面板
panel_info = self . panels [ panel_name ]
# 隐藏主内容区域
self . control_frame_top . pack_forget ( )
self . control_frame_middle . pack_forget ( )
self . control_frame_bottom . pack_forget ( )
# 显示目标面板
panel_info [ " frame " ] . pack ( side = tk . TOP , fill = tk . BOTH , expand = True , padx = 5 , pady = 5 )
# 更新按钮样式
if panel_info [ " button " ] :
panel_info [ " button " ] . configure ( style = " SidebarSelected.TButton " )
# 更新状态
setattr ( self , panel_info [ " visible_attr " ] , True )
self . current_panel = panel_name
def hide_all_panels ( self ) :
""" 隐藏所有面板,显示主内容区域 """
# 隐藏所有注册的面板
for panel_name , panel_info in self . panels . items ( ) :
panel_info [ " frame " ] . pack_forget ( )
if panel_info [ " button " ] :
panel_info [ " button " ] . configure ( style = " Sidebar.TButton " )
setattr ( self , panel_info [ " visible_attr " ] , False )
# 显示主内容区域
self . control_frame_top . pack (
side = tk . TOP , fill = tk . BOTH , expand = True , padx = 0 , pady = 5
)
self . control_frame_middle . pack (
side = tk . TOP , fill = tk . BOTH , expand = True , padx = 0 , pady = 5
)
self . control_frame_bottom . pack (
side = tk . TOP , fill = tk . BOTH , expand = True , padx = 0 , pady = 5
)
self . current_panel = None
def change_test_type ( self , test_type ) :
""" 切换测试类型 """
# 切换测试类型时,自动隐藏日志面板和 Local Dimming 面板
if self . current_panel in ( " log " , " local_dimming " ) :
self . hide_all_panels ( )
# 先保存当前测试类型的色度参数
if hasattr ( self , " cct_x_ideal_var " ) :
def _save_current_cct_params ( self , swallow_errors = True ) :
""" 按当前测试类型分发保存对应的 CCT 参数。 """
try :
current_type = self . config . current_test_type
if current_type == " screen_module " :
self . save_cct_params ( )
elif current_type == " sdr_movie " :
self . save_sdr_cct_params ( )
elif current_type == " hdr_movie " :
if hasattr ( self , " save_hdr_cct_params " ) :
elif current_type == " hdr_movie " and hasattr ( self , " save_hdr_cct_params " ) :
self . save_hdr_cct_params ( )
except Exception as e :
if not swallow_errors :
raise
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( f " 保存参数失败: { str ( e ) } " )
# 更新测试类型
def _save_cct_params_before_test_type_switch ( self ) :
""" 切换测试类型前,按当前类型保存色度参数。 """
if not hasattr ( self , " cct_x_ideal_var " ) :
return
self . _save_current_cct_params ( )
def _set_gamut_combos_state ( self , state , success_msg = None , error_prefix = " 色域参考标准状态切换失败 " ) :
""" 统一切换三个色域参考下拉框的状态。 """
try :
for attr in ( " screen_gamut_combo " , " sdr_gamut_combo " , " hdr_gamut_combo " ) :
combo = getattr ( self , attr , None )
if combo is not None :
combo . configure ( state = state )
if success_msg and hasattr ( self , " log_gui " ) :
self . log_gui . log ( success_msg )
except Exception as e :
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( f " { error_prefix } : { str ( e ) } " )
def _hide_recalc_buttons ( self , include_gamut = False ) :
""" 隐藏重新计算按钮。include_gamut=True 时同时隐藏色域重算按钮。 """
attrs = [ " recalc_cct_btn " , " sdr_recalc_cct_btn " , " hdr_recalc_cct_btn " ]
if include_gamut :
attrs + = [ " recalc_gamut_btn " , " sdr_recalc_gamut_btn " , " hdr_recalc_gamut_btn " ]
hidden = 0
for attr in attrs :
btn = getattr ( self , attr , None )
if btn is None :
continue
try :
btn . grid_remove ( )
hidden + = 1
except Exception :
pass
return hidden
def _disable_debug_panel ( self ) :
""" 禁用并隐藏单步调试面板(统一实现)。 """
if hasattr ( self , " debug_panel " ) :
try :
self . debug_panel . disable_all_debug ( )
self . log_gui . log ( " ✓ 单步调试已禁用 " )
except Exception as e :
self . log_gui . log ( f " [Error] 禁用单步调试失败: { str ( e ) } " )
if hasattr ( self , " debug_container " ) :
try :
self . debug_container . pack_forget ( )
self . log_gui . log ( " ✓ 单步调试面板已隐藏 " )
except Exception as e :
self . log_gui . log ( f " [Error] 隐藏调试面板失败: { str ( e ) } " )
def _set_config_panel_btn_state ( self , state ) :
""" 统一设置配置面板按钮状态( disabled/normal) 。 """
if not hasattr ( self , " config_panel_frame " ) :
return
try :
self . config_panel_frame . btn . configure ( state = state )
except Exception :
pass
def _apply_current_test_type ( self , test_type ) :
""" 更新 UI 变量与配置中的当前测试类型。 """
self . test_type_var . set ( test_type )
if hasattr ( self , " config " ) and hasattr ( self . config , " set_current_test_type " ) :
success = self . config . set_current_test_type ( test_type )
if not success and hasattr ( self , " log_gui " ) :
self . log_gui . log ( f " 切换测试类型失败: { test_type } " )
# 更新测试项目和侧边栏
self . update_test_items ( )
self . update_sidebar_selection ( )
self . on_test_type_change ( )
def _switch_signal_format_tabs ( self , test_type ) :
""" 切换信号格式 Tab 到目标测试类型。 """
if not hasattr ( self , " signal_tabs " ) :
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " [Error] signal_tabs 尚未创建 " )
return
# ========== ✅ 1. 切换信号格式 Tab ==========
if hasattr ( self , " signal_tabs " ) :
try :
# 定义测试类型与信号格式 Tab 的映射
tab_mapping = {
" screen_module " : 0 , # 屏模组测试
" sdr_movie " : 1 , # SDR测试
" hdr_movie " : 2 , # HDR
" screen_module " : 0 ,
" sdr_movie " : 1 ,
" hdr_movie " : 2 ,
}
target_tab = tab_mapping . get ( test_type , 0 )
# 先启用所有 Tab
for i in range ( 3 ) :
self . signal_tabs . tab ( i , state = " normal " )
# 切换到目标 Tab
self . signal_tabs . select ( target_tab )
# 强制刷新显示
self . signal_tabs . update ( )
self . root . update_idletasks ( )
# 强制显示对应的 Frame
if target_tab == 0 :
self . screen_module_signal_frame . tkraise ( )
elif target_tab == 1 :
@@ -526,78 +451,56 @@ class PQAutomationApp:
elif target_tab == 2 :
self . hdr_signal_frame . tkraise ( )
# 禁用其他 Tab
for i in range ( 3 ) :
if i != target_tab :
self . signal_tabs . tab ( i , state = " disabled " )
# 日志记录
if hasattr ( self , " log_gui " ) :
tab_names = [ " 屏模组测试 " , " SDR测试 " , " HDR " ]
self . log_gui . log ( f " ✓ 已切换到 { tab_names [ target_tab ] } 信号格式 " )
except Exception as e :
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( f " 切换信号格式失败: { str ( e ) } " )
else :
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ⚠️ signal_tabs 尚未创建 " )
# ========== 2. 动态切换 Gamma/EOTF Tab ==========
if hasattr ( self , " chart_notebook " ) :
def _switch_chart_tabs_by_test_type ( self , test_type ) :
""" 按测试类型切换 Gamma/EOTF 与客户模板结果 Tab。 """
if not hasattr ( self , " chart_notebook " ) :
return
try :
current_tabs = list ( self . chart_notebook . tabs ( ) )
# 获取当前 Tab 的索引
gamma_tab_id = str ( self . gamma_chart_frame )
eotf_tab_id = str ( self . eotf_chart_frame )
if test_type == " hdr_movie " :
# ========== HDR 测试:移除 Gamma, 添加 EOTF ==========
# 1. 如果 Gamma Tab 存在,移除它
if gamma_tab_id in current_tabs :
gamma_index = current_tabs . index ( gamma_tab_id )
self . chart_notebook . forget ( gamma_index )
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 已隐藏 Gamma 曲线 Tab " )
# 2. 如果 EOTF Tab 不存在,添加它(在色域图之后)
if eotf_tab_id not in current_tabs :
self . chart_notebook . insert (
1 , self . eotf_chart_frame , text = " EOTF 曲线 "
)
self . chart_notebook . insert ( 1 , self . eotf_chart_frame , text = " EOTF 曲线 " )
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 已显示 EOTF 曲线 Tab " )
else :
# ========== SDR/屏模组测试:移除 EOTF, 添加 Gamma ==========
# 1. 如果 EOTF Tab 存在,移除它
if eotf_tab_id in current_tabs :
eotf_index = current_tabs . index ( eotf_tab_id )
self . chart_notebook . forget ( eotf_index )
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 已隐藏 EOTF 曲线 Tab " )
# 2. 如果 Gamma Tab 不存在,添加它(在色域图之后)
if gamma_tab_id not in current_tabs :
self . chart_notebook . insert (
1 , self . gamma_chart_frame , text = " Gamma 曲线 "
)
self . chart_notebook . insert ( 1 , self . gamma_chart_frame , text = " Gamma 曲线 " )
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 已显示 Gamma 曲线 Tab " )
# ========== 3. 仅在 SDR 测试显示客户模板结果 Tab ==========
custom_tab_id = str ( self . custom_template_tab_frame )
current_tabs = list ( self . chart_notebook . tabs ( ) )
if test_type == " sdr_movie " :
if custom_tab_id not in current_tabs :
self . chart_notebook . add (
self . custom_template_tab_frame ,
text = " 客户模板结果显示 " ,
)
self . chart_notebook . add ( self . custom_template_tab_frame , text = " 客户模板结果显示 " )
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 已显示客户模板结果 Tab " )
else :
@@ -606,211 +509,155 @@ class PQAutomationApp:
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( " ✓ 已隐藏客户模板结果 Tab " )
# 刷新显示
self . chart_notebook . update_idletasks ( )
except Exception as e :
if hasattr ( self , " log_gui " ) :
self . log_gui . log ( f " 切换 Gamma/EOTF Tab 失败: { str ( e ) } " )
def on _test_type_change ( self ) :
""" 根据 测试类型更新内容区域 """
test_type = self . test_type_var . get ( )
def change _test_type( self , test_type ):
""" 切换 测试类型"""
# 切换测试类型时,自动隐藏日志面板和 Local Dimming 面板
if self . current_panel in ( " log " , " local_dimming " ) :
self . hide_all_panels ( )
self . _save_cct_params_before_test_type_switch ( )
self . _apply_current_test_type ( test_type )
# 获取当前测试类型的配置
if hasattr ( self , " config " ) and hasattr ( self . config , " get_current_config " ) :
current_config = self . config . get_current_config ( )
# 更新测试项目和侧边栏
self . update_test_items ( )
self . update_sidebar_selection ( )
self . on_test_type_change ( )
self . _switch_signal_format_tabs ( test_type )
self . _switch_chart_tabs_by_test_type ( test_type )
# 更新配置信息显示
self . update_config_info_display ( )
# SDR 选中时显示客户模版按钮
self . update_custom_button_visibility ( )
def start_test ( self ) :
""" 开始测试 """
# 检查设备连接状态
def _check_start_preconditions ( self ) :
""" 检查开始测试前置条件:设备连接 & 未在测试中。 """
if self . ca is None or self . ucd is None :
messagebox . showerror ( " 错误 " , " 请先连接CA410和信号发生器 " )
return
# 检查是否已经在测试中
return False
if self . testing :
messagebox . showinfo ( " 提示 " , " 测试已在进行中 " )
return False
return True
def _collapse_config_panel_for_test ( self ) :
""" 收起配置项并禁用其按钮。 """
if not hasattr ( self , " config_panel_frame " ) :
return
# ✅ 禁用并隐藏单步调试
if hasattr ( self , " debug_panel " ) :
self . debug_panel . disable_all_debug ( )
self . log_gui . log ( " ✓ 单步调试已禁用 " )
if hasattr ( self , " debug_container " ) :
self . debug_container . pack_forget ( )
self . log_gui . log ( " ✓ 单步调试面板已隐藏 " )
# 获取测试类型和测试项目
test_type = self . test_type_var . get ( )
test_items = self . get_selected_test_items ( )
if not test_items :
messagebox . showinfo ( " 提示 " , " 请至少选择一个测试项目 " )
return
# 自动收起配置项
if hasattr ( self , " config_panel_frame " ) :
try :
if self . config_panel_frame . winfo_viewable ( ) :
self . config_panel_frame . btn . invoke ( )
self . root . update_idletasks ( )
time . sleep ( 0.2 )
except :
except Exception :
pass
self . _set_config_panel_btn_state ( " disabled " )
# 禁用配置项按钮
try :
self . config_panel_frame . btn . configure ( state = " disabled " )
except :
pass
# ✅ 新增:禁用色域参考标准下拉框
try :
if hasattr ( self , " screen_gamut_combo " ) :
self . screen_gamut_combo . configure ( state = " disabled " )
if hasattr ( self , " sdr_gamut_combo " ) :
self . sdr_gamut_combo . configure ( state = " disabled " )
if hasattr ( self , " hdr_gamut_combo " ) :
self . hdr_gamut_combo . configure ( state = " disabled " )
except Exception as e :
self . log_gui . log ( f " 禁用色域参考标准失败: { str ( e ) } " )
# 隐藏所有重新计算按钮
if hasattr ( self , " recalc_cct_btn " ) :
try :
self . recalc_cct_btn . grid_remove ( )
except :
pass
if hasattr ( self , " sdr_recalc_cct_btn " ) :
try :
self . sdr_recalc_cct_btn . grid_remove ( )
except :
pass
if hasattr ( self , " hdr_recalc_cct_btn " ) :
try :
self . hdr_recalc_cct_btn . grid_remove ( )
except :
pass
# 更新UI状态
def _set_ui_testing_state ( self ) :
""" 切换主 UI 到测试中状态(按钮禁用、状态栏、清空日志/图表)。 """
self . testing = True
self . start_btn . config ( state = tk . DISABLED )
self . stop_btn . config ( state = tk . NORMAL )
self . save_btn . config ( state = tk . DISABLED )
self . clear_config_btn . config ( state = tk . DISABLED )
self . status_var . set ( " 测试进行中... " )
# 清空日志和图表
self . log_gui . clear_log ( )
self . clear_chart ( )
# 根据测试类型显示不同提示
if test_type == " screen_module " :
# 屏模组测试:提示 byPass All PQ
message = f " 开始屏模组性能测试,请 byPass All PQ "
def _prepare_ui_for_test_start ( self ) :
""" 为开始测试准备 UI( 禁用调试、收起配置、禁用色域、隐藏重算按钮、切换状态) 。 """
self . _disable_debug_panel ( )
self . _collapse_config_panel_for_test ( )
self . _set_gamut_combos_state ( " disabled " , error_prefix = " 禁用色域参考标准失败 " )
self . _hide_recalc_buttons ( include_gamut = False )
self . _set_ui_testing_state ( )
elif test_type == " sdr_movie " :
# SDR测试: 提示设置正确图像模式
message = f " 开始 SDR Movie 测试,请设置正确的图像模式 "
elif test_type == " hdr_movie " :
# HDR测试: 提示设置正确图像模式
message = f " 开始 HDR Movie 测试,请设置正确的图像模式 "
else :
message = f " 开始 { self . get_test_type_name ( test_type ) } 测试 "
confirm = messagebox . askyesno ( " 确认测试 " , message )
if not confirm :
def _rollback_test_start ( self ) :
""" 用户取消测试时恢复 UI 状态。 """
self . testing = False
self . start_btn . config ( state = tk . NORMAL )
self . stop_btn . config ( state = tk . DISABLED )
self . clear_config_btn . config ( state = tk . NORMAL )
self . status_var . set ( " 测试已取消 " )
self . _set_config_panel_btn_state ( " normal " )
# 恢复配置项按钮
if hasattr ( self , " config_panel_frame " ) :
try :
self . config_panel_frame . btn . configure ( state = " normal " )
except :
pass
return
def _build_test_start_message ( self , test_type ) :
""" 根据测试类型生成确认弹框提示文案。 """
if test_type == " screen_module " :
return " 开始屏模组性能测试,请 byPass All PQ "
if test_type == " sdr_movie " :
return " 开始 SDR Movie 测试,请设置正确的图像模式 "
if test_type == " hdr_movie " :
return " 开始 HDR Movie 测试,请设置正确的图像模式 "
return f " 开始 { self . get_test_type_name ( test_type ) } 测试 "
# 在新线程中执行测试
def _launch_test_thread ( self , test_type , test_items ) :
""" 在新线程中执行测试。 """
self . test_thread = threading . Thread (
target = self . run_test , args = ( test_type , test_items )
)
self . test_thread . daemon = True
self . test_thread . start ( )
def stop _test ( self ) :
""" 停止测试 - 放弃本次所有数据(完全集成版) """
if not self . testing :
def start _test ( self ) :
""" 开始测试 """
if not self . _check_start_preconditions ( ) :
return
# ========== 1. 添加确认对话框 ==========
confirm = messagebox . askyesno (
test_type = self . test_type_var . get ( )
test_items = self . get_selected_test_items ( )
if not test_items :
messagebox . showinfo ( " 提示 " , " 请至少选择一个测试项目 " )
return
self . _prepare_ui_for_test_start ( )
message = self . _build_test_start_message ( test_type )
if not messagebox . askyesno ( " 确认测试 " , message ) :
self . _rollback_test_start ( )
return
self . _launch_test_thread ( test_type , test_items )
def _confirm_stop_test ( self ) :
""" 弹出确认停止测试对话框。 """
return messagebox . askyesno (
" 确认停止测试 " ,
" 测试正在进行中,确定要停止吗? \n \n ⚠️ 停止后将放弃本次测试的所有数据,无法保存。" ,
" 测试正在进行中,确定要停止吗? \n \n 停止后将放弃本次测试的所有数据,无法保存。 " ,
icon = " warning " ,
)
i f not confirm :
self . log_gui . log ( " 用户取消停止操作 " )
return
# ========== 2. 立即设置停止标志 ==========
self . testing = False # ← 关键:先设置标志,让测试线程停止
de f _signal_stop_and_update_ui ( self ) :
""" 设置停止标志并立即更新 UI 以反馈给用户。 """
self . testing = False
self . log_gui . log ( " = " * 50 )
self . log_gui . log ( " ⚠️ 正在停止测试..." )
self . log_gui . log ( " 正在停止测试... " )
self . log_gui . log ( " = " * 50 )
# ========== 3. 立即更新UI状态( 让用户感知到停止) ==========
self . stop_btn . config ( state = tk . DISABLED )
self . status_var . set ( " 正在停止测试,请稍候... " )
self . root . update ( ) # 立即刷新界面
self . root . update ( )
# ========== 4. 等待测试线程结束 ==========
if self . test_thread and self . test_thread . is_alive ( ) :
def _wait_for_test_thread ( self , timeout_seconds = 5 ) :
""" 等待测试线程结束,最多 timeout_seconds 秒,同时保持 UI 响应。 """
if not ( self . test_thread and self . test_thread . is_alive ( ) ) :
return
self . log_gui . log ( " 等待测试线程结束... " )
# 等待最多5秒
for i in range ( 50 ) : # 50 * 0.1秒 = 5秒
for _ in range ( int ( timeout_seconds * 10 ) ) :
if not self . test_thread . is_alive ( ) :
break
time . sleep ( 0.1 )
self . root . update ( ) # 保持界面响应
self . root . update ( )
if self . test_thread . is_alive ( ) :
self . log_gui . log ( " ⚠️ 测试线程未能正常结束,将在后台继续等待" )
self . log_gui . log ( " [Error] 测试线程未能正常结束,将在后台继续等待" )
else :
self . log_gui . log ( " ✓ 测试线程已结束 " )
# ========== 5. 延迟1秒后执行清理( 使用内部函数) ==========
def cleanup_and_finish ( ) :
""" 清理数据并完成停止操作 """
# ========== 5.1 清理测试数据 ==========
def _clear_test_data ( self ) :
""" 清空测试结果对象与中间数据缓存。 """
try :
self . log_gui . log ( " 清理测试数据... " )
# 清空测试结果对象
if hasattr ( self , " results " ) :
self . results = None
self . log_gui . log ( " ✓ 测试结果对象已清空 " )
# 清空中间数据缓存
for attr in [
" gamut_results " ,
" gamma_results " ,
@@ -820,35 +667,31 @@ class PQAutomationApp:
] :
if hasattr ( self , attr ) :
setattr ( self , attr , None )
self . log_gui . log ( " ✓ 所有中间数据已清空 " )
except Exception as e :
self . log_gui . log ( f " ⚠️ 清理数据时出错: { str ( e ) } " )
self . log_gui . log ( f " [Error] 清理数据时出错: { str ( e ) } " )
# ========== 5.2 清空图表显示 ==========
def _clear_charts_and_tables ( self ) :
""" 清空图表与客户模板结果表格,并跳转到色域图 Tab。 """
try :
self . clear_chart ( )
self . log_gui . log ( " ✓ 图表已清空 " )
except Exception as e :
self . log_gui . log ( f " ⚠️ 清空图表时出错: { str ( e ) } " )
self . log_gui . log ( f " [Error] 清空图表时出错: { str ( e ) } " )
try :
self . clear_custom_template_results ( )
self . log_gui . log ( " ✓ 客户模板结果表格已清空 " )
except Exception as e :
self . log_gui . log ( f " ⚠️ 清空客户模板结果表格失败: { str ( e ) } " )
# ========== 5.2.5 跳转到色域图Tab( 第一个Tab) ==========
self . log_gui . log ( f " [Error] 清空客户模板结果表格失败: { str ( e ) } " )
try :
if hasattr ( self , " chart_notebook " ) :
self . chart_notebook . select ( self . gamut_chart_frame )
self . root . update_idletasks ( ) # ← 刷新界面
self . log_gui . log ( " ✓ 已跳转到色域图界面 " )
self . root . update_idletasks ( )
except Exception as e :
self . log_gui . log ( f " ⚠️ 跳转到色域图失败: { str ( e ) } " )
self . log_gui . log ( f " [Error] 跳转到色域图失败: { str ( e ) } " )
# ========== 5.3 更新UI状态 ==========
def _restore_ui_after_stop ( self ) :
""" 恢复主按钮与状态栏到非测试态。 """
self . set_custom_result_table_locked ( False )
self . start_btn . config ( state = tk . NORMAL )
self . stop_btn . config ( state = tk . DISABLED )
@@ -857,81 +700,40 @@ class PQAutomationApp:
if hasattr ( self , " custom_btn " ) :
self . custom_btn . config ( state = tk . NORMAL )
self . status_var . set ( " 测试已停止 - 数据已清空 " )
self . log_gui . log ( " ✓ UI状态已更新 " )
# ========== 5.4 恢复配置项按钮 ==========
if hasattr ( self , " config_panel_frame " ) :
try :
self . config_panel_frame . btn . configure ( state = " normal " )
self . log_gui . log ( " ✓ 配置项已恢复 " )
except :
pass
self . _set_config_panel_btn_state ( " normal " )
# ========== 5.4.5 禁用色域参考标准下拉框 ==========
try :
if hasattr ( self , " screen_gamut_combo " ) :
self . screen_gamut_combo . configure ( state = " disabled " )
if hasattr ( self , " sdr_gamut_combo " ) :
self . sdr _gamut_combo. configure ( state = " disabled " )
if hasattr ( self , " hdr_gamut_combo " ) :
self . hdr_gamut_combo . configure ( state = " disabled " )
self . log_gui . log ( " ✓ 色域参考标准已禁用 " )
except Exception as e :
self . log_gui . log ( f " 禁用色域参考标准失败: { str ( e ) } " )
# ========== 5.5 隐藏所有重新计算按钮 ==========
try :
button_hidden_count = 0
for btn_attr in [
" recalc_cct_btn " ,
" sdr_recalc_cct_btn " ,
" hdr_recalc_cct_btn " ,
" recalc_gamut_btn " , # ✅ 新增
" sdr_recalc_gamut_btn " , # ✅ 新增
" hdr_recalc_gamut_btn " , # ✅ 新增
] :
if hasattr ( self , btn_attr ) :
try :
getattr ( self , btn_attr ) . grid_remove ( )
button_hidden_count + = 1
except :
pass
if button_hidden_count > 0 :
self . log_gui . log ( f " ✓ 已隐藏 { button_hidden_count } 个重新计算按钮 " )
except Exception as e :
self . log_gui . log ( f " ⚠️ 隐藏按钮时出错: { str ( e ) } " )
# ========== 5.6 禁用并隐藏单步调试 ==========
if hasattr ( self , " debug_panel " ) :
try :
self . debug_panel . disable_all_debug ( )
self . log_gui . log ( " ✓ 单步调试已禁用 " )
except Exception as e :
self . log_gui . log ( f " ⚠️ 禁用单步调试失败: { str ( e ) } " )
# ✅ 隐藏调试面板
if hasattr ( self , " debug_container " ) :
try :
self . debug_container . pack_forget ( )
self . log_gui . log ( " ✓ 单步调试面板已隐藏 " )
except Exception as e :
self . log_gui . log ( f " ⚠️ 隐藏调试面板失败: { str ( e ) } " )
# ========== 5.7 最终日志 ==========
def _finalize_stop_test ( self ) :
""" 延迟执行的清理流程总入口(由 root.after 调用)。 """
self . _clear_test_data ( )
self . _clear_charts_and_tables ( )
self . _restore_ui_after_stop ( )
self . _set _gamut_combos_state (
" disabled " ,
success_msg = " ✓ 色域参考标准已禁用 " ,
error_prefix = " 禁用 色域参考标准失败 " ,
)
hidden = self . _hide_recalc_buttons ( include_gamut = True )
if hidden > 0 :
self . log_gui . log ( f " ✓ 已隐藏 { hidden } 个重新计算按钮 " )
self . _disable_debug_panel ( )
self . log_gui . log ( " = " * 50 )
self . log_gui . log ( " ✓ 测试已停止,所有数据已清空 " )
self . log_gui . log ( " = " * 50 )
# ========== 5.8 显示提示信息 ==========
messagebox . showinfo (
" 测试已停止 " ,
" 测试已停止,本次测试数据已清空。 \n \n 可以重新开始新的测试。 " ,
)
# ========== 延迟1秒后执行清理 ==========
self . root . after ( 1000 , cleanup_and_finish )
def stop_test ( self ) :
""" 停止测试 - 放弃本次所有数据(完全集成版) """
if not self . testing :
return
if not self . _confirm_stop_test ( ) :
return
self . _signal_stop_and_update_ui ( )
self . _wait_for_test_thread ( timeout_seconds = 5 )
self . root . after ( 1000 , self . _finalize_stop_test )
# ==================== 保存测试结果 ====================
def save_results ( self ) :
@@ -970,12 +772,12 @@ class PQAutomationApp:
# 3) 成功提示
log ( " = " * 50 )
log ( f " ✅ 测试结果已保存到目录: { result_dir } " )
log ( f " 测试结果已保存到目录: { result_dir } " )
log ( " = " * 50 )
messagebox . showinfo ( " 成功 " , f " 测试结果已保存到目录: \n { result_dir } " )
except Exception as e :
self . log_gui . log ( f " ❌ 保存测试结果失败: { str ( e ) } " )
self . log_gui . log ( f " [Error] 保存测试结果失败: { str ( e ) } " )
import traceback
self . log_gui . log ( traceback . format_exc ( ) )
@@ -1052,7 +854,7 @@ class PQAutomationApp:
self . save_pq_config ( )
except Exception as e :
self . log_gui . log ( f " 更新配置失败: { str ( e ) } " )
self . log_gui . log ( f " [Error] 更新配置失败: { str ( e ) } " )
def update_config_and_tabs ( self ) :
""" 更新配置并同步Tab状态 """
@@ -1060,21 +862,8 @@ class PQAutomationApp:
self . update_chart_tabs_state ( )
# 根据当前测试类型保存对应参数
current_test_type = self . config . current_test_type
selected_items = self . get_selected_test_ite ms( )
if current_test_type == " screen_module " :
if " cct " in selected_items :
self . save_cct_params ( )
elif current_test_type == " sdr_movie " :
if " cct " in selected_items :
self . save_sdr_cct_params ( )
elif current_test_type == " hdr_movie " :
if " cct " in selected_items :
if hasattr ( self , " save_hdr_cct_params " ) :
self . save_hdr_cct_params ( )
if " cct " in self . get_selected_test_items ( ) :
self . _save_current_cct_para ms ( )
# 控制参数框的显示
self . toggle_cct_params_frame ( )