From 6395529d3df87279441d85850e1ee3c56753e8f3 Mon Sep 17 00:00:00 2001 From: marques <20172333133@m.scnu.edu.cn> Date: Thu, 28 Aug 2025 20:28:38 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84SA=E6=89=93=E6=A0=87=E7=95=8C?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- func/Module_SA_label_v2.py | 1919 +++++++++++++++++++++++ func/Module_mainwindow.py | 2 +- func/utils/ConfigParams.py | 52 +- ui/MainWindow/MainWindow_SA_label_v2.py | 690 ++++++++ ui/MainWindow/MainWindow_SA_label_v2.ui | 1099 +++++++++++++ ui/setting/SA_label_input_setting_v2.py | 361 +++++ ui/setting/SA_label_input_setting_v2.ui | 456 ++++++ 7 files changed, 4569 insertions(+), 10 deletions(-) create mode 100644 func/Module_SA_label_v2.py create mode 100644 ui/MainWindow/MainWindow_SA_label_v2.py create mode 100644 ui/MainWindow/MainWindow_SA_label_v2.ui create mode 100644 ui/setting/SA_label_input_setting_v2.py create mode 100644 ui/setting/SA_label_input_setting_v2.ui diff --git a/func/Module_SA_label_v2.py b/func/Module_SA_label_v2.py new file mode 100644 index 0000000..f41f60e --- /dev/null +++ b/func/Module_SA_label_v2.py @@ -0,0 +1,1919 @@ +from gc import collect +from pathlib import Path +from traceback import format_exc + +import matplotlib.pyplot as plt +import pandas as pd +from PySide6.QtCore import QCoreApplication, QAbstractTableModel, QModelIndex, Qt, QTimer +from PySide6.QtGui import QAction, QFont, QColor, QCursor, QIntValidator +from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication, QButtonGroup, QTableWidget, QTableWidgetItem, \ + QHeaderView, QPlainTextEdit +from matplotlib import gridspec +from matplotlib.backends.backend_qt import NavigationToolbar2QT +from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg + +from numpy import array, zeros, append, linspace, place, nan +from overrides import overrides +from pandas import read_csv, DataFrame, Series, concat +from yaml import dump, load, FullLoader + +from func.utils.ConfigParams import Filename, Params +from func.Filters.Preprocessing import Butterworth_for_ECG_PreProcess +from func.utils.PublicFunc import PublicFunc +from func.utils.Constants import Constants +from func.utils.Result import Result + +from ui.MainWindow.MainWindow_SA_label_v2 import Ui_MainWindow_SA_label +from ui.setting.SA_label_input_setting_v2 import Ui_MainWindow_SA_label_input_setting + +ButtonState = { + "Default": { + "pushButton_input_setting": True, + "pushButton_input": True, + "pushButton_quick_remark_input_waitingForTalk": False, + "pushButton_quick_remark_input_maybeDesaturation": False, + "pushButton_quick_remark_input_maybeWrongLabeled": False, + "pushButton_quick_remark_input_durationNoEnough": False, + "pushButton_quick_remark_input_littleChange": False, + "pushButton_quick_remark_input_noNormalRespBetweenArtifact": False, + "pushButton_quick_remark_input_lowSignalNoiseRatio": False, + "pushButton_quick_remark_input_changeOnMiddle": False, + "pushButton_save": False, + "pushButton_prev": False, + "pushButton_next": False, + "pushButton_confirmLabel": False, + "pushButton_jump_to": False, + "pushButton_previous10s": False, + "pushButton_previous30s": False, + "pushButton_previous_half": False, + "pushButton_next10s": False, + "pushButton_next30s": False, + "pushButton_next_half": False + }, + "Current": { + "pushButton_input_setting": True, + "pushButton_input": True, + "pushButton_quick_remark_input_waitingForTalk": False, + "pushButton_quick_remark_input_maybeDesaturation": False, + "pushButton_quick_remark_input_maybeWrongLabeled": False, + "pushButton_quick_remark_input_durationNoEnough": False, + "pushButton_quick_remark_input_littleChange": False, + "pushButton_quick_remark_input_noNormalRespBetweenArtifact": False, + "pushButton_quick_remark_input_lowSignalNoiseRatio": False, + "pushButton_quick_remark_input_changeOnMiddle": False, + "pushButton_save": False, + "pushButton_prev": False, + "pushButton_next": False, + "pushButton_confirmLabel": False, + "pushButton_jump_to": False, + "pushButton_previous10s": False, + "pushButton_previous30s": False, + "pushButton_previous_half": False, + "pushButton_next10s": False, + "pushButton_next30s": False, + "pushButton_next_half": False + } +} + + +class SettingWindow(QMainWindow): + def __init__(self, root_path, sampID): + super(SettingWindow, self).__init__() + self.ui = Ui_MainWindow_SA_label_input_setting() + self.ui.setupUi(self) + + self.root_path = root_path + self.sampID = sampID + + self.msgBox = QMessageBox() + self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE) + + self.params = {} + self.connect_signals() + self.__read_config__() + # self.__examine_freq__() + + def connect_signals(self): + self.ui.plainTextEdit_file_path_input_signal_Abd.textChanged.connect(self.__set_plaintext_font_color_by_path__) + self.ui.plainTextEdit_file_path_input_signal_FlowP.textChanged.connect( + self.__set_plaintext_font_color_by_path__) + self.ui.plainTextEdit_file_path_input_signal_FlowT.textChanged.connect( + self.__set_plaintext_font_color_by_path__) + self.ui.plainTextEdit_file_path_input_signal_OrgBCG.textChanged.connect( + self.__set_plaintext_font_color_by_path__) + self.ui.plainTextEdit_file_path_input_signal_SpO2.textChanged.connect( + self.__set_plaintext_font_color_by_path__) + self.ui.plainTextEdit_file_path_input_signal_Tho.textChanged.connect(self.__set_plaintext_font_color_by_path__) + + 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(Params.SA_LABEL_CONFIG_FILE_PATH).exists(): + with open(Params.SA_LABEL_CONFIG_FILE_PATH, "w") as f: + dump(Params.SA_LABEL_CONFIG_NEW_CONTENT, f) + + with open(Params.SA_LABEL_CONFIG_FILE_PATH, "r") as f: + file_config = load(f.read(), Loader=FullLoader) + self.params.update({"Config": file_config}) + + sync_bcg_path = Path(self.root_path) / Filename.PATH_ORGBCG_ALIGNED / str(self.sampID) + sync_psg_path = Path(self.root_path) / Filename.PATH_PSG_ALIGNED / str(self.sampID) + label_path = Path(self.root_path) / Filename.PATH_LABEL / str(self.sampID) + + self.params.update({ + "Path": { + "Input_OrgBCG": sync_bcg_path, + "Input_Tho": sync_psg_path, + "Input_Abd": sync_psg_path, + "Input_FlowT": sync_psg_path, + "Input_FlowP": sync_psg_path, + "Input_SpO2": sync_psg_path, + + "Input_SA_Label": sync_psg_path / f"{Filename.SA_LABEL_SYNC}{Params.ENDSWITH_CSV}", + "Input_Artifact_A": label_path / f"{Filename.ARTIFACT_A}{Params.ENDSWITH_CSV}", + + "SA_Label_Revised": label_path / f"{Filename.SA_LABEL_CORRECTED}{Params.ENDSWITH_CSV}", + # "Save_2": label_path / f"{Filename.SA_LABEL_ADD}{Params.ENDSWITH_CSV}" + } + }) + self.__auto_find_file__() + self.__update_ui_info__() + + def __write_config__(self): + # 从界面写入配置 + self.params["Path"]["Input_OrgBCG"] = self.ui.plainTextEdit_file_path_input_signal_OrgBCG.toPlainText() + self.params["Path"]["Input_Tho"] = self.ui.plainTextEdit_file_path_input_signal_Tho.toPlainText() + self.params["Path"]["Input_Abd"] = self.ui.plainTextEdit_file_path_input_signal_Abd.toPlainText() + self.params["Path"]["Input_FlowT"] = self.ui.plainTextEdit_file_path_input_signal_FlowT.toPlainText() + self.params["Path"]["Input_FlowP"] = self.ui.plainTextEdit_file_path_input_signal_FlowP.toPlainText() + self.params["Path"]["Input_SpO2"] = self.ui.plainTextEdit_file_path_input_signal_SpO2.toPlainText() + self.params["Path"]["Input_Artifact_A"] = self.ui.plainTextEdit_file_path_input_artifact.toPlainText() + self.params["Path"]["Input_SA_Label"] = self.ui.plainTextEdit_file_path_input_label.toPlainText() + self.params["Path"]["SA_Label_Revised"] = self.ui.plainTextEdit_file_path_save.toPlainText() + # self.params["Path"]["Save_2"] = self.ui.plainTextEdit_file_path_save_2.toPlainText() + + self.params["Config"]["InputConfig"]["OrgBCGFreq"] = self.ui.spinBox_input_freq_signal_OrgBCG.value() + self.params["Config"]["InputConfig"]["ThoFreq"] = self.ui.spinBox_input_freq_signal_Tho.value() + self.params["Config"]["InputConfig"]["AbdFreq"] = self.ui.spinBox_input_freq_signal_Abd.value() + self.params["Config"]["InputConfig"]["FlowTFreq"] = self.ui.spinBox_input_freq_signal_FlowT.value() + self.params["Config"]["InputConfig"]["FlowPFreq"] = self.ui.spinBox_input_freq_signal_FlowP.value() + self.params["Config"]["InputConfig"]["SpO2Freq"] = self.ui.spinBox_input_freq_signal_SpO2.value() + + # 保存配置到文件 + with open(Params.SA_LABEL_CONFIG_FILE_PATH, "w") as f: + dump(self.params["Config"], f) + + self.close() + + def __update_ui_info__(self): + self.ui.spinBox_input_freq_signal_OrgBCG.setValue(self.params["Config"]["InputConfig"]["OrgBCGFreq"]) + self.ui.spinBox_input_freq_signal_Tho.setValue(self.params["Config"]["InputConfig"]["ThoFreq"]) + self.ui.spinBox_input_freq_signal_Abd.setValue(self.params["Config"]["InputConfig"]["AbdFreq"]) + self.ui.spinBox_input_freq_signal_FlowT.setValue(self.params["Config"]["InputConfig"]["FlowTFreq"]) + self.ui.spinBox_input_freq_signal_FlowP.setValue(self.params["Config"]["InputConfig"]["FlowPFreq"]) + self.ui.spinBox_input_freq_signal_SpO2.setValue(self.params["Config"]["InputConfig"]["SpO2Freq"]) + + self.ui.plainTextEdit_file_path_input_signal_OrgBCG.setPlainText(str(self.params["Path"]["Input_OrgBCG"])) + self.ui.plainTextEdit_file_path_input_signal_Tho.setPlainText(str(self.params["Path"]["Input_Tho"])) + self.ui.plainTextEdit_file_path_input_signal_Abd.setPlainText(str(self.params["Path"]["Input_Abd"])) + self.ui.plainTextEdit_file_path_input_signal_FlowT.setPlainText(str(self.params["Path"]["Input_FlowT"])) + self.ui.plainTextEdit_file_path_input_signal_FlowP.setPlainText(str(self.params["Path"]["Input_FlowP"])) + self.ui.plainTextEdit_file_path_input_signal_SpO2.setPlainText(str(self.params["Path"]["Input_SpO2"])) + self.ui.plainTextEdit_file_path_input_artifact.setPlainText(str(self.params["Path"]["Input_Artifact_A"])) + self.ui.plainTextEdit_file_path_input_label.setPlainText(str(self.params["Path"]["Input_SA_Label"])) + self.ui.plainTextEdit_file_path_save.setPlainText(str(self.params["Path"]["SA_Label_Revised"])) + # self.ui.plainTextEdit_file_path_save_2.setPlainText(str(self.params["Path"]["Save_2"])) + + def __auto_find_file__(self): + check_signal_type_list = ["Input_OrgBCG", "Input_Tho", "Input_Abd", "Input_FlowT", "Input_FlowP", "Input_SpO2"] + + def find_file(file_path: Path, _type, endswith): + if file_path.is_file(): + file_path = file_path.parent + + filename_start = getattr(Filename, f"{_type.upper()}_SYNC") + + result = PublicFunc.examine_file(file_path, filename_start, endswith) + if result.status: + self.params["Path"][f"Input_{_type}"] = result.data["path"] + + self.params["Config"]["InputConfig"][f"{_type}Freq"] = result.data["freq"] + else: + self.params["Path"][f"Input_{_type}"] = file_path + self.params["Config"]["InputConfig"][f"{_type}Freq"] = -1 + + for signal_type in check_signal_type_list: + signal_file_path = Path(self.params["Path"][signal_type]) + + find_file(signal_file_path, signal_type.split("_")[-1], endswith=Params.ENDSWITH_TXT) + + self.__update_ui_info__() + + def __rollback_config__(self): + self.__read_config__() + + def __set_plaintext_font_color_by_path__(self): + if isinstance(self.sender(), QPlainTextEdit): + input_path = Path(self.sender().toPlainText()) + else: + raise TypeError("Sender is not a QPlainTextEdit instance.") + + if input_path.exists() and input_path.is_file(): + self.sender().setStyleSheet("QPlainTextEdit { color: green; }") + else: + self.sender().setStyleSheet("QPlainTextEdit { color: red; }") + + +class Data: + def __init__(self, config): + self.config = config + self.OrgBCG = None + self.Tho = None + self.Abd = None + self.FlowT = None + self.FlowP = None + self.SpO2 = None + self.lowPass20Hz = None + self.lowPassResp = None + self.OrgBCG_Resampled = None + self.Tho_Resampled = None + self.Abd_Resampled = None + self.FlowT_Resampled = None + self.FlowP_Resampled = None + self.SpO2_Resampled = None + self.lowPass20Hz_Resampled = None + self.lowPassResp_Resampled = None + self.channel = { + "orgdata": None, + "0.7lowpass_resp": None, + "Effort Tho": None, + "Effort Abd": None, + "Flow T": None, + "Flow P": None, + "SpO2": None + } + + self.event_type_to_value = { + "Hypopnea": 1, + "Central apnea": 2, + "Obstructive apnea": 3, + "Mixed apnea": 4, + } + + self.event_label_origin = None + self.event_label_revised = None + self.event_index_origin = None + self.event_index_revised = None + self.artifact_label = None + + self.Artifact = None + self.df_origin = None + self.df_revised = None + + def open_file(self): + # 仅判断文件是否存在 + check_file_list = ["Input_OrgBCG", "Input_Tho", "Input_Abd", "Input_FlowT", "Input_FlowP", "Input_SpO2", + "Input_SA_Label"] + + for file_key in check_file_list: + if (not self.config["Path"][file_key].is_file()) or (not self.config["Path"][file_key].exists()): + return Result().failure(info=Constants.INPUT_FAILURE + "\n" + + self.config["Path"][file_key] + + Constants.FAILURE_REASON["Path_Not_Exist"]) + + try: + self.OrgBCG = read_csv(self.config["Path"]["Input_OrgBCG"], encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.Tho = read_csv(self.config["Path"]["Input_Tho"], encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.Abd = read_csv(self.config["Path"]["Input_Abd"], encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.FlowT = read_csv(self.config["Path"]["Input_FlowT"], encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.FlowP = read_csv(self.config["Path"]["Input_FlowP"], encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.SpO2 = read_csv(self.config["Path"]["Input_SpO2"], encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + + if self.config["Path"]["Input_Artifact_A"].exists() and self.config["Path"]["Input_Artifact_A"].is_file(): + self.Artifact = read_csv(self.config["Path"]["Input_Artifact_A"], + encoding=Params.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + else: + self.Artifact = array([]) + + + except Exception as e: + return Result().failure(info=Constants.INPUT_FAILURE + + Constants.FAILURE_REASON["Open_Data_Exception"] + "\n" + format_exc()) + + try: + # 检查体动标签正确性,长度 + PublicFunc.examine_artifact(self.Artifact) + self.Artifact = self.Artifact.reshape(-1, 4) + except Exception as e: + return Result().failure(info=Constants.INPUT_FAILURE + + Constants.FAILURE_REASON[ + "Get_Artifact_Format_Exception"] + "\n" + format_exc()) + + self.config.update({ + "SignalSecond": int(len(self.OrgBCG) // self.config["Config"]["InputConfig"]["OrgBCGFreq"]) + }) + + # 根据秒数对信号截断 + self.OrgBCG = self.OrgBCG[:self.config["SignalSecond"] * self.config["Config"]["InputConfig"]["OrgBCGFreq"]] + self.Tho = self.Tho[:self.config["SignalSecond"] * self.config["Config"]["InputConfig"]["ThoFreq"]] + self.Abd = self.Abd[:self.config["SignalSecond"] * self.config["Config"]["InputConfig"]["AbdFreq"]] + self.FlowT = self.FlowT[:self.config["SignalSecond"] * self.config["Config"]["InputConfig"]["FlowTFreq"]] + self.FlowP = self.FlowP[:self.config["SignalSecond"] * self.config["Config"]["InputConfig"]["FlowPFreq"]] + self.SpO2 = self.SpO2[:self.config["SignalSecond"] * self.config["Config"]["InputConfig"]["SpO2Freq"]] + self.event_label_origin = zeros(len(self.OrgBCG)) + self.event_label_revised = zeros(len(self.OrgBCG)) + self.event_index_origin = zeros(len(self.OrgBCG)) + self.event_index_revised = zeros(len(self.OrgBCG)) + + return Result().success(info=Constants.INPUT_FINISHED) + + def get_archive(self): + # if (not Path(self.config["Path"]["Save"]).exists()) or (not Path(self.config["Path"]["Save_2"]).exists()): + plot_freq = self.config["Config"]["InputConfig"]["PlotFreq"] + self.df_origin = read_csv(self.config["Path"]["Input_SA_Label"], encoding="gbk") + self.df_origin["Start"] = self.df_origin["Start"].astype(int) + self.df_origin["End"] = self.df_origin["End"].astype(int) + self.df_origin["Index"] = self.df_origin["Index"].astype(int) + + self.df_origin = self.df_origin.sort_values(by="Start", ascending=True) + for one_data in self.df_origin.index: + one_data = self.df_origin.loc[one_data] + start = one_data["Start"] + end = one_data["End"] + + self.event_label_origin[start * plot_freq:end * plot_freq] = self.event_type_to_value[ + one_data["Event type"]] + self.event_index_origin[start:end] = one_data["Index"] + + if not Path(self.config["Path"]["SA_Label_Revised"]).exists(): + self.df_revised = self.df_origin.copy() + + self.df_revised["score"] = -1 + self.df_revised["remark"] = "" + self.df_revised["correct_Start"] = -1 + self.df_revised["correct_End"] = -1 + self.df_revised["correct_EventsType"] = "" + self.df_revised["isLabeled"] = -1 + + self.event_label_revised = self.event_label_origin.copy() + self.event_index_revised = self.event_index_origin.copy() + + return Result().success(info=Constants.ARCHIVE_NOT_EXIST) + else: + try: + self.df_revised = read_csv(self.config["Path"]["SA_Label_Revised"], encoding="gbk") + csv_headers = self.df_revised.columns.tolist() + revised_columns = ["Index", "isLabeled", "correct_EventsType", "score", "correct_Start", "correct_End", + "remark"] + if not all(col in csv_headers for col in revised_columns): + return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON[ + "Label_Format_Exception"] + "\n" + f"缺少列: {', '.join(set(revised_columns) - set(csv_headers))}") + + # 类型转换 + self.df_revised["correct_Start"] = self.df_revised["correct_Start"].astype(int) + self.df_revised["correct_End"] = self.df_revised["correct_End"].astype(int) + self.df_revised["isLabeled"] = self.df_revised["isLabeled"].astype(int) + self.df_revised["score"] = self.df_revised["score"].astype(int) + self.df_revised["Index"] = self.df_revised["Index"].astype(int) + self.df_revised["remark"] = self.df_revised["remark"].astype(str) + self.df_revised["correct_EventsType"] = self.df_revised["correct_EventsType"].astype(str) + + for index in self.df_revised.index: + one_data = self.df_revised.iloc[index] + if one_data["score"] == 3: + continue + + start = one_data["correct_Start"] if one_data["isLabeled"] == 1 else one_data["Start"] + end = one_data["correct_End"] if one_data["isLabeled"] == 1 else one_data["End"] + + self.event_label_revised[start * plot_freq:end * plot_freq] = self.event_type_to_value[ + one_data["Event type"]] + self.event_index_revised[start:end] = one_data["Index"] + + except Exception as e: + return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON[ + "Label_Format_Exception"] + "\n" + format_exc()) + + return Result().success(info=Constants.ARCHIVE_EXIST) + + def preprocess(self): + if self.OrgBCG is None: + return Result().failure(info=Constants.PREPROCESS_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"]) + + try: + plot_freq = self.config["Config"]["InputConfig"]["PlotFreq"] + orgbcg_freq = self.config["Config"]["InputConfig"]["OrgBCGFreq"] + + self.lowPass20Hz = Butterworth_for_ECG_PreProcess(self.OrgBCG, + orgbcg_freq, + 'lowpass', low_cut=20, order=3) + self.lowPassResp = Butterworth_for_ECG_PreProcess(self.OrgBCG, + orgbcg_freq, + 'lowpass', low_cut=0.7, order=3) + self.artifact_label = zeros(len(self.event_label_origin)) + + for i, artifact_type, start, end in self.Artifact: + start = int(start) // (orgbcg_freq // plot_freq) + end = int(end) // (orgbcg_freq // plot_freq) + artifact_type = int(artifact_type) + 5 + start = 0 if start < 0 else start + if end < 0: + continue + if start > len(self.event_label_origin): + continue + self.artifact_label[start:end] = artifact_type + + except Exception as e: + return Result().failure(info=Constants.PREPROCESS_FAILURE + + Constants.FAILURE_REASON["Preprocess_Exception"] + "\n" + format_exc()) + + return Result().success(info=Constants.PREPROCESS_FINISHED) + + def resample(self): + if self.Tho is None or self.Abd is None or self.FlowT is None or self.FlowP is None or self.SpO2 is None or self.lowPass20Hz is None or self.lowPassResp is None: + return Result().failure(info=Constants.RESAMPLE_FAILURE + + Constants.FAILURE_REASON["Data_Not_Exist"]) + + try: + def _resample_signal(signal, orig_freq, target_freq): + if orig_freq < target_freq: + return signal.repeat(int(target_freq / orig_freq)) + elif orig_freq > target_freq: + return signal[::int(orig_freq / target_freq)] + else: + return signal + + plot_freq = self.config["Config"]["InputConfig"]["PlotFreq"] + orgbcg_freq = self.config["Config"]["InputConfig"]["OrgBCGFreq"] + + self.lowPass20Hz_Resampled = _resample_signal(self.lowPass20Hz, orgbcg_freq, plot_freq) + self.lowPassResp_Resampled = _resample_signal(self.lowPassResp, orgbcg_freq, plot_freq) + self.Tho_Resampled = _resample_signal(self.Tho, self.config["Config"]["InputConfig"]["ThoFreq"], plot_freq) + self.Abd_Resampled = _resample_signal(self.Abd, self.config["Config"]["InputConfig"]["AbdFreq"], plot_freq) + self.FlowT_Resampled = _resample_signal(self.FlowT, self.config["Config"]["InputConfig"]["FlowTFreq"], + plot_freq) + self.FlowP_Resampled = _resample_signal(self.FlowP, self.config["Config"]["InputConfig"]["FlowPFreq"], + plot_freq) + self.SpO2_Resampled = _resample_signal(self.SpO2, self.config["Config"]["InputConfig"]["SpO2Freq"], + plot_freq) + + self.channel["orgdata"] = self.lowPass20Hz_Resampled + self.channel["0.7lowpass_resp"] = self.lowPassResp_Resampled + self.channel["Effort Tho"] = self.Tho_Resampled + self.channel["Effort Abd"] = self.Abd_Resampled + self.channel["Flow T"] = self.FlowT_Resampled + self.channel["Flow P"] = self.FlowP_Resampled + self.channel["SpO2"] = self.SpO2_Resampled + except Exception as e: + return Result().failure(info=Constants.RESAMPLE_FAILURE + + Constants.FAILURE_REASON["Resample_Exception"] + "\n" + format_exc()) + + return Result().success(info=Constants.RESAMPLE_FINISHED) + + def save(self): + if self.df_revised is None: + return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"]) + + try: + self.df_revised.to_csv(self.config["Path"]["SA_Label_Revised"], mode='w', index=None, encoding="gbk") + except PermissionError as e: + return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_Permission_Denied"]) + except FileNotFoundError as e: + return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_File_Not_Found"]) + except Exception as e: + return Result().failure(info=Constants.SAVE_FAILURE + + Constants.FAILURE_REASON["Save_Exception"] + "\n" + format_exc()) + + return Result().success(Constants.SAVE_FINISHED) + + +class DataFrameModel(QAbstractTableModel): + def __init__(self, dataframe, display_columns, header_mapping): + super().__init__() + self._dataframe = dataframe + self._display_columns = display_columns + self._showdata = dataframe[display_columns].copy() # Copy the DataFrame to avoid modifying the original + self.filter_txt = "" # Initialize filter text + self._header_mapping = header_mapping # Dictionary for custom header names + + def rowCount(self, parent=QModelIndex()): + return len(self._showdata) + + def columnCount(self, parent=QModelIndex()): + return len(self._display_columns) + + def data(self, index, role=Qt.DisplayRole): + if not index.isValid(): + return None + row = index.row() + col = index.column() + column_name = self._display_columns[col] + value = self._showdata.iloc[row][column_name] + + if role == Qt.DisplayRole: + # 返回显示的文本 + return str(value) + + # 当isLabeled列为1时,整行前景变色 + if role == Qt.ForegroundRole: + if "isLabeled" in self._dataframe.columns: + try: + is_labeled_value = pd.to_numeric(self._showdata.iloc[row]["isLabeled"], errors='coerce') + if is_labeled_value == 1: + return QColor(0, 255, 0) + else: + return QColor(255, 165, 0) + except Exception as e: + return None + + return None + + def headerData(self, section, orientation, role=Qt.DisplayRole): + if role != Qt.DisplayRole: + return None + if orientation == Qt.Horizontal: + # Return custom header name if available, otherwise use column name + return self._header_mapping.get(self._display_columns[section], self._display_columns[section]) + return str(section + 1) + + def get_data(self, index): + if not index.isValid(): + return None + row = index.row() + col = "Index" + if 0 <= row < len(self._showdata): + return self._showdata.iloc[row][col] + return None + + def flags(self, index): + # Make table non-editable + return Qt.ItemIsSelectable | Qt.ItemIsEnabled + + def filter_data(self, filter_text): + self.filter_txt = filter_text + if filter_text == "": + self._showdata = self._dataframe[self._display_columns].copy() + else: + # Filter the DataFrame based on the filter text + filtered_df = self._dataframe[ + self._dataframe.apply(lambda row: row.astype(str).str.contains(filter_text, case=False).any(), axis=1)] + self._showdata = filtered_df[self._display_columns].copy() + + self.layoutChanged.emit() # Notify the view that the data has changed + + def update_all(self, _dataframe): + # 整张表更新或增删一行 + self._dataframe = _dataframe + self._showdata = _dataframe[self._display_columns].copy() # Update the displayed data + self.filter_data(self.filter_txt) + self.layoutChanged.emit() + + +class MainWindow_SA_label(QMainWindow): + def __init__(self): + super(MainWindow_SA_label, self).__init__() + self.ui = Ui_MainWindow_SA_label() + self.ui.setupUi(self) + + self.root_path = None + self.sampID = None + + self.data = None + + self.setting = None + self.config = None + + self.buttonGroup_event = None + self.buttonGroup_class = None + + # 初始化进度条 + self.progressbar = None + PublicFunc.add_progressbar(self) + + # 初始化画框 + self.fig = None + self.canvas = None + self.figToolbar = None + self.gs = None + + self.ui.textBrowser_info.setStyleSheet("QTextBrowser { background-color: rgb(255, 255, 200); }") + PublicFunc.__styleAllButton__(self, ButtonState) + + # 设定事件和其对应颜色 + # event_code color event + # 0 黑色 背景 + # 1 粉色 低通气 + # 2 蓝色 中枢性 + # 3 红色 阻塞型 + # 4 灰色 混合型 + # 5 绿色 血氧饱和度下降 + # 6 橙色 大体动 + # 7 橙色 小体动 + # 8 橙色 深呼吸 + # 9 橙色 脉冲体动 + # 10 橙色 无效片段 + self.color_cycle = ["black", "pink", "blue", "red", "silver", "green", "orange", "orange", "orange", "orange", + "orange"] + + self.display_columns_origin = ["Index", "Event type", "Start", "End"] + self.display_columns_name_origin = ["事件编号", "事件类型", "起始时间(s)", "终止时间(s)"] + self.header_mapping_origin = dict(zip(self.display_columns_origin, self.display_columns_name_origin)) + + self.display_columns_revised = ["Index", "isLabeled", "correct_EventsType", "score", "correct_Start", + "correct_End"] + self.display_columns_name_revised = ["事件编号", "已标注", "修正事件类型", "标签类型", "起始时间(s)", + "终止时间(s)"] + self.header_mapping_revised = dict(zip(self.display_columns_revised, self.display_columns_name_revised)) + + self.event_type_to_radio = { + "Obstructive apnea": self.ui.radioButton_OSA, + "Central apnea": self.ui.radioButton_CSA, + "Mixed apnea": self.ui.radioButton_MSA, + "Hypopnea": self.ui.radioButton_HPY + } + + self.score_to_radio = { + "1": self.ui.radioButton_1_class, + "2": self.ui.radioButton_2_class, + "3": self.ui.radioButton_3_class + } + + self.msgBox = QMessageBox() + self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE) + + self.event_ploy_collection = {} + self.selected_event_info = None + self.selected_event_rect = None + self.press = None + + @overrides + def show(self, root_path, sampID): + super().show() + self.root_path = root_path + self.sampID = sampID + + self.setting = SettingWindow(root_path, sampID) + self.config = self.setting.params + self.config.update( + { + "WindowStartSecond": 0, + "WindowSignalSecond": int(self.ui.comboBox_window_signal_length.currentText()), + } + ) + + # 初始化画框 + self.fig = plt.figure(figsize=(12, 9), dpi=100) + self.canvas = FigureCanvasQTAgg(self.fig) + self.figToolbar = CustomNavigationToolbar(self.canvas, self) + + self.figToolbar.cid_mouse_press = self.canvas.mpl_connect('button_press_event', self.on_press) + self.figToolbar.cid_mouse_release = self.canvas.mpl_connect('button_release_event', self.on_release) + self.figToolbar.cid_mouse_move = self.canvas.mpl_connect('motion_notify_event', self.on_motion) + + self.ui.verticalLayout_canvas.addWidget(self.canvas) + self.ui.verticalLayout_canvas.addWidget(self.figToolbar) + + self.gs = gridspec.GridSpec(7, 1, height_ratios=[1, 1, 1, 1, 1, 3, 2]) + 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(Params.FORMATTER) + self.ax0.set_ylim((85, 100)) + 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(Params.FORMATTER) + self.ax1.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + self.ax2 = self.fig.add_subplot(self.gs[2], sharex=self.ax0) + self.ax2.grid(True) + self.ax2.xaxis.set_major_formatter(Params.FORMATTER) + self.ax2.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + self.ax3 = self.fig.add_subplot(self.gs[3], sharex=self.ax0) + self.ax3.grid(True) + self.ax3.xaxis.set_major_formatter(Params.FORMATTER) + self.ax3.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + self.ax4 = self.fig.add_subplot(self.gs[4], sharex=self.ax0) + self.ax4.grid(True) + self.ax4.xaxis.set_major_formatter(Params.FORMATTER) + self.ax4.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + self.ax5 = self.fig.add_subplot(self.gs[5], sharex=self.ax0) + self.ax5.grid(True) + self.ax5.xaxis.set_major_formatter(Params.FORMATTER) + self.ax5.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + self.ax6 = self.fig.add_subplot(self.gs[6], sharex=self.ax0) + self.ax6.grid(True) + self.ax6.xaxis.set_major_formatter(Params.FORMATTER) + + self.channel_to_ax = { + "SpO2": self.ax0, + "Flow T": self.ax1, + "Flow P": self.ax2, + "Effort Tho": self.ax3, + "Effort Abd": self.ax4, + "0.7lowpass_resp": self.ax5, + "orgdata": self.ax6, + } + + PublicFunc.__resetAllButton__(self, ButtonState) + + self.ui.comboBox_window_signal_length.lineEdit().setValidator(QIntValidator(10, 1200)) + + self.ui.tableView_label_revised.doubleClicked.connect(self.show_selected_event) + self.ui.tableView_label.doubleClicked.connect(self.show_selected_event) + + self.buttonGroup_event = QButtonGroup(self) + self.buttonGroup_class = QButtonGroup(self) + + self.buttonGroup_event.addButton(self.ui.radioButton_OSA) + self.buttonGroup_event.addButton(self.ui.radioButton_CSA) + self.buttonGroup_event.addButton(self.ui.radioButton_MSA) + self.buttonGroup_event.addButton(self.ui.radioButton_HPY) + self.buttonGroup_class.addButton(self.ui.radioButton_1_class) + self.buttonGroup_class.addButton(self.ui.radioButton_2_class) + self.buttonGroup_class.addButton(self.ui.radioButton_3_class) + self.ui.radioButton_OSA.setChecked(True) + self.ui.radioButton_2_class.setChecked(True) + + self.ui.pushButton_input.clicked.connect(self.__slot_btn_input__) + self.ui.pushButton_input_setting.clicked.connect(self.setting.show) + + self.ui.comboBox_window_signal_length.currentTextChanged.connect(self.__update_window_signal_length__) + self.ui.pushButton_best_fit.clicked.connect(self.best_fit) + + # self.ui.pushButton_prev.clicked.connect(self.__slot_btn_move__) + # self.ui.pushButton_next.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_save.clicked.connect(self.__save_revise_df__) + self.ui.pushButton_quick_remark_input_waitingForTalk.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_maybeDesaturation.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_maybeWrongLabeled.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_durationNoEnough.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_littleChange.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_noNormalRespBetweenArtifact.clicked.connect( + self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_lowSignalNoiseRatio.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_quick_remark_input_changeOnMiddle.clicked.connect(self.__slot_btn_quick_remark__) + self.ui.pushButton_previous10s.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_previous30s.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_previous_half.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_next10s.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_next30s.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_next_half.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_jump_to.clicked.connect(self.__slot_btn_move__) + self.ui.pushButton_confirmLabel.clicked.connect(self.__slot_btn_confirmLabel__) + + # 输入防抖 + self.debounce_timer1 = QTimer() + self.debounce_timer1.setSingleShot(True) + self.debounce_timer1.setInterval(500) + self.debounce_timer1.timeout.connect(self.apply_text_filter) + self.ui.lineEdit_filter_label_origin.textChanged.connect(self.debounce_timer1.start) + + self.debounce_timer2 = QTimer() + self.debounce_timer2.setSingleShot(True) + self.debounce_timer2.setInterval(500) + self.debounce_timer2.timeout.connect(self.apply_text_filter) + self.ui.lineEdit_filter_label_revised.textChanged.connect(self.debounce_timer2.start) + + # 快捷键 + self.ui.pushButton_confirmLabel.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_BTN_CONFIRMLABEL_SHORTCUT_KEY)) + self.ui.radioButton_OSA.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_OSA_SHORTCUT_KEY)) + self.ui.radioButton_CSA.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_CSA_SHORTCUT_KEY)) + self.ui.radioButton_MSA.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_MSA_SHORTCUT_KEY)) + self.ui.radioButton_HPY.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_HPY_SHORTCUT_KEY)) + self.ui.radioButton_1_class.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_1_CLASS_SHORTCUT_KEY)) + self.ui.radioButton_2_class.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_2_CLASS_SHORTCUT_KEY)) + self.ui.radioButton_3_class.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_RADIOBUTTON_3_CLASS_SHORTCUT_KEY)) + self.ui.pushButton_quick_remark_input_waitingForTalk.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_BTN_QUICK_REMARK_WAITINGFORTALK_SHORTCUT_KEY)) + self.ui.pushButton_previous10s.setShortcut(QCoreApplication.translate("MainWindow", Params. + SA_LABEL_BTN_PREV_10s_SHORTCUT_KEY)) + self.ui.pushButton_previous30s.setShortcut(QCoreApplication.translate("MainWindow", Params. + SA_LABEL_BTN_PREV_30s_SHORTCUT_KEY)) + self.ui.pushButton_previous_half.setShortcut(QCoreApplication.translate("MainWindow", Params. + SA_LABEL_BTN_PREV_HALF_SHORTCUT_KEY)) + self.ui.pushButton_next10s.setShortcut(QCoreApplication.translate("MainWindow", Params. + SA_LABEL_BTN_NEXT_10s_SHORTCUT_KEY)) + self.ui.pushButton_next30s.setShortcut(QCoreApplication.translate("MainWindow", Params. + SA_LABEL_BTN_NEXT_30s_SHORTCUT_KEY)) + self.ui.pushButton_next_half.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_BTN_NEXT_HALF_SHORTCUT_KEY)) + self.ui.pushButton_best_fit.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_BTN_BEST_FIT_SHORTCUT_KEY)) + + def load_data_to_table(self): + try: + self.data_model_origin = DataFrameModel(self.data.df_origin, self.display_columns_origin, + self.header_mapping_origin) + self.ui.tableView_label.setModel(self.data_model_origin) + + self.data_model_revised = DataFrameModel(self.data.df_revised, self.display_columns_revised, + self.header_mapping_revised) + self.ui.tableView_label_revised.setModel(self.data_model_revised) + + 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 best_fit(self): + plot_freq = self.config["Config"]["InputConfig"]["PlotFreq"] + start_point = self.config["WindowStartSecond"] * plot_freq + end_point = start_point + self.config["WindowSignalSecond"] * plot_freq + for channel, ax in self.channel_to_ax.items(): + signal_max = self.data.channel[channel][start_point: end_point].max() + signal_min = self.data.channel[channel][start_point: end_point].min() + if channel == "SpO2": + signal_max = 100 + signal_min = min(signal_min, 90) + else: + # 上限上移2%,下限下移2% + delta = abs(signal_max - signal_min) * 0.02 + signal_max = signal_max + delta + signal_min = signal_min - delta + + ax.set_ylim(int(signal_min), int(signal_max)) + self.canvas.draw() + + def apply_text_filter(self): + # 应用文本过滤器 + if self.sender() == self.debounce_timer1: + self.data_model_origin.filter_data(self.ui.lineEdit_filter_label_origin.text()) + + elif self.sender() == self.debounce_timer2: + self.data_model_revised.filter_data(self.ui.lineEdit_filter_label_revised.text()) + + def show_selected_event(self, index): + # 通过点击选中 + if isinstance(index, int): + table_index = index + keep_xlim = True + # 从左侧列表中直接获取当前表中选中行的事件编号列的内容 + elif hasattr(index, "model"): + if index.model() == self.data_model_origin: + table_index = self.data_model_origin.get_data(index) + elif index.model() == self.data_model_revised: + table_index = self.data_model_revised.get_data(index) + else: + return + keep_xlim = False + else: + return + self.show_event_info(table_index) + self.__jump_to__event__(table_index, keep_xlim=keep_xlim) + + def on_press(self, event): + # 只在编辑模式下响应左键按下事件 + if not (self.figToolbar.edit_active and event.inaxes == self.ax5 and event.button == 1): + return + # x轴坐标,因为使用linespace设置,故实际对应秒数 + x_press = event.xdata + # 边界容差 + side_tol = 0.2 + # print("press", x_press) + + if self.selected_event_info is None: + # 未选中事件之前,按下不响应,释放时处理 + # print(self.data.event_index_revised[int(x_press)], + # self.data.event_index_revised[int(x_press) - 2], + # self.data.event_index_revised[int(x_press) + 2]) + left_wo_sa = self.data.event_index_revised[max(0, int(x_press) - 1)] == 0 + right_wo_sa = self.data.event_index_revised[min(self.config["SignalSecond"], int(x_press) + 1)] == 0 + if left_wo_sa and right_wo_sa: + self.press = x_press, 'empty', -1, -1 + else: + self.press = None + # print("press", "1", self.press) + else: + x_rect = self.selected_event_rect.get_x() + width = self.selected_event_rect.get_width() + event_left = x_rect + event_right = x_rect + width + + # 检查是否按在边界上 + if abs(x_press - event_left) < side_tol: + self.press = x_press, 'left', x_rect, width + elif abs(x_press - event_right) < side_tol: + self.press = x_press, 'right', x_rect, width + elif event_left < x_press < event_right: + self.press = x_press, 'middle', x_rect, width + else: + # 按下了其他事件上 + self.press = None + # print("press", "2", self.press) + + self.canvas.draw_idle() + + def on_motion(self, event): + if not (self.figToolbar.edit_active and event.inaxes == self.ax5): + return + + x_motion = event.xdata + # 边界容差 + side_tol = 0.2 + if event.button == 1: + # 已经选中事件的情况下 + if self.selected_event_info is not None: + if self.press is not None: + + x_press, side, x_rect, width = self.press + dx = x_motion - x_press + + if side == 'left': + self.canvas.setCursor(QCursor(Qt.CursorShape.SizeHorCursor)) + # 吸附到最近的一秒 + new_start = round(x_press + dx) + new_width = width - (new_start - x_rect) + elif side == 'right': + self.canvas.setCursor(QCursor(Qt.CursorShape.SizeHorCursor)) + new_end = round(x_rect + width + dx) + new_width = new_end - x_rect + new_start = x_rect + elif side == 'middle': + self.canvas.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor)) + new_start = round(x_rect + dx) + new_width = width + else: + raise ValueError( + f'event({self.selected_event_info}) was selected, side:({side}) must be left/right/middle') + + new_width = max(new_width, 1) # 最小宽度为1秒 + + self.selected_event_rect.set_x(new_start) + self.selected_event_rect.set_width(new_width) + + else: + if self.press is not None and self.press[1] == 'empty': + x_press, side, x_rect, width = self.press + dx = x_motion - x_press + if dx > 1: + self.canvas.setCursor(QCursor(Qt.CursorShape.SizeHorCursor)) + # 是否绑定了rect + if self.selected_event_rect is None: + # 起始位置向下取整 + self.selected_event_rect = event.inaxes.axvspan(int(x_press), int(x_press) + 1, + color="orange", + alpha=0.4) + else: + x_rect = self.selected_event_rect.get_x() + new_right = round(int(x_press) + dx) + new_width = new_right - x_rect + self.selected_event_rect.set_width(new_width) + else: + self.canvas.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) + # 删除创建的矩形框 + if self.selected_event_rect is not None: + self.selected_event_rect.remove() + self.selected_event_rect = None + + else: + # 如果没有选中事件,则恢复默认光标样式 + self.canvas.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) + # 非按下时,靠近边界时,修改光标样式 + elif event.button is None: + if self.selected_event_info is not None and self.selected_event_rect is not None: + # 非按下时,靠近边界时,修改光标样式 + x_rect = self.selected_event_rect.get_x() + width = self.selected_event_rect.get_width() + event_left = x_rect + event_right = x_rect + width + + # 检查是否靠近边界 + if abs(x_motion - event_left) < side_tol: + self.canvas.setCursor(QCursor(Qt.CursorShape.SizeHorCursor)) + elif abs(x_motion - event_right) < side_tol: + self.canvas.setCursor(QCursor(Qt.CursorShape.SizeHorCursor)) + elif event_left < x_motion < event_right: + self.canvas.setCursor(QCursor(Qt.CursorShape.OpenHandCursor)) + else: + self.canvas.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) + self.canvas.draw_idle() + + def on_release(self, event): + if not (self.figToolbar.edit_active and event.inaxes == self.ax5 and event.button == 1): + return + # print("release", self.selected_event_info, self.press) + x_release = event.xdata + + # 检查是否位于SA事件上 用于选中事件 + if self.data.event_index_revised[int(x_release)] != 0: + event_indices = self.data.event_index_revised[int(x_release)] + event_row = self.data.df_revised[self.data.df_revised["Index"] == event_indices].iloc[0] + else: + event_row = None + + # 未选中SA事件下 + if self.selected_event_info is None: + # 非拖动的情况下 停在了某个SA事件上 则显示事件信息 + if self.press is None and event_row is not None: + self.selected_event_info = event_row.copy() + self.selected_event_rect = self.event_ploy_collection[self.selected_event_info["Index"]] + self.show_selected_event(int(event_row["Index"])) + # 高亮 透明度设置为0.4 + self.selected_event_rect.set_alpha(0.4) + self.ui.spinBox_correctStart.setValue(self.selected_event_rect.get_x()) + self.ui.spinBox_correctEnd.setValue( + self.selected_event_rect.get_x() + self.selected_event_rect.get_width()) + + elif self.press is None and event_row is None: + pass + + # 拖动创建事件的情况下,即正在创建SA事件 + elif self.press is not None and self.press[1] == 'empty': + x_press = self.press[0] + dx = x_release - x_press + if dx > 1 and self.selected_event_rect is not None: + new_start = int(x_press) + new_end = round(int(x_press) + dx) + if sum(self.data.event_index_revised[ + max(0, new_start - 1):min(new_end + 1, self.config["SignalSecond"])]) != 0: + # 与已有事件重叠,删除创建的矩形框 + self.selected_event_rect.remove() + self.selected_event_rect = None + self.selected_event_info = None + QMessageBox.warning(self, "警告", "新建事件与已有事件重叠,请重新创建!") + elif new_end - new_start < 10: + # 事件过短,删除创建的矩形框 + QMessageBox.warning(self, "警告", "新建事件持续时间短于十秒!") + + # 生成临时event_info + event_info_temp = { + "Index": -1, + "isLabeled": 0, + "correct_EventsType": "Obstructive apnea", + "score": "2", + "correct_Start": new_start, + "correct_End": new_end, + "remark": "" + } + self.selected_event_info = event_info_temp + self.ui.spinBox_correctStart.setValue(new_start) + self.ui.spinBox_correctEnd.setValue(new_end) + + else: + # 删除创建的矩形框 + if self.selected_event_rect is not None: + self.selected_event_rect.remove() + self.selected_event_rect = None + self.selected_event_info = None + + # 选中SA事件的情况下 + else: + # 非拖动的情况下 + if self.press is None: + # 点击了新建SA事件区域 + if event_row is None and self.selected_event_info["Index"] == -1: + x_rect = self.selected_event_rect.get_x() + width = self.selected_event_rect.get_width() + event_left = x_rect + event_right = x_rect + width + + if event_left <= x_release <= event_right: + # 点击选中事件的矩形框内,无需响应 + pass + else: + # 点击选中事件的矩形框外,取消选中 + self.selected_event_rect.remove() + self.selected_event_rect = None + self.selected_event_info = None + self.__clear_event_info__() + + # 点击了空白区域 + elif event_row is None: + self.selected_event_rect.set_alpha(0.2) + self.selected_event_info = None + self.selected_event_rect = None + self.__clear_event_info__() + + elif self.selected_event_info["Index"] != event_row["Index"]: + # TODO 弹窗提醒是否切换 + self.selected_event_rect.set_alpha(0.2) + self.selected_event_info = event_row.copy() + self.selected_event_rect = self.event_ploy_collection[self.selected_event_info["Index"]] + self.selected_event_rect.set_alpha(0.4) + self.show_selected_event(int(event_row["Index"])) + else: + # 点击同一个SA事件,无需响应 + pass + + else: + # 松开时,更新数据 + if self.press is not None: + x_press, side, x_rect, width = self.press + dx = x_release - x_press + + event_left = x_rect + event_right = x_rect + width + + if side == 'left': + new_start = round(x_press + dx) + new_end = event_right + elif side == 'right': + new_start = event_left + new_end = round(x_rect + width + dx) + elif side == 'middle': + new_start = round(x_rect + dx) + new_end = new_start + width + else: + raise ValueError( + f'event({self.selected_event_info}) was selected, side:({side}) must be left/right/middle') + + new_start = max(new_start, 0) + new_end = max(new_end, new_start + 1) + if new_end - new_start < 10: + QMessageBox.warning(self, "警告", "事件持续时间短于十秒!") + + # 更新到spinbox + self.ui.spinBox_correctStart.setValue(new_start) + self.ui.spinBox_correctEnd.setValue(new_end) + + self.press = None + self.canvas.setCursor(QCursor(Qt.CursorShape.ArrowCursor)) + self.canvas.draw_idle() + + @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() + + # 清空画框 + if self.ax0 is not None: + self.ax0.clear() + if self.ax1 is not None: + self.ax1.clear() + if self.ax2 is not None: + self.ax2.clear() + if self.ax3 is not None: + self.ax3.clear() + if self.ax4 is not None: + self.ax4.clear() + if self.ax5 is not None: + self.ax5.clear() + if self.ax6 is not None: + self.ax6.clear() + + # 释放资源 + self.setting.close() + del self.data + self.fig.clf() + plt.close(self.fig) + self.deleteLater() + collect() + self.canvas = None + event.accept() + else: + event.ignore() + + + def __save_revise_df__(self): + try: + # 过滤掉Index大于origin的行中score为3的行 + origin_max_index = self.data.df_origin["Index"].max() + mask = (self.data.df_revised["Index"] > origin_max_index) & (self.data.df_revised["score"] == 3) + + self.data.df_revised = self.data.df_revised[~mask].reset_index(drop=True) + self.data.df_revised.to_csv(self.config["Path"]["SA_Label_Revised"], index=False) + except Exception as e: + return Result().failure(info=Constants.SAVE_FAILURE + + Constants.FAILURE_REASON["Save_revised_csv_Exception"] + "\n" + format_exc()) + return Result().success(info=Constants.SAVE_FINISHED) + + def __clear_event_info__(self): + self.ui.label_BCG_Index.setText("0/0") + self.ui.label_BCG_Info.setText("Epoch:0 Duration:0s New Duration:0s") + self.ui.lineEdit_remark.setText("") + self.ui.radioButton_OSA.setChecked(False) + self.ui.radioButton_CSA.setChecked(False) + self.ui.radioButton_MSA.setChecked(False) + self.ui.radioButton_HPY.setChecked(False) + self.ui.radioButton_1_class.setChecked(False) + self.ui.radioButton_2_class.setChecked(False) + self.ui.radioButton_3_class.setChecked(False) + self.ui.spinBox_correctStart.setValue(0) + self.ui.spinBox_correctEnd.setValue(0) + + def __reset__(self): + ButtonState["Current"].update(ButtonState["Default"].copy()) + + def __update_window_signal_length__(self): + self.config["WindowSignalSecond"] = int(self.ui.comboBox_window_signal_length.currentText()) + self.ui.pushButton_next_half.setProperty("offset", self.config["WindowSignalSecond"] // 2) + self.ui.pushButton_previous_half.setProperty("offset", -self.config["WindowSignalSecond"] // 2) + self.__jump_to__by_time__(self.config["WindowStartSecond"]) + + def __jump_to__event__(self, index, keep_xlim=True): + event_info = self.data.df_revised[self.data.df_revised["Index"] == index].iloc[0].copy() + start_time = event_info["correct_Start"] if event_info["isLabeled"] != -1 else event_info["Start"] + end_time = event_info["correct_End"] if event_info["isLabeled"] != -1 else event_info["End"] + if keep_xlim: + middle_time = self.config["WindowStartSecond"] + else: + if end_time - start_time > self.config["WindowSignalSecond"]: + middle_time = start_time - self.config["WindowSignalSecond"] // 2 + else: + middle_time = (start_time + end_time - self.config["WindowSignalSecond"]) // 2 + + self.__jump_to__by_time__(middle_time) + + def __jump_to__by_time__(self, start_time, offset=0): + """ + Jump to a specific time in the signal. + :param start_time: The start time in seconds to jump to. + :param offset: The offset in seconds to move from the start_time. + :return: + """ + + # if start_time < 0 or start_time > self.config["SignalSecond"]: + # raise ValueError("Time must be within the signal length.") + + # Update the start start_time for the plot + start_time, end_time = self.check_start_end(start_time, offset) + self.config["WindowStartSecond"] = start_time + self.ax0.set_xlim((start_time, end_time)) + self.canvas.draw() + + def show_event_info(self, index): + event_info = self.data.df_revised[self.data.df_revised["Index"] == index].iloc[0].copy() + if self.selected_event_info is not None: + if self.selected_event_info["Index"] != event_info["Index"]: + if self.selected_event_rect is not None: + self.selected_event_rect.set_alpha(0.2) + + self.selected_event_info = event_info + self.selected_event_rect = self.event_ploy_collection[event_info["Index"]] + if self.selected_event_rect is not None: + self.selected_event_rect.set_alpha(0.4) + # show event index info + bcg_duration = int(event_info["End"]) - int(event_info["Start"]) + bcg_new_duration = int(event_info["correct_End"]) - int(event_info["correct_Start"]) + self.ui.label_BCG_Index.setText(f"{index}/{len(self.data.df_revised)}") + self.ui.label_BCG_Info.setText( + f"Epoch:{event_info['Epoch']} Duration:{bcg_duration}s New Duration:{bcg_new_duration}") + + # show remark + remark = event_info.get("remark", "") + if pd.isna(remark): + self.ui.lineEdit_remark.setText("") + else: + self.ui.lineEdit_remark.setText(str(remark)) + + # show event type + if event_info["isLabeled"] == -1: + # If not labeled, show original event type + event_type = event_info["Event type"] + else: + # If labeled, show corrected event type + event_type = event_info["correct_EventsType"] + + if event_type in self.event_type_to_radio: + self.event_type_to_radio[event_type].setChecked(True) + else: + raise ValueError(f"Unknown event type: {event_type} event_info={event_info}") + + # show score + + # Default to 2 if (not found or -1) + score = str(event_info.get("score", -1)) + score = "2" if score == "-1" else score + + if score in self.score_to_radio: + self.score_to_radio[score].setChecked(True) + else: + raise ValueError(f"Unknown score: {score} event_info={event_info}") + + # show start and end time to spinbox + if event_info["isLabeled"] == -1: + self.ui.spinBox_correctStart.setValue(event_info["Start"]) + self.ui.spinBox_correctEnd.setValue(event_info["End"]) + else: + self.ui.spinBox_correctStart.setValue(event_info["correct_Start"]) + self.ui.spinBox_correctEnd.setValue(event_info["correct_End"]) + + def __slot_btn_confirmLabel__(self): + if self.selected_event_info is None: + PublicFunc.msgbox_output(self, "未选中任何事件,无法保存!", Constants.MSGBOX_TYPE_WARNING) + PublicFunc.text_output(self.ui, "未选中任何事件,无法保存!", Constants.TIPS_TYPE_ERROR) + return + + if self.data is None or self.data.df_revised is None: + PublicFunc.msgbox_output(self, "请先载入数据", Constants.MSGBOX_TYPE_WARNING) + PublicFunc.text_output(self.ui, "请先载入数据", Constants.TIPS_TYPE_ERROR) + return + + index = int(self.selected_event_info["Index"]) + + if self.ui.radioButton_3_class.isChecked(): + if index != -1: + self.delete_event(index) + else: + if index == -1: + self.add_event() + elif index < 1 or index > len(self.data.df_revised): + PublicFunc.msgbox_output(self.ui, f"不合理的事件编号:{index}", Constants.MSGBOX_TYPE_WARNING) + PublicFunc.text_output(self.ui, f"不合理的事件编号:{index}", Constants.TIPS_TYPE_ERROR) + else: + self.edit_event(index) + + def __update_label_from_ui__(self, event_info, is_new=False): + # Update the event type + if self.ui.radioButton_OSA.isChecked(): + event_info["correct_EventsType"] = "Obstructive apnea" + elif self.ui.radioButton_CSA.isChecked(): + event_info["correct_EventsType"] = "Central apnea" + elif self.ui.radioButton_MSA.isChecked(): + event_info["correct_EventsType"] = "Mixed apnea" + elif self.ui.radioButton_HPY.isChecked(): + event_info["correct_EventsType"] = "Hypopnea" + else: + raise ValueError("No event type selected.") + + # Update the start and end time + # 边界检查 + if self.ui.spinBox_correctStart.value() >= self.ui.spinBox_correctEnd.value(): + raise ValueError("Start time must be less than end time.") + if self.ui.spinBox_correctStart.value() < 0 or self.ui.spinBox_correctEnd.value() < 0: + raise ValueError("Start and end times must be non-negative.") + signal_length = self.config["SignalSecond"] + if self.ui.spinBox_correctStart.value() > signal_length or self.ui.spinBox_correctEnd.value() > signal_length: + raise ValueError("Start and end times must be within the signal length.") + + if is_new: + event_info["Start"] = self.ui.spinBox_correctStart.value() + event_info["End"] = self.ui.spinBox_correctEnd.value() + event_info["EventType"] = event_info["correct_EventsType"] + + event_info["correct_Start"] = self.ui.spinBox_correctStart.value() + event_info["correct_End"] = self.ui.spinBox_correctEnd.value() + + # Update the remark + remark_text = str(self.ui.lineEdit_remark.text()).strip() + if remark_text: + event_info["remark"] = remark_text + else: + event_info["remark"] = None + + # Update the score + if self.ui.radioButton_1_class.isChecked(): + event_info["score"] = 1 + elif self.ui.radioButton_2_class.isChecked(): + event_info["score"] = 2 + elif self.ui.radioButton_3_class.isChecked(): + event_info["score"] = 3 + else: + raise ValueError("No score selected.") + + return event_info + + def delete_event(self, index): + """ + 删除一个事件 + :param index: The index of the event in the DataFrame. + """ + if self.data is None or self.data.df_revised is None: + raise ValueError("Data or df_revised is not initialized.") + + if index < 1 or index > len(self.data.df_revised): + raise IndexError("Index out of bounds for df_revised.") + + event_info = self.data.df_revised[self.data.df_revised["Index"] == index].iloc[0].copy() + event_info = self.__update_label_from_ui__(event_info) + event_info["isLabeled"] = 1 # Mark as not labeled + + self.data.df_revised[self.data.df_revised["Index"] == index] = event_info + + # 更新表格模型 + self.data_model_revised.update_all(self.data.df_revised) + + # 去除事件索引映射 + self.data.event_index_revised[self.data.event_index_revised == event_info["Index"]] = 0 + + # 从poly_collection中移除 + if index in self.event_ploy_collection and self.event_ploy_collection[index] is not None: + poly = self.event_ploy_collection.pop(index) + poly.remove() + self.event_ploy_collection.update({index: None}) + self.canvas.draw_idle() + + self.selected_event_rect = None + # 清空选中状态 + self.selected_event_info = None + self.__clear_event_info__() + + def edit_event(self, index): + """ + Save the revised event information to the DataFrame. + :param index: The index of the event in the DataFrame. + """ + + # Get the current event info + event_info = self.data.df_revised[self.data.df_revised["Index"] == index].iloc[0].copy() + event_info = self.__update_label_from_ui__(event_info) + + # 检查是否已有事件重叠 + overlapping_indices = set(self.data.event_index_revised[ + max(0, event_info["correct_Start"] - 1):min(event_info["correct_End"] + 1, + self.config["SignalSecond"])]) + overlapping_indices.discard(event_info["Index"]) # 去除自身 + overlapping_indices.discard(0) # 去除空白区域标记 + if len(overlapping_indices) > 0: + PublicFunc.msgbox_output(self, "修改后的事件与已有事件重叠,无法保存!", Constants.MSGBOX_TYPE_WARNING) + PublicFunc.text_output(self.ui, "修改后的事件与已有事件重叠,无法保存!", Constants.TIPS_TYPE_ERROR) + return + + # 去除原始事件索引映射 + # 从event_index_revised获取index所在区间 + self.data.event_index_revised[self.data.event_index_revised == event_info["Index"]] = 0 + + # Save changes back to DataFrame + event_info["isLabeled"] = 1 # Mark as labeled + self.data.df_revised[self.data.df_revised["Index"] == index] = event_info + # Update table model + self.data_model_revised.update_all(self.data.df_revised) + + # Update event index mapping + self.data.event_index_revised[event_info["correct_Start"]:event_info["correct_End"]] = event_info["Index"] + + # Update selected event info + self.selected_event_info = event_info.copy() + if self.event_ploy_collection[event_info["Index"]] is not None: + self.event_ploy_collection[event_info["Index"]].remove() + + self.selected_event_rect = None + # Redraw rectangle + color = self.color_cycle[self.data.event_type_to_value[event_info["correct_EventsType"]]] + self.selected_event_rect = self.ax5.axvspan(event_info["correct_Start"], event_info["correct_End"], color=color, + alpha=0.4) + self.event_ploy_collection[event_info["Index"]] = self.selected_event_rect + self.show_selected_event(int(event_info["Index"])) + + def add_event(self): + """ + 新增一个事件到数据表 + """ + + # 获取index + if self.data.df_revised is not None: + index = self.data.df_revised["Index"].max() + 1 if not self.data.df_revised.empty else 1 + else: + raise ValueError("DataFrame df_revised is not initialized.") + + new_event = Params.SA_LABEL_NEW_EVENT_FORMAT.copy() + new_event["Index"] = index + new_event = self.__update_label_from_ui__(new_event, is_new=True) + new_event["isLabeled"] = 1 # 标记为已标记状态 + + # 检查是否已有事件重叠 + if sum(self.data.event_index_revised[max(0, new_event["correct_Start"] - 1):min(new_event["correct_End"] + 1, + self.config[ + "SignalSecond"])]) != 0: + PublicFunc.msgbox_output(self, "新建事件与已有事件重叠,无法新建!", Constants.MSGBOX_TYPE_WARNING) + PublicFunc.text_output(self.ui, "新建事件与已有事件重叠,无法新建!", Constants.TIPS_TYPE_ERROR) + return + + # 将新事件添加到临时DataFrame + temp_df = pd.DataFrame([new_event]) + temp_df = temp_df[self.data.df_revised.columns] # 确保列顺序一致 + temp_df.reset_index(drop=True, inplace=True) + + # 将临时DataFrame添加到df_revised + if self.data.df_revised is not None: + self.data.df_revised = pd.concat([self.data.df_revised, temp_df], ignore_index=True) + else: + self.data.df_revised = temp_df + # 更新表格模型 + self.data_model_revised.update_all(self.data.df_revised) + + # 更新事件索引映射 + self.data.event_index_revised[new_event["correct_Start"]:new_event["correct_End"]] = index + + # 更新event_info + self.selected_event_info = new_event.copy() + if self.selected_event_rect is not None: + self.selected_event_rect.remove() + + # 重新绘制矩形框 + color = self.color_cycle[self.data.event_type_to_value[new_event["correct_EventsType"]]] + self.selected_event_rect = self.ax5.axvspan(new_event["correct_Start"], new_event["correct_End"], color=color, + alpha=0.4) + self.event_ploy_collection[new_event["Index"]] = self.selected_event_rect + self.show_selected_event(int(new_event["Index"])) + + def reset_event(self, event_index): + """ + 从df_origin中重置事件信息到df_revised, df_origin只有df_revised的部分列,剩余的列恢复默认值 + """ + if self.data is None or self.data.df_revised is None: + raise ValueError("Data or df_revised is not initialized.") + + index = self.data.df_revised[self.data.df_revised["Index"] == event_index].index + if index < 1 or index > len(self.data.df_revised): + raise IndexError("Index out of bounds for df_revised.") + + # 获取原始事件信息 + original_event_info = self.data.df_origin[self.data.df_origin["Index"] == index].copy() + original_event_info["isLabeled"] = -1 # 重置为未标记状态 + original_event_info["correct_EventsType"] = "" # 重置为None + original_event_info["score"] = -1 # 重置为None + original_event_info["correct_Start"] = -1 # 重置为原始起始时间 + original_event_info["correct_End"] = -1 # 重置为原始终止时间 + original_event_info["remark"] = "" # 重置备注为None + # 检查是否有重叠 + if sum(self.data.event_index_revised[ + max(0, original_event_info["Start"] - 1):min(original_event_info["End"] + 1, + self.config["SignalSecond"])]) != original_event_info[ + "Index"]: + PublicFunc.msgbox_output(self, "重置事件与已有事件重叠,无法重置!", Constants.MSGBOX_TYPE_WARNING) + PublicFunc.text_output(self.ui, "重置事件与已有事件重叠,无法重置!", Constants.TIPS_TYPE_ERROR) + return + + # 更新事件索引映射 + self.data.event_index_revised[original_event_info["Start"]:original_event_info["End"]] = original_event_info[ + "Index"] + + # 重置事件信息到df_revised + self.data.df_revised[self.data.df_revised["Index"] == index] = original_event_info + + self.data_model_revised.update_all(self.data.df_revised) + + def __slot_btn_input__(self): + PublicFunc.__disableAllButton__(self, ButtonState) + + self.reset_axes() + self.canvas.draw() + + self.data = Data(self.config) + + # 导入数据 + PublicFunc.progressbar_update(self, 1, 9, Constants.INPUTTING_DATA, 0) + result = self.data.open_file() + if not result.status: + PublicFunc.text_output(self.ui, "(1/9)" + 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/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 获取存档 + PublicFunc.progressbar_update(self, 2, 9, Constants.LOADING_ARCHIVE, 15) + result = self.data.get_archive() + if not result.status: + PublicFunc.text_output(self.ui, "(2/9)" + 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/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 保存 + PublicFunc.progressbar_update(self, 3, 9, Constants.SAVING_DATA, 20) + result = self.data.save() + if not result.status: + PublicFunc.text_output(self.ui, "(3/9)" + 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/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 保存 + # PublicFunc.progressbar_update(self, 4, 9, Constants.SAVING_DATA, 25) + # result = self.data.save_2() + # if not result.status: + # PublicFunc.text_output(self.ui, "(4/9)" + 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/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 数据预处理 + PublicFunc.progressbar_update(self, 5, 9, Constants.PREPROCESSING_DATA, 30) + result = self.data.preprocess() + if not result.status: + PublicFunc.text_output(self.ui, "(5/9)" + 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/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 重采样 + PublicFunc.progressbar_update(self, 6, 9, Constants.RESAMPLING_DATA, 50) + result = self.data.resample() + for key, value in self.data.channel.items(): + PublicFunc.text_output(self.ui, key + "重采样后的长度:" + str(len(value)), Constants.TIPS_TYPE_INFO) + if not result.status: + PublicFunc.text_output(self.ui, "(6/9)" + 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/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 绘图 + PublicFunc.progressbar_update(self, 7, 9, Constants.DRAWING_DATA, 70) + # result = self.__plot__() + result = self.draw_signal() + if not result.status: + PublicFunc.text_output(self.ui, "(7/9)" + 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, "(7/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 更新表格 + PublicFunc.progressbar_update(self, 8, 9, Constants.UPDATING_TABLEWIDGET, 90) + result = self.load_data_to_table() + if not result.status: + PublicFunc.text_output(self.ui, "(8/9)" + 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, "(8/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # 更新信息 + PublicFunc.progressbar_update(self, 9, 9, Constants.UPDATING_INFO, 95) + # result = self.update_UI_Args() + # if not result.status: + # PublicFunc.text_output(self.ui, "(9/9)" + 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, "(9/9)" + result.info, Constants.TIPS_TYPE_INFO) + + # if (self.data.df_revised["isLabeled"] == 1).all(): + # self.ui.checkBox_examineLabeled.setChecked(False) + # PublicFunc.msgbox_output(self, Constants.SA_LABEL_ALL_LABELED, Constants.MSGBOX_TYPE_INFO) + + self.__reset__() + self.setting.close() + + for action in self.figToolbar._actions.values(): + action.setEnabled(True) + + for key in ButtonState["Current"].keys(): + ButtonState["Current"][key] = True + + ButtonState["Current"]["pushButton_input"] = False + ButtonState["Current"]["pushButton_input_setting"] = False + + PublicFunc.finish_operation(self, ButtonState) + + def __slot_btn_move__(self): + sender = self.sender() + if sender.objectName().startswith("pushButton_previous") or sender.objectName().startswith("pushButton_next"): + offset = sender.property("offset") + PublicFunc.text_output(self.ui, f"移动 {offset} 秒", Constants.TIPS_TYPE_INFO) + self.__jump_to__by_time__(self.config["WindowStartSecond"], offset) + elif sender.objectName() == "pushButton_jump_to": + jump_time_str = self.ui.lineEdit_jump_second.text().strip() + if not jump_time_str.isdigit(): + PublicFunc.msgbox_output(self, "请输入有效的时间(秒)", Constants.MSGBOX_TYPE_WARNING) + return + jump_time = int(jump_time_str) + if jump_time < 0 or jump_time > self.config["SignalSecond"]: + PublicFunc.msgbox_output(self, "跳转时间必须在信号长度范围内", Constants.MSGBOX_TYPE_WARNING) + return + PublicFunc.text_output(self.ui, f"跳转到 {jump_time} 秒", Constants.TIPS_TYPE_INFO) + self.__jump_to__by_time__(jump_time) + + def __slot_btn_quick_remark__(self): + sender = self.sender() + + if sender == self.ui.pushButton_quick_remark_input_waitingForTalk: + self.ui.lineEdit_remark.setText("待讨论") + elif sender == self.ui.pushButton_quick_remark_input_maybeDesaturation: + self.ui.lineEdit_remark.setText("形似潮式呼吸") + self.ui.radioButton_1_class.setChecked(True) + elif sender == self.ui.pushButton_quick_remark_input_maybeWrongLabeled: + self.ui.lineEdit_remark.setText("疑似医生误标") + self.ui.radioButton_2_class.setChecked(True) + elif sender == self.ui.pushButton_quick_remark_input_durationNoEnough: + self.ui.lineEdit_remark.setText("时长不足") + self.ui.radioButton_2_class.setChecked(True) + elif sender == self.ui.pushButton_quick_remark_input_littleChange: + self.ui.lineEdit_remark.setText("起伏变化不大") + self.ui.radioButton_2_class.setChecked(True) + elif sender == self.ui.pushButton_quick_remark_input_noNormalRespBetweenArtifact: + self.ui.lineEdit_remark.setText("体动间无正常呼吸") + self.ui.radioButton_2_class.setChecked(True) + elif sender == self.ui.pushButton_quick_remark_input_lowSignalNoiseRatio: + self.ui.lineEdit_remark.setText("信噪比低") + self.ui.radioButton_2_class.setChecked(True) + elif sender == self.ui.pushButton_quick_remark_input_changeOnMiddle: + self.ui.lineEdit_remark.setText("中间起伏") + self.ui.radioButton_2_class.setChecked(True) + + # def __slot_checkBox_examineLabeled__(self): + # if self.ui.checkBox_examineLabeled.isChecked(): + # if (self.data.df_corrected.loc[self.config["EventLabelIndexList"]]["isLabeled"] == 1).all(): + # self.ui.checkBox_examineLabeled.setChecked(False) + # PublicFunc.text_output(self.ui, Constants.SA_LABEL_ALL_LABELED, Constants.TIPS_TYPE_INFO) + # PublicFunc.msgbox_output(self, Constants.SA_LABEL_ALL_LABELED, Constants.MSGBOX_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(Params.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(Params.FORMATTER) + self.ax1.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + if self.ax2 is not None: + self.ax2.clear() + self.ax2.grid(True) + self.ax2.xaxis.set_major_formatter(Params.FORMATTER) + self.ax2.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + if self.ax3 is not None: + self.ax3.clear() + self.ax3.grid(True) + self.ax3.xaxis.set_major_formatter(Params.FORMATTER) + self.ax3.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + if self.ax4 is not None: + self.ax4.clear() + self.ax4.grid(True) + self.ax4.xaxis.set_major_formatter(Params.FORMATTER) + self.ax4.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + + if self.ax5 is not None: + self.ax5.clear() + self.ax5.grid(True) + self.ax5.xaxis.set_major_formatter(Params.FORMATTER) + self.ax5.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE) + + if self.ax6 is not None: + self.ax6.clear() + self.ax6.grid(True) + self.ax6.xaxis.set_major_formatter(Params.FORMATTER) + + def draw_signal(self): + try: + self.plt_channel(channel="SpO2", start=0, end=self.config["SignalSecond"], + label_list=self.data.event_label_origin, event_code=[5]) + self.plt_channel(channel="Flow T", start=0, end=self.config["SignalSecond"], + label_list=self.data.event_label_origin, event_code=[1, 2, 3, 4]) + self.plt_channel(channel="Flow P", start=0, end=self.config["SignalSecond"], + label_list=self.data.event_label_origin, event_code=[1, 2, 3, 4]) + self.plt_channel(channel="Effort Tho", start=0, end=self.config["SignalSecond"], + label_list=self.data.event_label_origin, event_code=[1, 2, 3, 4]) + self.plt_channel(channel="Effort Abd", start=0, end=self.config["SignalSecond"], + label_list=self.data.event_label_origin, event_code=[1, 2, 3, 4]) + self.plt_channel(channel="0.7lowpass_resp", start=0, end=self.config["SignalSecond"]) + self.plt_interactive_event(channel="0.7lowpass_resp", label_df=self.data.df_revised) + self.plt_channel(channel="orgdata", start=0, end=self.config["SignalSecond"], + label_list=self.data.artifact_label, event_code=[6, 7, 8, 9, 10]) + + self.ax0.set_xlim(self.check_start_end(self.config["WindowStartSecond"], 0)) + self.canvas.draw() + except Exception as e: + return Result().failure(info=Constants.DRAW_FAILURE + "\n" + format_exc()) + + return Result().success(info=Constants.DRAW_FINISHED) + + def check_start_end(self, start, value): + if start + value < 0: + return 0, self.config["WindowSignalSecond"] + elif start + value + self.config["WindowSignalSecond"] > self.config["SignalSecond"]: + return start - self.config["WindowSignalSecond"], self.config["SignalSecond"] + else: + return start + value, start + value + self.config["WindowSignalSecond"] + + def plt_channel(self, channel, start, end, label_list=None, event_code=[1, 2, 3, 4], event_show_under=False): + plt_ = self.channel_to_ax[channel] + plot_freq = self.config["Config"]["InputConfig"]["PlotFreq"] + start = 0 if start < 0 else start + plt_.plot(linspace(start, end, (end - start) * plot_freq), + self.data.channel[channel][ + start * plot_freq:end * plot_freq], label=channel, + color=self.color_cycle[0]) + if label_list is not None: + for j in event_code: + mask = label_list[start * plot_freq:end * plot_freq] == j + + if event_show_under: + min_point = self.data.channel[channel][start * plot_freq:end * plot_freq].min() + len_segment = end * plot_freq - start * plot_freq + y = (min_point.repeat(len_segment) * mask).astype('float') + place(y, y == 0, nan) + else: + y = (self.data.channel[channel][start * plot_freq:end * plot_freq] * mask).astype('float') + + place(y, y == 0, nan) + plt_.plot(linspace(start, end, (end - start) * plot_freq), y, color=self.color_cycle[j], linestyle="-") + plt_.legend(fontsize=8, loc=1) + + def plt_interactive_event(self, channel, label_df): + """ + 绘制交互式事件标记 + :param channel: 信号通道名称 + :param label_df: 包含事件标记的DataFrame + """ + plt_ = self.channel_to_ax[channel] + plot_freq = self.config["Config"]["InputConfig"]["PlotFreq"] + for _, row in label_df.iterrows(): + if row["score"] == 3: + span = None + else: + if row["isLabeled"] == -1: + start = row["Start"] + end = row["End"] + event_type = row["Event type"] + else: + start = row["correct_Start"] + end = row["correct_End"] + event_type = row["correct_EventsType"] + + color = self.color_cycle[self.data.event_type_to_value[event_type]] + span = plt_.axvspan(start, end, color=color, alpha=0.2, label=event_type) + + self.event_ploy_collection.update({ + row["Index"]: span + }) + + +class CustomNavigationToolbar(NavigationToolbar2QT): + def __init__(self, canvas, parent): + super().__init__(canvas, parent) + self.edit_active = False + + # 移除原有除了pan的所有按钮 + for action in list(self._actions.values()): + if action.text() != 'Pan': + self.removeAction(action) + + # pan禁用按住右键滑动对x轴变化 + self.edit_action = QAction('编辑模式(Z)', self) + self.edit_action.setFont(QFont(Params.FONT, 14)) + self.edit_action.setCheckable(True) + self.edit_action.triggered.connect(self.toggle_edit) + self.edit_action.setShortcut( + QCoreApplication.translate("MainWindow", Params.SA_LABEL_BTN_EDIT_MODE_SHORTCUT_KEY)) + self.insertAction(self._actions['pan'], self.edit_action) + + # Connect Pan action to toggle off Edit + self._actions['pan'].triggered.connect(self.toggle_pan) + + def drag_pan(self, event): + """Callback for dragging in pan/zoom mode.""" + for ax in self._pan_info.axes: + # Using the recorded button at the press is safer than the current + # button, as multiple buttons can get pressed during motion. + # # 忽略pan按钮右键对时间尺度的拉伸操作 + # if self._pan_info.button == 3: + # ax.drag_pan(self._pan_info.button, event.key, ax._pan_start.x, event.y) + # else: + # ax.drag_pan(self._pan_info.button, event.key, event.x, event.y) + # 忽略对X轴的操作 + ax.drag_pan(self._pan_info.button, event.key, ax._pan_start.x, event.y) + + self.canvas.draw_idle() + + def toggle_edit(self): + """Handle Edit action toggle.""" + self.edit_active = self.edit_action.isChecked() + if self.edit_active: + # Uncheck Pan when Edit is active + self._actions['pan'].setChecked(False) + self._active = 'EDIT' + self.mode = 'edit mode' + self.set_message(f"编辑事件: {'启用' if self.edit_active else '禁用'}") + self.canvas.draw() + + def toggle_pan(self): + """Handle Pan action toggle.""" + if self._actions['pan'].isChecked(): + # Uncheck Edit when Pan is active + self.edit_action.setChecked(False) + self.edit_active = False + self.set_message("Pan 模式: 启用") + else: + self.set_message("Pan 模式: 禁用") + self.canvas.draw() diff --git a/func/Module_mainwindow.py b/func/Module_mainwindow.py index cd836f0..8325858 100644 --- a/func/Module_mainwindow.py +++ b/func/Module_mainwindow.py @@ -20,7 +20,7 @@ from func.Module_cut_PSG import MainWindow_cut_PSG from func.Module_artifact_label import MainWindow_artifact_label from func.Module_bcg_quality_label import MainWindow_bcg_quality_label from func.Module_resp_quality_label import MainWindow_resp_quality_label -from func.Module_SA_label import MainWindow_SA_label +from func.Module_SA_label_v2 import MainWindow_SA_label from func.utils.ConfigParams import Filename, Params from func.utils.Constants import Constants diff --git a/func/utils/ConfigParams.py b/func/utils/ConfigParams.py index 623a953..36a3887 100644 --- a/func/utils/ConfigParams.py +++ b/func/utils/ConfigParams.py @@ -343,18 +343,52 @@ class Params: "Back": 60 } } + + SA_LABEL_NEW_EVENT_FORMAT: dict = { + "Index": "", # 假设Epoch从1开始 + "Event type": "", # 原始事件类型为空 + "Stage": "", + "Time": "", + "Epoch": "", + "Date": "", + "Duration": "", # 计算持续时间 + "HR bef.": "", + "HR extr.": "", + "HR delta": "", + "O2 bef.": "", + "O2 min.": "", + "O2 delta": "", + "Body Position": "", + "Validation": "", + "Start": "", + "End": "", + "correct_Start": "", + "correct_End":"", + "correct_EventsType": "", # 修正事件类型为空 + "score": -1, # 默认分数为-1 + "isLabeled": -1, # 默认未标记状态 + "remark": "" # 默认备注为空 + } + SA_LABEL_TRANSPARENCY: float = 0.05 - SA_LABEL_BTN_PREV_SHORTCUT_KEY: str = "A" - SA_LABEL_BTN_NEXT_SHORTCUT_KEY: str = "D" + SA_LABEL_BTN_PREV_10s_SHORTCUT_KEY: str = "Q" + SA_LABEL_BTN_NEXT_10s_SHORTCUT_KEY: str = "E" + SA_LABEL_BTN_PREV_30s_SHORTCUT_KEY: str = "A" + SA_LABEL_BTN_NEXT_30s_SHORTCUT_KEY: str = "D" + SA_LABEL_BTN_PREV_HALF_SHORTCUT_KEY: str = "F" + SA_LABEL_BTN_NEXT_HALF_SHORTCUT_KEY: str = "G" + SA_LABEL_BTN_BEST_FIT_SHORTCUT_KEY: str = "X" + SA_LABEL_BTN_EDIT_MODE_SHORTCUT_KEY: str = "Z" SA_LABEL_BTN_CONFIRMLABEL_SHORTCUT_KEY: str = "S" SA_LABEL_BTN_QUICK_REMARK_WAITINGFORTALK_SHORTCUT_KEY: str = "J" - SA_LABEL_RADIOBUTTON_OSA_SHORTCUT_KEY: str = "1" - SA_LABEL_RADIOBUTTON_CSA_SHORTCUT_KEY: str = "2" - SA_LABEL_RADIOBUTTON_MSA_SHORTCUT_KEY: str = "3" - SA_LABEL_RADIOBUTTON_HPY_SHORTCUT_KEY: str = "4" - SA_LABEL_RADIOBUTTON_1_CLASS_SHORTCUT_KEY: str = "U" - SA_LABEL_RADIOBUTTON_2_CLASS_SHORTCUT_KEY: str = "I" - SA_LABEL_RADIOBUTTON_3_CLASS_SHORTCUT_KEY: str = "O" + SA_LABEL_RADIOBUTTON_OSA_SHORTCUT_KEY: str = "7" + SA_LABEL_RADIOBUTTON_CSA_SHORTCUT_KEY: str = "8" + SA_LABEL_RADIOBUTTON_MSA_SHORTCUT_KEY: str = "9" + SA_LABEL_RADIOBUTTON_HPY_SHORTCUT_KEY: str = "0" + SA_LABEL_RADIOBUTTON_1_CLASS_SHORTCUT_KEY: str = "1" + SA_LABEL_RADIOBUTTON_2_CLASS_SHORTCUT_KEY: str = "2" + SA_LABEL_RADIOBUTTON_3_CLASS_SHORTCUT_KEY: str = "3" + # 禁止实例化 def __new__(cls): diff --git a/ui/MainWindow/MainWindow_SA_label_v2.py b/ui/MainWindow/MainWindow_SA_label_v2.py new file mode 100644 index 0000000..cbe6979 --- /dev/null +++ b/ui/MainWindow/MainWindow_SA_label_v2.py @@ -0,0 +1,690 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'MainWindow_SA_label_v2.ui' +## +## Created by: Qt User Interface Compiler version 6.7.0 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QAbstractItemView, QAbstractSpinBox, QApplication, QCheckBox, + QComboBox, QGridLayout, QGroupBox, QHBoxLayout, + QHeaderView, QLabel, QLineEdit, QMainWindow, + QPushButton, QRadioButton, QSizePolicy, QSpacerItem, + QSpinBox, QStatusBar, QTableView, QTextBrowser, + QVBoxLayout, QWidget) + +class Ui_MainWindow_SA_label(object): + def setupUi(self, MainWindow_SA_label): + if not MainWindow_SA_label.objectName(): + MainWindow_SA_label.setObjectName(u"MainWindow_SA_label") + MainWindow_SA_label.resize(1921, 1080) + self.centralwidget = QWidget(MainWindow_SA_label) + self.centralwidget.setObjectName(u"centralwidget") + self.gridLayout = QGridLayout(self.centralwidget) + self.gridLayout.setObjectName(u"gridLayout") + self.groupBox_canvas = QGroupBox(self.centralwidget) + self.groupBox_canvas.setObjectName(u"groupBox_canvas") + font = QFont() + font.setPointSize(10) + self.groupBox_canvas.setFont(font) + self.verticalLayout = QVBoxLayout(self.groupBox_canvas) + self.verticalLayout.setObjectName(u"verticalLayout") + self.verticalLayout_canvas = QVBoxLayout() + self.verticalLayout_canvas.setObjectName(u"verticalLayout_canvas") + + self.verticalLayout.addLayout(self.verticalLayout_canvas) + + + self.gridLayout.addWidget(self.groupBox_canvas, 0, 1, 1, 1) + + self.groupBox_left = QGroupBox(self.centralwidget) + self.groupBox_left.setObjectName(u"groupBox_left") + self.groupBox_left.setFont(font) + self.verticalLayout_2 = QVBoxLayout(self.groupBox_left) + self.verticalLayout_2.setObjectName(u"verticalLayout_2") + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.pushButton_input_setting = QPushButton(self.groupBox_left) + self.pushButton_input_setting.setObjectName(u"pushButton_input_setting") + sizePolicy = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.pushButton_input_setting.sizePolicy().hasHeightForWidth()) + self.pushButton_input_setting.setSizePolicy(sizePolicy) + font1 = QFont() + font1.setPointSize(12) + self.pushButton_input_setting.setFont(font1) + + self.horizontalLayout.addWidget(self.pushButton_input_setting) + + self.pushButton_input = QPushButton(self.groupBox_left) + self.pushButton_input.setObjectName(u"pushButton_input") + sizePolicy.setHeightForWidth(self.pushButton_input.sizePolicy().hasHeightForWidth()) + self.pushButton_input.setSizePolicy(sizePolicy) + self.pushButton_input.setFont(font1) + + self.horizontalLayout.addWidget(self.pushButton_input) + + + self.verticalLayout_2.addLayout(self.horizontalLayout) + + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_2.addItem(self.verticalSpacer) + + self.checkBox = QCheckBox(self.groupBox_left) + self.checkBox.setObjectName(u"checkBox") + self.checkBox.setFont(font1) + + self.verticalLayout_2.addWidget(self.checkBox) + + self.pushButton_best_fit = QPushButton(self.groupBox_left) + self.pushButton_best_fit.setObjectName(u"pushButton_best_fit") + self.pushButton_best_fit.setFont(font1) + self.pushButton_best_fit.setChecked(False) + + self.verticalLayout_2.addWidget(self.pushButton_best_fit) + + self.horizontalLayout_6 = QHBoxLayout() + self.horizontalLayout_6.setObjectName(u"horizontalLayout_6") + self.label_4 = QLabel(self.groupBox_left) + self.label_4.setObjectName(u"label_4") + sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) + self.label_4.setSizePolicy(sizePolicy1) + self.label_4.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + + self.horizontalLayout_6.addWidget(self.label_4) + + self.comboBox_window_signal_length = QComboBox(self.groupBox_left) + self.comboBox_window_signal_length.addItem("") + self.comboBox_window_signal_length.addItem("") + self.comboBox_window_signal_length.addItem("") + self.comboBox_window_signal_length.addItem("") + self.comboBox_window_signal_length.addItem("") + self.comboBox_window_signal_length.setObjectName(u"comboBox_window_signal_length") + sizePolicy1.setHeightForWidth(self.comboBox_window_signal_length.sizePolicy().hasHeightForWidth()) + self.comboBox_window_signal_length.setSizePolicy(sizePolicy1) + self.comboBox_window_signal_length.setEditable(True) + self.comboBox_window_signal_length.setInsertPolicy(QComboBox.InsertPolicy.NoInsert) + + self.horizontalLayout_6.addWidget(self.comboBox_window_signal_length) + + self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.horizontalLayout_6.addItem(self.horizontalSpacer_5) + + + self.verticalLayout_2.addLayout(self.horizontalLayout_6) + + self.groupBox_label = QGroupBox(self.groupBox_left) + self.groupBox_label.setObjectName(u"groupBox_label") + self.gridLayout_3 = QGridLayout(self.groupBox_label) + self.gridLayout_3.setObjectName(u"gridLayout_3") + self.label_2 = QLabel(self.groupBox_label) + self.label_2.setObjectName(u"label_2") + self.label_2.setFont(font1) + + self.gridLayout_3.addWidget(self.label_2, 4, 0, 1, 1) + + self.verticalSpacer_4 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.gridLayout_3.addItem(self.verticalSpacer_4, 2, 0, 1, 2) + + self.lineEdit_filter_label_origin = QLineEdit(self.groupBox_label) + self.lineEdit_filter_label_origin.setObjectName(u"lineEdit_filter_label_origin") + self.lineEdit_filter_label_origin.setFont(font1) + + self.gridLayout_3.addWidget(self.lineEdit_filter_label_origin, 0, 1, 1, 1) + + self.lineEdit_filter_label_revised = QLineEdit(self.groupBox_label) + self.lineEdit_filter_label_revised.setObjectName(u"lineEdit_filter_label_revised") + self.lineEdit_filter_label_revised.setFont(font1) + + self.gridLayout_3.addWidget(self.lineEdit_filter_label_revised, 4, 1, 1, 1) + + self.label = QLabel(self.groupBox_label) + self.label.setObjectName(u"label") + self.label.setFont(font1) + + self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1) + + self.tableView_label = QTableView(self.groupBox_label) + self.tableView_label.setObjectName(u"tableView_label") + self.tableView_label.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + + self.gridLayout_3.addWidget(self.tableView_label, 1, 0, 1, 2) + + self.tableView_label_revised = QTableView(self.groupBox_label) + self.tableView_label_revised.setObjectName(u"tableView_label_revised") + self.tableView_label_revised.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) + + self.gridLayout_3.addWidget(self.tableView_label_revised, 5, 0, 1, 2) + + self.gridLayout_3.setRowStretch(0, 2) + + self.verticalLayout_2.addWidget(self.groupBox_label) + + self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_2.addItem(self.verticalSpacer_3) + + self.pushButton_save = QPushButton(self.groupBox_left) + self.pushButton_save.setObjectName(u"pushButton_save") + sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred) + sizePolicy2.setHorizontalStretch(0) + sizePolicy2.setVerticalStretch(0) + sizePolicy2.setHeightForWidth(self.pushButton_save.sizePolicy().hasHeightForWidth()) + self.pushButton_save.setSizePolicy(sizePolicy2) + self.pushButton_save.setFont(font1) + + self.verticalLayout_2.addWidget(self.pushButton_save) + + self.groupBox_4 = QGroupBox(self.groupBox_left) + self.groupBox_4.setObjectName(u"groupBox_4") + self.verticalLayout_6 = QVBoxLayout(self.groupBox_4) + self.verticalLayout_6.setObjectName(u"verticalLayout_6") + self.textBrowser_info = QTextBrowser(self.groupBox_4) + self.textBrowser_info.setObjectName(u"textBrowser_info") + + self.verticalLayout_6.addWidget(self.textBrowser_info) + + + self.verticalLayout_2.addWidget(self.groupBox_4) + + self.verticalLayout_2.setStretch(0, 2) + self.verticalLayout_2.setStretch(1, 1) + self.verticalLayout_2.setStretch(5, 15) + self.verticalLayout_2.setStretch(6, 1) + self.verticalLayout_2.setStretch(7, 2) + self.verticalLayout_2.setStretch(8, 5) + + self.gridLayout.addWidget(self.groupBox_left, 0, 0, 1, 1) + + self.groupBox_right = QGroupBox(self.centralwidget) + self.groupBox_right.setObjectName(u"groupBox_right") + self.groupBox_right.setFont(font) + self.gridLayout_4 = QGridLayout(self.groupBox_right) + self.gridLayout_4.setObjectName(u"gridLayout_4") + self.verticalSpacer_5 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.gridLayout_4.addItem(self.verticalSpacer_5, 1, 0, 1, 2) + + self.groupBox_examineBySecond = QGroupBox(self.groupBox_right) + self.groupBox_examineBySecond.setObjectName(u"groupBox_examineBySecond") + self.verticalLayout_4 = QVBoxLayout(self.groupBox_examineBySecond) + self.verticalLayout_4.setObjectName(u"verticalLayout_4") + self.horizontalLayout_5 = QHBoxLayout() + self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.label_3 = QLabel(self.groupBox_examineBySecond) + self.label_3.setObjectName(u"label_3") + + self.horizontalLayout_5.addWidget(self.label_3) + + self.lineEdit_jump_second = QLineEdit(self.groupBox_examineBySecond) + self.lineEdit_jump_second.setObjectName(u"lineEdit_jump_second") + + self.horizontalLayout_5.addWidget(self.lineEdit_jump_second) + + self.pushButton_jump_to = QPushButton(self.groupBox_examineBySecond) + self.pushButton_jump_to.setObjectName(u"pushButton_jump_to") + + self.horizontalLayout_5.addWidget(self.pushButton_jump_to) + + + self.verticalLayout_4.addLayout(self.horizontalLayout_5) + + self.gridLayout_8 = QGridLayout() + self.gridLayout_8.setObjectName(u"gridLayout_8") + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.gridLayout_8.addItem(self.horizontalSpacer, 0, 1, 1, 1) + + self.pushButton_next_half = QPushButton(self.groupBox_examineBySecond) + self.pushButton_next_half.setObjectName(u"pushButton_next_half") + self.pushButton_next_half.setFont(font1) + self.pushButton_next_half.setProperty("offset", 15) + + self.gridLayout_8.addWidget(self.pushButton_next_half, 1, 4, 1, 1) + + self.pushButton_previous10s = QPushButton(self.groupBox_examineBySecond) + self.pushButton_previous10s.setObjectName(u"pushButton_previous10s") + self.pushButton_previous10s.setFont(font1) + self.pushButton_previous10s.setProperty("offset", -10) + + self.gridLayout_8.addWidget(self.pushButton_previous10s, 0, 0, 1, 1) + + self.pushButton_previous_half = QPushButton(self.groupBox_examineBySecond) + self.pushButton_previous_half.setObjectName(u"pushButton_previous_half") + self.pushButton_previous_half.setFont(font1) + self.pushButton_previous_half.setProperty("offset", -15) + + self.gridLayout_8.addWidget(self.pushButton_previous_half, 0, 4, 1, 1) + + self.pushButton_next30s = QPushButton(self.groupBox_examineBySecond) + self.pushButton_next30s.setObjectName(u"pushButton_next30s") + self.pushButton_next30s.setFont(font1) + self.pushButton_next30s.setProperty("offset", 30) + + self.gridLayout_8.addWidget(self.pushButton_next30s, 1, 2, 1, 1) + + self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.gridLayout_8.addItem(self.horizontalSpacer_2, 1, 1, 1, 1) + + self.pushButton_previous30s = QPushButton(self.groupBox_examineBySecond) + self.pushButton_previous30s.setObjectName(u"pushButton_previous30s") + self.pushButton_previous30s.setFont(font1) + self.pushButton_previous30s.setProperty("offset", -30) + + self.gridLayout_8.addWidget(self.pushButton_previous30s, 0, 2, 1, 1) + + self.pushButton_next10s = QPushButton(self.groupBox_examineBySecond) + self.pushButton_next10s.setObjectName(u"pushButton_next10s") + self.pushButton_next10s.setFont(font1) + self.pushButton_next10s.setProperty("offset", 10) + + self.gridLayout_8.addWidget(self.pushButton_next10s, 1, 0, 1, 1) + + self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.gridLayout_8.addItem(self.horizontalSpacer_3, 0, 3, 1, 1) + + self.horizontalSpacer_4 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) + + self.gridLayout_8.addItem(self.horizontalSpacer_4, 1, 3, 1, 1) + + self.gridLayout_8.setColumnMinimumWidth(0, 3) + self.gridLayout_8.setColumnMinimumWidth(1, 1) + self.gridLayout_8.setColumnMinimumWidth(2, 3) + self.gridLayout_8.setColumnMinimumWidth(3, 1) + self.gridLayout_8.setColumnMinimumWidth(4, 3) + + self.verticalLayout_4.addLayout(self.gridLayout_8) + + self.verticalLayout_4.setStretch(0, 1) + self.verticalLayout_4.setStretch(1, 2) + + self.gridLayout_4.addWidget(self.groupBox_examineBySecond, 2, 0, 1, 2) + + self.groupBox_label_operation = QGroupBox(self.groupBox_right) + self.groupBox_label_operation.setObjectName(u"groupBox_label_operation") + self.verticalLayout_3 = QVBoxLayout(self.groupBox_label_operation) + self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.horizontalLayout_7 = QHBoxLayout() + self.horizontalLayout_7.setObjectName(u"horizontalLayout_7") + self.label_5 = QLabel(self.groupBox_label_operation) + self.label_5.setObjectName(u"label_5") + sizePolicy2.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) + self.label_5.setSizePolicy(sizePolicy2) + self.label_5.setFont(font1) + self.label_5.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter) + + self.horizontalLayout_7.addWidget(self.label_5) + + self.label_BCG_Index = QLabel(self.groupBox_label_operation) + self.label_BCG_Index.setObjectName(u"label_BCG_Index") + sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred) + sizePolicy3.setHorizontalStretch(0) + sizePolicy3.setVerticalStretch(0) + sizePolicy3.setHeightForWidth(self.label_BCG_Index.sizePolicy().hasHeightForWidth()) + self.label_BCG_Index.setSizePolicy(sizePolicy3) + self.label_BCG_Index.setFont(font1) + self.label_BCG_Index.setAlignment(Qt.AlignmentFlag.AlignCenter) + + self.horizontalLayout_7.addWidget(self.label_BCG_Index) + + self.horizontalLayout_7.setStretch(0, 1) + self.horizontalLayout_7.setStretch(1, 5) + + self.verticalLayout_3.addLayout(self.horizontalLayout_7) + + self.label_BCG_Info = QLabel(self.groupBox_label_operation) + self.label_BCG_Info.setObjectName(u"label_BCG_Info") + self.label_BCG_Info.setFont(font1) + self.label_BCG_Info.setAlignment(Qt.AlignmentFlag.AlignCenter) + + self.verticalLayout_3.addWidget(self.label_BCG_Info) + + self.horizontalLayout_3 = QHBoxLayout() + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.label_11 = QLabel(self.groupBox_label_operation) + self.label_11.setObjectName(u"label_11") + self.label_11.setFont(font1) + + self.horizontalLayout_3.addWidget(self.label_11) + + self.radioButton_OSA = QRadioButton(self.groupBox_label_operation) + self.radioButton_OSA.setObjectName(u"radioButton_OSA") + self.radioButton_OSA.setFont(font1) + self.radioButton_OSA.setChecked(True) + + self.horizontalLayout_3.addWidget(self.radioButton_OSA) + + self.radioButton_CSA = QRadioButton(self.groupBox_label_operation) + self.radioButton_CSA.setObjectName(u"radioButton_CSA") + self.radioButton_CSA.setFont(font1) + + self.horizontalLayout_3.addWidget(self.radioButton_CSA) + + self.radioButton_MSA = QRadioButton(self.groupBox_label_operation) + self.radioButton_MSA.setObjectName(u"radioButton_MSA") + self.radioButton_MSA.setFont(font1) + + self.horizontalLayout_3.addWidget(self.radioButton_MSA) + + self.radioButton_HPY = QRadioButton(self.groupBox_label_operation) + self.radioButton_HPY.setObjectName(u"radioButton_HPY") + self.radioButton_HPY.setFont(font1) + + self.horizontalLayout_3.addWidget(self.radioButton_HPY) + + + self.verticalLayout_3.addLayout(self.horizontalLayout_3) + + self.horizontalLayout_4 = QHBoxLayout() + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") + self.label_12 = QLabel(self.groupBox_label_operation) + self.label_12.setObjectName(u"label_12") + self.label_12.setFont(font1) + + self.horizontalLayout_4.addWidget(self.label_12) + + self.radioButton_1_class = QRadioButton(self.groupBox_label_operation) + self.radioButton_1_class.setObjectName(u"radioButton_1_class") + self.radioButton_1_class.setFont(font1) + self.radioButton_1_class.setChecked(True) + + self.horizontalLayout_4.addWidget(self.radioButton_1_class) + + self.radioButton_2_class = QRadioButton(self.groupBox_label_operation) + self.radioButton_2_class.setObjectName(u"radioButton_2_class") + self.radioButton_2_class.setFont(font1) + + self.horizontalLayout_4.addWidget(self.radioButton_2_class) + + self.radioButton_3_class = QRadioButton(self.groupBox_label_operation) + self.radioButton_3_class.setObjectName(u"radioButton_3_class") + self.radioButton_3_class.setFont(font1) + + self.horizontalLayout_4.addWidget(self.radioButton_3_class) + + + self.verticalLayout_3.addLayout(self.horizontalLayout_4) + + self.verticalSpacer_9 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_3.addItem(self.verticalSpacer_9) + + self.horizontalLayout_2 = QHBoxLayout() + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.label_6 = QLabel(self.groupBox_label_operation) + self.label_6.setObjectName(u"label_6") + self.label_6.setFont(font1) + + self.horizontalLayout_2.addWidget(self.label_6) + + self.lineEdit_remark = QLineEdit(self.groupBox_label_operation) + self.lineEdit_remark.setObjectName(u"lineEdit_remark") + self.lineEdit_remark.setFont(font1) + + self.horizontalLayout_2.addWidget(self.lineEdit_remark) + + + self.verticalLayout_3.addLayout(self.horizontalLayout_2) + + self.verticalSpacer_8 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_3.addItem(self.verticalSpacer_8) + + self.label_7 = QLabel(self.groupBox_label_operation) + self.label_7.setObjectName(u"label_7") + self.label_7.setFont(font1) + + self.verticalLayout_3.addWidget(self.label_7) + + self.gridLayout_5 = QGridLayout() + self.gridLayout_5.setObjectName(u"gridLayout_5") + self.pushButton_quick_remark_input_durationNoEnough = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_durationNoEnough.setObjectName(u"pushButton_quick_remark_input_durationNoEnough") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_durationNoEnough.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_durationNoEnough.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_durationNoEnough.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_durationNoEnough, 1, 1, 1, 1) + + self.pushButton_quick_remark_input_maybeWrongLabeled = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_maybeWrongLabeled.setObjectName(u"pushButton_quick_remark_input_maybeWrongLabeled") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_maybeWrongLabeled.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_maybeWrongLabeled.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_maybeWrongLabeled.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_maybeWrongLabeled, 1, 0, 1, 1) + + self.pushButton_quick_remark_input_noNormalRespBetweenArtifact = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_noNormalRespBetweenArtifact.setObjectName(u"pushButton_quick_remark_input_noNormalRespBetweenArtifact") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_noNormalRespBetweenArtifact.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_noNormalRespBetweenArtifact.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_noNormalRespBetweenArtifact.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_noNormalRespBetweenArtifact, 2, 1, 1, 1) + + self.pushButton_quick_remark_input_maybeDesaturation = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_maybeDesaturation.setObjectName(u"pushButton_quick_remark_input_maybeDesaturation") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_maybeDesaturation.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_maybeDesaturation.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_maybeDesaturation.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_maybeDesaturation, 0, 1, 1, 1) + + self.pushButton_quick_remark_input_littleChange = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_littleChange.setObjectName(u"pushButton_quick_remark_input_littleChange") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_littleChange.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_littleChange.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_littleChange.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_littleChange, 2, 0, 1, 1) + + self.pushButton_quick_remark_input_waitingForTalk = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_waitingForTalk.setObjectName(u"pushButton_quick_remark_input_waitingForTalk") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_waitingForTalk.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_waitingForTalk.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_waitingForTalk.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_waitingForTalk, 0, 0, 1, 1) + + self.pushButton_quick_remark_input_lowSignalNoiseRatio = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_lowSignalNoiseRatio.setObjectName(u"pushButton_quick_remark_input_lowSignalNoiseRatio") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_lowSignalNoiseRatio.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_lowSignalNoiseRatio.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_lowSignalNoiseRatio.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_lowSignalNoiseRatio, 3, 0, 1, 1) + + self.pushButton_quick_remark_input_changeOnMiddle = QPushButton(self.groupBox_label_operation) + self.pushButton_quick_remark_input_changeOnMiddle.setObjectName(u"pushButton_quick_remark_input_changeOnMiddle") + sizePolicy2.setHeightForWidth(self.pushButton_quick_remark_input_changeOnMiddle.sizePolicy().hasHeightForWidth()) + self.pushButton_quick_remark_input_changeOnMiddle.setSizePolicy(sizePolicy2) + self.pushButton_quick_remark_input_changeOnMiddle.setFont(font1) + + self.gridLayout_5.addWidget(self.pushButton_quick_remark_input_changeOnMiddle, 3, 1, 1, 1) + + + self.verticalLayout_3.addLayout(self.gridLayout_5) + + self.verticalSpacer_7 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_3.addItem(self.verticalSpacer_7) + + self.gridLayout_6 = QGridLayout() + self.gridLayout_6.setObjectName(u"gridLayout_6") + self.label_8 = QLabel(self.groupBox_label_operation) + self.label_8.setObjectName(u"label_8") + self.label_8.setFont(font1) + + self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 1) + + self.label_9 = QLabel(self.groupBox_label_operation) + self.label_9.setObjectName(u"label_9") + self.label_9.setFont(font1) + + self.gridLayout_6.addWidget(self.label_9, 1, 0, 1, 1) + + self.spinBox_correctStart = QSpinBox(self.groupBox_label_operation) + self.spinBox_correctStart.setObjectName(u"spinBox_correctStart") + self.spinBox_correctStart.setFont(font1) + self.spinBox_correctStart.setButtonSymbols(QAbstractSpinBox.ButtonSymbols.NoButtons) + self.spinBox_correctStart.setMaximum(100000) + + self.gridLayout_6.addWidget(self.spinBox_correctStart, 0, 1, 1, 1) + + self.spinBox_correctEnd = QSpinBox(self.groupBox_label_operation) + self.spinBox_correctEnd.setObjectName(u"spinBox_correctEnd") + self.spinBox_correctEnd.setFont(font1) + self.spinBox_correctEnd.setButtonSymbols(QAbstractSpinBox.ButtonSymbols.NoButtons) + self.spinBox_correctEnd.setMaximum(100000) + + self.gridLayout_6.addWidget(self.spinBox_correctEnd, 1, 1, 1, 1) + + + self.verticalLayout_3.addLayout(self.gridLayout_6) + + self.verticalSpacer_6 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) + + self.verticalLayout_3.addItem(self.verticalSpacer_6) + + self.gridLayout_7 = QGridLayout() + self.gridLayout_7.setObjectName(u"gridLayout_7") + self.pushButton_prev = QPushButton(self.groupBox_label_operation) + self.pushButton_prev.setObjectName(u"pushButton_prev") + sizePolicy2.setHeightForWidth(self.pushButton_prev.sizePolicy().hasHeightForWidth()) + self.pushButton_prev.setSizePolicy(sizePolicy2) + self.pushButton_prev.setFont(font1) + + self.gridLayout_7.addWidget(self.pushButton_prev, 0, 0, 1, 1) + + self.pushButton_next = QPushButton(self.groupBox_label_operation) + self.pushButton_next.setObjectName(u"pushButton_next") + sizePolicy2.setHeightForWidth(self.pushButton_next.sizePolicy().hasHeightForWidth()) + self.pushButton_next.setSizePolicy(sizePolicy2) + self.pushButton_next.setFont(font1) + + self.gridLayout_7.addWidget(self.pushButton_next, 1, 0, 1, 1) + + self.pushButton_confirmLabel = QPushButton(self.groupBox_label_operation) + self.pushButton_confirmLabel.setObjectName(u"pushButton_confirmLabel") + sizePolicy2.setHeightForWidth(self.pushButton_confirmLabel.sizePolicy().hasHeightForWidth()) + self.pushButton_confirmLabel.setSizePolicy(sizePolicy2) + self.pushButton_confirmLabel.setFont(font1) + + self.gridLayout_7.addWidget(self.pushButton_confirmLabel, 0, 1, 2, 1) + + + self.verticalLayout_3.addLayout(self.gridLayout_7) + + self.verticalLayout_3.setStretch(0, 1) + self.verticalLayout_3.setStretch(1, 1) + self.verticalLayout_3.setStretch(2, 2) + self.verticalLayout_3.setStretch(3, 2) + self.verticalLayout_3.setStretch(4, 1) + self.verticalLayout_3.setStretch(5, 2) + self.verticalLayout_3.setStretch(6, 1) + self.verticalLayout_3.setStretch(7, 1) + self.verticalLayout_3.setStretch(8, 6) + self.verticalLayout_3.setStretch(9, 1) + self.verticalLayout_3.setStretch(10, 2) + self.verticalLayout_3.setStretch(11, 1) + self.verticalLayout_3.setStretch(12, 3) + + self.gridLayout_4.addWidget(self.groupBox_label_operation, 0, 0, 1, 2) + + self.gridLayout_4.setRowStretch(0, 12) + + self.gridLayout.addWidget(self.groupBox_right, 0, 2, 1, 1) + + self.gridLayout.setColumnStretch(0, 3) + self.gridLayout.setColumnStretch(1, 7) + self.gridLayout.setColumnStretch(2, 3) + MainWindow_SA_label.setCentralWidget(self.centralwidget) + self.statusbar = QStatusBar(MainWindow_SA_label) + self.statusbar.setObjectName(u"statusbar") + MainWindow_SA_label.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow_SA_label) + + QMetaObject.connectSlotsByName(MainWindow_SA_label) + # setupUi + + def retranslateUi(self, MainWindow_SA_label): + MainWindow_SA_label.setWindowTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u7761\u7720\u547c\u5438\u6682\u505c\u4e8b\u4ef6\u6807\u6ce8", None)) + self.groupBox_canvas.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u7ed8\u56fe\u533a", None)) + self.groupBox_left.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u7761\u7720\u547c\u5438\u6682\u505c\u4e8b\u4ef6\u6807\u6ce8", None)) + self.pushButton_input_setting.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u5bfc\u5165\u8bbe\u7f6e", None)) + self.pushButton_input.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u5f00\u59cb\u5bfc\u5165", None)) + self.checkBox.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4fee\u6539\u540e\u81ea\u52a8\u4fdd\u5b58", None)) + self.pushButton_best_fit.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u81ea\u9002\u5e94\u5e45\u503c\uff08X\uff09", None)) + self.label_4.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u663e\u793a\u7a97\u53e3\u957f\u5ea6/\u79d2\uff1a", None)) + self.comboBox_window_signal_length.setItemText(0, QCoreApplication.translate("MainWindow_SA_label", u"30", None)) + self.comboBox_window_signal_length.setItemText(1, QCoreApplication.translate("MainWindow_SA_label", u"60", None)) + self.comboBox_window_signal_length.setItemText(2, QCoreApplication.translate("MainWindow_SA_label", u"120", None)) + self.comboBox_window_signal_length.setItemText(3, QCoreApplication.translate("MainWindow_SA_label", u"300", None)) + self.comboBox_window_signal_length.setItemText(4, QCoreApplication.translate("MainWindow_SA_label", u"600", None)) + + self.groupBox_label.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u6807\u7b7e\u8bb0\u5f55", None)) + self.label_2.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u786e\u8ba4\u540e\u7684\u6807\u7b7e", None)) + self.lineEdit_filter_label_origin.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label", u"\u7b5b\u9009\u5668", None)) + self.lineEdit_filter_label_revised.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label", u"\u7b5b\u9009\u5668", None)) + self.label.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u539f\u6570\u636e\u7684\u6807\u7b7e", None)) + self.pushButton_save.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u624b\u52a8\u4fdd\u5b58", None)) + self.groupBox_4.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u65e5\u5fd7", None)) + self.groupBox_right.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u7761\u7720\u547c\u5438\u6682\u505c\u4e8b\u4ef6\u6807\u6ce8", None)) + self.groupBox_examineBySecond.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u8df3\u8f6c", None)) + self.label_3.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u8d77\u59cb\u79d2\u6570\uff1a", None)) + self.pushButton_jump_to.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u8df3\u8f6c", None)) + self.pushButton_next_half.setText(QCoreApplication.translate("MainWindow_SA_label", u"+\u534a\u7a97(G)", None)) + self.pushButton_previous10s.setText(QCoreApplication.translate("MainWindow_SA_label", u"-10s(Q)", None)) + self.pushButton_previous_half.setText(QCoreApplication.translate("MainWindow_SA_label", u"-\u534a\u7a97(F)", None)) + self.pushButton_next30s.setText(QCoreApplication.translate("MainWindow_SA_label", u"+30s(D)", None)) + self.pushButton_previous30s.setText(QCoreApplication.translate("MainWindow_SA_label", u"-30s(A)", None)) + self.pushButton_next10s.setText(QCoreApplication.translate("MainWindow_SA_label", u"+10s(E)", None)) + self.groupBox_label_operation.setTitle(QCoreApplication.translate("MainWindow_SA_label", u"\u6807\u6ce8\u64cd\u4f5c", None)) + self.label_5.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8b\u4ef6\u7f16\u53f7\uff1a", None)) + self.label_BCG_Index.setText("") + self.label_BCG_Info.setText("") + self.label_11.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8b\u4ef6\u7c7b\u578b", None)) + self.radioButton_OSA.setText(QCoreApplication.translate("MainWindow_SA_label", u"OSA(1)", None)) + self.radioButton_CSA.setText(QCoreApplication.translate("MainWindow_SA_label", u"CSA(2)", None)) + self.radioButton_MSA.setText(QCoreApplication.translate("MainWindow_SA_label", u"MSA(3)", None)) + self.radioButton_HPY.setText(QCoreApplication.translate("MainWindow_SA_label", u"HPY(4)", None)) + self.label_12.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u6807\u7b7e\u7c7b\u578b", None)) + self.radioButton_1_class.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e00\u7c7b(U)", None)) + self.radioButton_2_class.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b(I)", None)) + self.radioButton_3_class.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u5220\u9664(O)", None)) + self.label_6.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u5907\u6ce8\uff1a", None)) + self.label_7.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u5feb\u901f\u5907\u6ce8\u8f93\u5165", None)) + self.pushButton_quick_remark_input_durationNoEnough.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b-\u65f6\u957f\u4e0d\u8db3", None)) + self.pushButton_quick_remark_input_maybeWrongLabeled.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b-\u7591\u4f3c\u533b\u751f\u8bef\u6807", None)) + self.pushButton_quick_remark_input_noNormalRespBetweenArtifact.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b-\u4f53\u52a8\u95f4\u65e0\u6b63\u5e38\u547c\u5438", None)) + self.pushButton_quick_remark_input_maybeDesaturation.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e00\u7c7b-\u5f62\u4f3c\u6f6e\u5f0f\u547c\u5438", None)) + self.pushButton_quick_remark_input_littleChange.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b-\u8d77\u4f0f\u53d8\u5316\u4e0d\u5927", None)) + self.pushButton_quick_remark_input_waitingForTalk.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u5f85\u8ba8\u8bba(N)", None)) + self.pushButton_quick_remark_input_lowSignalNoiseRatio.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b-\u4fe1\u566a\u6bd4\u4f4e", None)) + self.pushButton_quick_remark_input_changeOnMiddle.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e8c\u7c7b-\u4e2d\u95f4\u8d77\u4f0f", None)) + self.label_8.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4fee\u6b63\u540e\u8d77\u59cb\u65f6\u95f4(s)", None)) + self.label_9.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4fee\u6b63\u540e\u7ec8\u6b62\u65f6\u95f4(s)", None)) + self.pushButton_prev.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e0a\u4e00\u4e2a\u4e8b\u4ef6", None)) + self.pushButton_next.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u4e0b\u4e00\u4e2a\u4e8b\u4ef6", None)) + self.pushButton_confirmLabel.setText(QCoreApplication.translate("MainWindow_SA_label", u"\u786e\u5b9a\u6253\u6807\u53c2\u6570(S)", None)) + # retranslateUi + diff --git a/ui/MainWindow/MainWindow_SA_label_v2.ui b/ui/MainWindow/MainWindow_SA_label_v2.ui new file mode 100644 index 0000000..bc14785 --- /dev/null +++ b/ui/MainWindow/MainWindow_SA_label_v2.ui @@ -0,0 +1,1099 @@ + + + MainWindow_SA_label + + + + 0 + 0 + 1921 + 1080 + + + + 睡眠呼吸暂停事件标注 + + + + + + + + 10 + + + + 绘图区 + + + + + + + + + + + + + 10 + + + + 睡眠呼吸暂停事件标注 + + + + + + + + + 0 + 0 + + + + + 12 + + + + 导入设置 + + + + + + + + 0 + 0 + + + + + 12 + + + + 开始导入 + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 12 + + + + 修改后自动保存 + + + + + + + + 12 + + + + 自适应幅值(X) + + + false + + + + + + + + + + 0 + 0 + + + + 显示窗口长度/秒: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + true + + + QComboBox::InsertPolicy::NoInsert + + + + 30 + + + + + 60 + + + + + 120 + + + + + 300 + + + + + 600 + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Expanding + + + + 40 + 20 + + + + + + + + + + 标签记录 + + + + + + + 12 + + + + 确认后的标签 + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 12 + + + + 筛选器 + + + + + + + + 12 + + + + 筛选器 + + + + + + + + 12 + + + + 原数据的标签 + + + + + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + + + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + + + + + + + + + 12 + + + + 重置标签 + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 12 + + + + 手动保存 + + + + + + + 日志 + + + + + + + + + + + + + + + + 10 + + + + 睡眠呼吸暂停事件标注 + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + 跳转 + + + + + + + + 起始秒数: + + + + + + + + + + 跳转 + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 12 + + + + +半窗(G) + + + 15 + + + + + + + + 12 + + + + -10s(Q) + + + -10 + + + + + + + + 12 + + + + -半窗(F) + + + -15 + + + + + + + + 12 + + + + +30s(D) + + + 30 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 12 + + + + -30s(A) + + + -30 + + + + + + + + 12 + + + + +10s(E) + + + 10 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 标注操作 + + + + + + + + + 0 + 0 + + + + + 12 + + + + 事件编号: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + + 12 + + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + 12 + + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + 12 + + + + 事件类型 + + + + + + + + 12 + + + + OSA(7) + + + true + + + + + + + + 12 + + + + CSA(8) + + + + + + + + 12 + + + + MSA(9) + + + + + + + + 12 + + + + HPY(0) + + + + + + + + + + + + 12 + + + + 标签类型 + + + + + + + + 12 + + + + 一类(1) + + + true + + + + + + + + 12 + + + + 二类(2) + + + + + + + + 12 + + + + 删除(3) + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 12 + + + + 备注: + + + + + + + + 12 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 12 + + + + 快速备注输入 + + + + + + + + + + 0 + 0 + + + + + 12 + + + + 二类-时长不足 + + + + + + + + 0 + 0 + + + + + 12 + + + + 二类-疑似医生误标 + + + + + + + + 0 + 0 + + + + + 12 + + + + 二类-体动间无正常呼吸 + + + + + + + + 0 + 0 + + + + + 12 + + + + 一类-形似潮式呼吸 + + + + + + + + 0 + 0 + + + + + 12 + + + + 二类-起伏变化不大 + + + + + + + + 0 + 0 + + + + + 12 + + + + 待讨论(N) + + + + + + + + 0 + 0 + + + + + 12 + + + + 二类-信噪比低 + + + + + + + + 0 + 0 + + + + + 12 + + + + 二类-中间起伏 + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 12 + + + + 修正后起始时间(s) + + + + + + + + 12 + + + + 修正后终止时间(s) + + + + + + + + 12 + + + + QAbstractSpinBox::ButtonSymbols::NoButtons + + + 100000 + + + + + + + + 12 + + + + QAbstractSpinBox::ButtonSymbols::NoButtons + + + 100000 + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + 0 + + + + + 12 + + + + 确定打标参数(S) + + + + + + + + 0 + 0 + + + + + 12 + + + + 下一个事件 + + + + + + + + 0 + 0 + + + + + 12 + + + + 上一个事件 + + + + + + + + + + + + + + + + + + diff --git a/ui/setting/SA_label_input_setting_v2.py b/ui/setting/SA_label_input_setting_v2.py new file mode 100644 index 0000000..0f428ce --- /dev/null +++ b/ui/setting/SA_label_input_setting_v2.py @@ -0,0 +1,361 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'SA_label_input_setting_v2.ui' +## +## Created by: Qt User Interface Compiler version 6.7.0 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox, + QHBoxLayout, QLabel, QMainWindow, QPlainTextEdit, + QPushButton, QSizePolicy, QSpinBox, QStatusBar, + QVBoxLayout, QWidget) + +class Ui_MainWindow_SA_label_input_setting(object): + def setupUi(self, MainWindow_SA_label_input_setting): + if not MainWindow_SA_label_input_setting.objectName(): + MainWindow_SA_label_input_setting.setObjectName(u"MainWindow_SA_label_input_setting") + MainWindow_SA_label_input_setting.resize(848, 859) + self.centralwidget = QWidget(MainWindow_SA_label_input_setting) + self.centralwidget.setObjectName(u"centralwidget") + self.gridLayout = QGridLayout(self.centralwidget) + self.gridLayout.setObjectName(u"gridLayout") + self.pushButton_cancel = QPushButton(self.centralwidget) + self.pushButton_cancel.setObjectName(u"pushButton_cancel") + font = QFont() + font.setPointSize(12) + self.pushButton_cancel.setFont(font) + + self.gridLayout.addWidget(self.pushButton_cancel, 1, 3, 1, 1) + + self.groupBox = QGroupBox(self.centralwidget) + self.groupBox.setObjectName(u"groupBox") + font1 = QFont() + font1.setPointSize(10) + self.groupBox.setFont(font1) + self.gridLayout_2 = QGridLayout(self.groupBox) + self.gridLayout_2.setObjectName(u"gridLayout_2") + self.groupBox_file_path_save_2 = QGroupBox(self.groupBox) + self.groupBox_file_path_save_2.setObjectName(u"groupBox_file_path_save_2") + self.verticalLayout_10 = QVBoxLayout(self.groupBox_file_path_save_2) + self.verticalLayout_10.setObjectName(u"verticalLayout_10") + self.plainTextEdit_file_path_save_2 = QPlainTextEdit(self.groupBox_file_path_save_2) + self.plainTextEdit_file_path_save_2.setObjectName(u"plainTextEdit_file_path_save_2") + + self.verticalLayout_10.addWidget(self.plainTextEdit_file_path_save_2) + + + self.gridLayout_2.addWidget(self.groupBox_file_path_save_2, 7, 1, 1, 1) + + self.groupBox_file_path_input_label = QGroupBox(self.groupBox) + self.groupBox_file_path_input_label.setObjectName(u"groupBox_file_path_input_label") + self.verticalLayout_12 = QVBoxLayout(self.groupBox_file_path_input_label) + self.verticalLayout_12.setObjectName(u"verticalLayout_12") + self.plainTextEdit_file_path_input_label = QPlainTextEdit(self.groupBox_file_path_input_label) + self.plainTextEdit_file_path_input_label.setObjectName(u"plainTextEdit_file_path_input_label") + + self.verticalLayout_12.addWidget(self.plainTextEdit_file_path_input_label) + + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_label, 6, 1, 1, 1) + + self.groupBox_file_path_input_signal_FlowP = QGroupBox(self.groupBox) + self.groupBox_file_path_input_signal_FlowP.setObjectName(u"groupBox_file_path_input_signal_FlowP") + self.verticalLayout_8 = QVBoxLayout(self.groupBox_file_path_input_signal_FlowP) + self.verticalLayout_8.setObjectName(u"verticalLayout_8") + self.horizontalLayout_6 = QHBoxLayout() + self.horizontalLayout_6.setObjectName(u"horizontalLayout_6") + self.label_6 = QLabel(self.groupBox_file_path_input_signal_FlowP) + self.label_6.setObjectName(u"label_6") + self.label_6.setFont(font) + + self.horizontalLayout_6.addWidget(self.label_6) + + self.spinBox_input_freq_signal_FlowP = QSpinBox(self.groupBox_file_path_input_signal_FlowP) + self.spinBox_input_freq_signal_FlowP.setObjectName(u"spinBox_input_freq_signal_FlowP") + self.spinBox_input_freq_signal_FlowP.setFont(font) + self.spinBox_input_freq_signal_FlowP.setMinimum(1) + self.spinBox_input_freq_signal_FlowP.setMaximum(1000000) + + self.horizontalLayout_6.addWidget(self.spinBox_input_freq_signal_FlowP) + + + self.verticalLayout_8.addLayout(self.horizontalLayout_6) + + self.plainTextEdit_file_path_input_signal_FlowP = QPlainTextEdit(self.groupBox_file_path_input_signal_FlowP) + self.plainTextEdit_file_path_input_signal_FlowP.setObjectName(u"plainTextEdit_file_path_input_signal_FlowP") + + self.verticalLayout_8.addWidget(self.plainTextEdit_file_path_input_signal_FlowP) + + self.verticalLayout_8.setStretch(1, 2) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_signal_FlowP, 4, 1, 1, 1) + + self.groupBox_file_path_input_signal_Tho = QGroupBox(self.groupBox) + self.groupBox_file_path_input_signal_Tho.setObjectName(u"groupBox_file_path_input_signal_Tho") + self.verticalLayout_3 = QVBoxLayout(self.groupBox_file_path_input_signal_Tho) + self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.horizontalLayout_3 = QHBoxLayout() + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.label_3 = QLabel(self.groupBox_file_path_input_signal_Tho) + self.label_3.setObjectName(u"label_3") + self.label_3.setFont(font) + + self.horizontalLayout_3.addWidget(self.label_3) + + self.spinBox_input_freq_signal_Tho = QSpinBox(self.groupBox_file_path_input_signal_Tho) + self.spinBox_input_freq_signal_Tho.setObjectName(u"spinBox_input_freq_signal_Tho") + self.spinBox_input_freq_signal_Tho.setFont(font) + self.spinBox_input_freq_signal_Tho.setMinimum(1) + self.spinBox_input_freq_signal_Tho.setMaximum(1000000) + + self.horizontalLayout_3.addWidget(self.spinBox_input_freq_signal_Tho) + + + self.verticalLayout_3.addLayout(self.horizontalLayout_3) + + self.plainTextEdit_file_path_input_signal_Tho = QPlainTextEdit(self.groupBox_file_path_input_signal_Tho) + self.plainTextEdit_file_path_input_signal_Tho.setObjectName(u"plainTextEdit_file_path_input_signal_Tho") + + self.verticalLayout_3.addWidget(self.plainTextEdit_file_path_input_signal_Tho) + + self.verticalLayout_3.setStretch(1, 2) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_signal_Tho, 3, 1, 1, 1) + + self.groupBox_file_path_input_signal_FlowT = QGroupBox(self.groupBox) + self.groupBox_file_path_input_signal_FlowT.setObjectName(u"groupBox_file_path_input_signal_FlowT") + self.verticalLayout_7 = QVBoxLayout(self.groupBox_file_path_input_signal_FlowT) + self.verticalLayout_7.setObjectName(u"verticalLayout_7") + self.horizontalLayout_5 = QHBoxLayout() + self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") + self.label_5 = QLabel(self.groupBox_file_path_input_signal_FlowT) + self.label_5.setObjectName(u"label_5") + self.label_5.setFont(font) + + self.horizontalLayout_5.addWidget(self.label_5) + + self.spinBox_input_freq_signal_FlowT = QSpinBox(self.groupBox_file_path_input_signal_FlowT) + self.spinBox_input_freq_signal_FlowT.setObjectName(u"spinBox_input_freq_signal_FlowT") + self.spinBox_input_freq_signal_FlowT.setFont(font) + self.spinBox_input_freq_signal_FlowT.setMinimum(1) + self.spinBox_input_freq_signal_FlowT.setMaximum(1000000) + + self.horizontalLayout_5.addWidget(self.spinBox_input_freq_signal_FlowT) + + + self.verticalLayout_7.addLayout(self.horizontalLayout_5) + + self.plainTextEdit_file_path_input_signal_FlowT = QPlainTextEdit(self.groupBox_file_path_input_signal_FlowT) + self.plainTextEdit_file_path_input_signal_FlowT.setObjectName(u"plainTextEdit_file_path_input_signal_FlowT") + + self.verticalLayout_7.addWidget(self.plainTextEdit_file_path_input_signal_FlowT) + + self.verticalLayout_7.setStretch(1, 2) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_signal_FlowT, 4, 0, 1, 1) + + self.groupBox_file_path_input_artifact = QGroupBox(self.groupBox) + self.groupBox_file_path_input_artifact.setObjectName(u"groupBox_file_path_input_artifact") + self.verticalLayout_11 = QVBoxLayout(self.groupBox_file_path_input_artifact) + self.verticalLayout_11.setObjectName(u"verticalLayout_11") + self.plainTextEdit_file_path_input_artifact = QPlainTextEdit(self.groupBox_file_path_input_artifact) + self.plainTextEdit_file_path_input_artifact.setObjectName(u"plainTextEdit_file_path_input_artifact") + + self.verticalLayout_11.addWidget(self.plainTextEdit_file_path_input_artifact) + + self.verticalLayout_11.setStretch(0, 2) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_artifact, 6, 0, 1, 1) + + self.groupBox_file_path_input_signal_SpO2 = QGroupBox(self.groupBox) + self.groupBox_file_path_input_signal_SpO2.setObjectName(u"groupBox_file_path_input_signal_SpO2") + self.verticalLayout_9 = QVBoxLayout(self.groupBox_file_path_input_signal_SpO2) + self.verticalLayout_9.setObjectName(u"verticalLayout_9") + self.horizontalLayout_7 = QHBoxLayout() + self.horizontalLayout_7.setObjectName(u"horizontalLayout_7") + self.label_7 = QLabel(self.groupBox_file_path_input_signal_SpO2) + self.label_7.setObjectName(u"label_7") + self.label_7.setFont(font) + + self.horizontalLayout_7.addWidget(self.label_7) + + self.spinBox_input_freq_signal_SpO2 = QSpinBox(self.groupBox_file_path_input_signal_SpO2) + self.spinBox_input_freq_signal_SpO2.setObjectName(u"spinBox_input_freq_signal_SpO2") + self.spinBox_input_freq_signal_SpO2.setFont(font) + self.spinBox_input_freq_signal_SpO2.setMinimum(1) + self.spinBox_input_freq_signal_SpO2.setMaximum(1000000) + + self.horizontalLayout_7.addWidget(self.spinBox_input_freq_signal_SpO2) + + + self.verticalLayout_9.addLayout(self.horizontalLayout_7) + + self.plainTextEdit_file_path_input_signal_SpO2 = QPlainTextEdit(self.groupBox_file_path_input_signal_SpO2) + self.plainTextEdit_file_path_input_signal_SpO2.setObjectName(u"plainTextEdit_file_path_input_signal_SpO2") + + self.verticalLayout_9.addWidget(self.plainTextEdit_file_path_input_signal_SpO2) + + self.verticalLayout_9.setStretch(1, 2) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_signal_SpO2, 1, 1, 1, 1) + + self.groupBox_file_path_input_signal_Abd = QGroupBox(self.groupBox) + self.groupBox_file_path_input_signal_Abd.setObjectName(u"groupBox_file_path_input_signal_Abd") + self.verticalLayout_6 = QVBoxLayout(self.groupBox_file_path_input_signal_Abd) + self.verticalLayout_6.setObjectName(u"verticalLayout_6") + self.horizontalLayout_4 = QHBoxLayout() + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") + self.label_4 = QLabel(self.groupBox_file_path_input_signal_Abd) + self.label_4.setObjectName(u"label_4") + self.label_4.setFont(font) + + self.horizontalLayout_4.addWidget(self.label_4) + + self.spinBox_input_freq_signal_Abd = QSpinBox(self.groupBox_file_path_input_signal_Abd) + self.spinBox_input_freq_signal_Abd.setObjectName(u"spinBox_input_freq_signal_Abd") + self.spinBox_input_freq_signal_Abd.setFont(font) + self.spinBox_input_freq_signal_Abd.setMinimum(1) + self.spinBox_input_freq_signal_Abd.setMaximum(1000000) + + self.horizontalLayout_4.addWidget(self.spinBox_input_freq_signal_Abd) + + + self.verticalLayout_6.addLayout(self.horizontalLayout_4) + + self.plainTextEdit_file_path_input_signal_Abd = QPlainTextEdit(self.groupBox_file_path_input_signal_Abd) + self.plainTextEdit_file_path_input_signal_Abd.setObjectName(u"plainTextEdit_file_path_input_signal_Abd") + + self.verticalLayout_6.addWidget(self.plainTextEdit_file_path_input_signal_Abd) + + self.verticalLayout_6.setStretch(1, 2) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_signal_Abd, 3, 0, 1, 1) + + self.groupBox_file_path_save = QGroupBox(self.groupBox) + self.groupBox_file_path_save.setObjectName(u"groupBox_file_path_save") + self.verticalLayout_4 = QVBoxLayout(self.groupBox_file_path_save) + self.verticalLayout_4.setObjectName(u"verticalLayout_4") + self.plainTextEdit_file_path_save = QPlainTextEdit(self.groupBox_file_path_save) + self.plainTextEdit_file_path_save.setObjectName(u"plainTextEdit_file_path_save") + + self.verticalLayout_4.addWidget(self.plainTextEdit_file_path_save) + + + self.gridLayout_2.addWidget(self.groupBox_file_path_save, 7, 0, 1, 1) + + self.groupBox_file_path_input_signal_OrgBCG = QGroupBox(self.groupBox) + self.groupBox_file_path_input_signal_OrgBCG.setObjectName(u"groupBox_file_path_input_signal_OrgBCG") + self.verticalLayout_5 = QVBoxLayout(self.groupBox_file_path_input_signal_OrgBCG) + self.verticalLayout_5.setObjectName(u"verticalLayout_5") + self.horizontalLayout_2 = QHBoxLayout() + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.label_2 = QLabel(self.groupBox_file_path_input_signal_OrgBCG) + self.label_2.setObjectName(u"label_2") + self.label_2.setFont(font) + + self.horizontalLayout_2.addWidget(self.label_2) + + self.spinBox_input_freq_signal_OrgBCG = QSpinBox(self.groupBox_file_path_input_signal_OrgBCG) + self.spinBox_input_freq_signal_OrgBCG.setObjectName(u"spinBox_input_freq_signal_OrgBCG") + self.spinBox_input_freq_signal_OrgBCG.setFont(font) + self.spinBox_input_freq_signal_OrgBCG.setCursor(QCursor(Qt.ArrowCursor)) + self.spinBox_input_freq_signal_OrgBCG.setMinimum(1) + self.spinBox_input_freq_signal_OrgBCG.setMaximum(1000000) + + self.horizontalLayout_2.addWidget(self.spinBox_input_freq_signal_OrgBCG) + + + self.verticalLayout_5.addLayout(self.horizontalLayout_2) + + self.plainTextEdit_file_path_input_signal_OrgBCG = QPlainTextEdit(self.groupBox_file_path_input_signal_OrgBCG) + self.plainTextEdit_file_path_input_signal_OrgBCG.setObjectName(u"plainTextEdit_file_path_input_signal_OrgBCG") + + self.verticalLayout_5.addWidget(self.plainTextEdit_file_path_input_signal_OrgBCG) + + self.verticalLayout_5.setStretch(0, 2) + self.verticalLayout_5.setStretch(1, 3) + + self.gridLayout_2.addWidget(self.groupBox_file_path_input_signal_OrgBCG, 1, 0, 1, 1) + + self.checkBox_auto_find_file = QCheckBox(self.groupBox) + self.checkBox_auto_find_file.setObjectName(u"checkBox_auto_find_file") + + self.gridLayout_2.addWidget(self.checkBox_auto_find_file, 0, 0, 1, 1) + + + self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 4) + + self.pushButton_confirm = QPushButton(self.centralwidget) + self.pushButton_confirm.setObjectName(u"pushButton_confirm") + self.pushButton_confirm.setFont(font) + + self.gridLayout.addWidget(self.pushButton_confirm, 1, 2, 1, 1) + + MainWindow_SA_label_input_setting.setCentralWidget(self.centralwidget) + self.statusbar = QStatusBar(MainWindow_SA_label_input_setting) + self.statusbar.setObjectName(u"statusbar") + MainWindow_SA_label_input_setting.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow_SA_label_input_setting) + + QMetaObject.connectSlotsByName(MainWindow_SA_label_input_setting) + # setupUi + + def retranslateUi(self, MainWindow_SA_label_input_setting): + MainWindow_SA_label_input_setting.setWindowTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u5bfc\u5165\u8bbe\u7f6e", None)) + self.pushButton_cancel.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u53d6\u6d88", None)) + self.groupBox.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.groupBox_file_path_save_2.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u65b0\u589e\u7684\u547c\u5438\u6682\u505c\u6807\u7b7e\u4fdd\u5b58\u8def\u5f84", None)) + self.plainTextEdit_file_path_save_2.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u4fdd\u5b58\u8def\u5f84", None)) + self.groupBox_file_path_input_label.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684\u547c\u5438\u6682\u505c\u6807\u7b7e\u4fdd\u5b58\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_label.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.groupBox_file_path_input_signal_FlowP.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684Flow P\u8def\u5f84", None)) + self.label_6.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None)) + self.plainTextEdit_file_path_input_signal_FlowP.setPlainText("") + self.plainTextEdit_file_path_input_signal_FlowP.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_signal_FlowP.setProperty("signal_type", QCoreApplication.translate("MainWindow_SA_label_input_setting", u"FlowP", None)) + self.groupBox_file_path_input_signal_Tho.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684Effort Tho\u8def\u5f84", None)) + self.label_3.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None)) + self.plainTextEdit_file_path_input_signal_Tho.setPlainText("") + self.plainTextEdit_file_path_input_signal_Tho.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_signal_Tho.setProperty("signal_type", QCoreApplication.translate("MainWindow_SA_label_input_setting", u"Tho", None)) + self.groupBox_file_path_input_signal_FlowT.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684Flow T\u8def\u5f84", None)) + self.label_5.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None)) + self.plainTextEdit_file_path_input_signal_FlowT.setPlainText("") + self.plainTextEdit_file_path_input_signal_FlowT.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_signal_FlowT.setProperty("signal_type", QCoreApplication.translate("MainWindow_SA_label_input_setting", u"FlowT", None)) + self.groupBox_file_path_input_artifact.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u4f53\u52a8Artifact_a\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_artifact.setPlainText("") + self.plainTextEdit_file_path_input_artifact.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.groupBox_file_path_input_signal_SpO2.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684SpO2\u8def\u5f84", None)) + self.label_7.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None)) + self.plainTextEdit_file_path_input_signal_SpO2.setPlainText("") + self.plainTextEdit_file_path_input_signal_SpO2.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_signal_SpO2.setProperty("signal_type", QCoreApplication.translate("MainWindow_SA_label_input_setting", u"SpO2", None)) + self.groupBox_file_path_input_signal_Abd.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684Effort Abd\u8def\u5f84", None)) + self.label_4.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None)) + self.plainTextEdit_file_path_input_signal_Abd.setPlainText("") + self.plainTextEdit_file_path_input_signal_Abd.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_signal_Abd.setProperty("signal_type", QCoreApplication.translate("MainWindow_SA_label_input_setting", u"Abd", None)) + self.groupBox_file_path_save.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u4fee\u6b63\u540e\u7684\u547c\u5438\u6682\u505c\u6807\u7b7e\u4fdd\u5b58\u8def\u5f84", None)) + self.plainTextEdit_file_path_save.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u4fdd\u5b58\u8def\u5f84", None)) + self.groupBox_file_path_input_signal_OrgBCG.setTitle(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u540c\u6b65\u540e\u7684OrgBCG\u8def\u5f84", None)) + self.label_2.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None)) + self.spinBox_input_freq_signal_OrgBCG.setPrefix("") + self.plainTextEdit_file_path_input_signal_OrgBCG.setPlainText("") + self.plainTextEdit_file_path_input_signal_OrgBCG.setPlaceholderText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None)) + self.plainTextEdit_file_path_input_signal_OrgBCG.setProperty("signal_type", QCoreApplication.translate("MainWindow_SA_label_input_setting", u"OrgBCG", None)) + self.checkBox_auto_find_file.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u5ffd\u7565\u91c7\u6837\u7387 \u81ea\u52a8\u5339\u914d\u6587\u4ef6", None)) + self.pushButton_confirm.setText(QCoreApplication.translate("MainWindow_SA_label_input_setting", u"\u786e\u5b9a", None)) + # retranslateUi + diff --git a/ui/setting/SA_label_input_setting_v2.ui b/ui/setting/SA_label_input_setting_v2.ui new file mode 100644 index 0000000..cee78e8 --- /dev/null +++ b/ui/setting/SA_label_input_setting_v2.ui @@ -0,0 +1,456 @@ + + + MainWindow_SA_label_input_setting + + + + 0 + 0 + 848 + 859 + + + + 导入设置 + + + + + + + + 12 + + + + 取消 + + + + + + + + 10 + + + + 文件路径 + + + + + + false + + + 新增的呼吸暂停标签保存路径 + + + + + + 保存路径 + + + + + + + + + + 体动Artifact_a路径 + + + + + + + + + 文件路径 + + + + + + + + + + 同步后的Flow T路径 + + + + + + + + + 12 + + + + 采样率(Hz): + + + + + + + + 12 + + + + 1 + + + 1000000 + + + + + + + + + + + + 文件路径 + + + FlowT + + + + + + + + + + 修正后的呼吸暂停标签保存路径 + + + + + + 保存路径 + + + + + + + + + + 同步后的SpO2路径 + + + + + + + + + 12 + + + + 采样率(Hz): + + + + + + + + 12 + + + + 1 + + + 1000000 + + + + + + + + + + + + 文件路径 + + + SpO2 + + + + + + + + + + 同步后的Effort Tho路径 + + + + + + + + + 12 + + + + 采样率(Hz): + + + + + + + + 12 + + + + 1 + + + 1000000 + + + + + + + + + + + + 文件路径 + + + Tho + + + + + + + + + + 同步后的呼吸暂停标签保存路径 + + + + + + 文件路径 + + + + + + + + + + 同步后的Effort Abd路径 + + + + + + + + + 12 + + + + 采样率(Hz): + + + + + + + + 12 + + + + 1 + + + 1000000 + + + + + + + + + + + + 文件路径 + + + Abd + + + + + + + + + + 同步后的OrgBCG路径 + + + + + + + + + 12 + + + + 采样率(Hz): + + + + + + + + 12 + + + + ArrowCursor + + + + + + 1 + + + 1000000 + + + + + + + + + + + + 文件路径 + + + OrgBCG + + + + + + + + + + 同步后的Flow P路径 + + + + + + + + + 12 + + + + 采样率(Hz): + + + + + + + + 12 + + + + 1 + + + 1000000 + + + + + + + + + + + + 文件路径 + + + FlowP + + + + + + + + + + + + + + 12 + + + + 确定 + + + + + + + + + +