# -*-coding=utf-8-*-

# @Time : 2020/4/23 16:17
# @File : main_ui.py
import csv
import datetime
import gc
import re
import sys
import os
import tempfile
import pandas as pd
import xlwt
from pyqtgraph import ImageItem
sys.path.append("../")  # 解决潜在的路径依赖问题
sys.path.append("../../")  # 解决潜在的路径依赖问题
sys.path.append("../../../")  # 解决潜在的路径依赖问题
sys.path.append("../../../../")  # 解决潜在的路径依赖问题
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import threading
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from t1k.resource.draw_ut import draw_bscan_from_csv as draw_bscan_from_csv_zoom
from t1k.resource.convertor import T1kConvertor
from pathlib import PurePath
from t1k.resource.config import Config
from t1k.resource.network import ftp
from PyQt5.QtWidgets import QMenu, qApp, QAction, QFileDialog, QInputDialog, QDirModel, QFileSystemModel, \
    QGraphicsPixmapItem, QGraphicsScene, QTableWidgetItem
import warnings
# from t1k.resource.get_csv_list_from_csv import denoising
from t1k.resource.get_single_csv_from_csv_v2 import denoising
warnings.filterwarnings("ignore")
from t1k.resource.t1k_simple import RecordTestDesc
from t1k.resource.mysql_db import DB
from t1k.resource.db_util import get_db_manager, headers, get_md5, CONFIRM, NOT_CONFIRM, CHANGED
from t1k.ui import Ui_MainWindow, CustomImageItem
from t1k.child_window import *
from t1k.draw_frame import *
from PyQt5.QtCore import Qt, pyqtSignal
from t1k.resource.util import llogger,compress_file
from t1k.resource.sqltableview.sqltableview import CsvTableControl
from t1k.resource.db_conf import password, user
from t1k.resource.channel import parse_channels
LOG_PAHT = 'log'

current = datetime.datetime.now().strftime('%Y-%m-%d')
logger = llogger(f't1k_{current}', LOG_PAHT)


class MainWind(Ui_MainWindow, QMainWindow):
    process_t1k_sig = pyqtSignal(object)
    copy_mysql_sig = pyqtSignal(str)  # 信号
    show_table_sig = pyqtSignal(str)
    slot_show_table_sig = pyqtSignal(str)
    st_hits_sig = pyqtSignal(str,list)
    progress_bar_sig = pyqtSignal(int)
    dialog_sig = pyqtSignal(str,str)

    def __init__(self, parent=None):
        super(MainWind, self).__init__(parent)
        # UI初始化
        self.setupUi(self)
        # 数据初始化
        self.init_data()
        # todo 读取配置文件获得伤损类别，动态生成label
        # self.create_label(['test','test1'])
        # self.label_injurt_1
        # comment [xyun] 未导入t1k文件，禁用主窗口按钮。
        self.main_control_widget.setEnabled(False)
        # label_dict={}
        # self.create_label(label_dict,parent=self)
        # comment [xyun] 菜单栏-文件
        # self.load_t1k.setShortcut("Ctrl+O")
        # self.load_t1k.triggered.connect(self._load_t1k)
        self.load_project.triggered.connect(self.load_project_event)
        # 保存按钮设置不可点击。
        self.save.setDisabled(True)
        # self.save.triggered.connect(self.save_all_csv)


        # comment [xyun] 菜单栏-查看

        # comment [xyun] 菜单栏-设置

        self.interface_window = RunInterfaceStyle()
        self.channel_color_window = RunChannelColor()
        # self.draw_switch = DrawBoxWin()
        self.injury_category_window = RunInjuryCategory()
        self.relative_dviation_window = RunRelativeDviation()
        self.auto_save_window = RunAutoSave()
        self.work_dir_window = RunWorkDir(parent=self)

        self.interface_style.triggered.connect(self.interface_window.show)
        self.channel_color.triggered.connect(self.channel_color_window.show)
        self.injury_category.triggered.connect(self.injury_category_window.show)
        self.relative_deviation.triggered.connect(self.relative_dviation_window.show)
        self.auto_save.triggered.connect(self.auto_save_window.show)
        self.work_dir.triggered.connect(self.work_dir_window.show)
        self.frame_switch.triggered.connect(self.draw_switch_slot)
        self.save_as.triggered.connect(self.save_as_fun)

        # comment [xyun] 菜单栏-导出
        self.export_image.triggered.connect(self.export_file_event)

        # comment [xyun] 菜单栏-模型

        # comment [xyun] 菜单栏-网络
        self.upload_action.triggered.connect(self.upload_ftp)

        # comment [xyun] 菜单栏-帮助

        self.exit.triggered.connect(self.exit_event)

        # 表格配置
        setting = {'host':'localhost', 'user':user, 'password': password}
        self.csv_table_sql = CsvTableControl(self.g_config.tablesearchlist, self.g_config.chinese_dict, self.g_config.exception_list,self.analyze_widget, kwargs=setting)
        self.csv_table_sql.table.view.clicked.connect(self.view_thumbnail)
        self.csv_table_sql.table.view.key_up_down_signal.connect(self.csv_table_key_event)


        # 定时器
        # self.timer = QtCore.QTimer(self)  # 初始化一个定时器
        # self.timer.timeout.connect(self.save_csv_interval)  # 计时结束调用operate()方法
        # RECORD_TIME_INTERVAL = 5 * 60 * 1000
        # self.timer.start(RECORD_TIME_INTERVAL)  # 设置计时间隔并启动

        # comment [xyun] 初始化工作目录,绑定工作目录双击事件。
        work_path = yaml_config.work_option.get('path')
        self.model = QFileSystemModel()
        self.model.setRootPath(work_path)
        self.model.setNameFilterDisables(False)
        self.model.setNameFilters(['*.t1k'])
        self.dir_treeView.setModel(self.model)
        self.dir_treeView.setRootIndex(self.model.index(work_path))
        self.dir_treeView.doubleClicked.connect(self.clicked_treeView)

        self.process_draw_widget_timer = QtCore.QTimer(self)
        self.process_draw_widget_timer.timeout.connect(self.draw_main_widget_timer)

        self.main_layout = QtWidgets.QGridLayout()  # 实例化一个网格布局层
        self.main_widget.setLayout(self.main_layout)  # 设置主widget部件的布局为网格布局

        # comment [xyun] 界面控件子窗口
        self.jump_post_window = RunJumpPost()

        # comment [xyun] 主视图窗口及控件事件绑定。
        self.viewbox = self.main_widget.addViewBox(enableMouse=False, enableMenu=False)
        self.viewbox.setAspectLocked(False)
        # self.viewbox.enableAutoRange()
        self.viewbox.setBackgroundColor(QtGui.QColor(65, 67,66))
        self.image_item = CustomImageItem(axisOrder='row-major', parent=self.viewbox)
        self.image_item.draw_line_sig.connect(self.draw_distance_line)
        self.viewbox.addItem(self.image_item)

        self.injury_view = self.graphicsView.addViewBox()
        self.injury_view.setAspectLocked(True)
        self.injury_image_item = ImageItem(axisOrder='row-major')
        self.injury_view.addItem(self.injury_image_item)

        # 事件列表
        self.jump_gonglibiao_button.clicked.connect(self.control_jump_pos)
        self.backward_button.clicked.connect(self.control_backward)
        self.play_bottuon.toggled.connect(self.control_play)
        self.forward_button.clicked.connect(self.control_forward)
        self.ranging_pattern_button.clicked.connect(self.control_distance_pattern)
        self.ranging_pattern_button.toggled.connect(self.control_distance_check_button_status)

        self.crop_image_button.clicked.connect(self.crop_image)

        # self.draw_switch.draw_box_sig.connect(self.draw_switch_slot)
        self.copy_mysql_sig.connect(self.copy_mysql)
        self.process_t1k_sig.connect(self.parsing_t1k)
        self.show_table_sig.connect(self.show_table)
        self.relative_dviation_window.relate_change_sig.connect(self.change_related_position)
        self.image_item.amplification_sig.connect(self.amplification_image)
        self.image_item.restore_sig.connect(self.restore_image)
        self.slot_show_table_sig.connect(self.show_table_interval)
        self.save.triggered.connect(self.save_fun)
        self.st_hits_sig.connect(self.insert_allsthit)
        self.progress_bar_sig.connect(self.update_progress)
        self.dialog_sig.connect(self.show_msg)
        self.show()
    def show_msg(self,title,msg):
        QtWidgets.QMessageBox.about(self, title, msg)
    def show_table_interval(self,data):
        self.csv_table_sql.table.table_up_date()
    def update_progress(self,cur_pos):
        self.progressBar.setValue(cur_pos)

    def crop_image(self):
        '''
        截图
        :return:
        '''
        self.crop_image_mode = True

    def clear_widget(self):
        # todo 伤损详情

        # [child_widget.deleteLater() for child_widget in self.injurt_classify_widget.children() if
        #  isinstance(child_widget, QtWidgets.QLabel)]

        self.exception_count = 0
        self.total_count = 0

        self.error_num_label.setText('')
        self.normal_num_label.setText('')
        self.import_num_label.setText('')
        self.kilometer_label.setText('')
        self.total_width_label.setText('')
        self.total_height_table.setText('')
        self.current_speed_label.setText('')

        self.screw_crack_label.setText(str(0))
        self.track_crack_label.setText(str(0))
        self.base_defect_label.setText(str(0))
        self.transverse_defect_label.setText(str(0))
        self.vertical_defect_label.setText(str(0))


        # self.system_suspected_label.setText('')
        self.injury_image_item.clear()
        self.image_item.clear()

        for cls in self.g_config.all_category_list:
            self.cls_count[cls] = 0

    def load_project_event(self):
        '''
        菜单
        导入数据
        :return:
        '''
        # TODO 需要保证同一的t1k或者是proj
        fname_list = QtWidgets.QFileDialog.getOpenFileNames(self, 'Open file', '', "Project/T1k file (*.t1k *.proj)")
        self.t1k_file_list = fname_list[0]
        if len(self.t1k_file_list)>1:
            # t1k 列表
            for file in self.t1k_file_list:
                if file.endswith('*.t1k'):
                    file=file.split('_')[:2]
                    self.t1k_table='_'.join(file)

        # 单个文件
        elif len(self.t1k_file_list) == 1:
            self.clear_widget()
            file_path = self.t1k_file_list[0]
            filename = PurePath(file_path).name
            filename_prefix,filename_postfix = filename.split('.')
            project_name='_'.join(filename.split('_')[:2])
            project_name=filename_prefix
            self.project_name=project_name

            if filename_postfix=='proj':
                self.project_name = filename


            elif filename_postfix=='t1k':

                self.t1k_file = filename
                ret = self.sql_db.record_exists(filename_prefix)

                if ret is not None:
                    md5, total, exception, normal, update_time = ret
                    if md5:
                        # 使用事件替代
                        # self.msg('提示',f't1k文件已经在{update_time}执行')
                        self.dialog_sig.emit('提示',f't1k文件已经在{update_time}执行')
                        # 读取整个project

                        self.t1k_name.setText(filename_prefix)

                        self.get_sql_data(self.project_name,filename_prefix)
                        self.csv_table_sql.create_table(self.project_name)
                        self.csv_table_sql.table.table_up_date()
                        self.initialize_main_widget(None)

                        # 加载pos_bus
                        self.t1k_convertor=T1kConvertor(file_path,
                                          model_type=self.g_config.model_type,
                                          version=3)
                        self.t1k_convertor.t1k.chainages=list(self.sql_db.read_chainage(filename_prefix))

                        # 加test_desc
                        head_depth,web_depth,base_depth = self.sql_db.read_test_desc(filename_prefix)
                        self.test_desc=RecordTestDesc()

                        self.test_desc.web_depth=web_depth
                        self.test_desc.head_depth=head_depth
                        self.test_desc.base_depth=base_depth
                        self.finish=True
                        self.t1k_name.setText(filename_prefix+'完成识别')

                        self.update_progress(100)



                    else:
                        # self.finish=False
                        status=self.sql_db.is_exist('projects',project_name)
                        if not status:
                            # 未存在project
                            self.sql_db.create_project(self.project_name)

                        # self.sql_db.create_t1k_result_table('projects',project_name)
                        self.sql_db.create_ultra_recogn_custom(filename_prefix)
                        self.csv_table_sql.create_table(self.project_name)
                        self.task = threading.Thread(target=self.run_t1k, args=(file_path,filename_prefix,project_name))
                        self.stop_task = False
                        self.task.start()

                else:
                    self.msg('错误','读取数据库出错，请重启本软件！')




    def upload_ftp(self):
        # 将分析后文件加密并上传ftp
        'C:/Users/xyun/Desktop/bigcar_t1k/t1k/save_csv_out/804_DF090119_001.bfxt'
        if self.t1k_table:
            with tempfile.TemporaryDirectory(dir=os.getcwd()) as dirname:
                temp_file_dir = os.path.join(dirname, self.t1k_table+'.bxfs')
                self.export_data(temp_file_dir)
                ftp.upload(temp_file_dir, self.t1k_table+'.bxfs')



    def amplification_image(self, data_dict):
        # todo 获取y轴坐标不准确
        width = data_dict.get('width')
        top_rail = data_dict.get('top_rail')
        ex_distance = data_dict.get('ex_distance')

        # max_depth = 500
        # min_depth = 0
        if top_rail==0:
            max_depth = int((680-float(data_dict.get('max_depth')))/4)
            # max_depth = int((680-float(data_dict.get('max_depth')))/3)
            min_depth = int((680-float(data_dict.get('min_depth')))/4)
            # min_depth = int((680-float(data_dict.get('min_depth')))/3)
        else:
            max_depth = int((680-(float(data_dict.get('max_depth')-810)))/4)
            min_depth = int((680-(float(data_dict.get('min_depth'))-810))/4)

        if max_depth<min_depth:
            max_depth,min_depth=min_depth,max_depth

        image = draw_bscan_from_pos_sql_thumbview(self.current_table, int(self.pos_bus + ex_distance), top_rail,
                                                  int(width),
                                                  min_depth=min_depth, max_depth=max_depth)
        print('pos_bus:::::::',(self.pos_bus + ex_distance))
        if image is None:
            image = np.zeros(shape=(1590, 2400, 3))
            # image[:, :, 0] = 100
        # cv2.imwrite('test.png', image)

        origin_img = image
        image=np.flip(image)
        image=cv2.flip(image,1)
        h,w=image.shape[:2]

        # height, width = image.shape[0], image.shape[1]
        # 设置新的图片分辨率框架
        # viewbox_width_new = 2400
        # height_new = 1590
        # 判断图片的长宽比率

        if w / h >= self.image_item.width() / self.image_item.height():
            image = cv2.resize(image, (self.image_item.width(), int(h * self.image_item.width() / w)))
        else:
            image = cv2.resize(image, (int(w * self.image_item.height() / h), self.image_item.height()))

        self.image_item.setImage(image)
        self.image_item.parent.rbScaleBox.hide()
        if self.crop_image_mode:

            current=datetime.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')
            cv2.imwrite(os.path.join(self.snapshot_path,f'{self.current_table}-{current}.png'),origin_img)
            self.crop_image_mode=False
            self.msg('提示','截图成功')

    def restore_image(self):
        post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)
        self.draw_main_widget(self.current_table,self.pos_bus, f'{post:.6f}KM  {speed:.2f}KM/H', rect_option=self.draw_line)

    def change_related_position(self, data):
        # print('data',data)
        forward = data.get('forward')
        backward = data.get('backward')
        self.distance_delta = {
            'Farwards_Descending': -1 * forward,
            'Farwards_Aescending': forward,
            'Backwards_Descending': -1 * backward,
            'Backwards_Aescending': backward,
        }

    def draw_distance_line(self, ev):
        if self.ranging_pattern_button.isChecked():
            if self.ranging_x1:
                self.ranging_x2 = int(ev.pos().x())
            else:
                self.ranging_x1 = int(ev.pos().x())

            post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)
            image = draw_double_frame_sql(self.pos_bus, self.current_table, f'{post:.6f}KM  {speed:.2f}KM/H',
                        width=self.width,rect_option=self.draw_line,test_desc=self.test_desc)
            if self.ranging_x1 and self.ranging_x2:
                distance = abs(self.ranging_x2 - self.ranging_x1)
                reality_distance = self.width * distance / image.shape[1]
                text_coord = self.ranging_x1 if self.ranging_x1 < self.ranging_x2 else self.ranging_x2
                image = cv2.line(image, (self.ranging_x1, 0), (self.ranging_x1, image.shape[0]), (0, 255, 0), 3)
                image = cv2.line(image, (self.ranging_x2, 0), (self.ranging_x2, image.shape[0]), (0, 255, 0),
                                 3)
                image = cv2.putText(image, f'distance: {reality_distance}mm',
                                    (int(text_coord), int(image.shape[0] / 2)),
                                    cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 255), 2)
                image = np.flip(image)
                image = cv2.flip(image, 1)
                self.image_item.setImage(image)
                self.ranging_pattern_button.toggle()
            else:
                image = cv2.line(image, (self.ranging_x1, 0), (self.ranging_x1, image.shape[0]), (0, 255, 0), 3)
                image = np.flip(image)
                image = cv2.flip(image, 1)
                self.image_item.setImage(image)

    def control_distance_check_button_status(self):
        if self.ranging_pattern_button.isChecked():
            # 锁定按钮。
            self.play_bottuon.setEnabled(False)
            self.forward_button.setEnabled(False)
            self.backward_button.setEnabled(False)
            self.jump_gonglibiao_button.setEnabled(False)
        else:
            # 释放按钮状态。
            self.play_bottuon.setEnabled(True)
            self.forward_button.setEnabled(True)
            self.backward_button.setEnabled(True)
            self.jump_gonglibiao_button.setEnabled(True)

    def initialize_main_widget(self,table):
        # 允许操作主界面控制台。
        self.main_control_widget.setEnabled(True)
        [menu.setEnabled(True) for menu in self.menubar.children() if isinstance(menu, QtWidgets.QMenu)]
        # 渲染初始界面
        # post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)
        # self.draw_main_widget(table,self.pos_bus, f'{post:.6f}KM  {speed:.2f}KM/H', rect_option=self.draw_line)

    def init_data(self):
        '''
        数据初始化
        :return:
        '''
        self.g_config = Config()
        self.lang_map = self.g_config.chinese_dict
        self.test_desc = None
        self.t1k_convertor = None
        self.exception_count = 0
        self.total_count = 0
        self.stop_task = False
        self.total = 0
        self.pause = False
        self.t1k_results_save = []
        self.run_status = 0
        # comment [xyun] 设置主窗口播放速度，帧率等。
        self.pos_bus = 3000
        self.pos_delta = 100
        self.width = 600
        self.default_step = 0.3
        self.default_stay_time = 0.5
        self.step_lineEdit.setText(str(self.default_step))
        self.stay_time_lineEdit.setText(str(self.default_stay_time))
        self.crop_image_mode=False
        self.t1k_table = '' # remove this
        # self.table_name = ''
        self.finish = False
        self.draw_line = False
        # 添加里程修正
        # 正向检测下行-5米、正向检测上行+5米；反向检测下行-30米、反向检测上行+30米

        self.t1k_file = None
        self.project_name=None
        # self.g_config = get_global_config()
        # self.db = get_db_manager()
        self.cls_count = {}
        self.sql_db = DB()
        self.sql_db.initial_db_tb()
        self.sql_db.get_db_dict()

        self.yaml_config = yaml_config

        for cls in self.g_config.all_category_list:
            self.cls_count[cls] = 0

        self.csv_out_dir = "save_csv_out"

        self.root = os.path.dirname(__file__)

        if not os.path.exists(self.csv_out_dir):
            os.mkdir(self.csv_out_dir)
        self.snapshot_path='snapshot'

        if not os.path.exists(self.snapshot_path):
            os.mkdir(self.snapshot_path)

        forward = float(self.yaml_config.deviation.get('forward'))
        backward = float(self.yaml_config.deviation.get('backward'))

        self.distance_delta = {
            'Forwards_Descending': -1 * forward,
            'Forwards_Ascending': forward,
            'Backwards_Descending': -1 * backward,
            'Backwards_Ascending': backward,
        }

        self.post_delta=0

    def get_sql_data(self, project,file_name):
        # todo 确定输入file_name是否有后缀如 804_DF090119_001 是否有 _001, get_injury_label_data应输入804_DF090119
        label_map = {'异常螺孔': self.screw_crack_label, '轨底伤损': self.base_defect_label,
                     '轨头核伤': self.transverse_defect_label,
                     '轨头垂直劈裂': self.vertical_defect_label, '轨腰伤损': self.track_crack_label,

                     # '轨头其它':self.transverse_other_label,'，轨底其它':self.base_other_label
                     }
        total, exception, normal = self.sql_db.get_label_data(file_name)
        self.import_num_label.setText(str(total))
        self.error_num_label.setText(str(exception))
        self.normal_num_label.setText(str(normal))
        for injury in self.sql_db.get_injury_label_data(project):

            if injury[0] in label_map:
                print(injury[0])
                print(injury[1])
                if label_map.get(injury[0]):
                    print(injury[0],injury[1])
                    label_map.get(injury[0]).setText(str(injury[1]))

    def draw_main_widget(self,table,pos_bus, text, rect_option=True):
        '''
        根据pos_bus画图
        :param pos_bus:
        :param text:
        :return:
        '''
        print(f'pos_bus: {pos_bus}')

        frame = draw_double_frame_sql(pos_bus,table, text=text, width=self.width, rect_option=rect_option,test_desc=self.test_desc)

        # cv2.imwrite('test.jpg',frame)
        frame = np.flip(frame)

        frame = cv2.flip(frame, 1)

        self.image_item.setImage(frame)
        # print('viewbox外面的框')
        # print(self.viewbox.getAspectRatio())
        self.viewbox.autoRange()
        # self.image_item.setScaledMode()

        # print(self.viewbox.itemBoundingRect(self.image_item))

    def draw_main_widget_timer(self):
        post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)

        self.draw_main_widget(self.current_table,self.pos_bus, f'{post:.6f}KM  {speed:.2f}KM/H', rect_option=self.draw_line)
        self.pos_bus += self.width

    # comment [xyun] 主窗口控制台函数。
    def control_play(self):
        print(f"按钮是否被触发：{self.play_bottuon.isChecked()}")
        try:
            stay_time = float(self.stay_time_lineEdit.text())
        except:
            stay_time = self.default_stay_time
            self.stay_time_lineEdit.setText(str(self.default_stay_time))
            QMessageBox.about(self, u'提示', u"停留时间框中请输入正确的值(数字)。")
        if self.play_bottuon.isChecked():
            self.process_draw_widget_timer.start(stay_time * 1000)
        else:
            self.process_draw_widget_timer.stop()

    def control_backward(self):
        # 重置按钮状态
        if self.play_bottuon.isChecked():
            self.play_bottuon.toggle()
        if self.ranging_pattern_button.isChecked():
            self.ranging_pattern_button.toggle()

        try:
            step = float(self.step_lineEdit.text())
        except:
            step = self.default_step
            self.step_lineEdit.setText(str(self.default_step))
            QMessageBox.about(self, u'提示', u"步数框中请输入正确的值(数字)。")

        self.pos_bus -= step * 1000
        post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)
        self.draw_main_widget(self.current_table,self.pos_bus, f'{post:.6f}KM  {speed:.2f}KM/H', rect_option=self.draw_line)

    def control_forward(self):
        # 重置按钮状态
        if self.play_bottuon.isChecked():
            self.play_bottuon.toggle()
        if self.ranging_pattern_button.isChecked():
            self.ranging_pattern_button.toggle()

        try:
            step = float(self.step_lineEdit.text())
        except:
            step = self.default_step
            self.step_lineEdit.setText(str(self.default_step))
            QMessageBox.about(self, u'提示', u"步数框中请输入正确的值(数字)。")

        self.pos_bus += step * 1000
        post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)
        self.draw_main_widget(self.current_table,self.pos_bus, f'{post:.6f}KM  {speed:.2f}KM/H', rect_option=self.draw_line)

    def control_jump_pos(self):
        """
        点击"跳转公里标"按钮事件。将主界面画面跳转至对应公里标(若输入类型为pos则转换为pos_bus。)。
        :return: None
        """
        # 重置按钮状态
        if self.play_bottuon.isChecked():
            self.play_bottuon.toggle()
        if self.ranging_pattern_button.isChecked():
            self.ranging_pattern_button.toggle()

        self.jump_post_window.show()
        self.jump_post_window._signal.connect(self.jump_pos)

    def jump_pos(self, dict_data):
        """
        从跳转公里标子窗口中获取数据。
        :param dict_data: 子窗口中传递出来的用户输入数据。
        :return:
        """
        input_num = dict_data.get('input_num')
        imput_type = dict_data.get('input_type')
        add_post_delta=dict_data.get('add_post_delta')

        pos_bus = input_num
        if imput_type == 0:
            post, speed = self.t1k_convertor.t1k.pos_bus_to_post(input_num)
        else:
            post = input_num
            if add_post_delta:
                post=post-self.post_delta # TODO 读取该值
                print(f'添加了偏移量{post}')
            pos_bus, speed = self.t1k_convertor.t1k.post_to_pos_bus(post)
            # print('现在的pos_bus')

        self.pos_bus = pos_bus
        text = f'{post:.6f}KM  {speed:.2f}KM/H'
        self.draw_main_widget(self.current_table,self.pos_bus, text, rect_option=self.draw_line)

    def control_distance_pattern(self):
        # 更改播放状态，锁死播放、跳屏等按钮。
        self.ranging_x1 = None
        self.ranging_x2 = None
        if self.play_bottuon.isChecked():
            self.play_bottuon.toggle()
        post, speed = self.t1k_convertor.t1k.pos_bus_to_post(self.pos_bus)
        self.draw_main_widget(self.current_table,self.pos_bus, f'{post:.6f}KM  {speed:.2f}KM/H', rect_option=self.draw_line)

    def control_subline(self):
        pass

    def clicked_treeView(self, Qmodelidx):
        self.dir_treeView.setEnabled(False)
        self._load_t1k(self.model.filePath(Qmodelidx))
        self.unfold.close()
        self.dir_treeView.close()
        self.fold.show()

    def save_csv_interval(self):
        '''
        定时保存
        :return:
        '''

        if len(self.t1k_results_save) > 0:

            t1k_stem = PurePath(self.t1k_file).stem
            out_path = os.path.join(self.csv_out_dir, t1k_stem + '.csv')

            try:
                with open(out_path, 'w', newline='') as f:
                    f_csv = csv.DictWriter(f, fieldnames=headers)
                    f_csv.writeheader()

                    for index, item in enumerate(self.t1k_results_save):
                        # record, csv_str, cls, csv_str_ext, origin = item
                        d = {}
                        d['row'] = index
                        for idx, field in enumerate(item):
                            d[headers[idx + 1]] = field

                        f_csv.writerow(d)

            except Exception as e:
                print('定时保存')
                print(e)

    def about_msg(self):
        '''
        关于软件版本
        :return:
        '''
        self.msg('软件版本', self.ver)

    def clear_menu_method(self):
        total_count = self.csv_table.rowCount()

        for i in range(0, total_count)[::-1]:
            self.csv_table.removeRow(i)

        self.exception_count = 0
        self.total_count = 0
        self.t1k_results_save = []

        self.import_num_label.setText('{}'.format(0))
        self.error_num_label.setText('{}'.format(0))
        self.normal_num_label.setText('{}'.format(0))
        self.progressBar.setValue(0)

    def show_table(self, tablename):
        # self.csv_table_sql.create_table(tablename)
        self.initialize_main_widget()

    def read_database(self, t1k_file):
        '''
        从数据库载入sthit，ultra，result
        :param t1k_file:
        :return:
        '''
        # self.table_name = t1k_file
        self.t1k_convertor = T1kConvertor(None,
                                          model_type=self.g_config.model_type,
                                          version=0)

        # chainage 的顺序
        self.t1k_convertor.t1k.chainages = self.sql_db.read_chainage(self.t1k_table)
        # TODO 获取上一轮统计的个数

        self.show_table_sig.emit(self.t1k_table)

        # TODO 更新label

        self.dir_treeView.setEnabled(True)
        self.finish = True
        self.disableMenu(False)
        self.process_t1k_sig.emit({'prog': 1})
        # self.initialize_main_widget()

    def _load_t1k(self, fname=None):
        '''
        运行t1k文件，关联csv
        :return:
        '''
        # comment [xyun] 重新导入t1k文件，文件处理完成前禁用主窗口按钮。

        fname = [fname] if fname else QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', '', "T1k files (*.t1k)")
        if os.path.exists(fname[0]):
            self.main_control_widget.setEnabled(False)

            # self.extractAction.setDisabled(True)
            # self.clear_menu_method()
            [menu.setEnabled(False) for menu in self.menubar.children() if isinstance(menu, QtWidgets.QMenu)]
            self.import_t1k_name = fname
            self.t1k_file = fname[0]
            filename = PurePath(self.t1k_file).name

            self.t1k_table = filename.split('.')[0]
            # self.clear_menu_method()
            self.image_item.clear()
            # 数据库
            md5 = self.sql_db.exists(self.t1k_file)

            if md5:
                logger.info(f'{self.t1k_file} 已经解析过')
                self.reset()
                self.task = threading.Thread(target=self.read_database, args=(self.t1k_table,))
                self.stop_task = False
                self.task.start()

            else:
                self.reset()
                self.sql_db.create_t1k_result_table(self.t1k_table)
                self.csv_table_sql.create_table(self.t1k_table)
                self.task = threading.Thread(target=self.run_t1k_v0, args=(self.t1k_file,))
                self.stop_task = False
                self.task.start()

    def reset(self):
        '''
        重置全局变量
        :return:
        '''

        self.cls_count = {}
        for cls in self.g_config.all_category_list:
            self.cls_count[cls] = 0

        self.t1k_convertor = None
        self.exception_result_list = []
        self.normal_result_list = []
        self.total_count = 0
        self.exception_count = 0
        self.t1k_results_save = []
        self.test_desc=None
        # self.finish = False
        gc.collect()

    # def clear_menu_method(self):
    #     total_count = self.csv_table.rowCount()
    #
    #     for i in range(0, total_count)[::-1]:
    #         self.csv_table.removeRow(i)

    # 事件处理
    def draw_switch_slot(self, status):
        '''
        画框开关
        :param event:
        :return:
        '''
        self.draw_line = True if status else False
        if self.draw_line:
            logger.info('启动画框')
        else:
            logger.info('关闭画框')

    def open_t1k_folder(self):
        '''
        打开文件夹
        :return:
        '''
        foldername = QtWidgets.QFileDialog.getExistingDirectory(self, "选取t1k文件夹", "./")
        if not foldername:
            return None
        file_list = os.listdir(foldername)
        path_list = []

        for file in file_list:
            if file.endswith('t1k'):
                full_path = os.path.join(foldername, file)
                path_list.append(full_path)

        if len(path_list) == 0:
            self.msg('提示', '文件夹内没有t1k文件')
            return None

        self.clear_last()
        self.openDirAction.setDisabled(True)
        self.clear_menu_method()
        path_list = list(sorted(path_list))
        self.task = threading.Thread(target=self.run_t1k_batch, args=(path_list,))
        self.stop_task = False
        self.task.start()

    def save_as_other(self):
        '''
        另存为事件
        :return:
        '''

        t1k_stem = PurePath(self.t1k_file).stem
        out_path = os.path.join(self.csv_out_dir, t1k_stem + '.csv')

        save_fileName, ok2 = QtWidgets.QFileDialog.getSaveFileName(self,
                                                                   "文件保存",
                                                                   out_path,
                                                                   "CSV Files (*.csv)")

        if not ok2:
            return None

        if save_fileName:

            try:
                with open(save_fileName, 'w', newline='') as f:
                    f_csv = csv.DictWriter(f, fieldnames=headers)
                    f_csv.writeheader()

                    for index, item in enumerate(self.t1k_results_save):
                        # record, csv_str, cls, csv_str_ext, origin = item
                        d = {}
                        d['row'] = index
                        for idx, field in enumerate(item):
                            d[headers[idx + 1]] = field

                        f_csv.writerow(d)

                md5_code = get_md5(self.t1k_file)
                ui_str = self.process_label.text()
                self.db.update_file(self.t1k_file, out_path, md5_code, self.total_count, self.exception_count,
                                    self.total_count - self.exception_count, ui_str)
            except Exception as e:
                self.msg('提示', 'csv文件保存失败！')
            else:
                self.msg('提示', 'csv文件保存成功！')

    def export_file_event(self):
        if self.csv_table_sql.get_table_loaded_sta() == True:
            fname = QtWidgets.QFileDialog.getExistingDirectory(self, '选择导出文件夹', './')
            if not fname:
                return None
            else:
                tablefield = {'not_confirm': ['\"已审核\"']}
                self.csv_table_sql.table.model_field_lookups(tablefield)
                self.csv_table_to_excel(fname, self.csv_table_sql.tablesearchlist)
                self.csv_str_to_save_image(fname)
        else:
            self.msg('提示', '请先加载 t1k 文件！')

    def csv_table_to_excel(self, path, fieldlist):
        wb = xlwt.Workbook()
        ws = wb.add_sheet(self.t1k_table)
        column = 0
        for field in fieldlist:
            ws.write(0, column, field)
            column += 1
        for row in range(self.csv_table_sql.get_table_row_count()):
            column = 0
            for field in fieldlist:
                ws.write(row + 1, column, self.csv_table_sql.get_table_row_field(row, field))
                column += 1
        wb.save(path + '/' + self.t1k_table + '.xls')

    def csv_str_to_save_image(self, path):
        for row in range(self.csv_table_sql.get_table_row_count()):
            csv_str = self.csv_table_sql.get_table_row_field(row, 'csv_str')
            filename = self.csv_table_sql.get_table_row_field(row, 'ids')
            csv_file = io.StringIO(csv_str)
            image = draw_bscan_from_csv_zoom(csv_file, bk_color=(0, 0, 0))
            image = np.flip(image)
            image = cv2.flip(image, -1)
            fname = path + '/image'
            if not os.path.exists(fname):
                if os.makedirs(fname):
                    return None
            cv2.imencode('.png', image)[1].tofile(fname + '/' + str(filename) + '.png')

    def csv_table_search_field(self):
        self.tableseachactionall = dict()         # 全部伤损
        self.tableseachactionreview = dict()      # 待审核
        self.tableseachactionunreview = dict()    # 已审核
        self.tablesignalmapperall = QtCore.QSignalMapper(self)
        self.tablesignalmapperreview = QtCore.QSignalMapper(self)
        self.tablesignalmapperunreview = QtCore.QSignalMapper(self)
        self.csv_table_search_field_sun(self.tableseachactionall, self.tablesignalmapperall, self.menu_3, self.csv_table_search_field_event_all)
        self.csv_table_search_field_sun(self.tableseachactionreview, self.tablesignalmapperreview, self.menu_7, self.csv_table_search_field_event_review)
        self.csv_table_search_field_sun(self.tableseachactionunreview, self.tablesignalmapperunreview, self.menu_8, self.csv_table_search_field_event_unreview)

    def csv_table_search_field_sun(self, tableseachaction, tablesignalmapper, menu, fun):
        for key, value in self.g_config.chinese_dict.items():
            tableseachaction[key] = QAction(value, self)
            menu.addAction(tableseachaction[key])
            tableseachaction[key].setText(QtCore.QCoreApplication.translate("MainWindow", value))
            tablesignalmapper.setMapping(tableseachaction[key], value)
            tableseachaction[key].triggered.connect(tablesignalmapper.map)
        tablesignalmapper.mapped['QString'].connect(fun)

    def csv_table_search_field_event_all(self, data):
        print('csv_table_search_field_event_all:')
        print(data)

    def csv_table_search_field_event_review(self, data):
        print('csv_table_search_field_event_review:')
        print(data)

    def csv_table_search_field_event_unreview(self, data):
        print('csv_table_search_field_event_unreview:')
        print(data)

    def exit_event(self):
        '''
        退出
        :return:
        '''
        # self.db.close()
        self.stop_task = False
        qApp.exit()

    def open_csv_file(self):
        '''
        打开单个cvs文件
        :return:
        '''
        self.clear_menu_method()
        fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', '', "csv files (*.csv)")
        if os.path.exists(fname[0]):
            self.clear_last()
            self.t1k_file = fname[0]
            self.run_csv(fname[0])

    def open_all_csv_file(self):
        '''
        打开所有完整csv事件
        :return:
        '''
        fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', '', "csv files (*.csv)")
        if os.path.exists(fname[0]):
            self.clear_last()
            self.t1k_file = fname[0]
            print(self.t1k_file)
            (file, import_count, exception_count, normal_count, ui_str) = self.db.exists(self.t1k_file)

            self.run_csv_all_file(fname[0], import_count, exception_count, normal_count, ui_str)

    def save_as_csv_cls(self, custom_cls):
        def save_as_csv_inner():
            return self.save_as_csv(custom_cls)

        return save_as_csv_inner

    def clear_db(self):
        '''情况本地sqlite'''
        try:
            self.db.clear()
        except Exception as e:
            self.msg('错误', '清空数据库失败')
        else:
            self.msg('提示', '清空数据库成功')

    def run_csv_all_file(self, csv_file, import_count, exception_count, normal_count, ui_str):
        '''
        打开完整csv文件
        :param csv_file:
        :return:
        '''
        self.clear_menu_method(import_count, exception_count, normal_count)
        self.csv_task = threading.Thread(target=self.run_csv_thread, args=(csv_file, ui_str))
        # self.task = threading.Thread(target=lambda x: x.run_t1k(), args=(self,))
        # self.stop_task = False
        self.csv_task.start()

    def clear_table_data(self):
        self.total_count = 0
        self.exception_count = 0
        self.clear_menu_method(self.total_count, self.exception_count, self.total_count - self.exception_count)

    def run_csv_thread(self, csv_file, ui_str):
        '''
        读取csv结果 直接显示
        :param csv_file:
        :param ui_str:
        :return:
        '''
        self.process_label.setText(ui_str)

        t1k_convertor = T1kConvertor(csv_file, model_type=self.g_config.model_type)

        with open(csv_file) as f:
            f_csv = csv.DictReader(f)

            for line in f_csv:
                changed = line.get('changed')

                test_desc = line.get('test_desc')
                try:
                    rail_road, head_depth, web_depth, base_depth, post = test_desc.split(' ')
                except Exception as e:
                    print(e)
                    continue

                record_test_desc = RecordTestDesc()
                record_test_desc.railroad = rail_road
                record_test_desc.head_depth = int(head_depth)
                record_test_desc.web_depth = int(web_depth)
                record_test_desc.base_depth = int(base_depth)
                record_test_desc.post = float(post)
                self.test_desc = record_test_desc
                csv_str = line.get('csv_str')
                csv_str_ext = line.get('csv_str_ext')
                (img, cls, score), csv_str = t1k_convertor.process_csv(csv_str)
                chi_cls = self.g_config.chinese_dict.get(cls)
                self.t1k_results.append((None, csv_str, cls, csv_str_ext))  # csv_str-ext

                rowPosition = self.csv_table.rowCount()
                self.csv_table.insertRow(rowPosition)
                post = float(line.get('post'))
                score = float(line.get('score'))

                self.csv_table.setItem(rowPosition, 0, QTableWidgetItem(f"{line.get('top_rail')}"))
                self.csv_table.setItem(rowPosition, 1,
                                       QTableWidgetItem(f"{line.get('pos_bus')}({line.get('length')})"))
                self.csv_table.setItem(rowPosition, 2, QTableWidgetItem(f"{post:.6f}"))

                if changed == CONFIRM:
                    item_ = QTableWidgetItem(f"{line.get('chi_cls')}({score:.3f},{line.get('rec_code')})")

                    if line.get('chi_cls') in self.normal_list:
                        item_.setBackground(QtCore.Qt.green)
                    else:
                        item_.setBackground(QtCore.Qt.red)

                    self.csv_table.setItem(rowPosition, 3, item_)
                    self.csv_table.setItem(rowPosition, 5, QTableWidgetItem(CONFIRM))

                elif changed == NOT_CONFIRM:
                    item_ = QTableWidgetItem(f"{line.get('chi_cls')}({score:.3f},{line.get('rec_code')})")
                    item_.setBackground(QtCore.Qt.yellow)
                    self.csv_table.setItem(rowPosition, 3, item_)
                    self.csv_table.setItem(rowPosition, 5, QTableWidgetItem(NOT_CONFIRM))
                else:
                    self.csv_table.setItem(rowPosition, 5, QTableWidgetItem(CHANGED))

                    if changed in self.normal_list:
                        item_.setBackground(QtCore.Qt.green)
                    else:
                        item_.setBackground(QtCore.Qt.red)

                    item_ = QTableWidgetItem(f"{changed}")
                    item_.setBackground(QtCore.Qt.red)

                    self.csv_table.setItem(rowPosition, 3, item_)

                self.csv_table.setItem(rowPosition, 4, QTableWidgetItem(f"{line.get('severity')}"))

                self.t1k_results_save.append((line.get('top_rail'), line.get('pos_bus'), line.get('length'),
                                              line.get('post'), line.get('chi_cls'), line.get('score'),
                                              line.get('rec_code'), line.get('severity'), line.get('csv_str'),
                                              line.get('test_desc'), changed))
                time.sleep(0.001)
        self.extractAction.setDisabled(False)
        self.saveAllMenu.setDisabled(False)

    def save_all_csv(self):
        '''
        保存所有数据为csv文件
        :return:
        '''
        t1k_stem = PurePath(self.t1k_file).stem
        out_path = os.path.join(self.csv_out_dir, t1k_stem + '.csv')

        save_fileName, ok2 = QtWidgets.QFileDialog.getSaveFileName(self,
                                                                   "文件保存",
                                                                   out_path,
                                                                   "CSV Files (*.csv)")

        if not ok2:
            return None

        out_path = save_fileName
        has_Uncomfirm = 0
        with open(out_path, 'w', newline='') as f:
            f_csv = csv.DictWriter(f, fieldnames=headers)
            f_csv.writeheader()

            for index, item in enumerate(self.t1k_results_save):
                # record, csv_str, cls, csv_str_ext, origin = item
                d = {}
                d['row'] = index
                for idx, field in enumerate(item):
                    d[headers[idx + 1]] = field
                try:
                    if not has_Uncomfirm and item[11] == NOT_CONFIRM:
                        has_Uncomfirm = 1
                except Exception as e:
                    print(e)

                f_csv.writerow(d)

        md5_code = get_md5(self.t1k_file)
        ui_str = self.process_label.text()
        self.db.update_file(self.t1k_file, out_path, md5_code, self.total_count, self.exception_count,
                            self.total_count - self.exception_count, ui_str, has_Uncomfirm)
        self.msg('提示', 'csv文件保存成功！')

    def msg(self, title, content):
        '''
        弹出消息框
        :param title:
        :param content:
        :return:
        '''
        QtWidgets.QMessageBox.about(self, title, content)

    def save_as_fun(self):
        if self.finish:
            t1k_stem = PurePath(self.t1k_file).stem
            out_path = os.path.join(self.csv_out_dir, t1k_stem + '.bfxt')

            save_fileName, ok2 = QtWidgets.QFileDialog.getSaveFileName(self,
                                                                       "文件保存",
                                                                       out_path,
                                                                       "BFXT Files (*.bfxt)")

            if not ok2:
                return None

            # out_path = save_fileName
            t=threading.Thread(target=self.export_data,args=(save_fileName,))
            t.start()

    def export_data(self,save_fileName):
        ret = self.sql_db.read_t1k_result(self.t1k_table)
        headers=['ids', 'number', 'record_top_rail', 'record_pos_bus', 'speed', 'record_length', 'post', 'chi_cls', 'score', 'record_rec_code', 'record_severity', 'csv_str', 'csv_str_ext', 'test_desc_str', 'not_confirm', 'change_name']
        tmp_file=f'{self.t1k_table}.csv'
        try:
            df = pd.DataFrame(ret,columns=headers)
            df.to_csv(tmp_file)
            print(df.head())
        except Exception as e:
            logger.info(e)
        else:
            # 加密
            if compress_file([tmp_file],['.'],save_fileName):
                try:
                    os.remove(tmp_file)
                except Exception as e:
                    logger.error(e)

            else:
                pass


    def save_as_csv(self, custom_cls=None):
        idx = self.csv_table.currentRow()
        record, csv_str, cls, csv_str_ext, origin = self.t1k_results[idx]
        if origin is None:
            csv_str += '\n '
        else:
            csv_str += '\n' + origin
        if custom_cls:
            cls = custom_cls
        pos_bus = 0
        if record is not None:
            pos_bus = record.pos_bus
        t1k_stem = PurePath(self.t1k_file).stem
        if not os.path.exists(self.csv_out_dir + '/' + cls):
            os.mkdir(self.csv_out_dir + '/' + cls)
        with open(f"{self.csv_out_dir + '/' + cls}/{t1k_stem}_{pos_bus}_{cls}.csv", "w") as f:
            f.write(csv_str)
        csv_file = io.StringIO(csv_str)
        img_zoom = draw_bscan_from_csv_zoom(csv_file, bk_color=(0, 0, 0))
        cv2.imwrite(f"{self.csv_out_dir + '/' + cls}/{t1k_stem}_{pos_bus}_zoom_{cls}.jpg", img_zoom)
        csv_file.seek(0)
        img = draw_bscan_from_csv(csv_file, test_desc=self.test_desc)
        cv2.imwrite(f"{self.csv_out_dir + '/' + cls}/{t1k_stem}_{pos_bus}_{cls}.jpg", img)

    def parsing_t1k(self, dict_item):
        '''
        分析t1k是更新label
        :param dict_item:
        :return:
        '''
        prog = dict_item.get('prog')
        number = dict_item.get('number')
        chi_cls = dict_item.get('chi_cls')
        post = dict_item.get('post')
        speed = dict_item.get('speed')
        height = dict_item.get('height')
        # total_count = dict_item.get('total_count')
        # status = dict_item.get('status')
        # 更新状态栏， 每隔5条更新一次。

        if self.total_count % 5 == 0 or (post is None and prog == 1):
            self.screw_crack_label.setText(str(self.cls_count.get('bolt_hole_crack')))
            # self.bad_track_label.setText(str(self.cls_count.get('abnormal_joint')))
            # self.track_other_label.setText(str(self.cls_count.get('head_defect')))
            # self.weld_damage_label.setText(str(self.cls_count.get('weld_seam_defect')))
            self.track_crack_label.setText(str(self.cls_count.get('web_crack')))
            self.base_defect_label.setText(str(self.cls_count.get('base_defect')))
            self.transverse_defect_label.setText(str(self.cls_count.get('transverse_defect')))
            self.vertical_defect_label.setText(str(self.cls_count.get('vertical_defect')))

            # self.transverse_other_label.setText(str(self.cls_count.get('track_head_others'))) #
            # self.base_other_label.setText(str(self.cls_count.get('track_base_others')))

            self.import_num_label.setText(str(self.total_count))
            self.error_num_label.setText(str(self.exception_count))
            self.normal_num_label.setText(str(self.total_count - self.exception_count))

        if post is not None:
            pass
            # self.progressBar.setValue(int(prog * 100))

        else:
            if prog == 1:
                self.progressBar.setValue(100)
                self.msg('提示', '识别t1k完毕')
                self.csv_table_sql.table.table_up_date()

    def insert_allsthit(self,table,st_list):
        logger.info('写入st到数据库')
        # print(st_list)
        try:
            self.sql_db.insert_sthit_many(table,st_list)
        except Exception as e:
            logger.error(e)
            self.dialog_sig.emit('异常','写入数据库异常，请重新打开本软件')
        else:
            print('写完st')

    def csv_table_key_event(self):
        index = self.csv_table_sql.table.view.currentIndex()
        self.view_thumbnail(index)

    def view_thumbnail(self, index):
        '''
        查看缩略图
        :return:
        '''
        # 显示缩略图时 停止播放
        self.process_draw_widget_timer.stop()

        if self.play_bottuon.isChecked():
            self.play_bottuon.toggle()

        csv_str = self.csv_table_sql.table.model.record(index.row()).value('csv_str')
        csv_post = self.csv_table_sql.table.model.record(index.row()).value('post')
        csv_record_length = self.csv_table_sql.table.model.record(index.row()).value('record_length')
        csv_speed = self.csv_table_sql.table.model.record(index.row()).value('speed')
        csv_record_top_rail = self.csv_table_sql.table.model.record(index.row()).value('record_top_rail')
        chi_name = self.csv_table_sql.table.model.record(index.row()).value('chi_cls')
        pos_bus = self.csv_table_sql.table.model.record(index.row()).value('record_pos_bus')
        st_hit_table = self.csv_table_sql.table.model.record(index.row()).value('filename')
        self.current_table=st_hit_table
        csv_file = io.StringIO(csv_str)

        img_zoom = draw_bscan_from_csv_zoom(csv_file, bk_color=(0, 0, 0))
        # 局部图


        img_zoom = np.flip(img_zoom)
        img_zoom = cv2.flip(img_zoom, -1)
        img_zoom = cv2.flip(img_zoom, 0)



        '''图像缩放:使用pixmap的scare方法，参数aspectRatioMode=Qt.KeepAspectRatio设置为等比例缩放，
        aspectRatioMode=Qt.IgnoreAspectRatio为不按比例缩放'''
        ymax = int(self.csv_str2data(csv_str, 'fault_Y_max'))
        ymin = int(self.csv_str2data(csv_str, 'fault_Y_min'))
        height = ymax - ymin
        fault_num = int(self.csv_str2data(csv_str, 'fault_num'))

        self.injury_image_item.setImage(img_zoom)
        self.injury_view.autoRange()

        self.kilometer_label.setText(f'{csv_post:.6f}')
        self.total_width_label.setText(f'{csv_record_length}mm')
        self.total_height_table.setText(f'{height}mm')

        # 时速
        self.current_speed_label.setText(f'{csv_speed:.2f}km/h')
        self.system_suspected_label.setText(f'{chi_name}')

        # TODO 设置回波数 fault_num


        # if self.finish:
        text = f'{csv_post:.6f}KM  {csv_speed:.2f}KM/H'
        self.pos_bus = pos_bus - 50  # 将播放的pos_bus

        self.draw_main_widget(self.current_table,self.pos_bus, text, self.draw_line)  # 点击时为了界面好看

    def disableMenu(self, status):
        '''
        禁用打开按钮
        :param status:
        :return:
        '''
        # self.load_t1k.setDisabled(status)
        self.save.setDisabled(status)
        self.save_as.setDisabled(status)

    def csv_str2data(self, csv_str, string):
        '''
        提取csv的数据
        :param csv_str:
        :param string:
        :return:
        '''
        try:
            data = re.search(f'{string},(.*?),',csv_str).group(1)
        except:
            return None
        else:
             return data
    def process_sthit(self,st_hits):

        data = pd.DataFrame(st_hits,columns=['id','top_rail','position','depth','channel','ch_num'])

        new_data=denoising(data)
        return new_data

    def run_t1k(self, file_path,filename,project_name):
        '''
        运行t1k, 单个
        :param filename:
        file_path: 路径
        filename：文件名,
        filename_pre_fix：前缀部分
        :return:
        '''
        self.disableMenu(True)

        self.t1k_name.setText(filename+' 解析中')

        t1k_full_path = file_path
        self.t1k_convertor = T1kConvertor(file_path,
                                          model_type=self.g_config.model_type,
                                          version=3)

        total_len = len(self.t1k_convertor)
        # TODO 去重变量
        # filename = PurePath(self.t1k_file).name

        # table_name = filename.split('.')[0]
        self.current_table=filename
        self.test_desc = None
        # position_delta = 0
        number = 0
        cur_pos = 0
        logger.info(f'开始解析t1k--{filename}')

        # filename = PurePath(self.t1k_file).name  # TODO 有bug ?
        self.test_desc = None

        self.current_select_status = True

        st_hits = []
        # ultra_recog = []

        start_parse = time.time()

        logger.info(f'解析t1k.............')


        for data_pos, result in self.t1k_convertor.convert():
            cur_pos+=data_pos
            p = int(cur_pos/total_len*100)
            if p%5==0:
                self.progress_bar_sig.emit(p)

        self.progressBar.setValue(0)


        if self.test_desc is None and self.t1k_convertor.test_desc:
            self.test_desc = self.t1k_convertor.test_desc
            mov = self.test_desc.movement
            # print(str(self.test_desc))
            post_direct = self.test_desc.post_direction
            post_direct = post_direct.split(' ')[0]
            key = mov + '_' + post_direct
            position_delta = float(self.distance_delta.get(key, 0)) / 1000
            self.post_delta = position_delta

        end = time.time()
        logger.info(f'解析使用时间{end-start_parse:.2f}s')
        logger.info('去噪处理')
        self.t1k_name.setText(filename+' 识别中')
        origin_st_hit_list=[]
        for index, item in enumerate(self.t1k_convertor.st_hit_list):
            ch = item[3]  # 通道映射
            origin_st_hit_list.append(item)
            channel_list = parse_channels(ch)
            for i in channel_list:
                st_hits.append((item[1], item[0], item[2], i, ch))
        logger.info('发送时间')
        # self.st_hits_sig.emit(filename,origin_st_hit_list) #

        # TODO 批量操作会异常
        t=threading.Thread(target=self.insert_allsthit,args=(filename,origin_st_hit_list))
        t.start()

        logger.info('结束')
        st_hits = list(set(st_hits))
        st_hits = sorted(st_hits, key=lambda x: (x[1]))
        st_hits_ = []
        for idx, i in enumerate(st_hits):
            st_hits_.append((idx + 1, i[0], i[1], i[2], i[3], i[4]))

        logger.info(f'预处理使用时间{time.time()-end:.2f}')
        # start_noise = time.time()

        process_sthit = self.process_sthit(st_hits_)

        for cur_pos,total_len,item in process_sthit:

            # if self.stop_task =True:
            # print(f'current postion {cur_pos},{total_len}')
            self.progress_bar_sig.emit((cur_pos/total_len*100))
            top_rail = item.iloc[0]['top_rail']

            # pos_min_1 = item['position'].min()
            # pos_max_1 = item['position'].max()
            # depth_min_1 = item['depth'].min()
            # depth_max_1 = item['depth'].max()

            pos_col = item['position'].tolist()
            depth_col = item['depth'].tolist()
            ch_col = item['ch_num'].tolist()
            query = tuple(zip(pos_col, depth_col, ch_col))
            #
            if len(query) == 0:
                continue
            #
            csv_str = self.t1k_convertor.gen_st_hit_cluster(int(top_rail), query)
            # csv_str_ext = self.t1k_convertor.gen_st_hit_cluster(top_rail, query2)
            sub_result, csv_str = self.t1k_convertor.process_csv(csv_str)
            pos_bus_min = self.csv_str2data(csv_str, 'fault_X_min')
            pos_bus_max = self.csv_str2data(csv_str, 'fault_X_max')
            min_depth = int(self.csv_str2data(csv_str, 'fault_Y_min'))
            max_depth = int(self.csv_str2data(csv_str, 'fault_Y_max'))
            pos_bus = self.csv_str2data(csv_str, 'position')

            if pos_bus_min and pos_bus_max:
                pos_bus_min = int(pos_bus_min)
                pos_bus_max = int(pos_bus_max)
                pos_bus = int(pos_bus)

            length = pos_bus_max - pos_bus_min
            real_pos_bus = pos_bus+pos_bus_min
            query2 = self.t1k_convertor.db.query_st_hit(real_pos_bus - 1 + 125 + length,
                                                   real_pos_bus  - 125,
                                                   top_rail,
                                                   500, 0)
            if len(query2) == 0:
                continue

            # if real_pos_bus-int(pos_min_1)>5 or max_depth-int(depth_max_1)>2:
            #     print('pos_bus:',pos_bus)
            #     print('pos_min_1:',pos_min_1)
            #     print('depth max ',max_depth)
            #     print('depth ',depth_max_1)


            csv_str_ext = self.t1k_convertor.gen_st_hit_cluster(int(top_rail), query2)
            post,speed = self.t1k_convertor.t1k.pos_bus_to_post(real_pos_bus)
            (img, cls, score) = sub_result

            # logger.info(f'cls : {cls}')
            self.total_count += 1

            if self.stop_task:
                break

            chi_cls = self.g_config.chinese_dict.get(cls)
            # prog = cur_pos / total
            try:
                test_desc_str = str(self.test_desc)
            except Exception as e:
                logger.error(e)
                test_desc_str=''

            rec_code=1
            severity=1

            result_item = (number, int(top_rail), int(real_pos_bus), speed, int(length),
             post, self.post_delta, chi_cls, float(score), rec_code, severity,
             csv_str, csv_str_ext, test_desc_str, NOT_CONFIRM, chi_cls,filename)

            self.t1k_results_save.append(result_item)

            # TODO 插入
            # 使用线程
            # update_result_thread = threading.Thread(target=self.sql_db.save_t1k_results_mysql_single,args=(
            #                                         'projects',
            #                                           project_name,
            #                                           result_item))
            # update_result_thread.start()
            if cls in self.cls_count:
                self.cls_count[cls] += 1

            if cls in self.g_config.exception_list and score >= 0:
                self.exception_count += 1

                ultra_item = (real_pos_bus,int(top_rail),int(length),int(min_depth),int(max_depth))
                self.sql_db.insert_ultra_recogn_custom(filename,ultra_item)

            self.sql_db.save_t1k_results_mysql_single('projects',
                                                      project_name,
                                                      result_item)

            number += 1

            self.process_t1k_sig.emit(
                {'prog': 1,
                 # 'height':heigth,
                 'number': number,
                 'chi_cls': chi_cls,
                 # 'length':length,
                 'post': post,
                 # 'speed':speed
                 }
            )
            if self.exception_count ==5:
                self.slot_show_table_sig.emit('')

        logger.info('识别结束')

        # mysql插入数据

        # self.copy_mysql_sig.emit(filename,)
        sperry_ultra=False
        t=threading.Thread(target=self.copy_mysql,args=(t1k_full_path,filename,self.total_count,self.exception_count,self.total_count-self.exception_count,sperry_ultra))
        t.start()
        self.process_t1k_sig.emit({'prog': 1, 'total_count': self.total_count})
        self.t1k_name.setText(filename+' 完成识别')
    def run_t1k_v0(self, filename):
        '''
        运行t1k
        :param filename:
        :return:
        '''
        self.disableMenu(True)

        t1k_full_path = filename
        self.t1k_convertor = T1kConvertor(filename,
                                          model_type=self.g_config.model_type,
                                          version=3)

        total = len(self.t1k_convertor)
        # TODO 去重变量
        filename = PurePath(self.t1k_file).name

        # table_name = filename.split('.')[0]

        self.t1k_name.setText(filename)
        self.test_desc = None
        position_delta = 0
        number = 0
        cur_pos = 0
        logger.info('开始解析t1k')

        # filename = PurePath(self.t1k_file).name  # TODO 有bug ?
        self.test_desc = None
        detect_count = 0

        self.current_select_status = True

        st_hits = []


        start_parse = time.time()

        logger.info(f'解析t1k.............')

        # self.sql_db.create_t1k_result_table(table_name)
        # self.csv_table_sql.create_table(table_name)

        for data_pos, result in self.t1k_convertor.convert():
            pass


        if self.test_desc is None and self.t1k_convertor.test_desc:
            self.test_desc = self.t1k_convertor.test_desc
            mov = self.test_desc.movement
            print(str(self.test_desc))
            print(self.test_desc.data)
            post_direct = self.test_desc.post_direction
            post_direct = post_direct.split(' ')[0]
            key = mov + '_' + post_direct
            position_delta = float(self.distance_delta.get(key, 0)) / 1000
            self.post_delta = position_delta

        end = time.time()
        logger.info(f'解析使用时间{end-start_parse:.2f}s')
        logger.info('去噪处理')

        origin_st_hit_list=[]
        for index, item in enumerate(self.t1k_convertor.st_hit_list):
            ch = item[3]  # 通道映射
            origin_st_hit_list.append(item)
            channel_list = parse_channels(ch)
            for i in channel_list:
                st_hits.append((item[1], item[0], item[2], i, ch))

        self.st_hits_sig.emit(self.t1k_table,origin_st_hit_list)


        st_hits = list(set(st_hits))
        st_hits = sorted(st_hits, key=lambda x: (x[1]))
        st_hits_ = []
        for idx, i in enumerate(st_hits):
            st_hits_.append((idx + 1, i[0], i[1], i[2], i[3], i[4]))

        logger.info(f'预处理使用时间{time.time()-end:.2f}')
        start_noise = time.time()

        process_sthit = self.process_sthit(st_hits_)

        for item in process_sthit:
            top_rail = item.iloc[0]['top_rail']
            pos_col = item['position'].tolist()
            depth_col = item['depth'].tolist()
            ch_col = item['ch_num'].tolist()
            query = tuple(zip(pos_col, depth_col, ch_col))

            if len(query) == 0:
                continue

            csv_str = self.t1k_convertor.gen_st_hit_cluster(int(top_rail), query)
            # csv_str_ext = self.t1k_convertor.gen_st_hit_cluster(top_rail, query2)
            sub_result, csv_str = self.t1k_convertor.process_csv(csv_str)
            pos_bus_ext = self.csv_str2data(csv_str, 'fault_X_min')
            pos_bus_max = self.csv_str2data(csv_str, 'fault_X_max')
            pos_bus = self.csv_str2data(csv_str, 'position')

            if pos_bus_ext and pos_bus_max:
                pos_bus_ext = int(pos_bus_ext)
                pos_bus_max = int(pos_bus_max)
                pos_bus = int(pos_bus)

            length = pos_bus_max - pos_bus_ext

            query2 = self.t1k_convertor.db.query_st_hit(pos_bus - 1 + 125 + length,
                                                   pos_bus - 125,
                                                   top_rail,
                                                   500, 0)
            if len(query2) == 0:
                continue

            csv_str_ext = self.t1k_convertor.gen_st_hit_cluster(int(top_rail), query2)
            post,speed = self.t1k_convertor.t1k.pos_bus_to_post(pos_bus)
            (img, cls, score) = sub_result

            self.total_count += 1

            if self.stop_task:
                break

            chi_cls = self.g_config.chinese_dict.get(cls)
            prog = cur_pos / total

            if cls in self.cls_count:
                self.cls_count[cls] += 1

            if cls in self.g_config.exception_list and score >= 0:
                self.exception_count += 1

            test_desc_str = str(self.test_desc)
            rec_code=1
            severity=1

            result_item = (number, int(top_rail), int(pos_bus), speed, int(length),
             post, self.post_delta, chi_cls, float(score), rec_code, severity,
             csv_str, csv_str_ext, test_desc_str, NOT_CONFIRM, chi_cls)

            self.t1k_results_save.append(result_item)

            # TODO 插入

            self.sql_db.save_t1k_results_mysql_single('t1k_results_save',
                                                      self.t1k_table,
                                                      result_item)


            number += 1

            self.process_t1k_sig.emit(
                {'prog': prog,
                 # 'height':heigth,
                 'number': number,
                 'chi_cls': chi_cls,
                 # 'length':length,
                 'post': post,
                 # 'speed':speed
                 }
            )
            if self.exception_count ==5:
                self.slot_show_table_sig.emit('')

        logger.info('识别结束')

        # mysql插入数据

        # self.copy_mysql_sig.emit(t1k_full_path)
        sperry_ultra=False
        t=threading.Thread(target=self.copy_mysql_sig,args=(t1k_full_path,filename,sperry_ultra))
        t.start()

        self.process_t1k_sig.emit({'prog': 1, 'total_count': self.total_count})

    def copy_mysql(self, full_path,filename,total,exception,normal,sperry_ultra=False):
        '''
        保存数据到MYSQL
        :param t1k_filename:
        :return:
        '''
        # t1k_filename = PurePath(self.t1k_file).name
        # t1k_filename = t1k_filename.split('.')[0]
        logger.info(f'数据复制到mysql,t1k: {filename}')
        start_time = time.time()
        # st_list = []
        ultra_list = []

        # for st_item in self.t1k_convertor.db.get_sthit():
        #     st_list.append(st_item)
        #
        # self.sql_db.insert_sthit_many(t1k_filename, st_list)
        if sperry_ultra:
            for ultra in self.t1k_convertor.db.get_ultra():
                ultra_list.append(ultra)

            self.sql_db.insert_ultra_recognitions_many(filename, ultra_list)

        # table = t1k_filename.rstrip('.t1k')
        # 保存t1k_result_save到mysql
        # data_base = 't1k_result_save'
        # table = 'tlk_data'
        # table = t1k_filename
        # self.save_t1k_results_mysql(t1k_filename, self.t1k_results_save)

        #保存chainages到mysql
        # table = 'chainages_data'
        # table = t1k_filename

        self.save_chainages_mysql(filename, self.t1k_convertor.t1k.chainages)

        # 保存test_desc_railroad到mysql
        # data_base = 'test_desc'
        # table = t1k_filename
        # self.test_desc.data是一个列表，里面的每个元素都不是列表
        self.test_desc.data.append(filename)
        self.save_railroad_mysql(filename,self.test_desc.data)
        md5=get_md5(full_path)
        self.sql_db.insert_t1k_record(filename,md5,total,exception,normal)
        # self.table_name = t1k_filename

        # self.show_table_sig.emit(self.table_name)

        self.dir_treeView.setEnabled(True)
        self.finish = True
        self.disableMenu(False)
        self.initialize_main_widget(filename)
        logger.info(f'copy mysql used {time.time() - start_time}')

    def save_t1k_results_mysql(self,table,t1k_results_save):
        # tlk_data = DB()
        # t1k_results_save = list(map(self.numpy_float_trans_float,t1k_results_save))
        # table = 'tlk_data'
        self.sql_db.save_t1k_results_mysql(table,t1k_results_save)


    def save_chainages_mysql(self,table,chainages):
        #保存chainages到mysql
        # chain_data = DB()
        self.sql_db.save_chainages_mysql(table,chainages)

    def save_railroad_mysql(self,table,railroad):
        # railroad_data = DB()
        self.sql_db.save_railroad_mysql(table,railroad)

    def save_fun(self):
        '''保存当前结果'''
        # self.t1k_file
        md5 = get_md5(self.t1k_file)
        self.sql_db.t1k_record(self.t1k_table, md5)
        print('保存成功')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 添加字体。
    QtGui.QFontDatabase.addApplicationFont("./icon/PingFangMedium.ttf")
    # print(QtGui.QFontDatabase.applicationFontFamilies(_id))
    window = MainWind()
    sys.exit(app.exec_())
