from gc import collect from pathlib import Path from traceback import format_exc import matplotlib.pyplot as plt from PySide6.QtCore import QCoreApplication, QTimer from PySide6.QtGui import QAction, QFont from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication, QTableWidget, QTableWidgetItem, QHeaderView from matplotlib import gridspec, patches from matplotlib.backends.backend_qt import NavigationToolbar2QT from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg from numpy import array, append from numpy.fft import fft, fftfreq from overrides import overrides from pandas import read_csv, DataFrame, concat from scipy.signal import resample from yaml import dump, load, FullLoader from func.utils.PublicFunc import PublicFunc from func.utils.Constants import Constants, ConfigParams from func.utils.Result import Result from ui.MainWindow.MainWindow_artifact_label import Ui_MainWindow_artifact_label from ui.setting.artifact_label_input_setting import Ui_MainWindow_artifact_label_input_setting Config = { } ButtonState = { "Default": { "pushButton_input_setting": True, "pushButton_input": True, "pushButton_prev_move": False, "pushButton_pause": False, "pushButton_next_move": False, "pushButton_save": False, "pushButton_type_1": False, "pushButton_type_2": False, "pushButton_type_3": False, "pushButton_type_4": False, "pushButton_type_5": False, "pushButton_delete": False }, "Current": { "pushButton_input_setting": True, "pushButton_input": True, "pushButton_prev_move": False, "pushButton_pause": False, "pushButton_next_move": False, "pushButton_save": False, "pushButton_type_1": False, "pushButton_type_2": False, "pushButton_type_3": False, "pushButton_type_4": False, "pushButton_type_5": False, "pushButton_delete": False } } class SettingWindow(QMainWindow): def __init__(self, root_path, sampID): super(SettingWindow, self).__init__() self.ui = Ui_MainWindow_artifact_label_input_setting() self.ui.setupUi(self) self.root_path = root_path self.sampID = sampID self.config = None self.__read_config__() self.ui.spinBox_input_freq_orgBcg.valueChanged.connect(self.__update_ui__) self.ui.spinBox_input_freq_BCG.valueChanged.connect(self.__update_ui__) self.ui.pushButton_confirm.clicked.connect(self.__write_config__) self.ui.pushButton_cancel.clicked.connect(self.__rollback_config__) self.ui.pushButton_cancel.clicked.connect(self.close) def __read_config__(self): if not Path(ConfigParams.ARTIFACT_LABEL_CONFIG_FILE_PATH).exists(): with open(ConfigParams.ARTIFACT_LABEL_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.ARTIFACT_LABEL_CONFIG_NEW_CONTENT, f) with open(ConfigParams.ARTIFACT_LABEL_CONFIG_FILE_PATH, "r") as f: file_config = load(f.read(), Loader=FullLoader) Config.update(file_config) self.config = file_config Config.update({ "Path": { "Input_orgBcg": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_ALIGNED / Path(str(self.sampID)))), "Input_BCG": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_ALIGNED / Path(str(self.sampID)))), "Save_a": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_LABEL / Path(str(self.sampID)))), "Save_b": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_LABEL / Path(str(self.sampID)))), "Save_c": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_LABEL / Path(str(self.sampID)))) } }) # 数据回显 self.ui.spinBox_input_freq_orgBcg.setValue(Config["InputConfig"]["orgBcgFreq"]) self.ui.spinBox_input_freq_BCG.setValue(Config["InputConfig"]["BCGFreq"]) self.ui.plainTextEdit_file_path_input_orgBcg.setPlainText(Config["Path"]["Input_orgBcg"]) self.ui.plainTextEdit_file_path_input_BCG.setPlainText(Config["Path"]["Input_BCG"]) self.ui.plainTextEdit_file_path_save_a.setPlainText(Config["Path"]["Save_a"]) self.ui.plainTextEdit_file_path_save_b.setPlainText(Config["Path"]["Save_b"]) self.ui.plainTextEdit_file_path_save_c.setPlainText(Config["Path"]["Save_c"]) def __write_config__(self): # 从界面写入配置 Config["InputConfig"]["orgBcgFreq"] = self.ui.spinBox_input_freq_orgBcg.value() Config["InputConfig"]["BCGFreq"] = self.ui.spinBox_input_freq_BCG.value() Config["Path"]["Input_orgBcg"] = self.ui.plainTextEdit_file_path_input_orgBcg.toPlainText() Config["Path"]["Input_BCG"] = self.ui.plainTextEdit_file_path_input_BCG.toPlainText() Config["Path"]["Save_a"] = self.ui.plainTextEdit_file_path_save_a.toPlainText() Config["Path"]["Save_b"] = self.ui.plainTextEdit_file_path_save_b.toPlainText() Config["Path"]["Save_c"] = self.ui.plainTextEdit_file_path_save_c.toPlainText() # 保存配置到文件 self.config["InputConfig"]["orgBcgFreq"] = self.ui.spinBox_input_freq_orgBcg.value() self.config["InputConfig"]["BCGFreq"] = self.ui.spinBox_input_freq_BCG.value() with open(ConfigParams.ARTIFACT_LABEL_CONFIG_FILE_PATH, "w") as f: dump(self.config, f) self.close() def __rollback_config__(self): self.__read_config__() def __update_ui__(self): self.ui.plainTextEdit_file_path_input_orgBcg.setPlainText( str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_ALIGNED / Path(str(self.sampID)) / Path(ConfigParams.ORGBCG_SYNC + str(self.ui.spinBox_input_freq_orgBcg.value()) + ConfigParams.ENDSWITH_TXT)))) self.ui.plainTextEdit_file_path_input_BCG.setPlainText( str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_ALIGNED / Path(str(self.sampID)) / Path(ConfigParams.BCG_SYNC + str(self.ui.spinBox_input_freq_BCG.value()) + ConfigParams.ENDSWITH_TXT)))) class MainWindow_artifact_label(QMainWindow): def __init__(self): super(MainWindow_artifact_label, self).__init__() self.ui = Ui_MainWindow_artifact_label() self.ui.setupUi(self) self.root_path = None self.sampID = None self.data = None self.setting = None # 初始化进度条 self.progressbar = None PublicFunc.add_progressbar(self) #初始化画框 self.fig = None self.canvas = None self.figToolbar = None self.gs = None self.ax0 = None self.ax1 = None self.rectangles_ax0_patches = [] self.rectangles_ax1_patches = [] self.annotation_tableWidget = None self.rect_left = None self.rect_right = None self.is_left_button_pressed = None self.pressed_number = None self.rect_up = None self.rect_down = None # 初始化自动播放定时器 self.autoplay_xlim_start = None self.autoplay_xlim_end = None self.timer_autoplay = QTimer() self.timer_autoplay.timeout.connect(self.autoplay_move_xlim) Config.update({ "AutoplayArgs": { "AutoplayMode": "pause", "MoveLength": int(self.ui.label_moveLength_preset_1.text()), "MaxRange": int(self.ui.label_maxRange_preset_1.text()), "MoveSpeed": int(self.ui.label_moveSpeed_preset_1.text()) } }) self.msgBox = QMessageBox() self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE) @overrides def show(self, root_path, sampID): super().show() self.root_path = root_path self.sampID = sampID self.setting = SettingWindow(root_path, sampID) # 初始化画框 self.fig = plt.figure(figsize=(12, 9), dpi=100) self.canvas = FigureCanvasQTAgg(self.fig) self.figToolbar = CustomNavigationToolbar(self.canvas, self) self.figToolbar.action_Label_Artifact.setEnabled(False) for action in self.figToolbar._actions.values(): action.setEnabled(False) for action in self.figToolbar.actions(): if action.text() == "Subplots" or action.text() == "Customize": self.figToolbar.removeAction(action) self.figToolbar._actions['home'].triggered.connect(self.toggle_home) self.figToolbar._actions['zoom'].triggered.connect(self.toggle_zoom) self.figToolbar._actions['pan'].triggered.connect(self.toggle_pan) self.figToolbar.action_Label_Artifact.triggered.connect(self.toggle_label_artifact) self.ui.verticalLayout_canvas.addWidget(self.canvas) self.ui.verticalLayout_canvas.addWidget(self.figToolbar) self.gs = gridspec.GridSpec(2, 1, height_ratios=[1, 1]) self.fig.subplots_adjust(top=0.98, bottom=0.05, right=0.98, left=0.1, hspace=0, wspace=0) self.ax0 = self.fig.add_subplot(self.gs[0]) self.ax0.grid(True) self.ax0.xaxis.set_major_formatter(ConfigParams.FORMATTER) self.ax0.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) self.ax1 = self.fig.add_subplot(self.gs[1], sharex=self.ax0) self.ax1.grid(True) self.ax1.xaxis.set_major_formatter(ConfigParams.FORMATTER) PublicFunc.__resetAllButton__(self, ButtonState) self.ui.spinBox_moveLength.setValue(Config["CustomAutoplayArgs"]["MoveLength"]) self.ui.spinBox_maxRange.setValue(Config["CustomAutoplayArgs"]["MaxRange"]) self.ui.spinBox_moveSpeed.setValue(Config["CustomAutoplayArgs"]["MoveSpeed"]) self.ui.tableWidget_type_1.setHorizontalHeaderLabels(['序号', '起始时间', '终止时间']) self.ui.tableWidget_type_2.setHorizontalHeaderLabels(['序号', '起始时间', '终止时间']) self.ui.tableWidget_type_3.setHorizontalHeaderLabels(['序号', '起始时间', '终止时间']) self.ui.tableWidget_type_4.setHorizontalHeaderLabels(['序号', '起始时间', '终止时间']) self.ui.tableWidget_type_5.setHorizontalHeaderLabels(['序号', '起始时间', '终止时间']) self.ui.tableWidget_type_1.setEditTriggers(QTableWidget.NoEditTriggers) self.ui.tableWidget_type_2.setEditTriggers(QTableWidget.NoEditTriggers) self.ui.tableWidget_type_3.setEditTriggers(QTableWidget.NoEditTriggers) self.ui.tableWidget_type_4.setEditTriggers(QTableWidget.NoEditTriggers) self.ui.tableWidget_type_5.setEditTriggers(QTableWidget.NoEditTriggers) self.ui.tableWidget_type_1.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.ui.tableWidget_type_2.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.ui.tableWidget_type_3.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.ui.tableWidget_type_4.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.ui.tableWidget_type_5.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.ui.lineEdit_start_time.setValidator(ConfigParams.VALIDATOR_INTEGER) self.ui.lineEdit_end_time.setValidator(ConfigParams.VALIDATOR_INTEGER) self.ui.pushButton_input.clicked.connect(self.__slot_btn_input__) self.ui.pushButton_type_1.clicked.connect(self.__slot_btn_label__) self.ui.pushButton_type_2.clicked.connect(self.__slot_btn_label__) self.ui.pushButton_type_3.clicked.connect(self.__slot_btn_label__) self.ui.pushButton_type_4.clicked.connect(self.__slot_btn_label__) self.ui.pushButton_type_5.clicked.connect(self.__slot_btn_label__) self.ui.pushButton_save.clicked.connect(self.__slot_btn_save__) self.ui.pushButton_delete.clicked.connect(self.__slot_btn_delete_label__) self.ui.pushButton_prev_move.clicked.connect(self.__slot_btn_move__) self.ui.pushButton_pause.clicked.connect(self.__slot_btn_move__) self.ui.pushButton_next_move.clicked.connect(self.__slot_btn_move__) self.ui.radioButton_move_preset_1.toggled.connect(self.__change_autoplay_args__) self.ui.radioButton_move_preset_2.toggled.connect(self.__change_autoplay_args__) self.ui.radioButton_move_preset_3.toggled.connect(self.__change_autoplay_args__) self.ui.radioButton_move_custom.toggled.connect(self.__change_autoplay_args__) self.ui.tableWidget_type_1.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__) self.ui.tableWidget_type_2.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__) self.ui.tableWidget_type_3.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__) self.ui.tableWidget_type_4.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__) self.ui.tableWidget_type_5.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__) self.ui.pushButton_input_setting.clicked.connect(self.setting.show) @overrides def closeEvent(self, event): reply = QMessageBox.question(self, '确认', '确认退出吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: PublicFunc.__disableAllButton__(self, ButtonState) PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) QApplication.processEvents() # 清空画框 del self.rectangles_ax0_patches del self.rectangles_ax1_patches del self.annotation_tableWidget if self.ax0 is not None: self.ax0.clear() if self.ax1 is not None: self.ax1.clear() # 释放资源 del self.data self.fig.clf() plt.close(self.fig) self.deleteLater() collect() self.canvas = None event.accept() else: event.ignore() def __reset__(self): ButtonState["Current"].update(ButtonState["Default"].copy()) def __plot__(self): # 清空画框 if self.annotation_tableWidget is not None: self.annotation_tableWidget.remove() self.annotation_tableWidget = None self.reset_axes() sender = self.sender() if sender == self.ui.pushButton_input: try: self.ax0.plot(self.data.orgBcg, label=Constants.ARTIFACT_LABEL_PLOT_LABEL_ORGBCG_SYNC, color=Constants.PLOT_COLOR_BLUE) self.ax0.legend(loc=Constants.PLOT_UPPER_RIGHT) self.ax1.plot(self.data.BCG, label=Constants.ARTIFACT_LABEL_PLOT_LABEL_BCG_SYNC, color=Constants.PLOT_COLOR_BLUE) self.ax1.legend(loc=Constants.PLOT_UPPER_RIGHT) self.rect_down = min(self.ax0.get_ylim()[0], self.ax1.get_ylim()[0]) - 1000 self.rect_up = max(self.ax0.get_ylim()[1], self.ax1.get_ylim()[1]) + 1000 self.canvas.draw() self.__plot_artifact__() self.ax0.autoscale(False) self.ax1.autoscale(False) except Exception as e: return Result().failure(info=Constants.DRAW_FAILURE + "\n" + format_exc()) return Result().success(info=Constants.DRAW_FINISHED) else: self.canvas.draw() self.ax0.autoscale(False) self.ax1.autoscale(False) return Result().failure(info=Constants.DRAW_FAILURE) def __plot_artifact__(self): if len(self.ax0.patches) != 0: for patch in self.ax0.patches: if patch in self.rectangles_ax0_patches: patch.remove() if len(self.ax1.patches) != 0: for patch in self.ax1.patches: if patch in self.rectangles_ax1_patches: patch.remove() self.rectangles_ax0_patches = [] self.rectangles_ax1_patches = [] self.ax1.callbacks.connect('xlim_changed', lambda ax: self.on_xlim_change(ax)) self.canvas.draw() # 绘制体动 for i in range(0, len(self.data.df_Artifact_a)): if self.data.df_Artifact_a.iloc[i][1] == 1: # 橙色,剧烈体动 rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_DEEP_YELLOW) self.rectangles_ax0_patches.append(rectangle) rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_DEEP_YELLOW) self.rectangles_ax1_patches.append(rectangle) elif self.data.df_Artifact_a.iloc[i][1] == 2: # 黄色,脉冲体动 rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_YELLOW) self.rectangles_ax0_patches.append(rectangle) rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_YELLOW) self.rectangles_ax1_patches.append(rectangle) elif self.data.df_Artifact_a.iloc[i][1] == 3: # 青色,常规体动 rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_AQUA) self.rectangles_ax0_patches.append(rectangle) rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_AQUA) self.rectangles_ax1_patches.append(rectangle) elif self.data.df_Artifact_a.iloc[i][1] == 4: # 紫色,疑似鼾声 rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_PURPLE_PINK) self.rectangles_ax0_patches.append(rectangle) rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_PURPLE_PINK) self.rectangles_ax1_patches.append(rectangle) elif self.data.df_Artifact_a.iloc[i][1] == 5: # 灰色,离床 rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_DEEP_GREY) self.rectangles_ax0_patches.append(rectangle) rectangle = patches.Rectangle((self.data.df_Artifact_a.iloc[i][2], self.rect_down), width=(self.data.df_Artifact_a.iloc[i][3] - self.data.df_Artifact_a.iloc[i][2]), height=self.rect_up - self.rect_down, fill=True, alpha=ConfigParams.ARTIFACT_LABEL_LABEL_TRANSPARENCY, color=Constants.PLOT_COLOR_DEEP_GREY) self.rectangles_ax1_patches.append(rectangle) for patch in self.rectangles_ax0_patches: self.ax0.add_patch(patch) for patch in self.rectangles_ax1_patches: self.ax1.add_patch(patch) self.canvas.draw() def update_label_args(self, rect_left, rect_right): try: # 计算傅里叶变换 fft_result = fft(self.data.orgBcg[rect_left:rect_right]) fft_freqs = fftfreq(len(self.data.orgBcg[rect_left:rect_right]), 1 / Config["InputConfig"]["orgBcgFreq"]) # 确定频率高于20Hz的成分 high_freq_indices = abs(fft_freqs) > 20 high_freq_fft = fft_result[high_freq_indices] # 计算能量 total_energy = sum(abs(fft_result) ** 2) high_freq_energy = sum(abs(high_freq_fft) ** 2) # 计算能量占比 energy_ratio_orgData_sync = round((high_freq_energy * 100 / total_energy), 6) # 计算傅里叶变换 fft_result = fft(self.data.orgBcg[rect_left:rect_right]) fft_freqs = fftfreq(len(self.data.orgBcg[rect_left:rect_right]), 1 / Config["InputConfig"]["orgBcgFreq"]) # 确定频率高于20Hz的成分 high_freq_indices = abs(fft_freqs) > 20 high_freq_fft = fft_result[high_freq_indices] # 计算能量 total_energy = sum(abs(fft_result) ** 2) high_freq_energy = sum(abs(high_freq_fft) ** 2) # 计算能量占比 energy_ratio_BCG_sync = round((high_freq_energy * 100 / total_energy), 6) self.ui.lineEdit_energy_percent_orgBcg.setText(str(energy_ratio_orgData_sync) + "%") self.ui.lineEdit_energy_percent_BCG.setText(str(energy_ratio_BCG_sync) + "%") self.ui.lineEdit_start_time.setText(str(int(self.rect_left))) self.ui.lineEdit_end_time.setText(str(int(self.rect_right))) self.ui.lineEdit_duration.setText(str(int(self.rect_right) - int(self.rect_left))) except Exception: self.ui.lineEdit_energy_percent_orgBcg.setText("获取异常") self.ui.lineEdit_energy_percent_BCG.setText("获取异常") self.ui.lineEdit_start_time.setText("获取异常") self.ui.lineEdit_end_time.setText("获取异常") self.ui.lineEdit_duration.setText("获取异常") def update_tableWidget(self): try: self.ui.tableWidget_type_1.clearContents() self.ui.tableWidget_type_2.clearContents() self.ui.tableWidget_type_3.clearContents() self.ui.tableWidget_type_4.clearContents() self.ui.tableWidget_type_5.clearContents() self.ui.tableWidget_type_1.setRowCount(0) self.ui.tableWidget_type_2.setRowCount(0) self.ui.tableWidget_type_3.setRowCount(0) self.ui.tableWidget_type_4.setRowCount(0) self.ui.tableWidget_type_5.setRowCount(0) for row, data in self.data.df_Artifact_a.iterrows(): if data["type"] == 1: self.ui.tableWidget_type_1.setRowCount(self.ui.tableWidget_type_1.rowCount() + 1) self.ui.tableWidget_type_1.setItem(self.ui.tableWidget_type_1.rowCount() - 1, 0, QTableWidgetItem(str(data["number"]))) self.ui.tableWidget_type_1.setItem(self.ui.tableWidget_type_1.rowCount() - 1, 1, QTableWidgetItem(str(data["startTime"]))) self.ui.tableWidget_type_1.setItem(self.ui.tableWidget_type_1.rowCount() - 1, 2, QTableWidgetItem(str(data["endTime"]))) elif data["type"] == 2: self.ui.tableWidget_type_2.setRowCount(self.ui.tableWidget_type_2.rowCount() + 1) self.ui.tableWidget_type_2.setItem(self.ui.tableWidget_type_2.rowCount() - 1, 0, QTableWidgetItem(str(data["number"]))) self.ui.tableWidget_type_2.setItem(self.ui.tableWidget_type_2.rowCount() - 1, 1, QTableWidgetItem(str(data["startTime"]))) self.ui.tableWidget_type_2.setItem(self.ui.tableWidget_type_2.rowCount() - 1, 2, QTableWidgetItem(str(data["endTime"]))) elif data["type"] == 3: self.ui.tableWidget_type_3.setRowCount(self.ui.tableWidget_type_3.rowCount() + 1) self.ui.tableWidget_type_3.setItem(self.ui.tableWidget_type_3.rowCount() - 1, 0, QTableWidgetItem(str(data["number"]))) self.ui.tableWidget_type_3.setItem(self.ui.tableWidget_type_3.rowCount() - 1, 1, QTableWidgetItem(str(data["startTime"]))) self.ui.tableWidget_type_3.setItem(self.ui.tableWidget_type_3.rowCount() - 1, 2, QTableWidgetItem(str(data["endTime"]))) elif data["type"] == 4: self.ui.tableWidget_type_4.setRowCount(self.ui.tableWidget_type_4.rowCount() + 1) self.ui.tableWidget_type_4.setItem(self.ui.tableWidget_type_4.rowCount() - 1, 0, QTableWidgetItem(str(data["number"]))) self.ui.tableWidget_type_4.setItem(self.ui.tableWidget_type_4.rowCount() - 1, 1, QTableWidgetItem(str(data["startTime"]))) self.ui.tableWidget_type_4.setItem(self.ui.tableWidget_type_4.rowCount() - 1, 2, QTableWidgetItem(str(data["endTime"]))) elif data["type"] == 5: self.ui.tableWidget_type_5.setRowCount(self.ui.tableWidget_type_5.rowCount() + 1) self.ui.tableWidget_type_5.setItem(self.ui.tableWidget_type_5.rowCount() - 1, 0, QTableWidgetItem(str(data["number"]))) self.ui.tableWidget_type_5.setItem(self.ui.tableWidget_type_5.rowCount() - 1, 1, QTableWidgetItem(str(data["startTime"]))) self.ui.tableWidget_type_5.setItem(self.ui.tableWidget_type_5.rowCount() - 1, 2, QTableWidgetItem(str(data["endTime"]))) except Exception as e: return Result().failure(info=Constants.UPDATE_FAILURE + Constants.FAILURE_REASON["Update_tableWidget_Exception"] + "\n" + format_exc()) return Result().success(info=Constants.UPDATE_FINISHED) def update_Info(self): try: artifact_amount = [0, 0, 0, 0, 0] artifact_total_duration = [0, 0, 0, 0, 0] tableWidget_list = [self.ui.tableWidget_type_1, self.ui.tableWidget_type_2, self.ui.tableWidget_type_3, self.ui.tableWidget_type_4, self.ui.tableWidget_type_5] for index, tableWidget in enumerate(tableWidget_list): sum_of_differences = 0 for row in range(tableWidget.rowCount()): startTime = tableWidget.item(row, 1) endTime = tableWidget.item(row, 2) if startTime and endTime: # 确保单元格不为空 startTime = int(startTime.text()) endTime = int(endTime.text()) difference_amount = endTime - startTime sum_of_differences += difference_amount artifact_amount[index] = tableWidget.rowCount() artifact_total_duration[index] = sum_of_differences self.ui.spinBox_amount_type_1.setValue(artifact_amount[0]) self.ui.spinBox_amount_type_2.setValue(artifact_amount[1]) self.ui.spinBox_amount_type_3.setValue(artifact_amount[2]) self.ui.spinBox_amount_type_4.setValue(artifact_amount[3]) self.ui.spinBox_amount_type_5.setValue(artifact_amount[4]) self.ui.spinBox_duration_type_1.setValue(artifact_total_duration[0]) self.ui.spinBox_duration_type_2.setValue(artifact_total_duration[1]) self.ui.spinBox_duration_type_3.setValue(artifact_total_duration[2]) self.ui.spinBox_duration_type_4.setValue(artifact_total_duration[3]) self.ui.spinBox_duration_type_5.setValue(artifact_total_duration[4]) except Exception as e: return Result().failure(info=Constants.UPDATE_FAILURE + Constants.FAILURE_REASON["Update_Info_Exception"] + "\n" + format_exc()) return Result().success(info=Constants.UPDATE_FAILURE) def __slot_btn_input__(self): PublicFunc.__disableAllButton__(self, ButtonState) self.reset_axes() self.canvas.draw() self.data = Data() # 导入数据 PublicFunc.progressbar_update(self, 1, 6, Constants.INPUTTING_DATA, 0) result = self.data.open_file() if not result.status: PublicFunc.text_output(self.ui, "(1/6)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(1/6)" + result.info, Constants.TIPS_TYPE_INFO) # 重采样 PublicFunc.progressbar_update(self, 2, 6, Constants.RESAMPLING_DATA, 0) result = self.data.resample() if not result.status: PublicFunc.text_output(self.ui, "(2/6)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(2/6)" + result.info, Constants.TIPS_TYPE_INFO) # 获取存档 PublicFunc.progressbar_update(self, 3, 6, Constants.LOADING_ARCHIVE, 50) result = self.data.get_archive() if not result.status: PublicFunc.text_output(self.ui, "(3/6)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(3/6)" + result.info, Constants.TIPS_TYPE_INFO) # 绘图 PublicFunc.progressbar_update(self, 4, 6, Constants.DRAWING_DATA, 60) result = self.__plot__() if not result.status: PublicFunc.text_output(self.ui, "(4/6)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(4/6)" + result.info, Constants.TIPS_TYPE_INFO) # 更新表格 PublicFunc.progressbar_update(self, 5, 6, Constants.UPDATING_TABLEWIDGET, 90) result = self.update_tableWidget() if not result.status: PublicFunc.text_output(self.ui, "(5/6)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(5/6)" + result.info, Constants.TIPS_TYPE_INFO) # 更新信息 PublicFunc.progressbar_update(self, 6, 6, Constants.UPDATING_INFO, 95) result = self.update_Info() if not result.status: PublicFunc.text_output(self.ui, "(6/6)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(6/6)" + result.info, Constants.TIPS_TYPE_INFO) self.__reset__() self.figToolbar.action_Label_Artifact.setEnabled(True) for action in self.figToolbar._actions.values(): action.setEnabled(True) ButtonState["Current"]["pushButton_input"] = False ButtonState["Current"]["pushButton_input_setting"] = False ButtonState["Current"]["pushButton_prev_move"] = True ButtonState["Current"]["pushButton_next_move"] = True ButtonState["Current"]["pushButton_pause"] = True ButtonState["Current"]["pushButton_save"] = True ButtonState["Current"]["pushButton_type_1"] = True ButtonState["Current"]["pushButton_type_2"] = True ButtonState["Current"]["pushButton_type_3"] = True ButtonState["Current"]["pushButton_type_4"] = True ButtonState["Current"]["pushButton_type_5"] = True PublicFunc.finish_operation(self, ButtonState) def __slot_btn_label__(self): sender = self.sender() flag = False select_row = array([]).astype(dict) if sender == self.ui.pushButton_type_1: type = 1 elif sender == self.ui.pushButton_type_2: type = 2 elif sender == self.ui.pushButton_type_3: type = 3 elif sender == self.ui.pushButton_type_4: type = 4 elif sender == self.ui.pushButton_type_5: type = 5 else: raise ValueError() if (self.ui.lineEdit_start_time.text() == Constants.STRING_IS_EMPTY or self.ui.lineEdit_end_time.text() == Constants.STRING_IS_EMPTY): PublicFunc.msgbox_output(self, Constants.ARTIFACT_LABEL_MISS_ARGS, Constants.MSGBOX_TYPE_ERROR) return for index, row in self.data.df_Artifact_a.iterrows(): value_startTime = row['startTime'] value_endTime = row['endTime'] # if ((value_endTime >= int(self.ui.lineEdit_start_time.text()) and value_startTime <= int(self.ui.lineEdit_start_time.text())) or # (value_endTime >= int(self.ui.lineEdit_end_time.text()) and value_startTime <= int(self.ui.lineEdit_end_time.text())) or # (value_startTime >= int(self.ui.lineEdit_start_time.text()) and value_endTime <= int(self.ui.lineEdit_end_time.text())) or # (value_startTime <= int(self.ui.lineEdit_start_time.text()) and value_endTime >= int(self.ui.lineEdit_end_time.text()))): # select_row = append(select_row, row) # TODO:体动选取区域的判别尚未做的很完整,选中多个已有的体动的区域时可能会出现问题 if (type == 1 and row['type'] == 1) or (type == 2 and row['type'] == 2) or (type == 3 and row['type'] == 3): if ((value_startTime > int(self.ui.lineEdit_start_time.text()) and (value_startTime - int(self.ui.lineEdit_end_time.text()) <= 2 * Config["InputConfig"]["UseFreq"])) or (value_endTime < int(self.ui.lineEdit_end_time.text()) and (int(self.ui.lineEdit_end_time.text()) - value_endTime <= 2 * Config["InputConfig"]["UseFreq"]))): reply = QMessageBox.question(self, '确认', '{}{},是否确认合并?'.format(Constants.ARTIFACT_LABEL_MERGE, row['number']), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: self.data.df_Artifact_a.loc[self.data.df_Artifact_a['number'] == row['number'], ['number', 'type', 'startTime', 'endTime']] = [ int(row['number']), int(type), int(self.ui.lineEdit_start_time.text()) if int(self.ui.lineEdit_start_time.text()) < int(row['startTime']) else int(row['startTime']), int(self.ui.lineEdit_end_time.text()) if int(self.ui.lineEdit_end_time.text()) > int(row['endTime']) else int(row['endTime']) ] new_row = {'number': int(row['number']), 'type': int(type), 'startTime': int(self.ui.lineEdit_start_time.text()), 'endTime': int(self.ui.lineEdit_end_time.text())} flag = True break else: return if value_startTime <= int(self.ui.lineEdit_end_time.text()) and int( self.ui.lineEdit_start_time.text()) <= value_endTime: PublicFunc.msgbox_output(self, f"{Constants.ARTIFACT_LABEL_OVERLAPPING}{row['number']}", Constants.MSGBOX_TYPE_ERROR) return if not flag: new_row = {'number': int(len(self.data.df_Artifact_a) + 1), 'type': int(type), 'startTime': int(self.ui.lineEdit_start_time.text()), 'endTime': int(self.ui.lineEdit_end_time.text())} self.data.df_Artifact_a = concat([self.data.df_Artifact_a, DataFrame([new_row])], ignore_index=True) sorted_part = self.data.df_Artifact_a[['type', 'startTime', 'endTime']].sort_values(by='startTime').reset_index( drop=True) self.data.df_Artifact_a[['type', 'startTime', 'endTime']] = sorted_part self.update_tableWidget() self.update_Info() self.__plot_artifact__() target_row = self.data.df_Artifact_a[ self.data.df_Artifact_a.eq(int(self.ui.lineEdit_start_time.text())).any(axis=1)] if not target_row.empty: first_column_value = target_row.iloc[0, 0] # 获取第1列的值 else: raise AttributeError() PublicFunc.text_output(self.ui, f"新增体动标签{first_column_value}, 类型{new_row['type']},从{int(self.ui.lineEdit_start_time.text())}ms到{int(self.ui.lineEdit_end_time.text())}ms", Constants.TIPS_TYPE_INFO) self.__slot_btn_save__() def __slot_btn_save__(self): PublicFunc.__disableAllButton__(self, ButtonState) # 保存 PublicFunc.progressbar_update(self, 1, 1, Constants.SAVING_DATA, 0) amount = [ self.ui.spinBox_amount_type_1.value(), self.ui.spinBox_amount_type_2.value(), self.ui.spinBox_amount_type_3.value(), self.ui.spinBox_amount_type_4.value(), self.ui.spinBox_amount_type_5.value() ] duration = [ self.ui.spinBox_duration_type_1.value(), self.ui.spinBox_duration_type_2.value(), self.ui.spinBox_duration_type_3.value(), self.ui.spinBox_duration_type_4.value(), self.ui.spinBox_duration_type_5.value() ] result = self.data.save(amount, duration) if not result.status: PublicFunc.text_output(self.ui, "(1/1)" + result.info, Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR) PublicFunc.finish_operation(self, ButtonState) return else: PublicFunc.text_output(self.ui, "(1/1)" + result.info, Constants.TIPS_TYPE_INFO) PublicFunc.finish_operation(self, ButtonState) def __slot_btn_delete_label__(self): if self.pressed_number is not None: self.data.df_Artifact_a = self.data.df_Artifact_a[self.data.df_Artifact_a['number'] != self.pressed_number] self.data.df_Artifact_a = self.data.df_Artifact_a.reset_index(drop=True) sorted_part = self.data.df_Artifact_a[['type', 'startTime', 'endTime']].sort_values(by='startTime').reset_index( drop=True) self.data.df_Artifact_a[['type', 'startTime', 'endTime']] = sorted_part self.data.df_Artifact_a['number'] = self.data.df_Artifact_a.index + 1 try: if self.annotation_tableWidget: self.annotation_tableWidget.remove() except AttributeError: pass self.annotation_tableWidget = None self.reset_labelBtn_color() self.update_tableWidget() self.update_Info() self.__plot_artifact__() self.__slot_btn_save__() PublicFunc.text_output(self.ui, f"{self.pressed_number}{Constants.ARTIFACT_LABEL_DELETE_ARTIFACT_SUCCESSFULLY}", Constants.TIPS_TYPE_INFO) else: PublicFunc.text_output(self.ui, f"{self.pressed_number}{Constants.ARTIFACT_LABEL_DELETE_ARTIFACT_FAILURE}", Constants.TIPS_TYPE_ERROR) PublicFunc.msgbox_output(self, f"{self.pressed_number}{Constants.ARTIFACT_LABEL_DELETE_ARTIFACT_FAILURE}", Constants.MSGBOX_TYPE_ERROR) self.pressed_number = None def __slot_btn_move__(self): if self.data is None: return sender = self.sender() if sender == self.ui.pushButton_prev_move: Config["AutoplayArgs"]["AutoplayMode"] = "prev" self.autoplay_xlim_start = int(self.ax0.get_xlim()[1] - Config["AutoplayArgs"]["MaxRange"]) self.autoplay_xlim_end = int(self.ax0.get_xlim()[1]) if self.autoplay_xlim_end > len(self.data.BCG): self.autoplay_xlim_start = int(len(self.data.BCG) - Config["AutoplayArgs"]["MaxRange"]) self.autoplay_xlim_end = int(len(self.data.BCG)) self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end) self.canvas.draw() self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"]) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_PREV_MOVE, Constants.TIPS_TYPE_INFO) elif sender == self.ui.pushButton_next_move: Config["AutoplayArgs"]["AutoplayMode"] = "next" self.autoplay_xlim_start = int(self.ax0.get_xlim()[0]) self.autoplay_xlim_end = int(self.ax0.get_xlim()[0] + Config["AutoplayArgs"]["MaxRange"]) if self.autoplay_xlim_start < 0: self.autoplay_xlim_start = 0 self.autoplay_xlim_end = 0 + Config["AutoplayArgs"]["MaxRange"] self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end) self.canvas.draw() self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"]) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_NEXT_MOVE, Constants.TIPS_TYPE_INFO) elif sender == self.ui.pushButton_pause: Config["AutoplayArgs"]["AutoplayMode"] = "pause" self.timer_autoplay.stop() PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_PAUSE, Constants.TIPS_TYPE_INFO) def __change_autoplay_args__(self): sender = self.sender() if sender == self.ui.radioButton_move_preset_1 and self.ui.radioButton_move_preset_1.isChecked(): Config["AutoplayArgs"]["MoveLength"] = int(self.ui.label_moveLength_preset_1.text()) Config["AutoplayArgs"]["MaxRange"] = int(self.ui.label_maxRange_preset_1.text()) Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.label_moveSpeed_preset_1.text()) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_SWITCH_PRESET_1, Constants.TIPS_TYPE_INFO) elif sender == self.ui.radioButton_move_preset_2 and self.ui.radioButton_move_preset_2.isChecked(): Config["AutoplayArgs"]["MoveLength"] = int(self.ui.label_moveLength_preset_2.text()) Config["AutoplayArgs"]["MaxRange"] = int(self.ui.label_maxRange_preset_2.text()) Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.label_moveSpeed_preset_2.text()) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_SWITCH_PRESET_2, Constants.TIPS_TYPE_INFO) elif sender == self.ui.radioButton_move_preset_3 and self.ui.radioButton_move_preset_3.isChecked(): Config["AutoplayArgs"]["MoveLength"] = int(self.ui.label_moveLength_preset_3.text()) Config["AutoplayArgs"]["MaxRange"] = int(self.ui.label_maxRange_preset_3.text()) Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.label_moveSpeed_preset_3.text()) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_SWITCH_PRESET_3, Constants.TIPS_TYPE_INFO) elif sender == self.ui.radioButton_move_custom and self.ui.radioButton_move_custom.isChecked(): Config["AutoplayArgs"]["MoveLength"] = int(self.ui.spinBox_moveLength.value()) Config["AutoplayArgs"]["MaxRange"] = int(self.ui.spinBox_maxRange.value()) Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.spinBox_moveSpeed.value()) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_SWITCH_CUSTOM, Constants.TIPS_TYPE_INFO) if Config["AutoplayArgs"]["AutoplayMode"] == "next": self.autoplay_xlim_start = int(self.ax0.get_xlim()[0]) self.autoplay_xlim_end = int(self.ax0.get_xlim()[0] + Config["AutoplayArgs"]["MaxRange"]) if self.autoplay_xlim_start < 0: self.autoplay_xlim_start = 0 self.autoplay_xlim_end = 0 + Config["AutoplayArgs"]["MaxRange"] self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end) self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"]) elif Config["AutoplayArgs"]["AutoplayMode"] == "prev": self.autoplay_xlim_start = int(self.ax0.get_xlim()[1] - Config["AutoplayArgs"]["MaxRange"]) self.autoplay_xlim_end = int(self.ax0.get_xlim()[1]) if self.autoplay_xlim_end > len(self.data.BCG): self.autoplay_xlim_start = int(self.data.BCG) - Config["AutoplayArgs"]["MaxRange"] self.autoplay_xlim_end = int(self.data.BCG) self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end) self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"]) elif Config["AutoplayArgs"]["AutoplayMode"] == "pause": self.timer_autoplay.stop() def __slot_tableWidget_on_cell_double_clicked__(self, row, col): if Config["AutoplayArgs"]["AutoplayMode"] != "pause": self.ui.pushButton_pause.click() sender = self.sender() if sender == self.ui.tableWidget_type_1: self.pressed_number = int( self.ui.tableWidget_type_1.item(row, 0).text()) startTime = int(self.ui.tableWidget_type_1.item(row, 1).text()) endTime = int(self.ui.tableWidget_type_1.item(row, 2).text()) type = 1 elif sender == self.ui.tableWidget_type_2: self.pressed_number = int( self.ui.tableWidget_type_2.item(row, 0).text()) startTime = int(self.ui.tableWidget_type_2.item(row, 1).text()) endTime = int(self.ui.tableWidget_type_2.item(row, 2).text()) type = 2 elif sender == self.ui.tableWidget_type_3: self.pressed_number = int( self.ui.tableWidget_type_3.item(row, 0).text()) startTime = int(self.ui.tableWidget_type_3.item(row, 1).text()) endTime = int(self.ui.tableWidget_type_3.item(row, 2).text()) type = 3 elif sender == self.ui.tableWidget_type_4: self.pressed_number = int( self.ui.tableWidget_type_4.item(row, 0).text()) startTime = int(self.ui.tableWidget_type_4.item(row, 1).text()) endTime = int(self.ui.tableWidget_type_4.item(row, 2).text()) type = 4 elif sender == self.ui.tableWidget_type_5: self.pressed_number = int( self.ui.tableWidget_type_5.item(row, 0).text()) startTime = int(self.ui.tableWidget_type_5.item(row, 1).text()) endTime = int(self.ui.tableWidget_type_5.item(row, 2).text()) type = 5 else: raise ValueError() self.ax0.set_xlim(startTime - 60000, endTime + 60000) self.annotation_tableWidget = self.ax0.annotate(f'number={int(self.pressed_number)}, duration={int(endTime - startTime)}ms', xy=(int(startTime + (endTime - startTime) / 2), self.ax0.get_ylim()[0]), xytext=( int(startTime + (endTime - startTime) / 2), self.ax0.get_ylim()[0] + ( self.ax0.get_ylim()[1] - self.ax0.get_ylim()[ 0]) * 0.1), arrowprops=dict(facecolor=Constants.PLOT_COLOR_BLACK, shrink=0.1)) self.change_labelBtn_color(type) self.ui.pushButton_delete.setEnabled(True) self.canvas.draw() self.rect_left = startTime self.rect_right = endTime self.update_label_args(int(self.rect_left), int(self.rect_right)) PublicFunc.text_output(self.ui, f"{Constants.ARTIFACT_LABEL_JUMP_ARTIFACT}{str(self.pressed_number)}", Constants.TIPS_TYPE_INFO) def reset_axes(self): if self.ax0 is not None: self.ax0.clear() self.ax0.grid(True) self.ax0.xaxis.set_major_formatter(ConfigParams.FORMATTER) self.ax0.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) if self.ax1 is not None: self.ax1.clear() self.ax1.grid(True) self.ax1.xaxis.set_major_formatter(ConfigParams.FORMATTER) def on_xlim_change(self, event_ax): try: if self.annotation_tableWidget: self.annotation_tableWidget.remove() except AttributeError: pass self.annotation_tableWidget = None self.reset_labelBtn_color() ButtonState["Current"]["pushButton_delete"] = False PublicFunc.__enableAllButton__(self, ButtonState) def autoplay_move_xlim(self): if Config["AutoplayArgs"]["AutoplayMode"] == "prev" and self.autoplay_xlim_start < 0: Config["AutoplayArgs"]["AutoplayMode"] = "pause" self.timer_autoplay.stop() elif Config["AutoplayArgs"]["AutoplayMode"] == "next" and self.autoplay_xlim_end > len(self.data.BCG): Config["AutoplayArgs"]["AutoplayMode"] = "pause" self.timer_autoplay.stop() else: if Config["AutoplayArgs"]["AutoplayMode"] == "next": self.autoplay_xlim_start += Config["AutoplayArgs"]["MoveLength"] self.autoplay_xlim_end += Config["AutoplayArgs"]["MoveLength"] elif Config["AutoplayArgs"]["AutoplayMode"] == "prev": self.autoplay_xlim_start -= Config["AutoplayArgs"]["MoveLength"] self.autoplay_xlim_end -= Config["AutoplayArgs"]["MoveLength"] self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end) self.canvas.draw() def change_labelBtn_color(self, type): if type == 1: self.ui.pushButton_type_1.setStyleSheet( Constants.ARTIFACT_LABEL_LABELBTN_STYLE_1) self.ui.pushButton_type_2.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_3.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_4.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_5.setStyleSheet(Constants.STRING_IS_EMPTY) elif type == 2: self.ui.pushButton_type_1.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_2.setStyleSheet( Constants.ARTIFACT_LABEL_LABELBTN_STYLE_2) self.ui.pushButton_type_3.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_4.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_5.setStyleSheet(Constants.STRING_IS_EMPTY) elif type == 3: self.ui.pushButton_type_1.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_2.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_3.setStyleSheet( Constants.ARTIFACT_LABEL_LABELBTN_STYLE_3) self.ui.pushButton_type_4.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_5.setStyleSheet(Constants.STRING_IS_EMPTY) elif type == 4: self.ui.pushButton_type_1.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_2.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_3.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_4.setStyleSheet( Constants.ARTIFACT_LABEL_LABELBTN_STYLE_4) self.ui.pushButton_type_5.setStyleSheet(Constants.STRING_IS_EMPTY) elif type == 5: self.ui.pushButton_type_1.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_2.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_3.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_4.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_5.setStyleSheet( Constants.ARTIFACT_LABEL_LABELBTN_STYLE_5) def reset_labelBtn_color(self): self.ui.pushButton_type_1.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_2.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_3.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_4.setStyleSheet(Constants.STRING_IS_EMPTY) self.ui.pushButton_type_5.setStyleSheet(Constants.STRING_IS_EMPTY) def toggle_home(self): if Config["AutoplayArgs"]["AutoplayMode"] != "pause": self.ui.pushButton_pause.click() if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None: self.figToolbar.rect_patch_ax0.remove() self.figToolbar.rect_patch_ax0 = None self.figToolbar.rect_patch_ax1.remove() self.figToolbar.rect_patch_ax1 = None self.rect_left = None self.rect_right = None self.ui.lineEdit_start_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_end_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_duration.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_orgBcg.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_BCG.setText(Constants.STRING_IS_EMPTY) self.canvas.draw() self.ax0.autoscale(True) self.ax1.autoscale(True) self.ax1.relim() self.ax1.autoscale_view() self.canvas.draw() self.ax0.autoscale(False) self.ax1.autoscale(False) PublicFunc.text_output(self.ui, Constants.ARTIFACT_LABEL_RECOVER_SCALE, Constants.TIPS_TYPE_INFO) def toggle_zoom(self): if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None: self.figToolbar.rect_patch_ax0.remove() self.figToolbar.rect_patch_ax0 = None self.figToolbar.rect_patch_ax1.remove() self.figToolbar.rect_patch_ax1 = None self.rect_left = None self.rect_right = None self.ui.lineEdit_start_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_end_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_duration.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_orgBcg.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_BCG.setText(Constants.STRING_IS_EMPTY) self.canvas.draw() def toggle_pan(self): if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None: self.figToolbar.rect_patch_ax0.remove() self.figToolbar.rect_patch_ax0 = None self.figToolbar.rect_patch_ax1.remove() self.figToolbar.rect_patch_ax1 = None self.rect_left = None self.rect_right = None self.ui.lineEdit_start_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_end_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_duration.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_orgBcg.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_BCG.setText(Constants.STRING_IS_EMPTY) self.canvas.draw() def toggle_label_artifact(self, state): if state: self.deactivate_figToolbar_buttons() self.figToolbar.action_Label_Artifact.setChecked(True) self.figToolbar.cid_mouse_press = self.canvas.mpl_connect( "button_press_event", self.on_click) self.figToolbar.cid_mouse_release = self.canvas.mpl_connect( "button_release_event", self.on_release) self.figToolbar.cid_mouse_hold = self.canvas.mpl_connect( "motion_notify_event", self.on_hold) else: if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None: self.figToolbar.rect_patch_ax0.remove() self.figToolbar.rect_patch_ax0 = None self.figToolbar.rect_patch_ax1.remove() self.figToolbar.rect_patch_ax1 = None self.rect_left = None self.rect_right = None self.ui.lineEdit_start_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_end_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_duration.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_orgBcg.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_BCG.setText(Constants.STRING_IS_EMPTY) self.canvas.draw() if self.figToolbar.cid_mouse_press is not None: self.canvas.mpl_disconnect(self.figToolbar.cid_mouse_press) self.figToolbar.cid_mouse_press = None if self.figToolbar.cid_mouse_release is not None: self.canvas.mpl_disconnect(self.figToolbar.cid_mouse_release) self.figToolbar.cid_mouse_release = None if self.figToolbar.cid_mouse_hold: self.canvas.mpl_disconnect(self.figToolbar.cid_mouse_hold) self.figToolbar.cid_mouse_hold = None def deactivate_figToolbar_buttons(self): for action in self.figToolbar._actions.values(): if action.isChecked(): if action == self.figToolbar._actions['pan']: self.figToolbar.pan() if action == self.figToolbar._actions['zoom']: self.figToolbar.zoom() def on_click(self, event): if self.figToolbar.action_Label_Artifact.isChecked(): self.reset_labelBtn_color() ButtonState["Current"]["pushButton_delete"] = False PublicFunc.__enableAllButton__(self, ButtonState) if event.button == 1: self.is_left_button_pressed = True self.figToolbar.rect_start_x = event.xdata # 如果矩形patch已存在,先移除 if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None: self.figToolbar.rect_patch_ax0.remove() self.figToolbar.rect_patch_ax0 = None self.figToolbar.rect_patch_ax1.remove() self.figToolbar.rect_patch_ax1 = None self.rect_left = None self.rect_right = None self.ui.lineEdit_start_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_end_time.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_duration.setText(Constants.STRING_IS_EMPTY) self.canvas.draw() self.ui.lineEdit_energy_percent_orgBcg.setText(Constants.STRING_IS_EMPTY) self.ui.lineEdit_energy_percent_BCG.setText(Constants.STRING_IS_EMPTY) def on_release(self, event): if self.figToolbar.rect_start_x is not None: self.figToolbar.rect_end_x = event.xdata if self.figToolbar.rect_start_x is not None and self.figToolbar.rect_end_x is not None: if self.figToolbar.rect_start_x < self.figToolbar.rect_end_x: self.rect_left = self.figToolbar.rect_start_x self.rect_right = self.figToolbar.rect_end_x elif self.figToolbar.rect_start_x > self.figToolbar.rect_end_x: self.rect_left = self.figToolbar.rect_end_x self.rect_right = self.figToolbar.rect_start_x else: self.rect_left = self.figToolbar.rect_start_x self.rect_right = self.figToolbar.rect_start_x else: self.rect_left = self.figToolbar.rect_start_x self.rect_right = self.figToolbar.rect_start_x if event.button == 1 and self.is_left_button_pressed: self.is_left_button_pressed = False if self.rect_left < 0: self.rect_left = 0 elif self.rect_left >= len(self.data.BCG): self.rect_left = 0 self.rect_right = 0 if self.rect_right >= len(self.data.BCG): self.rect_right = len(self.data.BCG) - 1 elif self.rect_right < 0: self.rect_left = 0 self.rect_right = 0 if int(self.rect_left) != int(self.rect_right): self.update_label_args(int(self.rect_left), int(self.rect_right)) self.figToolbar.rect_start_x = None self.figToolbar.rect_end_x = None self.canvas.draw() def on_hold(self, event): if self.figToolbar.rect_start_x is not None and event.xdata is not None: self.figToolbar.rect_end_x = event.xdata # 如果矩形patch不存在,则创建一个新的 if self.figToolbar.rect_patch_ax0 is None: if self.is_left_button_pressed: self.figToolbar.rect_patch_ax0 = patches.Rectangle((0, 0), 1, 1, fill=True, alpha=0.2, color='r') self.ax0.add_patch(self.figToolbar.rect_patch_ax0) # 如果矩形patch不存在,则创建一个新的 if self.figToolbar.rect_patch_ax1 is None: if self.is_left_button_pressed: self.figToolbar.rect_patch_ax1 = patches.Rectangle((0, 0), 1, 1, fill=True, alpha=0.2, color='r') self.ax1.add_patch(self.figToolbar.rect_patch_ax1) # 更新矩形patch的位置和大小 x_start = self.figToolbar.rect_start_x x_end = self.figToolbar.rect_end_x self.figToolbar.rect_patch_ax0.set_xy((min(x_start, x_end), self.rect_down)) self.figToolbar.rect_patch_ax0.set_width(abs(x_end - x_start)) self.figToolbar.rect_patch_ax0.set_height(self.rect_up - self.rect_down) self.figToolbar.rect_patch_ax1.set_xy((min(x_start, x_end), self.rect_down)) self.figToolbar.rect_patch_ax1.set_width(abs(x_end - x_start)) self.figToolbar.rect_patch_ax1.set_height(self.rect_up - self.rect_down) self.canvas.draw() class Data: def __init__(self): self.orgBcg = None self.BCG = None self.Artifact_a = None self.df_Artifact_a = DataFrame(columns=["number", "type", "startTime", "endTime"]) def open_file(self): if Path(Config["Path"]["Input_orgBcg"]).is_file(): Config["Path"]["Input_orgBcg"] = str(Path(Config["Path"]["Input_orgBcg"]).parent) if Path(Config["Path"]["Input_BCG"]).is_file(): Config["Path"]["Input_BCG"] = str(Path(Config["Path"]["Input_BCG"]).parent) result = PublicFunc.examine_file(Config["Path"]["Input_orgBcg"], ConfigParams.ORGBCG_SYNC) if result.status: Config["Path"]["Input_orgBcg"] = result.data["path"] Config["InputConfig"]["orgBcgFreq"] = result.data["freq"] else: return result result = PublicFunc.examine_file(Config["Path"]["Input_BCG"], ConfigParams.BCG_SYNC) if result.status: Config["Path"]["Input_BCG"] = result.data["path"] Config["InputConfig"]["BCGFreq"] = result.data["freq"] else: return result Config["Path"]["Save_a"] = str( Path(Config["Path"]["Save_a"]) / Path(ConfigParams.ARTIFACT_A + ConfigParams.ENDSWITH_TXT)) Config["Path"]["Save_b"] = str( Path(Config["Path"]["Save_b"]) / Path(ConfigParams.ARTIFACT_B + ConfigParams.ENDSWITH_TXT)) Config["Path"]["Save_c"] = str( Path(Config["Path"]["Save_c"]) / Path(ConfigParams.ARTIFACT_C + ConfigParams.ENDSWITH_CSV)) try: self.orgBcg = read_csv(Config["Path"]["Input_orgBcg"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) self.BCG = read_csv(Config["Path"]["Input_BCG"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) except Exception as e: return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON["Open_Data_Exception"] + "\n" + format_exc()) # 检查同步后的orgBcg信号和同步后的BCG信号长度是否相同 if len(self.orgBcg) != len(self.BCG): return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON["Data_Length_not_Correct"]) return Result().success(info=Constants.INPUT_FINISHED) def resample(self): if self.orgBcg is None: Result().failure(info=Constants.RESAMPLE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"]) try: if Config["InputConfig"]["orgBcgFreq"] != Config["InputConfig"]["UseFreq"]: self.orgBcg = resample(self.orgBcg, int(len(self.orgBcg) * (Config["InputConfig"]["UseFreq"] / Config["InputConfig"]["orgBcgFreq"]))) else: return Result().success(info=Constants.RESAMPLE_NO_NEED) except Exception as e: Result().failure(info=Constants.RESAMPLE_FAILURE + Constants.FAILURE_REASON["Resample_Exception"] + "\n" + format_exc()) return Result().success(info=Constants.RESAMPLE_FINISHED) def get_archive(self): if not Path(Config["Path"]["Save_a"]).exists(): self.Artifact_a = [] return Result().success(info=Constants.ARCHIVE_NOT_EXIST) else: self.Artifact_a = read_csv(Config["Path"]["Save_a"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) try: # 检查体动标签正确性,长度 if len(self.Artifact_a) % 4 != 0: return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON[ "Artifact_Format_Not_Correct"]) for i in range(0, len(self.Artifact_a), 4): unit_data = self.Artifact_a[i:i + 4] if len(unit_data) < 4: break self.df_Artifact_a.loc[len(self.df_Artifact_a)] = [int(unit_data[j]) for j in range(4)] except Exception as e: return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON[ "Get_Artifact_Format_Exception"] + "\n" + format_exc()) return Result().success(info=Constants.ARCHIVE_EXIST) def save(self, amount, duration): if (not Path(Config["Path"]["Save_a"]).parent.exists()) or (not Path(Config["Path"]["Save_a"]).parent.is_dir()): Path(Config["Path"]["Save_a"]).parent.mkdir(parents=True, exist_ok=True) if (not Path(Config["Path"]["Save_b"]).parent.exists()) or (not Path(Config["Path"]["Save_b"]).parent.is_dir()): Path(Config["Path"]["Save_b"]).parent.mkdir(parents=True, exist_ok=True) if (not Path(Config["Path"]["Save_c"]).parent.exists()) or (not Path(Config["Path"]["Save_c"]).parent.is_dir()): Path(Config["Path"]["Save_c"]).parent.mkdir(parents=True, exist_ok=True) try: # Artifact_a.txt和Artifact_c.csv的内容遍历 output_lines = [] for row_index in range(len(self.df_Artifact_a)): for col in self.df_Artifact_a.columns: value = self.df_Artifact_a.at[row_index, col] output_lines.append(str(value)) # Artifact_b.txt的内容遍历 dict = { 'type': [1, 2, 3, 4, 5], 'amount': [int(amount[0]), int(amount[1]), int(amount[2]), int(amount[3]), int(amount[4])], 'duration': [int(duration[0]), int(duration[1]), int(duration[2]), int(duration[3]), int(duration[4])] } df_Artifact_b = DataFrame(dict) # 写入文件并保存 self.df_Artifact_a.to_csv(Path(Config["Path"]["Save_a"]), header=False, index=False, sep='\n') df_Artifact_b.to_csv(Path(Config["Path"]["Save_b"]), header=False, index=False, sep='\n') self.df_Artifact_a.to_csv(Path(Config["Path"]["Save_c"]), index=False, encoding=ConfigParams.GBK_ENCODING) except Exception as e: return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON[ "Save_Exception"] + "\n" + format_exc()) return Result().success(info=Constants.SAVE_FINISHED) class CustomNavigationToolbar(NavigationToolbar2QT): def __init__(self, canvas, parent): super().__init__(canvas, parent) # 初始化画框工具栏 self.action_Label_Artifact = QAction(Constants.ARTIFACT_LABEL_ACTION_LABEL, self) self.action_Label_Artifact.setFont(QFont(ConfigParams.FONT, 14)) self.action_Label_Artifact.setCheckable(True) self.action_Label_Artifact.setShortcut(QCoreApplication.translate( "MainWindow", ConfigParams.ARTIFACT_LABEL_ACTION_LABEL_ARTIFACT_SHORTCUT_KEY)) self.insertAction(self._actions['pan'], self.action_Label_Artifact) self._actions['pan'].setShortcut(QCoreApplication.translate( "MainWindow", ConfigParams.ACTION_PAN_SHORTCUT_KEY)) self._actions['zoom'].setShortcut(QCoreApplication.translate( "MainWindow", ConfigParams.ACTION_ZOOM_SHORTCUT_KEY)) # 用于存储事件连接ID self.cid_mouse_press = None self.cid_mouse_release = None self.cid_mouse_hold = None # 初始化矩形选择区域 self.rect_start_x = None self.rect_end_x = None self.rect_patch_ax0 = None # 用于绘制矩形的patch self.rect_patch_ax1 = None # 用于绘制矩形的patch def home(self, *args): pass def zoom(self, *args): super().zoom(*args) self.deactivate_figToorbar_label_artifact() def pan(self, *args): super().pan(*args) self.deactivate_figToorbar_label_artifact() def deactivate_figToorbar_label_artifact(self): if self.action_Label_Artifact.isChecked(): self.action_Label_Artifact.setChecked(False) if self.cid_mouse_press is not None: self.canvas.mpl_disconnect(self.cid_mouse_press) self.cid_mouse_press = None if self.cid_mouse_release is not None: self.canvas.mpl_disconnect(self.cid_mouse_release) self.cid_mouse_release = None