From aad3dfba49a5c5237d82c5b69643e08e6b4dc953 Mon Sep 17 00:00:00 2001 From: Yorusora Date: Fri, 9 May 2025 17:21:55 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=AE=8C=E5=96=84=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E8=A7=84=E8=8C=83=202=E3=80=81=E5=AE=8C=E6=88=90<=E5=86=97?= =?UTF-8?q?=E4=BD=99=E6=95=B0=E6=8D=AE=E5=88=87=E5=89=B2=E5=92=8C=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=98=A0=E5=B0=84>=E7=9A=84=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- func/Module_cut_PSG.py | 307 ++++++++++++++++++++++++++++ func/Module_detect_Jpeak.py | 35 +--- func/Module_detect_Rpeak.py | 31 +-- func/Module_label_check.py | 60 +----- func/Module_mainwindow.py | 19 +- func/Module_precisely_align.py | 100 ++------- func/Module_preprocess.py | 26 +-- func/utils/ConfigParams.py | 52 ++++- func/utils/Constants.py | 26 +++ func/utils/PublicFunc.py | 5 - ui/MainWindow/MainWindow_cut_PSG.py | 47 ++++- ui/MainWindow/MainWindow_cut_PSG.ui | 47 ++++- ui/MainWindow/MainWindow_menu.py | 18 +- ui/MainWindow/MainWindow_menu.ui | 54 +++-- 数据结构化输入和输出命名规范.md | 12 +- 15 files changed, 567 insertions(+), 272 deletions(-) create mode 100644 func/Module_cut_PSG.py diff --git a/func/Module_cut_PSG.py b/func/Module_cut_PSG.py new file mode 100644 index 0000000..f30d40a --- /dev/null +++ b/func/Module_cut_PSG.py @@ -0,0 +1,307 @@ +from ast import literal_eval +from gc import collect +from math import floor +from pathlib import Path + +from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication +from numpy import array +from overrides import overrides +from pandas import read_csv, DataFrame +from yaml import dump, load, FullLoader + +from func.utils.PublicFunc import PublicFunc +from func.utils.Constants import Constants, ConfigParams + +from ui.MainWindow.MainWindow_cut_PSG import Ui_MainWindow_cut_PSG + + +Config = { + +} + +ButtonState = { + "Default": { + "pushButton_execute": True + }, + "Current": { + "pushButton_execute": True + } +} + + +class MainWindow_cut_PSG(QMainWindow): + + def __init__(self): + super(MainWindow_cut_PSG, self).__init__() + self.ui = Ui_MainWindow_cut_PSG() + self.ui.setupUi(self) + + self.root_path = None + self.sampID = None + + self.__read_config__() + + self.data = None + + # 初始化进度条 + self.ui.progressbar.setStyleSheet(Constants.PROGRESSBAR_STYLE) + self.progressbar = self.ui.progressbar + + self.msgBox = QMessageBox() + self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE) + + @overrides + def show(self, root_path, sampID): + super().show() + self.root_path = root_path + self.sampID = sampID + + PublicFunc.__resetAllButton__(self, ButtonState) + + Config.update({ + "Path": { + "InputFolder": str(Path(self.root_path) / ConfigParams.PUBLIC_PATH_PSG_TEXT / Path(str(self.sampID))), + "SaveFolder": str(Path(self.root_path) / ConfigParams.PUBLIC_PATH_PSG_ALIGNED / Path(str(self.sampID))), + "InputAlignInfo": str(Path(self.root_path) / ConfigParams.PUBLIC_PATH_PSG_ALIGNED / Path(str(self.sampID)) / (ConfigParams.CUT_PSG_SAVE_ECG_ALIGNINFO_FILENAME + ConfigParams.ENDSWITH_TXT)) + } + }) + + self.ui.plainTextEdit_channel.setPlainText(', '.join(Config["ChannelInput"].keys())) + self.ui.plainTextEdit_label.setPlainText(', '.join(Config["LabelInput"].keys())) + + self.ui.pushButton_execute.clicked.connect(self.__slot_btn_execute__) + + @overrides + def closeEvent(self, event): + PublicFunc.__disableAllButton__(self, ButtonState) + + PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) + QApplication.processEvents() + + # 释放资源 + del self.data + self.deleteLater() + collect() + event.accept() + + @staticmethod + def __reset__(): + ButtonState["Current"].update(ButtonState["Default"].copy()) + + def __read_config__(self): + if not Path(ConfigParams.CUT_PSG_CONFIG_FILE_PATH).exists(): + with open(ConfigParams.CUT_PSG_CONFIG_FILE_PATH, "w") as f: + dump(ConfigParams.CUT_PSG_CONFIG_NEW_CONTENT, f) + + with open(ConfigParams.CUT_PSG_CONFIG_FILE_PATH, "r") as f: + file_config = load(f.read(), Loader=FullLoader) + Config.update(file_config) + + # 数据回显 + self.ui.spinBox_ECGFreq.setValue(Config["ECGFreq"]) + + def __slot_btn_execute__(self): + PublicFunc.__disableAllButton__(self, ButtonState) + + self.data = Data() + Config["ECGFreq"] = self.ui.spinBox_ECGFreq.value() + + # 检查文件是否存在并获取其数据采样率 + PublicFunc.progressbar_update(self, 1, 5, Constants.CUT_PSG_GETTING_FILE_AND_FREQ, 0) + status, info = self.data.get_file_and_freq() + if not status: + PublicFunc.text_output(self.ui, "(1/5)" + info, Constants.TIPS_TYPE_ERROR) + PublicFunc.msgbox_output(self, info, Constants.MSGBOX_TYPE_ERROR) + PublicFunc.finish_operation(self, ButtonState) + return + else: + PublicFunc.text_output(self.ui, "(1/5)" + info, Constants.TIPS_TYPE_INFO) + + PublicFunc.finish_operation(self, ButtonState) + + # 导入数据 + PublicFunc.progressbar_update(self, 2, 5, Constants.INPUTTING_DATA, 10) + status, info = self.data.open_file() + if not status: + PublicFunc.text_output(self.ui, "(2/5)" + info, Constants.TIPS_TYPE_ERROR) + PublicFunc.msgbox_output(self, info, Constants.MSGBOX_TYPE_ERROR) + PublicFunc.finish_operation(self, ButtonState) + return + else: + PublicFunc.text_output(self.ui, "(2/5)" + info, Constants.TIPS_TYPE_INFO) + + PublicFunc.finish_operation(self, ButtonState) + + # 切割数据 + PublicFunc.progressbar_update(self, 3, 5, Constants.CUT_PSG_CUTTING_DATA, 40) + status, info = self.data.cut_data() + if not status: + PublicFunc.text_output(self.ui, "(3/5)" + info, Constants.TIPS_TYPE_ERROR) + PublicFunc.msgbox_output(self, info, Constants.MSGBOX_TYPE_ERROR) + PublicFunc.finish_operation(self, ButtonState) + return + else: + PublicFunc.text_output(self.ui, "(3/5)" + info, Constants.TIPS_TYPE_INFO) + + PublicFunc.finish_operation(self, ButtonState) + + # 标签映射 + PublicFunc.progressbar_update(self, 4, 5, Constants.CUT_PSG_ALIGNING_LABEL, 60) + status, info = self.data.align_label() + if not status: + PublicFunc.text_output(self.ui, "(4/5)" + info, Constants.TIPS_TYPE_ERROR) + PublicFunc.msgbox_output(self, info, Constants.MSGBOX_TYPE_ERROR) + PublicFunc.finish_operation(self, ButtonState) + return + else: + PublicFunc.text_output(self.ui, "(4/5)" + info, Constants.TIPS_TYPE_INFO) + + PublicFunc.finish_operation(self, ButtonState) + + # 切割数据 + PublicFunc.progressbar_update(self, 5, 5, Constants.SAVING_DATA, 70) + status, info = self.data.save() + if not status: + PublicFunc.text_output(self.ui, "(5/5)" + info, Constants.TIPS_TYPE_ERROR) + PublicFunc.msgbox_output(self, info, Constants.MSGBOX_TYPE_ERROR) + PublicFunc.finish_operation(self, ButtonState) + return + else: + PublicFunc.text_output(self.ui, "(5/5)" + info, Constants.TIPS_TYPE_INFO) + PublicFunc.msgbox_output(self, info, Constants.TIPS_TYPE_INFO) + + PublicFunc.finish_operation(self, ButtonState) + + +class Data: + + def __init__(self): + self.alignInfo = None + + self.raw = {key: array([]) for key in Config["ChannelInput"]} + self.freq = {key: 0 for key in Config["ChannelInput"]} + + self.SALabel = None + self.startTime = None + + def get_file_and_freq(self): + try: + for file_path in Path(Config["Path"]["InputFolder"]).glob('*'): + if file_path.is_file(): + file_stem = Path(file_path).stem + for key, prefix in Config["ChannelInput"].items(): + if file_stem.startswith(prefix): + freq_str = file_stem.rsplit('_', 1)[1] + try: + freq = int(freq_str) + self.freq[key] = freq + except ValueError: + return False, Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Filename_Format_not_Correct"] + for value in self.freq.values(): + if value == 0: + return False, Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Filename_Format_not_Correct"] + if not any((Config["LabelInput"]["SA Label"] + Config["EndWith"]["SA Label"]) in str(file) for file in Path(Config["Path"]["InputFolder"]).glob('*')): + return False, Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE + Constants.CUT_PSG_FAILURE_REASON["File_not_Exist"] + if not any((Config["StartTime"] + Config["EndWith"]["StartTime"]) in str(file) for file in Path(Config["Path"]["InputFolder"]).glob('*')): + return False, Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE + Constants.CUT_PSG_FAILURE_REASON["File_not_Exist"] + if not Path(Config["Path"]["InputAlignInfo"]).exists(): + return False, Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE + Constants.CUT_PSG_FAILURE_REASON["File_not_Exist"] + except Exception: + return False, Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE + Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE["Get_File_and_Freq_Excepetion"] + + return True, Constants.CUT_PSG_GET_FILE_AND_FREQ_FINISHED + + def open_file(self): + try: + for key in Config["ChannelInput"].keys(): + self.raw[key] = read_csv(Path(Config["Path"]["InputFolder"]) / Path((Config["ChannelInput"][key] + str(self.freq[key]) + Config["EndWith"][key])), + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.SALabel = read_csv(Path(Config["Path"]["InputFolder"]) / Path((Config["LabelInput"]["SA Label"] + Config["EndWith"]["SA Label"])), + encoding=ConfigParams.GBK_ENCODING) + self.startTime = read_csv(Path(Config["Path"]["InputFolder"]) / Path((Config["StartTime"] + Config["EndWith"]["StartTime"])), + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.alignInfo = read_csv(Path(Config["Path"]["InputAlignInfo"]), + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.alignInfo = literal_eval(self.alignInfo[0]) + except Exception: + return False, Constants.INPUT_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Read_Data_Exception"] + + return True, Constants.INPUT_FINISHED + + def cut_data(self): + try: + for key, raw in self.raw.items(): + # 转换切割点 + ECG_freq = Config["ECGFreq"] + raw_freq = self.freq[key] + duration_second = ((self.alignInfo["cut_index"]["back_ECG"] - self.alignInfo["cut_index"]["front_ECG"]) // 1000) + 1 + start_index_cut = floor(self.alignInfo["cut_index"]["front_ECG"] * (raw_freq / ECG_freq)) + end_index_cut = start_index_cut + (duration_second * raw_freq) + + try: + # 切割信号 + self.raw[key] = self.raw[key][start_index_cut:end_index_cut] + except Exception: + return False, Constants.CUT_PSG_CUT_DATA_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Cut_Data_Length_not_Correct"] + except Exception: + return False, Constants.CUT_PSG_CUT_DATA_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Cut_Data_Exception"] + + return True, Constants.CUT_PSG_CUT_DATA_FINISHED + + def align_label(self): + try: + # 读取SA标签 + self.SALabel = self.SALabel.loc[:, ~self.SALabel.columns.str.contains("^Unnamed")] + self.SALabel = self.SALabel[self.SALabel["Event type"].isin(ConfigParams.CUT_PSG_SALABEL_EVENT)] + self.SALabel["Duration"] = self.SALabel["Duration"].astype(str) + self.SALabel["Duration"] = self.SALabel["Duration"].str.replace(r' \(.*?\)', '', regex=True) + except Exception: + return False, Constants.CUT_PSG_ALIGN_LABEL_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Align_Label_SALabel_Format_not_Correct"] + + try: + # 获取记录开始时间 + start_time = str(self.startTime[0]).split(" ")[1] + start_time = Data.get_time_to_seconds(start_time) + + # 计算起始时间秒数和终止时间秒数 + self.SALabel["Start"] = (self.SALabel["Time"].apply(self.get_time_to_seconds) - start_time).apply( + lambda x: x + 24 * 3600 if x < 0 else x).astype(int) + self.SALabel["End"] = self.SALabel["Start"] + self.SALabel["Duration"].astype(float).round(0).astype(int) + + # 标签映射 + ECG_length = self.alignInfo["cut_index"]["back_ECG"] - self.alignInfo["cut_index"]["front_ECG"] + self.SALabel["Start"] = self.SALabel["Start"] - round((self.alignInfo["cut_index"]["front_ECG"] / 1000)) + self.SALabel["End"] = self.SALabel["End"] - round((self.alignInfo["cut_index"]["front_ECG"] / 1000)) + self.SALabel = self.SALabel[self.SALabel["End"] >= 0] + self.SALabel.loc[self.SALabel["Start"] < 0, "Start"] = 0 + self.SALabel = self.SALabel[self.SALabel["Start"] < ECG_length] + self.SALabel.loc[self.SALabel["End"] >= ECG_length, "End"] = ECG_length - 1 + except Exception: + return False, Constants.CUT_PSG_ALIGN_LABEL_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Align_Label_Exception"] + + return True, Constants.CUT_PSG_ALIGN_LABEL_FINISHED + + def save(self): + for raw in self.raw.values(): + if len(raw) == 0: + return False, Constants.SAVING_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Save_Data_not_Exist"] + + try: + for key, raw in self.raw.items(): + DataFrame(raw.reshape(-1)).to_csv(Path(Config["Path"]["SaveFolder"]) / Path((Config["ChannelSave"][key] + str(self.freq[key]) + Config["EndWith"][key])), + index=False, header=False) + self.SALabel.to_csv(Path(Config["Path"]["SaveFolder"]) / Path((Config["LabelSave"]["SA Label"] + Config["EndWith"]["SA Label"])), + index=False, + encoding="gbk") + except Exception: + return False, Constants.SAVING_FAILURE + Constants.CUT_PSG_FAILURE_REASON["Save_Exception"] + + return True, Constants.SAVING_FINISHED + + @staticmethod + def get_time_to_seconds(time_str): + h, m, s = map(int, time_str.split(":")) + return h * 3600 + m * 60 + s \ No newline at end of file diff --git a/func/Module_detect_Jpeak.py b/func/Module_detect_Jpeak.py index 91df024..c49358a 100644 --- a/func/Module_detect_Jpeak.py +++ b/func/Module_detect_Jpeak.py @@ -41,7 +41,6 @@ ButtonState = { class SettingWindow(QMainWindow): def __init__(self, root_path, sampID): - super(SettingWindow, self).__init__() self.ui = Ui_MainWindow_detect_Jpeak_input_setting() self.ui.setupUi(self) @@ -58,7 +57,6 @@ class SettingWindow(QMainWindow): self.ui.pushButton_cancel.clicked.connect(self.close) def __read_config__(self): - if not Path(ConfigParams.DETECT_JPEAK_CONFIG_FILE_PATH).exists(): with open(ConfigParams.DETECT_JPEAK_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.DETECT_JPEAK_CONFIG_NEW_CONTENT, f) @@ -87,7 +85,6 @@ class SettingWindow(QMainWindow): self.ui.plainTextEdit_file_path_save.setPlainText(Config["Path"]["Save"]) def __write_config__(self): - # 从界面写入配置 Config["InputConfig"]["Freq"] = self.ui.spinBox_input_freq.value() Config["Path"]["Input"] = self.ui.plainTextEdit_file_path_input.toPlainText() @@ -104,11 +101,9 @@ class SettingWindow(QMainWindow): self.close() def __rollback_config__(self): - self.__read_config__() def __update_ui__(self): - self.ui.plainTextEdit_file_path_input.setPlainText( str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_TEXT / @@ -117,10 +112,10 @@ class SettingWindow(QMainWindow): str(self.ui.spinBox_input_freq.value()) + ConfigParams.ENDSWITH_TXT)))) + class MainWindow_detect_Jpeak(QMainWindow): def __init__(self): - super(MainWindow_detect_Jpeak, self).__init__() self.ui = Ui_MainWindow_detect_Jpeak() self.ui.setupUi(self) @@ -149,7 +144,6 @@ class MainWindow_detect_Jpeak(QMainWindow): @overrides def show(self, root_path, sampID): - super().show() self.root_path = root_path self.sampID = sampID @@ -196,7 +190,6 @@ class MainWindow_detect_Jpeak(QMainWindow): @overrides def closeEvent(self, event): - PublicFunc.__disableAllButton__(self, ButtonState) PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) @@ -217,12 +210,10 @@ class MainWindow_detect_Jpeak(QMainWindow): @staticmethod def __reset__(): - ButtonState["Current"].update(ButtonState["Default"].copy()) ButtonState["Current"]["pushButton_view"] = True def __plot__(self): - # 清空画框 self.reset_axes() @@ -249,7 +240,6 @@ class MainWindow_detect_Jpeak(QMainWindow): return status, info def __update_config__(self): - Config["Filter"]["BandPassLow"] = self.ui.doubleSpinBox_bandPassLow.value() Config["Filter"]["BandPassHigh"] = self.ui.doubleSpinBox_bandPassHigh.value() Config["PeaksValue"] = self.ui.spinBox_peaksValue.value() @@ -260,7 +250,6 @@ class MainWindow_detect_Jpeak(QMainWindow): Config["DetectMethod"] = self.ui.comboBox_model.currentText() def __slot_btn_input__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 清空画框 @@ -300,7 +289,6 @@ class MainWindow_detect_Jpeak(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_view__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 数据预处理 @@ -348,7 +336,6 @@ class MainWindow_detect_Jpeak(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_save__(self): - reply = QMessageBox.question(self, Constants.QUESTION_TITLE, Constants.QUESTION_CONTENT + Config["Path"]["Save"], QMessageBox.Yes | QMessageBox.No, @@ -382,13 +369,11 @@ class MainWindow_detect_Jpeak(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def reset_axes(self): - self.ax0.clear() self.ax0.grid(True) self.ax0.xaxis.set_major_formatter(ConfigParams.FORMATTER) def update_ui_comboBox_model(self, model_list): - self.ui.comboBox_model.clear() self.ui.comboBox_model.addItems(model_list) @@ -396,31 +381,25 @@ class MainWindow_detect_Jpeak(QMainWindow): class Data: def __init__(self): - - self.file_path_input = Config["Path"]["Input"] - self.file_path_save = Config["Path"]["Save"] - self.raw_data = None self.processed_data = None self.peak = None self.interval = None def open_file(self): - if not Path(Config["Path"]["Input"]).exists(): return False, Constants.INPUT_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Data_Path_Not_Exist"] try: - self.raw_data = read_csv(self.file_path_input, - encoding=ConfigParams.UTF8_ENCODING, - header=None).to_numpy().reshape(-1) + self.raw_data = read_csv(Config["Path"]["Input"], + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) except Exception: return False, Constants.INPUT_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Read_Data_Exception"] return True, Constants.INPUT_FINISHED def preprocess(self): - if self.raw_data is None: return False, Constants.DETECT_JPEAK_PROCESS_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Raw_Data_Not_Exist"] @@ -436,7 +415,6 @@ class Data: return True, Constants.DETECT_JPEAK_PROCESS_FINISHED def predict_Jpeak(self, model): - if not (Path(model.model_folder_path) / Path(model.selected_model)).exists(): return False, Constants.DETECT_JPEAK_PREDICT_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Model_File_Not_Exist"] @@ -458,12 +436,11 @@ class Data: return True, Constants.DETECT_JPEAK_PREDICT_FINISHED def save(self, chunk): - if self.peak is None: return False, Constants.SAVING_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Peak_Not_Exist"] try: - chunk.to_csv(self.file_path_save, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save"], mode='a', index=False, header=False) except Exception: return False, Constants.SAVING_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Save_Exception"] @@ -473,14 +450,12 @@ class Data: class Model: def __init__(self): - self.model_folder_path = Config["ModelFolderPath"] self.model_list = None self.selected_model_path = None self.selected_model = None def seek_model(self): - if not Path(Config["ModelFolderPath"]).exists(): return False, Constants.DETECT_JPEAK_LOAD_FAILURE + Constants.DETECT_JPEAK_FAILURE_REASON["Model_Path_Not_Exist"] diff --git a/func/Module_detect_Rpeak.py b/func/Module_detect_Rpeak.py index 6c35c67..3fee804 100644 --- a/func/Module_detect_Rpeak.py +++ b/func/Module_detect_Rpeak.py @@ -41,7 +41,6 @@ ButtonState = { class SettingWindow(QMainWindow): def __init__(self, root_path, sampID): - super(SettingWindow, self).__init__() self.ui = Ui_MainWindow_detect_Rpeak_input_setting() self.ui.setupUi(self) @@ -58,7 +57,6 @@ class SettingWindow(QMainWindow): self.ui.pushButton_cancel.clicked.connect(self.close) def __read_config__(self): - if not Path(ConfigParams.DETECT_RPEAK_CONFIG_FILE_PATH).exists(): with open(ConfigParams.DETECT_RPEAK_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.DETECT_RPEAK_CONFIG_NEW_CONTENT, f) @@ -86,7 +84,6 @@ class SettingWindow(QMainWindow): self.ui.plainTextEdit_file_path_save.setPlainText(Config["Path"]["Save"]) def __write_config__(self): - # 从界面写入配置 Config["InputConfig"]["Freq"] = self.ui.spinBox_input_freq.value() Config["Path"]["Input"] = self.ui.plainTextEdit_file_path_input.toPlainText() @@ -101,11 +98,9 @@ class SettingWindow(QMainWindow): self.close() def __rollback_config__(self): - self.__read_config__() def __update_ui__(self): - self.ui.plainTextEdit_file_path_input.setPlainText( str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_PSG_TEXT / @@ -118,7 +113,6 @@ class SettingWindow(QMainWindow): class MainWindow_detect_Rpeak(QMainWindow): def __init__(self): - super(MainWindow_detect_Rpeak, self).__init__() self.ui = Ui_MainWindow_detect_Rpeak() self.ui.setupUi(self) @@ -147,7 +141,6 @@ class MainWindow_detect_Rpeak(QMainWindow): @overrides def show(self, root_path, sampID): - super().show() self.root_path = root_path self.sampID = sampID @@ -190,7 +183,6 @@ class MainWindow_detect_Rpeak(QMainWindow): @overrides def closeEvent(self, event): - PublicFunc.__disableAllButton__(self, ButtonState) PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) @@ -211,12 +203,10 @@ class MainWindow_detect_Rpeak(QMainWindow): @staticmethod def __reset__(): - ButtonState["Current"].update(ButtonState["Default"].copy()) ButtonState["Current"]["pushButton_view"] = True def __plot__(self): - # 清空画框 self.reset_axes() @@ -247,14 +237,12 @@ class MainWindow_detect_Rpeak(QMainWindow): return status, info def __update_config__(self): - Config["Filter"]["BandPassLow"] = self.ui.doubleSpinBox_bandPassLow.value() Config["Filter"]["BandPassHigh"] = self.ui.doubleSpinBox_bandPassHigh.value() Config["PeaksValue"] = self.ui.spinBox_peaksValue.value() Config["DetectMethod"] = self.ui.comboBox_method.currentText() def __slot_btn_input__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 清空画框 @@ -300,7 +288,6 @@ class MainWindow_detect_Rpeak(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_view__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 数据预处理 @@ -347,7 +334,6 @@ class MainWindow_detect_Rpeak(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_save__(self): - reply = QMessageBox.question(self, Constants.QUESTION_TITLE, Constants.QUESTION_CONTENT + Config["Path"]["Save"], QMessageBox.Yes | QMessageBox.No, @@ -381,7 +367,6 @@ class MainWindow_detect_Rpeak(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def reset_axes(self): - self.ax0.clear() self.ax1.clear() self.ax0.grid(True) @@ -391,7 +376,6 @@ class MainWindow_detect_Rpeak(QMainWindow): self.ax1.xaxis.set_major_formatter(ConfigParams.FORMATTER) def update_ui_comboBox_method(self, method_list): - self.ui.comboBox_method.clear() self.ui.comboBox_method.addItems(method_list) @@ -399,9 +383,6 @@ class MainWindow_detect_Rpeak(QMainWindow): class Data: def __init__(self): - self.file_path_input = Config["Path"]["Input"] - self.file_path_save = Config["Path"]["Save"] - self.raw_data = None self.processed_data = None self.peak = None @@ -409,21 +390,19 @@ class Data: self.RRIV = None def open_file(self): - if not Path(Config["Path"]["Input"]).exists(): return False, Constants.INPUT_FAILURE + Constants.DETECT_RPEAK_FAILURE_REASON["Data_Path_Not_Exist"] try: - self.raw_data = read_csv(self.file_path_input, - encoding=ConfigParams.UTF8_ENCODING, - header=None).to_numpy().reshape(-1) + self.raw_data = read_csv(Config["Path"]["Input"], + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) except Exception: return False, Constants.INPUT_FAILURE + Constants.DETECT_RPEAK_FAILURE_REASON["Read_Data_Exception"] return True, Constants.INPUT_FINISHED def preprocess(self): - if self.raw_data is None: return False, Constants.DETECT_RPEAK_PROCESS_FAILURE + Constants.DETECT_RPEAK_FAILURE_REASON["Raw_Data_Not_Exist"] @@ -438,7 +417,6 @@ class Data: return True, Constants.DETECT_RPEAK_PROCESS_FINISHED def predict_Rpeak(self): - if self.processed_data is None: return False, Constants.DETECT_RPEAK_PREDICT_FAILURE + Constants.DETECT_RPEAK_FAILURE_REASON["Processed_Data_Not_Exist"] @@ -453,12 +431,11 @@ class Data: return True, Constants.DETECT_RPEAK_PREDICT_FINISHED def save(self, chunk): - if self.peak is None: return False, Constants.SAVING_FAILURE + Constants.DETECT_RPEAK_FAILURE_REASON["Peak_Not_Exist"] try: - chunk.to_csv(self.file_path_save, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save"], mode='a', index=False, header=False) except Exception: return False, Constants.SAVING_FAILURE + Constants.DETECT_RPEAK_FAILURE_REASON["Save_Exception"] diff --git a/func/Module_label_check.py b/func/Module_label_check.py index 7b79b5e..c9553e8 100644 --- a/func/Module_label_check.py +++ b/func/Module_label_check.py @@ -49,7 +49,6 @@ ButtonState = { class SettingWindow(QMainWindow): def __init__(self, mode, root_path, sampID): - super(SettingWindow, self).__init__() self.ui = Ui_MainWindow_label_check_input_setting() self.ui.setupUi(self) @@ -70,7 +69,6 @@ class SettingWindow(QMainWindow): self.ui.pushButton_cancel.clicked.connect(self.close) def __read_config__(self): - if not Path(ConfigParams.LABEL_CHECK_CONFIG_FILE_PATH).exists(): with open(ConfigParams.LABEL_CHECK_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.LABEL_CHECK_CONFIG_NEW_CONTENT, f) @@ -132,7 +130,6 @@ class SettingWindow(QMainWindow): raise ValueError("模式不存在") def __write_config__(self): - # 从界面写入配置 Config["InputConfig"]["Freq"] = self.ui.spinBox_input_freq_signal.value() Config["Path"]["Input_Signal"] = self.ui.plainTextEdit_file_path_input_signal.toPlainText() @@ -158,11 +155,9 @@ class SettingWindow(QMainWindow): self.close() def __rollback_config__(self): - self.__read_config__() def __update_ui__(self): - if self.mode == "BCG": self.ui.plainTextEdit_file_path_input_signal.setPlainText( str((Path(self.root_path) / @@ -186,7 +181,6 @@ class SettingWindow(QMainWindow): class MainWindow_label_check(QMainWindow): def __init__(self): - super(MainWindow_label_check, self).__init__() self.ui = Ui_MainWindow_label_check() self.ui.setupUi(self) @@ -236,7 +230,6 @@ class MainWindow_label_check(QMainWindow): @overrides def show(self, mode, root_path, sampID): - super().show() self.mode = mode self.root_path = root_path @@ -307,7 +300,6 @@ class MainWindow_label_check(QMainWindow): @overrides def closeEvent(self, event): - PublicFunc.__disableAllButton__(self, ButtonState) PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) @@ -331,11 +323,9 @@ class MainWindow_label_check(QMainWindow): @staticmethod def __reset__(): - ButtonState["Current"].update(ButtonState["Default"].copy()) def __plot__(self): - # 清空画框 if self.point_peak_original is not None: self.point_peak_original.remove() @@ -373,7 +363,6 @@ class MainWindow_label_check(QMainWindow): return status, info def __plot_peaks__(self): - try: self.point_peak_original, = self.ax0.plot(self.data.original_peak, self.data.original_peak_y, 'ro', label=Constants.LABEL_CHECK_PLOT_LABEL_PEAK_ORIGINAL) @@ -389,14 +378,12 @@ class MainWindow_label_check(QMainWindow): return True, Constants.DRAWING_FINISHED def __redraw_peaks__(self): - self.point_peak_corrected.remove() self.point_peak_corrected, = self.ax1.plot(self.data.corrected_peak, self.data.corrected_peak_y, 'ro', label=Constants.LABEL_CHECK_PLOT_LABEL_PEAK_CORRECTED) self.canvas.draw() def __update_tableWidget_and_info__(self): - if self.data.original_peak is None or self.data.corrected_peak is None: return False, Constants.LABEL_CHECK_FAILURE_REASON["Peak_Not_Exist"] @@ -419,7 +406,6 @@ class MainWindow_label_check(QMainWindow): return status, info def __update_config__(self): - Config["FindPeaks"]["MinInterval"] = self.ui.doubleSpinBox_findpeaks_min_interval.value() Config["FindPeaks"]["MinHeight"] = self.ui.doubleSpinBox_findpeaks_min_height.value() Config["CustomAutoplayArgs"]["MoveLength"] = self.ui.spinBox_moveLength.value() @@ -427,7 +413,6 @@ class MainWindow_label_check(QMainWindow): Config["CustomAutoplayArgs"]["MoveSpeed"] = self.ui.spinBox_moveSpeed.value() def __slot_btn_input__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 清空画框 @@ -526,7 +511,6 @@ class MainWindow_label_check(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_save__(self): - reply = QMessageBox.question(self, Constants.QUESTION_TITLE, Constants.QUESTION_CONTENT + Config["Path"]["Save"], QMessageBox.Yes | QMessageBox.No, @@ -560,7 +544,6 @@ class MainWindow_label_check(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_move__(self): - if self.data is None: return @@ -594,7 +577,6 @@ class MainWindow_label_check(QMainWindow): PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_PAUSE, Constants.TIPS_TYPE_INFO) def __change_autoplay_args__(self): - sender = self.sender() if sender == self.ui.radioButton_move_preset_1 and self.ui.radioButton_move_preset_1.isChecked(): @@ -637,7 +619,6 @@ class MainWindow_label_check(QMainWindow): self.timer_autoplay.stop() def __slot_tableWidget_on_cell_double_clicked__(self, row, col): - if Config["AutoplayArgs"]["AutoplayMode"] != "pause": self.ui.pushButton_pause.click() @@ -657,7 +638,6 @@ class MainWindow_label_check(QMainWindow): PublicFunc.text_output(self.ui, f"{Constants.LABEL_CHECK_JUMP_X_INDEX}{str(int(x))}", Constants.TIPS_TYPE_INFO) def reset_axes(self): - self.ax0.clear() self.ax1.clear() self.ax0.grid(True) @@ -667,7 +647,6 @@ class MainWindow_label_check(QMainWindow): self.ax1.xaxis.set_major_formatter(ConfigParams.FORMATTER) def on_xlim_change(self, event_ax): - try: if self.annotation_tableWidget is not None: self.annotation_tableWidget.remove() @@ -676,7 +655,6 @@ class MainWindow_label_check(QMainWindow): pass def autoplay_move_xlim(self): - if Config["AutoplayArgs"]["AutoplayMode"] == "prev" and self.autoplay_xlim_start < 0: Config["AutoplayArgs"]["AutoplayMode"] = "pause" self.timer_autoplay.stop() @@ -694,7 +672,6 @@ class MainWindow_label_check(QMainWindow): self.canvas.draw() def on_motion(self, event): - if event.inaxes and self.ui.checkBox_show_reference_line.isChecked(): # Clear previous reference lines and temporary points for line in self.ax0.lines[1:]: @@ -715,7 +692,6 @@ class MainWindow_label_check(QMainWindow): self.canvas.draw() def toggle_home(self): - if Config["AutoplayArgs"]["AutoplayMode"] != "pause": self.ui.pushButton_pause.click() self.ax0.autoscale(True) @@ -728,7 +704,6 @@ class MainWindow_label_check(QMainWindow): PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_RECOVER_SCALE, Constants.TIPS_TYPE_INFO) def toggle_changeLabel(self, state): - if state: self.deactivate_figToolbar_buttons() self.figToolbar.action_Label_Multiple.setChecked(True) @@ -750,7 +725,6 @@ class MainWindow_label_check(QMainWindow): self.figToolbar.cid_mouse_hold = None def deactivate_figToolbar_buttons(self): - for action in self.figToolbar._actions.values(): if action.isChecked() == True: if action == self.figToolbar._actions['pan']: @@ -759,7 +733,6 @@ class MainWindow_label_check(QMainWindow): self.figToolbar.zoom() def on_click(self, event): - if self.figToolbar.action_Label_Multiple.isChecked(): if event.button == 1 or event.button == 3: # 左键或右键 if event.button == 1: @@ -776,7 +749,6 @@ class MainWindow_label_check(QMainWindow): self.canvas.draw() def on_release(self, event): - if self.figToolbar.action_Label_Multiple.isChecked(): if self.figToolbar.rect_start_x is not None: self.figToolbar.rect_end_x = event.xdata @@ -839,8 +811,7 @@ class MainWindow_label_check(QMainWindow): self.data.corrected_peak.sort() self.data.corrected_peak_y = [self.data.processed_data[x] for x in self.data.corrected_peak] self.__update_tableWidget_and_info__() - DataFrame(self.data.corrected_peak).to_csv(self.data.file_path_save, - index=False, header=False) + DataFrame(self.data.corrected_peak).to_csv(Config["Path"]["Save"], index=False, header=False) # 移除矩形patch if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None: self.figToolbar.rect_patch_ax0.remove() @@ -850,7 +821,6 @@ class MainWindow_label_check(QMainWindow): self.canvas.draw() def on_hold(self, event): - if self.figToolbar.rect_start_x is not None and event.xdata is not None: self.figToolbar.rect_end_x = event.xdata @@ -896,10 +866,6 @@ class MainWindow_label_check(QMainWindow): class Data: def __init__(self): - self.file_path_input_signal = Config["Path"]["Input_Signal"] - self.file_path_input_peak = Config["Path"]["Input_Peak"] - self.file_path_save = Config["Path"]["Save"] - self.raw_data = None self.processed_data = None self.original_peak = None @@ -908,15 +874,14 @@ class Data: self.corrected_peak_y = None def open_file(self): - if (not Path(Config["Path"]["Input_Signal"]).exists()) or (not Path(Config["Path"]["Input_Peak"]).exists()): return False, Constants.INPUT_FAILURE + Constants.LABEL_CHECK_FAILURE_REASON["Data_Path_Not_Exist"] try: - self.raw_data = read_csv(self.file_path_input_signal, - encoding=ConfigParams.UTF8_ENCODING, - header=None).to_numpy().reshape(-1) - self.original_peak = read_csv(self.file_path_input_peak, + self.raw_data = read_csv(Config["Path"]["Input_Signal"], + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.original_peak = read_csv(Config["Path"]["Input_Peak"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) except Exception: @@ -925,18 +890,16 @@ class Data: return True, Constants.INPUT_FINISHED def get_archive(self): - if not Path(Config["Path"]["Save"]).exists(): self.corrected_peak = self.original_peak return True, Constants.LABEL_CHECK_ARCHIVE_NOT_EXIST else: - self.corrected_peak = read_csv(self.file_path_save, - encoding=ConfigParams.UTF8_ENCODING, - header=None).to_numpy().reshape(-1) + self.corrected_peak = read_csv(Config["Path"]["Save"], + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) return True, Constants.LABEL_CHECK_ARCHIVE_EXIST def preprocess(self): - if self.raw_data is None: return False, Constants.LABEL_CHECK_PROCESS_FAILURE + Constants.LABEL_CHECK_FAILURE_REASON["Raw_Data_Not_Exist"] @@ -963,12 +926,11 @@ class Data: return True, Constants.LABEL_CHECK_PROCESS_FINISHED def save(self, chunk): - if self.corrected_peak is None: return False, Constants.SAVING_FAILURE + Constants.LABEL_CHECK_FAILURE_REASON["Peak_Not_Exist"] try: - chunk.to_csv(self.file_path_save, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save"], mode='a', index=False, header=False) except Exception: return False, Constants.SAVING_FAILURE + Constants.LABEL_CHECK_FAILURE_REASON["Save_Exception"] @@ -1007,21 +969,17 @@ class CustomNavigationToolbar(NavigationToolbar2QT): self.rect_patch_ax1 = None # 用于绘制矩形的patch def home(self, *args): - pass def zoom(self, *args): - super().zoom(*args) self.deactivate_figToorbar_changeLabel_mode() def pan(self, *args): - super().pan(*args) self.deactivate_figToorbar_changeLabel_mode() def deactivate_figToorbar_changeLabel_mode(self): - if self.action_Label_Multiple.isChecked(): self.action_Label_Multiple.setChecked(False) if self.cid_mouse_press is not None: diff --git a/func/Module_mainwindow.py b/func/Module_mainwindow.py index f18e683..4210251 100644 --- a/func/Module_mainwindow.py +++ b/func/Module_mainwindow.py @@ -12,6 +12,7 @@ from func.Module_detect_Jpeak import MainWindow_detect_Jpeak from func.Module_detect_Rpeak import MainWindow_detect_Rpeak from func.Module_label_check import MainWindow_label_check from func.Module_precisely_align import MainWindow_precisely_align +from func.Module_cut_PSG import MainWindow_cut_PSG from func.utils.Constants import Constants, ConfigParams @@ -27,7 +28,6 @@ Config = { class MainWindow(QMainWindow, Ui_Signal_Label): def __init__(self): - super(MainWindow, self).__init__() self.ui = Ui_Signal_Label() self.ui.setupUi(self) @@ -45,6 +45,7 @@ class MainWindow(QMainWindow, Ui_Signal_Label): self.detect_Rpeak = None self.label_check = None self.precisely_align = None + self.cut_PSG = None # 绑定槽函数 self.ui.pushButton_open.clicked.connect(self.__slot_btn_open__) @@ -55,10 +56,10 @@ class MainWindow(QMainWindow, Ui_Signal_Label): self.ui.pushButton_label_check_BCG.clicked.connect(self.__slot_btn_label_check__) self.ui.pushButton_label_check_ECG.clicked.connect(self.__slot_btn_label_check__) self.ui.pushButton_precisely_align.clicked.connect(self.__slot_btn_precisely_align__) + self.ui.pushButton_cut_PSG.clicked.connect(self.__slot_btn_cut_PSG__) @staticmethod def __read_config__(): - if not Path(ConfigParams.PUBLIC_CONFIG_FILE_PATH).exists(): with open(ConfigParams.PUBLIC_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.PUBLIC_CONFIG_NEW_CONTENT, f) @@ -69,12 +70,10 @@ class MainWindow(QMainWindow, Ui_Signal_Label): @staticmethod def __write_config__(): - with open(Path(ConfigParams.PUBLIC_CONFIG_FILE_PATH), "w") as f: dump(Config, f) def __slot_btn_open__(self): - file_dialog = QFileDialog() file_dialog.setFileMode(QFileDialog.Directory) file_dialog.setOption(QFileDialog.ShowDirsOnly, True) @@ -89,7 +88,6 @@ class MainWindow(QMainWindow, Ui_Signal_Label): PublicFunc.msgbox_output(self, Constants.OPERATION_CANCELED, Constants.MSGBOX_TYPE_INFO) def __slot_btn_preprocess__(self): - self.preprocess = MainWindow_preprocess() sender = self.sender() @@ -104,21 +102,18 @@ class MainWindow(QMainWindow, Ui_Signal_Label): self.preprocess.show(mode, root_path, sampID) def __slot_btn_detect_Jpeak__(self): - self.detect_Jpeak = MainWindow_detect_Jpeak() root_path = self.ui.plainTextEdit_root_path.toPlainText() sampID = int(self.ui.comboBox_sampID.currentText()) self.detect_Jpeak.show(root_path, sampID) def __slot_btn_detect_Rpeak__(self): - self.detect_Rpeak = MainWindow_detect_Rpeak() root_path = self.ui.plainTextEdit_root_path.toPlainText() sampID = int(self.ui.comboBox_sampID.currentText()) self.detect_Rpeak.show(root_path, sampID) def __slot_btn_label_check__(self): - self.label_check = MainWindow_label_check() sender = self.sender() @@ -133,14 +128,18 @@ class MainWindow(QMainWindow, Ui_Signal_Label): self.label_check.show(mode, root_path, sampID) def __slot_btn_precisely_align__(self): - self.precisely_align = MainWindow_precisely_align() root_path = self.ui.plainTextEdit_root_path.toPlainText() sampID = int(self.ui.comboBox_sampID.currentText()) self.precisely_align.show(root_path, sampID) - def seek_sampID(self, path): + def __slot_btn_cut_PSG__(self): + self.cut_PSG = MainWindow_cut_PSG() + root_path = self.ui.plainTextEdit_root_path.toPlainText() + sampID = int(self.ui.comboBox_sampID.currentText()) + self.cut_PSG.show(root_path, sampID) + def seek_sampID(self, path): if not Path(path).exists(): PublicFunc.msgbox_output(self, Constants.MAINWINDOW_ROOT_PATH_NOT_EXIST, Constants.MSGBOX_TYPE_ERROR) return diff --git a/func/Module_precisely_align.py b/func/Module_precisely_align.py index 20dcb73..410795f 100644 --- a/func/Module_precisely_align.py +++ b/func/Module_precisely_align.py @@ -82,7 +82,6 @@ ButtonState = { class SettingWindow(QMainWindow): def __init__(self, root_path, sampID): - super(SettingWindow, self).__init__() self.ui = Ui_MainWindow_precisely_align_input_setting() self.ui.setupUi(self) @@ -99,7 +98,6 @@ class SettingWindow(QMainWindow): self.ui.pushButton_cancel.clicked.connect(self.close) def __read_config__(self): - if not Path(ConfigParams.PRECISELY_ALIGN_CONFIG_FILE_PATH).exists(): with open(ConfigParams.PRECISELY_ALIGN_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.PRECISELY_ALIGN_CONFIG_NEW_CONTENT, f) @@ -213,7 +211,6 @@ class SettingWindow(QMainWindow): self.ui.plainTextEdit_file_path_save_Rpeak.setPlainText(Config["Path"]["Save_Rpeak"]) def __write_config__(self): - # 从界面写入配置 Config["InputConfig"]["ECGFreq"] = self.ui.spinBox_input_freq_ECG.value() Config["Path"]["Input_orgBcg"] = self.ui.plainTextEdit_file_path_input_orgBcg.toPlainText() @@ -238,11 +235,9 @@ class SettingWindow(QMainWindow): self.close() def __rollback_config__(self): - self.__read_config__() def __update_ui__(self): - self.ui.plainTextEdit_file_path_input_orgBcg.setPlainText( str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_TEXT / @@ -302,7 +297,6 @@ class SettingWindow(QMainWindow): class MainWindow_precisely_align(QMainWindow): def __init__(self): - super(MainWindow_precisely_align, self).__init__() self.ui = Ui_MainWindow_precisely_align() self.ui.setupUi(self) @@ -356,7 +350,6 @@ class MainWindow_precisely_align(QMainWindow): @overrides def show(self, root_path, sampID): - super().show() self.root_path = root_path self.sampID = sampID @@ -408,7 +401,6 @@ class MainWindow_precisely_align(QMainWindow): @overrides def closeEvent(self, event): - PublicFunc.__disableAllButton__(self, ButtonState) PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) @@ -449,11 +441,9 @@ class MainWindow_precisely_align(QMainWindow): @staticmethod def __reset__(): - ButtonState["Current"].update(ButtonState["Default"].copy()) def __plot__(self, plot_element=None): - # 清空画框 if self.figToolbar.ax0_BCG_rectangle_front is not None: self.figToolbar.ax0_BCG_rectangle_front.remove() @@ -526,11 +516,11 @@ class MainWindow_precisely_align(QMainWindow): label=Constants.PRECISELY_ALIGN_PLOT_LABEL_SELECTED_POINT) self.ax0.plot(plot_element["front"]["corre"], 'o', color=Constants.PLOT_COLOR_BLUE, markersize=3, picker=True, pickradius=5) - self.ax1.stem(plot_element["front"]["RRIVs"], markerfmt="b.", + self.ax1.stem(plot_element["front"]["RRIVs"], markerfmt="b.", linefmt=Constants.PLOT_COLOR_ORANGE, label=Constants.PRECISELY_ALIGN_PLOT_LABEL_RRIV) self.stem_black0 = self.ax1.stem(arange(plot_element["front"]["shift"], plot_element["front"]["shift"] + len(plot_element["front"]["JJIVs"])), - plot_element["front"]["JJIVs"], markerfmt="ko", + plot_element["front"]["JJIVs"], markerfmt="ko", linefmt=Constants.PLOT_COLOR_GREEN, label=Constants.PRECISELY_ALIGN_PLOT_LABEL_JJIV) self.ax2.set_title( "back\ncorre_IIV: {}, corre_II: {}\nsame_sign_rate:{}, total_time_ratio: {}\nshift: {}, alignment offset: {} seconds\noffset_interval: {}, anchor_J: {}, anchor_R: {}".format( @@ -547,11 +537,11 @@ class MainWindow_precisely_align(QMainWindow): label=Constants.PRECISELY_ALIGN_PLOT_LABEL_SELECTED_POINT) self.ax2.plot(plot_element["back"]["corre"], 'o', color=Constants.PLOT_COLOR_BLUE, markersize=3, picker=True, pickradius=5) - self.ax3.stem(plot_element["back"]["RRIVs"], markerfmt="b.", + self.ax3.stem(plot_element["back"]["RRIVs"], markerfmt="b.", linefmt=Constants.PLOT_COLOR_ORANGE, label=Constants.PRECISELY_ALIGN_PLOT_LABEL_RRIV) self.stem_black1 = self.ax3.stem(arange(plot_element["back"]["shift"], plot_element["back"]["shift"] + len(plot_element["back"]["JJIVs"])), - plot_element["back"]["JJIVs"], markerfmt="ko", + plot_element["back"]["JJIVs"], markerfmt="ko", linefmt=Constants.PLOT_COLOR_GREEN, label=Constants.PRECISELY_ALIGN_PLOT_LABEL_JJIV) self.ax0.legend(loc=Constants.PLOT_UPPER_RIGHT) @@ -607,7 +597,6 @@ class MainWindow_precisely_align(QMainWindow): return status, info def __update_info__(self): - self.ui.spinBox_BCG_front_JJIV_1.setValue(Config["IV_Coordinate"]["BCG_front_1"]) self.ui.spinBox_BCG_front_JJIV_2.setValue(Config["IV_Coordinate"]["BCG_front_2"]) self.ui.spinBox_BCG_back_JJIV_1.setValue(Config["IV_Coordinate"]["BCG_back_1"]) @@ -626,7 +615,6 @@ class MainWindow_precisely_align(QMainWindow): self.ui.spinBox_ECG_back_Signal_2.setValue(Config["Coordinate"]["ECG_back_2"]) def __slot_btn_input__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 清空画框 @@ -679,7 +667,6 @@ class MainWindow_precisely_align(QMainWindow): def __slot_btn_calculate_correlation__(self, test1=None, shift_front=None, shift_back=None): # TODO:这里有个未知的问BUG,虽然不影响功能,但会影响代码整洁性,第一个形参赋值为None时,之后使用变量时将会变成False,不知道为什么 - PublicFunc.__disableAllButton__(self, ButtonState) sender = self.sender() @@ -740,7 +727,6 @@ class MainWindow_precisely_align(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_correlation_align__(self): - PublicFunc.__disableAllButton__(self, ButtonState) sender = self.sender() @@ -783,7 +769,6 @@ class MainWindow_precisely_align(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_view_align__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 数据后处理 @@ -812,7 +797,6 @@ class MainWindow_precisely_align(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_save__(self): - reply = QMessageBox.question(self, Constants.QUESTION_TITLE, Constants.QUESTION_CONTENT, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) @@ -944,7 +928,6 @@ class MainWindow_precisely_align(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __update_coordinate__(self): - try: if self.data is not None: if self.data.Jpeak is None or self.data.Rpeak is None: @@ -1005,7 +988,6 @@ class MainWindow_precisely_align(QMainWindow): pass def reset_axes(self): - for ax in self.fig.axes: self.fig.delaxes(ax) if self.ax0 is not None: @@ -1030,7 +1012,6 @@ class MainWindow_precisely_align(QMainWindow): self.ax4.xaxis.set_major_formatter(ConfigParams.FORMATTER) def redraw_calculate_coordination(self, plot_element=None): - if plot_element is not None and plot_element["mode"] == "select": if self.selected_point0 is not None: self.selected_point0.remove() @@ -1065,7 +1046,7 @@ class MainWindow_precisely_align(QMainWindow): label=Constants.PRECISELY_ALIGN_PLOT_LABEL_SELECTED_POINT) self.stem_black0 = self.ax1.stem(arange(plot_element["front"]["shift"], plot_element["front"]["shift"] + len(plot_element["front"]["JJIVs"])), - plot_element["front"]["JJIVs"], markerfmt="ko", + plot_element["front"]["JJIVs"], markerfmt="ko", linefmt=Constants.PLOT_COLOR_GREEN, label=Constants.PRECISELY_ALIGN_PLOT_LABEL_JJIV) self.selected_point1, = self.ax2.plot(plot_element["back"]["shift"], plot_element["back"]["corre"][plot_element["back"]["shift"]] + 1, 'v', @@ -1073,7 +1054,7 @@ class MainWindow_precisely_align(QMainWindow): label=Constants.PRECISELY_ALIGN_PLOT_LABEL_SELECTED_POINT) self.stem_black1 = self.ax3.stem(arange(plot_element["back"]["shift"], plot_element["back"]["shift"] + len(plot_element["back"]["JJIVs"])), - plot_element["back"]["JJIVs"], markerfmt="ko", + plot_element["back"]["JJIVs"], markerfmt="ko", linefmt=Constants.PLOT_COLOR_GREEN, label=Constants.PRECISELY_ALIGN_PLOT_LABEL_JJIV) self.ax0.autoscale(False) @@ -1090,7 +1071,6 @@ class MainWindow_precisely_align(QMainWindow): return False, Constants.DRAWING_FAILURE def redraw_correlation_align(self, plot_element=None): - if plot_element is not None and plot_element["mode"] == "select": if self.selected_point0 is not None: self.selected_point0.remove() @@ -1129,7 +1109,6 @@ class MainWindow_precisely_align(QMainWindow): return False, Constants.DRAWING_FAILURE def toggle_home(self): - if self.ax0 is not None: self.ax0.autoscale(True) self.ax0.relim() @@ -1164,7 +1143,6 @@ class MainWindow_precisely_align(QMainWindow): PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_RECOVER_SCALE, Constants.TIPS_TYPE_INFO) def toggle_getRange(self, state): - if state: self.deactivate_figToolbar_buttons() self.figToolbar.action_Get_Range.setChecked(True) @@ -1186,7 +1164,6 @@ class MainWindow_precisely_align(QMainWindow): self.figToolbar.cid_mouse_hold = None def deactivate_figToolbar_buttons(self): - for action in self.figToolbar._actions.values(): if action.isChecked() == True: if action == self.figToolbar._actions['pan']: @@ -1195,7 +1172,6 @@ class MainWindow_precisely_align(QMainWindow): self.figToolbar.zoom() def on_click(self, event): - if self.figToolbar.action_Get_Range.isChecked(): if event.button == 1: self.is_left_button_pressed = True @@ -1222,7 +1198,6 @@ class MainWindow_precisely_align(QMainWindow): self.canvas.draw() def on_release(self, event): - if self.figToolbar.action_Get_Range.isChecked(): if self.figToolbar.rect_start_x is not None: self.figToolbar.rect_end_x = event.xdata @@ -1321,7 +1296,6 @@ class MainWindow_precisely_align(QMainWindow): self.canvas.draw() def on_hold(self, event): - if event.button == 1: if self.figToolbar.rect_start_x is not None and event.xdata is not None: self.figToolbar.rect_end_x = event.xdata @@ -1376,7 +1350,6 @@ class MainWindow_precisely_align(QMainWindow): self.canvas.draw() def on_pick(self, event): - this_line = event.artist if this_line.axes == self.ax0: xdata = this_line.get_xdata() @@ -1412,20 +1385,6 @@ class MainWindow_precisely_align(QMainWindow): class Data: def __init__(self): - - self.file_path_input_orgBcg = Config["Path"]["Input_orgBcg"] - self.file_path_input_BCG = Config["Path"]["Input_BCG"] - self.file_path_input_Jpeak = Config["Path"]["Input_Jpeak"] - self.file_path_input_ECG = Config["Path"]["Input_ECG"] - self.file_path_input_Rpeak = Config["Path"]["Input_Rpeak"] - self.file_path_save_BCG_AlignInfo = Config["Path"]["Save_BCG_AlignInfo"] - self.file_path_save_ECG_AlignInfo = Config["Path"]["Save_ECG_AlignInfo"] - self.file_path_save_orgBcg = Config["Path"]["Save_orgBcg"] - self.file_path_save_BCG = Config["Path"]["Save_BCG"] - self.file_path_save_ECG = Config["Path"]["Save_ECG"] - self.file_path_save_Jpeak = Config["Path"]["Save_Jpeak"] - self.file_path_save_Rpeak = Config["Path"]["Save_Rpeak"] - self.raw_orgBcg = None self.raw_BCG = None self.Jpeak = None @@ -1461,7 +1420,6 @@ class Data: self.argmax_ECG = None def open_file(self): - if ((not Path(Config["Path"]["Input_BCG"]).exists()) or (not Path(Config["Path"]["Input_Jpeak"]).exists()) or (not Path(Config["Path"]["Input_ECG"]).exists()) @@ -1469,19 +1427,19 @@ class Data: return False, Constants.INPUT_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Data_Path_Not_Exist"] try: - self.raw_orgBcg = read_csv(self.file_path_input_orgBcg, + self.raw_orgBcg = read_csv(Config["Path"]["Input_orgBcg"], + encoding=ConfigParams.UTF8_ENCODING, + header=None).to_numpy().reshape(-1) + self.raw_BCG = read_csv(Config["Path"]["Input_BCG"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) - self.raw_BCG = read_csv(self.file_path_input_BCG, - encoding=ConfigParams.UTF8_ENCODING, - header=None).to_numpy().reshape(-1) - self.Jpeak = read_csv(self.file_path_input_Jpeak, + self.Jpeak = read_csv(Config["Path"]["Input_Jpeak"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) - self.raw_ECG = read_csv(self.file_path_input_ECG, + self.raw_ECG = read_csv(Config["Path"]["Input_ECG"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) - self.Rpeak = read_csv(self.file_path_input_Rpeak, + self.Rpeak = read_csv(Config["Path"]["Input_Rpeak"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) self.argmax_BCG = np_argmax(self.raw_BCG) @@ -1492,7 +1450,6 @@ class Data: return True, Constants.INPUT_FINISHED def data_process_for_calculate_correlation(self): - result = {} if self.Jpeak is None or self.Rpeak is None: @@ -1511,7 +1468,6 @@ class Data: return True, Constants.PRECISELY_ALIGN_PROCESS_FINISHED, result def calculate_correlation_front(self, mode, shift_front=None): - result = {} if ((Config["IV_Coordinate"]["BCG_front_1"] == Config["IV_Coordinate"]["BCG_front_2"]) @@ -1579,7 +1535,6 @@ class Data: return True, Constants.PRECISELY_ALIGN_CALCULATE_FINISHED_FRONT, result def calculate_correlation_back(self, mode, shift_back=None): - result = {} if ((Config["IV_Coordinate"]["BCG_back_1"] == Config["IV_Coordinate"]["BCG_back_2"]) @@ -1647,7 +1602,6 @@ class Data: return True, Constants.PRECISELY_ALIGN_CALCULATE_FINISHED_BACK, result def correlation_align(self, mode): - result = {} try: @@ -1734,7 +1688,6 @@ class Data: return True, Constants.PRECISELY_ALIGN_ALIGN_CORRELATION_FINISHED, result def data_postprocess(self): - try: if len(self.correlation_align_point_match_ECG) != 2 and len(self.correlation_align_point_match_BCG) != 2: off = 0 @@ -1790,7 +1743,6 @@ class Data: return True, f"{Constants.PRECISELY_ALIGN_POSTPROCESS_VIEW_FINISHED},BCG前后段被切割的坐标值为[{frontcut_index_BCG}, {backcut_index_BCG}],ECG前后段被切割的坐标值为[{frontcut_index_ECG}, {backcut_index_ECG}]" def save_alignInfo(self): - try: save_data = { "front": { @@ -1823,69 +1775,63 @@ class Data: } } save_data = [str(save_data)] - DataFrame(save_data).to_csv(self.file_path_save_BCG_AlignInfo, index=False, header=False) - DataFrame(save_data).to_csv(self.file_path_save_ECG_AlignInfo, index=False, header=False) - except Exception as e: - print(e) + DataFrame(save_data).to_csv(Config["Path"]["Save_BCG_AlignInfo"], index=False, header=False) + DataFrame(save_data).to_csv(Config["Path"]["Save_ECG_AlignInfo"], index=False, header=False) + except Exception: return False, Constants.PRECISELY_ALIGN_SAVING_ALIGNINFO_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Save_Exception"] return True, Constants.PRECISELY_ALIGN_SAVING_ALIGNINFO_FINISHED def save_res_orgBcg(self, chunk): - if self.res_orgBcg is None: return False, Constants.PRECISELY_ALIGN_SAVING_RES_ORGBCG_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["res_orgBcg_Not_Exist"] try: - chunk.to_csv(self.file_path_save_orgBcg, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save_orgBcg"], mode='a', index=False, header=False) except Exception: return False, Constants.PRECISELY_ALIGN_SAVING_RES_ORGBCG_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Save_Exception"] return True, Constants.PRECISELY_ALIGN_SAVING_RES_ORGBCG_FINISHED def save_res_BCG(self, chunk): - if self.res_BCG is None: return False, Constants.PRECISELY_ALIGN_SAVING_RES_BCG_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["res_BCG_Not_Exist"] try: - chunk.to_csv(self.file_path_save_BCG, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save_BCG"], mode='a', index=False, header=False) except Exception: return False, Constants.PRECISELY_ALIGN_SAVING_RES_BCG_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Save_Exception"] return True, Constants.PRECISELY_ALIGN_SAVING_RES_BCG_FINISHED def save_cut_ECG(self, chunk): - if self.cut_ECG is None: return False, Constants.PRECISELY_ALIGN_SAVING_CUT_ECG_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["cut_ECG_Not_Exist"] try: - chunk.to_csv(self.file_path_save_ECG, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save_ECG"], mode='a', index=False, header=False) except Exception: return False, Constants.PRECISELY_ALIGN_SAVING_CUT_ECG_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Save_Exception"] return True, Constants.PRECISELY_ALIGN_SAVING_CUT_ECG_FINISHED def save_Jpeak(self, chunk): - if self.cut_Jpeak is None: return False, Constants.PRECISELY_ALIGN_SAVING_CUT_JPEAK_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["cut_Jpeak_Not_Exist"] try: - chunk.to_csv(self.file_path_save_Jpeak, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save_Jpeak"], mode='a', index=False, header=False) except Exception: return False, Constants.PRECISELY_ALIGN_SAVING_CUT_JPEAK_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Save_Exception"] return True, Constants.PRECISELY_ALIGN_SAVING_CUT_JPEAK_FINISHED def save_Rpeak(self, chunk): - if self.cut_Rpeak is None: return False, Constants.PRECISELY_ALIGN_SAVING_CUT_RPEAK_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["cut_Rpeak_Not_Exist"] try: - chunk.to_csv(self.file_path_save_Rpeak, mode='a', index=False, header=False) + chunk.to_csv(Config["Path"]["Save_Rpeak"], mode='a', index=False, header=False) except Exception: return False, Constants.PRECISELY_ALIGN_SAVING_CUT_RPEAK_FAILURE + Constants.PRECISELY_ALIGN_FAILURE_REASON["Save_Exception"] @@ -1926,21 +1872,17 @@ class CustomNavigationToolbar(NavigationToolbar2QT): self.ax1_ECG_rectangle_back = None def home(self, *args): - pass def zoom(self, *args): - super().zoom(*args) self.deactivate_figToorbar_getRange_mode() def pan(self, *args): - super().pan(*args) self.deactivate_figToorbar_getRange_mode() def deactivate_figToorbar_getRange_mode(self): - if self.action_Get_Range.isChecked(): self.action_Get_Range.setChecked(False) if self.cid_mouse_press is not None: diff --git a/func/Module_preprocess.py b/func/Module_preprocess.py index e0b8b84..690818f 100644 --- a/func/Module_preprocess.py +++ b/func/Module_preprocess.py @@ -42,7 +42,6 @@ ButtonState = { class SettingWindow(QMainWindow): def __init__(self, mode, root_path, sampID): - super(SettingWindow, self).__init__() self.ui = Ui_MainWindow_preprocess_input_setting() self.ui.setupUi(self) @@ -61,7 +60,6 @@ class SettingWindow(QMainWindow): self.ui.pushButton_cancel.clicked.connect(self.close) def __read_config__(self): - if not Path(ConfigParams.PREPROCESS_CONFIG_FILE_PATH).exists(): with open(ConfigParams.PREPROCESS_CONFIG_FILE_PATH, "w") as f: dump(ConfigParams.PREPROCESS_CONFIG_NEW_CONTENT, f) @@ -109,7 +107,6 @@ class SettingWindow(QMainWindow): self.ui.plainTextEdit_file_path_save.setPlainText(Config["Path"]["Save"]) def __write_config__(self): - # 从界面写入配置 Config["InputConfig"]["Freq"] = self.ui.spinBox_input_freq.value() Config["OutputConfig"]["Freq"] = self.ui.spinBox_output_freq.value() @@ -126,11 +123,9 @@ class SettingWindow(QMainWindow): self.close() def __rollback_config__(self): - self.__read_config__() def __update_ui__(self): - if self.mode == "BCG": self.ui.plainTextEdit_file_path_input.setPlainText( str((Path(self.root_path) / @@ -168,7 +163,6 @@ class SettingWindow(QMainWindow): class MainWindow_preprocess(QMainWindow): def __init__(self): - super(MainWindow_preprocess, self).__init__() self.ui = Ui_MainWindow_preprocess() self.ui.setupUi(self) @@ -197,7 +191,6 @@ class MainWindow_preprocess(QMainWindow): @overrides def show(self, mode, root_path, sampID): - super().show() self.mode = mode self.root_path = root_path @@ -244,7 +237,6 @@ class MainWindow_preprocess(QMainWindow): @overrides def closeEvent(self, event): - PublicFunc.__disableAllButton__(self, ButtonState) PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN)) @@ -264,12 +256,10 @@ class MainWindow_preprocess(QMainWindow): @staticmethod def __reset__(): - ButtonState["Current"].update(ButtonState["Default"].copy()) ButtonState["Current"]["pushButton_view"] = True def __plot__(self): - # 清空画框 self.reset_axes() @@ -293,7 +283,6 @@ class MainWindow_preprocess(QMainWindow): return status, info def __update_config__(self): - if self.mode == "BCG": Config["Filter"]["BCGBandPassOrder"] = self.ui.spinBox_bandPassOrder.value() Config["Filter"]["BCGBandPassLow"] = self.ui.doubleSpinBox_bandPassLow.value() @@ -306,7 +295,6 @@ class MainWindow_preprocess(QMainWindow): raise ValueError("模式不存在") def __slot_btn_input__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 清空画框 @@ -331,7 +319,6 @@ class MainWindow_preprocess(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_view__(self): - PublicFunc.__disableAllButton__(self, ButtonState) # 数据预处理 @@ -360,7 +347,6 @@ class MainWindow_preprocess(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def __slot_btn_save__(self): - reply = QMessageBox.question(self, Constants.QUESTION_TITLE, Constants.QUESTION_CONTENT + Config["Path"]["Save"], QMessageBox.Yes | QMessageBox.No, @@ -394,7 +380,6 @@ class MainWindow_preprocess(QMainWindow): PublicFunc.finish_operation(self, ButtonState) def reset_axes(self): - self.ax0.clear() self.ax0.grid(True) self.ax0.xaxis.set_major_formatter(ConfigParams.FORMATTER) @@ -403,20 +388,15 @@ class MainWindow_preprocess(QMainWindow): class Data: def __init__(self): - - self.file_path_input = Config["Path"]["Input"] - self.file_path_save = Config["Path"]["Save"] - self.raw_data = None self.processed_data = None def open_file(self): - if not Path(Config["Path"]["Input"]).exists(): return False, Constants.INPUT_FAILURE + Constants.PREPROCESS_FAILURE_REASON["Data_Path_Not_Exist"] try: - self.raw_data = read_csv(self.file_path_input, + self.raw_data = read_csv(Config["Path"]["Input"], encoding=ConfigParams.UTF8_ENCODING, header=None).to_numpy().reshape(-1) except Exception: @@ -425,7 +405,6 @@ class Data: return True, Constants.INPUT_FINISHED def preprocess(self): - if self.raw_data is None: return False, Constants.PREPROCESS_PROCESS_FAILURE + Constants.PREPROCESS_FAILURE_REASON["Raw_Data_Not_Exist"] @@ -454,12 +433,11 @@ class Data: return True, Constants.PREPROCESS_PROCESS_FINISHED def save(self, chunk): - if self.processed_data is None: return False, Constants.SAVING_FAILURE + Constants.PREPROCESS_FAILURE_REASON["Processed_Data_Not_Exist"] try: - chunk.to_csv(self.file_path_save, mode='a', index=False, header=False, float_format='%.4f') + chunk.to_csv(Config["Path"]["Save"], mode='a', index=False, header=False, float_format='%.4f') except Exception: return False, Constants.SAVING_FAILURE + Constants.PREPROCESS_FAILURE_REASON["Save_Exception"] diff --git a/func/utils/ConfigParams.py b/func/utils/ConfigParams.py index 9c94d2e..8513331 100644 --- a/func/utils/ConfigParams.py +++ b/func/utils/ConfigParams.py @@ -33,7 +33,7 @@ class ConfigParams: # 预处理 PREPROCESS_CONFIG_FILE_PATH: str = "./config/Config_preprocess.yaml" - PREPROCESS_CONFIG_NEW_CONTENT = { + PREPROCESS_CONFIG_NEW_CONTENT: dict = { "InputConfig": { "Freq": 1000 }, @@ -57,7 +57,7 @@ class ConfigParams: # BCG的J峰算法定位 DETECT_JPEAK_CONFIG_FILE_PATH: str = "./config/Config_detect_Jpeak.yaml" - DETECT_JPEAK_CONFIG_NEW_CONTENT = { + DETECT_JPEAK_CONFIG_NEW_CONTENT: dict = { "InputConfig": { "Freq": 1000 }, @@ -79,7 +79,7 @@ class ConfigParams: # ECG的R峰算法定位 DETECT_RPEAK_CONFIG_FILE_PATH: str = "./config/Config_detect_Rpeak.yaml" - DETECT_RPEAK_CONFIG_NEW_CONTENT = { + DETECT_RPEAK_CONFIG_NEW_CONTENT: dict = { "InputConfig": { "Freq": 1000 }, @@ -96,7 +96,7 @@ class ConfigParams: # 人工纠正 LABEL_CHECK_CONFIG_FILE_PATH: str = "./config/Config_label_check.yaml" - LABEL_CHECK_CONFIG_NEW_CONTENT = { + LABEL_CHECK_CONFIG_NEW_CONTENT: dict = { "InputConfig": { "Freq": 1000 }, @@ -130,7 +130,7 @@ class ConfigParams: # 数据精同步 PRECISELY_ALIGN_CONFIG_FILE_PATH: str = "./config/Config_precisely_align.yaml" - PRECISELY_ALIGN_CONFIG_NEW_CONTENT = { + PRECISELY_ALIGN_CONFIG_NEW_CONTENT: dict = { "InputConfig": { "ECGFreq": 1000 } @@ -152,10 +152,46 @@ class ConfigParams: PRECISELY_ALIGN_SAVE_PEAK_CHUNK_SIZE: int = 100 # 冗余数据切割和标签映射 + CUT_PSG_CONFIG_FILE_PATH: str = "./config/Config_cut_PSG.yaml" + CUT_PSG_CONFIG_NEW_CONTENT: dict = { + "ECGFreq": 1000, + "ChannelInput": { + "Effort Tho": "Effort Tho_Raw_", + "Effort Abd": "Effort Abd_Raw_", + "Flow Patient": "Flow Patient_Raw_", + "Snore": "Snore_Raw_", + "SpO2": "SpO2_Raw_", + "5_class": "5_class_Raw_" + }, + "LabelInput": { + "SA Label": "SA Label_Raw" + }, + "StartTime": "StartTime_Raw", + "ChannelSave": { + "Effort Tho": "Effort Tho_Sync_", + "Effort Abd": "Effort Abd_Sync_", + "Flow Patient": "Flow Patient_Sync_", + "Snore": "Snore_Sync_", + "SpO2": "SpO2_Sync_", + "5_class": "5_class_Sync_" + }, + "LabelSave": { + "SA Label": "SA Label_Sync" + }, + "EndWith": { + "Effort Tho": ENDSWITH_TXT, + "Effort Abd": ENDSWITH_TXT, + "Flow Patient": ENDSWITH_TXT, + "Snore": ENDSWITH_TXT, + "SpO2": ENDSWITH_TXT, + "5_class": ENDSWITH_TXT, + "SA Label": ENDSWITH_CSV, + "StartTime": ENDSWITH_TXT + }, + } + CUT_PSG_SAVE_ECG_ALIGNINFO_FILENAME: str = "Align_info" + CUT_PSG_SALABEL_EVENT: list = ["Hypopnea", "Central apnea", "Obstructive apnea", "Mixed apnea"] - CUT_PSG_CHANNEL_LIST: list = ["Effort Abd_Raw_", "Effort Tho_Raw_", "Flow Patient_Raw_", "Snore_Raw_", "SpO2_Raw_"] - CUT_PST_LABEL_LIST: list = ["5_class_Raw_", "SA Label_Raw"] - CUT_PST_STARTTIME: list = ["StartTime_Raw"] # 体动标注 diff --git a/func/utils/Constants.py b/func/utils/Constants.py index 435c123..4fbc214 100644 --- a/func/utils/Constants.py +++ b/func/utils/Constants.py @@ -269,6 +269,32 @@ class Constants: PRECISELY_ALIGN_NO_POINT_IN_THE_INTERVAL: str = "所选区间内无有效点" PRECISELY_ALIGN_ACTION_GET_RANGE_NAME: str = f"设置范围({ConfigParams.PRECISELY_ALIGN_ACTION_GET_RANGE_SHORTCUT_KEY})" + # 冗余数据切割和标签映射 + CUT_PSG_GETTING_FILE_AND_FREQ: str = "正在获取文件及其采样率" + CUT_PSG_GET_FILE_AND_FREQ_FINISHED: str = "获取文件及其采样率完成" + CUT_PSG_GET_FILE_AND_FREQ_FAILURE: str = "获取文件及其采样率失败" + + CUT_PSG_CUTTING_DATA: str = "正在切割数据" + CUT_PSG_CUT_DATA_FINISHED: str = "切割数据完成" + CUT_PSG_CUT_DATA_FAILURE: str = "切割数据失败" + + CUT_PSG_ALIGNING_LABEL: str = "正在映射标签" + CUT_PSG_ALIGN_LABEL_FINISHED: str = "映射标签完成" + CUT_PSG_ALIGN_LABEL_FAILURE: str = "映射标签失败" + + CUT_PSG_FAILURE_REASON: str = { + "Filename_Format_not_Correct": "(文件名格式不正确)", + "File_not_Exist": "(需要处理的文件不存在)", + "Get_File_and_Freq_Excepetion": "(检查文件是否存在并获取其数据采样率异常)", + "Read_Data_Exception": "(读取数据异常)", + "Cut_Data_Length_not_Correct": "(切割数据时长度不正确)", + "Cut_Data_Exception": "(切割数据异常)", + "Align_Label_SALabel_Format_not_Correct": "(映射标签时SA Label中的文件格式不正确)", + "Align_Label_Exception": "(映射标签异常)", + "Save_Data_not_Exist": "(需要保存的数据不存在)", + "Save_Exception": "(保存异常)" + } + # 体动标注 # TODO:弃用 diff --git a/func/utils/PublicFunc.py b/func/utils/PublicFunc.py index 651ab43..4bc75f5 100644 --- a/func/utils/PublicFunc.py +++ b/func/utils/PublicFunc.py @@ -130,7 +130,6 @@ class PublicFunc: @staticmethod def add_progressbar(mainWindow): - mainWindow.progressbar = QProgressBar() mainWindow.progressbar.setRange(0, 100) mainWindow.progressbar.setValue(0) @@ -139,17 +138,14 @@ class PublicFunc: @staticmethod def statusbar_show_msg(mainWindow, msg): - mainWindow.ui.statusbar.showMessage(msg) @staticmethod def statusbar_clear_msg(mainWindow): - mainWindow.ui.statusbar.clearMessage() @staticmethod def finish_operation(mainWindow, buttonState): - PublicFunc.statusbar_show_msg(mainWindow, PublicFunc.format_status_msg(Constants.OPERATION_FINISHED)) mainWindow.progressbar.setValue(100) QApplication.processEvents() @@ -158,7 +154,6 @@ class PublicFunc: @staticmethod def progressbar_update(mainWindow, current: int, total: int, msg: str, progressbarState: int): - if current > total: raise ValueError("当前进度值大于总进度值") if progressbarState < 0 or progressbarState > 100: diff --git a/ui/MainWindow/MainWindow_cut_PSG.py b/ui/MainWindow/MainWindow_cut_PSG.py index bfca523..a093573 100644 --- a/ui/MainWindow/MainWindow_cut_PSG.py +++ b/ui/MainWindow/MainWindow_cut_PSG.py @@ -18,8 +18,8 @@ from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient, QTransform) from PySide6.QtWidgets import (QApplication, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QMainWindow, QPlainTextEdit, QProgressBar, - QPushButton, QSizePolicy, QSpacerItem, QStatusBar, - QTextBrowser, QVBoxLayout, QWidget) + QPushButton, QSizePolicy, QSpacerItem, QSpinBox, + QStatusBar, QTextBrowser, QVBoxLayout, QWidget) class Ui_MainWindow_cut_PSG(object): def setupUi(self, MainWindow_cut_PSG): @@ -82,6 +82,7 @@ class Ui_MainWindow_cut_PSG(object): self.plainTextEdit_channel = QPlainTextEdit(self.groupBox_2) self.plainTextEdit_channel.setObjectName(u"plainTextEdit_channel") + self.plainTextEdit_channel.setEnabled(False) self.horizontalLayout.addWidget(self.plainTextEdit_channel) @@ -100,6 +101,7 @@ class Ui_MainWindow_cut_PSG(object): self.plainTextEdit_label = QPlainTextEdit(self.groupBox_2) self.plainTextEdit_label.setObjectName(u"plainTextEdit_label") + self.plainTextEdit_label.setEnabled(False) self.horizontalLayout_6.addWidget(self.plainTextEdit_label) @@ -108,6 +110,27 @@ class Ui_MainWindow_cut_PSG(object): self.verticalLayout_5.addLayout(self.horizontalLayout_6) + self.horizontalLayout_7 = QHBoxLayout() + self.horizontalLayout_7.setObjectName(u"horizontalLayout_7") + self.label_7 = QLabel(self.groupBox_2) + self.label_7.setObjectName(u"label_7") + self.label_7.setFont(font) + + self.horizontalLayout_7.addWidget(self.label_7) + + self.spinBox_ECGFreq = QSpinBox(self.groupBox_2) + self.spinBox_ECGFreq.setObjectName(u"spinBox_ECGFreq") + self.spinBox_ECGFreq.setFont(font) + self.spinBox_ECGFreq.setMinimum(1) + self.spinBox_ECGFreq.setMaximum(1000000) + + self.horizontalLayout_7.addWidget(self.spinBox_ECGFreq) + + self.horizontalLayout_7.setStretch(0, 1) + self.horizontalLayout_7.setStretch(1, 1) + + self.verticalLayout_5.addLayout(self.horizontalLayout_7) + self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding) self.verticalLayout_5.addItem(self.verticalSpacer) @@ -124,23 +147,24 @@ class Ui_MainWindow_cut_PSG(object): self.verticalLayout_5.addLayout(self.horizontalLayout_2) - self.progressBar = QProgressBar(self.groupBox_2) - self.progressBar.setObjectName(u"progressBar") + self.progressbar = QProgressBar(self.groupBox_2) + self.progressbar.setObjectName(u"progressbar") sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) sizePolicy1.setHorizontalStretch(0) sizePolicy1.setVerticalStretch(0) - sizePolicy1.setHeightForWidth(self.progressBar.sizePolicy().hasHeightForWidth()) - self.progressBar.setSizePolicy(sizePolicy1) - self.progressBar.setStyleSheet(u"") - self.progressBar.setValue(0) + sizePolicy1.setHeightForWidth(self.progressbar.sizePolicy().hasHeightForWidth()) + self.progressbar.setSizePolicy(sizePolicy1) + self.progressbar.setStyleSheet(u"") + self.progressbar.setValue(0) - self.verticalLayout_5.addWidget(self.progressBar) + self.verticalLayout_5.addWidget(self.progressbar) self.verticalLayout_5.setStretch(0, 2) self.verticalLayout_5.setStretch(1, 2) - self.verticalLayout_5.setStretch(2, 2) - self.verticalLayout_5.setStretch(3, 1) + self.verticalLayout_5.setStretch(2, 1) + self.verticalLayout_5.setStretch(3, 2) self.verticalLayout_5.setStretch(4, 1) + self.verticalLayout_5.setStretch(5, 1) self.gridLayout_2.addWidget(self.groupBox_2, 0, 0, 1, 2) @@ -182,6 +206,7 @@ class Ui_MainWindow_cut_PSG(object): self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow_cut_PSG", u"\u786e\u5b9a\u6570\u636e", None)) self.label_2.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u9700\u8981\u5207\u5272\u7684\u901a\u9053\u540d\uff1a", None)) self.label_6.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u9700\u8981\u6620\u5c04\u7684\u6807\u7b7e\uff1a", None)) + self.label_7.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u6570\u636e\u7cbe\u540c\u6b65\u65f6ECG\u7684\u91c7\u6837\u7387\uff1a", None)) self.label_show.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u70b9\u51fb\u6267\u884c\u4ee5\u5f00\u59cb...", None)) self.pushButton_execute.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u6267\u884c", None)) # retranslateUi diff --git a/ui/MainWindow/MainWindow_cut_PSG.ui b/ui/MainWindow/MainWindow_cut_PSG.ui index ae28df2..00f2dc2 100644 --- a/ui/MainWindow/MainWindow_cut_PSG.ui +++ b/ui/MainWindow/MainWindow_cut_PSG.ui @@ -57,7 +57,7 @@ 确定数据 - + @@ -76,7 +76,11 @@ - + + + false + + @@ -95,7 +99,42 @@ - + + + false + + + + + + + + + + + + 12 + + + + 数据精同步时ECG的采样率: + + + + + + + + 12 + + + + 1 + + + 1000000 + + @@ -132,7 +171,7 @@ - + 0 diff --git a/ui/MainWindow/MainWindow_menu.py b/ui/MainWindow/MainWindow_menu.py index 6470880..eefa04f 100644 --- a/ui/MainWindow/MainWindow_menu.py +++ b/ui/MainWindow/MainWindow_menu.py @@ -151,13 +151,28 @@ class Ui_Signal_Label(object): self.verticalLayout.addLayout(self.horizontalLayout_6) + self.horizontalLayout_4 = QHBoxLayout() + self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") self.pushButton_precisely_align = QPushButton(self.centralwidget) self.pushButton_precisely_align.setObjectName(u"pushButton_precisely_align") sizePolicy.setHeightForWidth(self.pushButton_precisely_align.sizePolicy().hasHeightForWidth()) self.pushButton_precisely_align.setSizePolicy(sizePolicy) self.pushButton_precisely_align.setFont(font) - self.verticalLayout.addWidget(self.pushButton_precisely_align) + self.horizontalLayout_4.addWidget(self.pushButton_precisely_align) + + self.pushButton_cut_PSG = QPushButton(self.centralwidget) + self.pushButton_cut_PSG.setObjectName(u"pushButton_cut_PSG") + sizePolicy.setHeightForWidth(self.pushButton_cut_PSG.sizePolicy().hasHeightForWidth()) + self.pushButton_cut_PSG.setSizePolicy(sizePolicy) + self.pushButton_cut_PSG.setFont(font) + + self.horizontalLayout_4.addWidget(self.pushButton_cut_PSG) + + self.horizontalLayout_4.setStretch(0, 3) + self.horizontalLayout_4.setStretch(1, 1) + + self.verticalLayout.addLayout(self.horizontalLayout_4) self.pushButton_artifact_label = QPushButton(self.centralwidget) self.pushButton_artifact_label.setObjectName(u"pushButton_artifact_label") @@ -226,6 +241,7 @@ class Ui_Signal_Label(object): self.pushButton_label_check_BCG.setText(QCoreApplication.translate("Signal_Label", u"BCG\u7684J\u5cf0\u4eba\u5de5\u7ea0\u6b63", None)) self.pushButton_label_check_ECG.setText(QCoreApplication.translate("Signal_Label", u"ECG\u7684R\u5cf0\u4eba\u5de5\u7ea0\u6b63", None)) self.pushButton_precisely_align.setText(QCoreApplication.translate("Signal_Label", u"\u6570\u636e\u7cbe\u540c\u6b65", None)) + self.pushButton_cut_PSG.setText(QCoreApplication.translate("Signal_Label", u"\u5197\u4f59\u6570\u636e\u5207\u5272\u548c\u6807\u7b7e\u6620\u5c04", None)) self.pushButton_artifact_label.setText(QCoreApplication.translate("Signal_Label", u"\u4f53\u52a8\u6807\u6ce8", None)) self.pushButton_bcg_quality_label.setText(QCoreApplication.translate("Signal_Label", u"BCG\u7684\u8d28\u91cf\u6807\u6ce8", None)) self.pushButton_resp_quality_label.setText(QCoreApplication.translate("Signal_Label", u"\u547c\u5438\u53ef\u7528\u6027\u53ca\u95f4\u671f\u6807\u6ce8", None)) diff --git a/ui/MainWindow/MainWindow_menu.ui b/ui/MainWindow/MainWindow_menu.ui index caad66b..0ce6795 100644 --- a/ui/MainWindow/MainWindow_menu.ui +++ b/ui/MainWindow/MainWindow_menu.ui @@ -223,22 +223,44 @@ - - - - 0 - 0 - - - - - 14 - - - - 数据精同步 - - + + + + + + 0 + 0 + + + + + 14 + + + + 数据精同步 + + + + + + + + 0 + 0 + + + + + 14 + + + + 冗余数据切割和标签映射 + + + + diff --git a/数据结构化输入和输出命名规范.md b/数据结构化输入和输出命名规范.md index b9f5c09..145ee7d 100644 --- a/数据结构化输入和输出命名规范.md +++ b/数据结构化输入和输出命名规范.md @@ -16,7 +16,7 @@ |-SA Label_corrected.csv |-SA Label_add.csv` .../OrgBCG_Aligned/ - |-Align_info.txt + |-Align_info.yaml |-BCG_Sync_采样率.txt |-orgBcg_Sync_采样率.txt |-Jpeak_Sync.txt @@ -28,7 +28,7 @@ .../OrgBCG_Origin/ |-... .../PSG_Aligned/ - |-Align_info.txt + |-Align_info.yaml |-ECG_Sync_采样率.txt |-Rpeak_Sync.txt |-5_class_Sync_1.txt @@ -168,13 +168,13 @@ 输出: -PSG的对齐信息:`./PSG_Aligned//Align_info.txt` +PSG的对齐信息:`./PSG_Aligned//Align_info.yaml` 同步后的ECG信号:`./PSG_Aligned//ECG_Sync_采样率.txt` 同步后的R峰坐标:`./PSG_Aligned//Rpeak_Sync.txt` -BCG的对齐信息:`./OrgBCG_Aligned//Align_info.txt` +BCG的对齐信息:`./OrgBCG_Aligned//Align_info.yaml` 同步后的BCG信号:`./OrgBCG_Aligned//BCG_Sync_采样率.txt` @@ -188,9 +188,9 @@ BCG的对齐信息:`./OrgBCG_Aligned//Align_info.txt` 输入: -PSG的对齐信息:`./PSG_Aligned//Align_info.txt` +PSG的对齐信息:`./PSG_Aligned//Align_info.yaml` -BCG的对齐信息:`./OrgBCG_Aligned//Align_info.txt` +BCG的对齐信息:`./OrgBCG_Aligned//Align_info.yaml` 原始的其他PSG通道信号:`./PSG_Text//通道名_Raw_采样率.txt`(通道名包括:Effort Abd, Effort Tho, Flow Patient, Snore, SpO2)