diff --git a/func/Module_approximately_align.py b/func/Module_approximately_align.py index 3931182..62e8d97 100644 --- a/func/Module_approximately_align.py +++ b/func/Module_approximately_align.py @@ -3,7 +3,6 @@ from pathlib import Path from traceback import format_exc import matplotlib.pyplot as plt -from PySide6.QtCore import QEvent from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication from matplotlib.backends.backend_qt import NavigationToolbar2QT from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas @@ -275,8 +274,6 @@ class MainWindow_approximately_align(QMainWindow): PublicFunc.__resetAllButton__(self, ButtonState) - # self.ui.groupBox_align_position.setEnabled(False) - self.ui.pushButton_input.clicked.connect(self.__slot_btn_input__) self.ui.pushButton_input_setting.clicked.connect(self.setting.show) self.ui.pushButton_Standardize.clicked.connect(self.__slot_btn_Standardize__) diff --git a/func/Module_artifact_label.py b/func/Module_artifact_label.py index 6dc3687..5c3365c 100644 --- a/func/Module_artifact_label.py +++ b/func/Module_artifact_label.py @@ -9,7 +9,7 @@ from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication, QTableWidg from matplotlib import gridspec, patches from matplotlib.backends.backend_qt import NavigationToolbar2QT from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg -from numpy import array, sum as np_sum, nonzero +from numpy import array from numpy.fft import fft, fftfreq from overrides import overrides from pandas import read_csv, DataFrame, concat diff --git a/func/Module_mainwindow.py b/func/Module_mainwindow.py index 8cdc416..cd836f0 100644 --- a/func/Module_mainwindow.py +++ b/func/Module_mainwindow.py @@ -365,6 +365,21 @@ class MainWindow(QMainWindow, Ui_Signal_Label): path.mkdir(parents=True, exist_ok=True) def set_dark_mode_status(self): + module_list = [ + self, + self.approximately_align, + self.preprocess, + self.detect_Jpeak, + self.detect_Rpeak, + self.label_check, + self.precisely_align, + self.cut_PSG, + self.artifact_label, + self.bcg_quality_label, + self.resp_quality_label, + self.SA_label + ] + try: if self.ui.checkBox_darkmode.isChecked(): QApplication.styleHints().setColorScheme(Qt.ColorScheme.Dark) @@ -374,65 +389,12 @@ class MainWindow(QMainWindow, Ui_Signal_Label): PublicFunc.msgbox_output(self, Constants.MAINWINDOW_DARKMODE_FAILURE + "。" + format_exc(), Constants.MSGBOX_TYPE_ERROR) return - try: - MainWindow.update_widget_style(self) - except RuntimeError: - pass - try: - if self.approximately_align is not None: - MainWindow.update_widget_style(self.approximately_align) - except RuntimeError: - pass - try: - if self.preprocess is not None: - MainWindow.update_widget_style(self.preprocess) - except RuntimeError: - pass - try: - if self.detect_Jpeak is not None: - MainWindow.update_widget_style(self.detect_Jpeak) - except RuntimeError: - pass - try: - if self.detect_Rpeak is not None: - MainWindow.update_widget_style(self.detect_Rpeak) - except RuntimeError: - pass - try: - if self.label_check is not None: - MainWindow.update_widget_style(self.label_check) - except RuntimeError: - pass - try: - if self.precisely_align is not None: - MainWindow.update_widget_style(self.precisely_align) - except RuntimeError: - pass - try: - if self.cut_PSG is not None: - MainWindow.update_widget_style(self.cut_PSG) - except RuntimeError: - pass - try: - if self.artifact_label is not None: - MainWindow.update_widget_style(self.artifact_label) - except RuntimeError: - pass - try: - if self.bcg_quality_label is not None: - MainWindow.update_widget_style(self.bcg_quality_label) - except RuntimeError: - pass - try: - if self.resp_quality_label is not None: - MainWindow.update_widget_style(self.resp_quality_label) - except RuntimeError: - pass - try: - if self.SA_label is not None: - MainWindow.update_widget_style(self.SA_label) - except RuntimeError: - pass + for module in module_list: + try: + if module is not None: + MainWindow.update_widget_style(module) + except RuntimeError: + pass @staticmethod def update_widget_style(mainWindow): diff --git a/func/Module_precisely_align.py b/func/Module_precisely_align.py index 8648515..d82d462 100644 --- a/func/Module_precisely_align.py +++ b/func/Module_precisely_align.py @@ -1645,9 +1645,6 @@ class Data: self.correlation_align_point_match_ECG = array([]).astype(int) self.correlation_align_point_match_BCG = array([]).astype(int) - self.argmax_BCG = None - self.argmax_ECG = None - def open_file(self): if Path(Config["Path"]["Input_OrgBCG"]).is_file(): Config["Path"]["Input_OrgBCG"] = str(Path(Config["Path"]["Input_OrgBCG"]).parent) @@ -1740,8 +1737,6 @@ class Data: self.Rpeak = read_csv(Config["Path"]["Input_Rpeak"], encoding=Params.UTF8_ENCODING, header=None).to_numpy().reshape(-1) - self.argmax_BCG = np_argmax(self.raw_BCG) - self.argmax_ECG = np_argmax(self.raw_ECG) except Exception as e: return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON["Open_Data_Exception"] + "\n" + format_exc()) @@ -1949,55 +1944,63 @@ class Data: def correlation_align(self, mode): try: if mode == "init": - anchor0 = [Config["front"]["anchor_R"], Config["front"]["anchor_J"]] - anchor1 = [Config["back"]["anchor_R"], Config["back"]["anchor_J"]] - Config["orgfs"] = ((int(anchor1[1]) - int(anchor0[1])) * Config["InputConfig"]["UseFreq"] / - (int(anchor1[0]) - int(anchor0[0]))) - Config["offset_anchor"] = anchor0[0] - anchor0[1] - - orgfs = Config["orgfs"] - off = Config["offset_anchor"] + Config["orgfs"] = ((int(Config["back"]["anchor_J"]) - int(Config["front"]["anchor_J"])) * Config["InputConfig"]["UseFreq"] / + (int(Config["back"]["anchor_R"]) - int(Config["front"]["anchor_R"]))) + Config["offset_anchor"] = Config["front"]["anchor_R"] - Config["front"]["anchor_J"] self.res_orgBcg = self.raw_orgBcg.copy() self.res_BCG = self.raw_BCG.copy() self.cut_ECG = self.raw_ECG.copy() self.cut_Rpeak = self.Rpeak.copy() - if off > 0: - self.cut_ECG = self.cut_ECG[off:] - anchor0[0] = anchor0[0] - off - anchor1[0] = anchor1[0] - off - idxs = where(self.cut_Rpeak > off)[0] - self.cut_Rpeak = self.cut_Rpeak[idxs] - off + Config["frontcut_index_BCG"], Config["frontcut_index_ECG"] = 0, 0 + + if Config["offset_anchor"] > 0: + self.cut_ECG = self.cut_ECG[Config["offset_anchor"]:] + Config["front"]["anchor_R"] = Config["front"]["anchor_R"] - Config["offset_anchor"] + Config["back"]["anchor_R"] = Config["back"]["anchor_R"] - Config["offset_anchor"] + idxs = where(self.cut_Rpeak > Config["offset_anchor"])[0] + self.cut_Rpeak = self.cut_Rpeak[idxs] - Config["offset_anchor"] + Config["frontcut_index_ECG"] += Config["offset_anchor"] else: - self.res_BCG = self.res_BCG[-off:] - self.res_orgBcg = self.res_orgBcg[-off:] - anchor0[1] = anchor0[1] + off - anchor1[1] = anchor1[1] + off + self.res_BCG = self.res_BCG[-Config["offset_anchor"]:] + self.res_orgBcg = self.res_orgBcg[-Config["offset_anchor"]:] + Config["front"]["anchor_J"] = Config["front"]["anchor_J"] + Config["offset_anchor"] + Config["back"]["anchor_J"] = Config["back"]["anchor_J"] + Config["offset_anchor"] + Config["frontcut_index_BCG"] -= Config["offset_anchor"] - self.res_BCG = resample(self.res_BCG, orgfs, Config["InputConfig"]["UseFreq"]) - self.res_orgBcg = resample(self.res_orgBcg, orgfs, Config["InputConfig"]["UseFreq"]) + self.res_BCG = resample(self.res_BCG, Config["orgfs"], Config["InputConfig"]["UseFreq"]) + self.res_orgBcg = resample(self.res_orgBcg, Config["orgfs"], Config["InputConfig"]["UseFreq"]) - anchor0[1] = round(int(anchor0[1]) * Config["InputConfig"]["UseFreq"] / orgfs) - anchor1[1] = round(int(anchor1[1]) * Config["InputConfig"]["UseFreq"] / orgfs) - off = anchor1[0] - anchor1[1] + Config["front"]["anchor_J"] = round(int(Config["front"]["anchor_J"]) * Config["InputConfig"]["UseFreq"] / Config["orgfs"]) + Config["back"]["anchor_J"] = round(int(Config["back"]["anchor_J"]) * Config["InputConfig"]["UseFreq"] / Config["orgfs"]) + Config["offset_anchor"] = Config["back"]["anchor_R"] - Config["back"]["anchor_J"] - if off > 0: - self.cut_ECG = self.cut_ECG[off:] - anchor0[0] = anchor0[0] - off - anchor1[0] = anchor1[0] - off - idxs = where(self.cut_Rpeak > off)[0] - self.cut_Rpeak = self.cut_Rpeak[idxs] - off + if Config["offset_anchor"] > 0: + self.cut_ECG = self.cut_ECG[Config["offset_anchor"]:] + Config["front"]["anchor_R"] = Config["front"]["anchor_R"] - Config["offset_anchor"] + Config["back"]["anchor_R"] = Config["back"]["anchor_R"] - Config["offset_anchor"] + idxs = where(self.cut_Rpeak > Config["offset_anchor"])[0] + self.cut_Rpeak = self.cut_Rpeak[idxs] - Config["offset_anchor"] + Config["frontcut_index_ECG"] += Config["offset_anchor"] * Config["orgfs"] / Config["InputConfig"]["UseFreq"] else: - self.res_BCG = self.res_BCG[-off:] - self.res_orgBcg = self.res_orgBcg[-off:] - anchor0[1] = anchor0[1] + off - anchor1[1] = anchor1[1] + off + self.res_BCG = self.res_BCG[-Config["offset_anchor"]:] + self.res_orgBcg = self.res_orgBcg[-Config["offset_anchor"]:] + Config["front"]["anchor_J"] = Config["front"]["anchor_J"] + Config["offset_anchor"] + Config["back"]["anchor_J"] = Config["back"]["anchor_J"] + Config["offset_anchor"] + Config["frontcut_index_BCG"] -= Config["offset_anchor"] * Config["orgfs"] / Config["InputConfig"]["UseFreq"] datalen = np_min([len(self.cut_ECG), len(self.res_BCG)]) self.cut_ECG = self.cut_ECG[:datalen] self.res_BCG = self.res_BCG[:datalen] self.res_orgBcg = self.res_orgBcg[:datalen] + + Config["frontcut_index_BCG"] = int(Config["frontcut_index_BCG"]) + Config["frontcut_index_ECG"] = int(Config["frontcut_index_ECG"]) + + Config["backcut_index_BCG"] = int(Config["frontcut_index_BCG"] + datalen) + Config["backcut_index_ECG"] = int(Config["frontcut_index_ECG"] + datalen) + a = np_max([np_max(self.cut_ECG), np_max(self.res_BCG)]) b = np_min([np_min(self.cut_ECG), np_min(self.res_BCG)]) peak_ECG, _ = find_peaks(self.cut_ECG) @@ -2009,8 +2012,8 @@ class Data: result = { "res_BCG": self.res_BCG, "cut_ECG": self.cut_ECG, - "anchor00": anchor0[0], - "anchor10": anchor1[0], + "anchor00": Config["front"]["anchor_R"], + "anchor10": Config["back"]["anchor_R"], "a": a, "b": b, "peak_ECG": peak_ECG, @@ -2034,38 +2037,41 @@ class Data: 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 + Config["offset_anchor"] = 0 else: self.correlation_align_point_match_ECG.sort() self.correlation_align_point_match_BCG.sort() - off = round(((int(self.correlation_align_point_match_ECG[1]) - int( + Config["offset_anchor"] = round(((int(self.correlation_align_point_match_ECG[1]) - int( self.correlation_align_point_match_BCG[1])) + (int(self.correlation_align_point_match_ECG[0]) - int( self.correlation_align_point_match_BCG[0]))) / 2) - anchor0 = [Config["front"]["anchor_R"], Config["front"]["anchor_J"]] - anchor1 = [Config["back"]["anchor_R"], Config["back"]["anchor_J"]] - - if off > 0: - self.cut_ECG = self.cut_ECG[off:] - anchor0[0] = anchor0[0] - off - anchor1[0] = anchor1[0] - off - self.cut_Rpeak = self.cut_Rpeak[where(self.cut_Rpeak > off)[0]] - off + if Config["offset_anchor"] > 0: + self.cut_ECG = self.cut_ECG[Config["offset_anchor"]:] + Config["front"]["anchor_R"] = Config["front"]["anchor_R"] - Config["offset_anchor"] + Config["back"]["anchor_R"] = Config["back"]["anchor_R"] - Config["offset_anchor"] + self.cut_Rpeak = self.cut_Rpeak[where(self.cut_Rpeak > Config["offset_anchor"])[0]] - Config["offset_anchor"] + Config["frontcut_index_ECG"] += Config["offset_anchor"] * Config["orgfs"] / Config["InputConfig"]["UseFreq"] else: - self.res_BCG = self.res_BCG[-off:] - self.res_orgBcg = self.res_orgBcg[-off:] - anchor0[1] = anchor0[1] + off - anchor1[1] = anchor1[1] + off + self.res_BCG = self.res_BCG[-Config["offset_anchor"]:] + self.res_orgBcg = self.res_orgBcg[-Config["offset_anchor"]:] + Config["front"]["anchor_J"] = Config["front"]["anchor_J"] + Config["offset_anchor"] + Config["back"]["anchor_J"] = Config["back"]["anchor_J"] + Config["offset_anchor"] + Config["frontcut_index_BCG"] -= Config["offset_anchor"] * Config["orgfs"] / Config["InputConfig"]["UseFreq"] datalen = np_min([len(self.cut_ECG), len(self.res_BCG)]) self.cut_ECG = self.cut_ECG[:datalen] self.res_BCG = self.res_BCG[:datalen] self.res_orgBcg = self.res_orgBcg[:datalen] + Config["frontcut_index_BCG"] = int(Config["frontcut_index_BCG"]) + Config["frontcut_index_ECG"] = int(Config["frontcut_index_ECG"]) + + Config["backcut_index_BCG"] = int(Config["frontcut_index_BCG"] + datalen) + Config["backcut_index_ECG"] = int(Config["frontcut_index_ECG"] + datalen) + idxs = where(self.cut_Rpeak < datalen)[0] self.cut_Rpeak = self.cut_Rpeak[idxs] - Config["offset_correct"] = off - self.cut_Jpeak = [] peaks, _ = find_peaks(self.res_BCG) for i in self.cut_Rpeak: @@ -2074,22 +2080,11 @@ class Data: self.cut_Jpeak.append(peaks[idx]) self.cut_Jpeak = asarray(self.cut_Jpeak).astype(int) - frontcut_index_BCG = int( - (self.argmax_BCG - np_argmax(self.res_BCG) / Config["InputConfig"]["UseFreq"] * Config["orgfs"])) - backcut_index_BCG = int(len(self.res_BCG) / Config["InputConfig"]["UseFreq"] * Config["orgfs"] + np_argmax( - self.raw_BCG) - np_argmax(self.res_BCG) / Config["InputConfig"]["UseFreq"] * Config["orgfs"]) - frontcut_index_ECG = self.argmax_ECG - np_argmax(self.cut_ECG) - backcut_index_ECG = len(self.cut_ECG) + self.argmax_ECG - np_argmax(self.cut_ECG) - - Config["frontcut_index_BCG"] = frontcut_index_BCG - Config["backcut_index_BCG"] = backcut_index_BCG - Config["frontcut_index_ECG"] = frontcut_index_ECG - Config["backcut_index_ECG"] = backcut_index_ECG except Exception as e: return Result().failure(info=Constants.PRECISELY_ALIGN_POSTPROCESS_VIEW_FAILURE + Constants.FAILURE_REASON["PostProcess_Align_Exception"] + "\n" + format_exc()) - info = f"{Constants.PRECISELY_ALIGN_POSTPROCESS_VIEW_FINISHED},BCG前后段被切割的坐标值为[{frontcut_index_BCG}, {backcut_index_BCG}],ECG前后段被切割的坐标值为[{frontcut_index_ECG}, {backcut_index_ECG}]" + info = f"{Constants.PRECISELY_ALIGN_POSTPROCESS_VIEW_FINISHED},BCG前后段被切割的坐标值为[{Config['frontcut_index_BCG']}, {Config['backcut_index_BCG']}],ECG前后段被切割的坐标值为[{Config['frontcut_index_ECG']}, {Config['backcut_index_ECG']}]" return Result().success(info=info) def save_alignInfo(self): diff --git a/func/utils/Constants.py b/func/utils/Constants.py index 1e96ac1..7eebd61 100644 --- a/func/utils/Constants.py +++ b/func/utils/Constants.py @@ -94,6 +94,30 @@ class Constants: background-color: rgba(255, 0, 0, 128); /* 鼠标悬停时的背景颜色 */ }""" + CHECKBOX_STYLE_NORMAL: str = ''' + QCheckBox { + border: 2px solid rgb(128, 128, 128); + border-radius: 11px; + } + QCheckBox::indicator{ + width: 20px; + height: 20px; + border-radius: 10px; + } + + QCheckBox::indicator:checked { + background-color: rgba(0, 255, 0, 192); + image: url(./image/correct.svg); + } + + QCheckBox::indicator:unchecked { + background-color: rgba(255, 0, 0, 128); + } + + QCheckBox::indicator:disabled { + background-color: rgba(119, 136, 153, 128); + }''' + FAILURE_REASON: dict = { "Path_Not_Exist": "(路径不存在)", "File_Not_Exist": "(数据文件不存在)", diff --git a/func/utils/PublicFunc.py b/func/utils/PublicFunc.py index 8945b48..474486a 100644 --- a/func/utils/PublicFunc.py +++ b/func/utils/PublicFunc.py @@ -2,7 +2,7 @@ from datetime import datetime from logging import error, info from pathlib import Path -from PySide6.QtWidgets import QMessageBox, QWidget, QPushButton, QProgressBar, QApplication, QRadioButton +from PySide6.QtWidgets import QMessageBox, QWidget, QPushButton, QProgressBar, QApplication, QRadioButton, QCheckBox from func.utils.Constants import Constants from func.utils.CustomException import TipsTypeValueNotExistError, MsgBoxTypeValueNotExistError @@ -152,6 +152,8 @@ class PublicFunc: if isinstance(widget, QPushButton): if widget.objectName() in buttonState["Default"].keys(): widget.setStyleSheet(Constants.LABELBTN_STYLE_NORMAL) + if isinstance(widget, QCheckBox): + widget.setStyleSheet(Constants.CHECKBOX_STYLE_NORMAL) @staticmethod def add_progressbar(mainWindow): diff --git a/image/correct.svg b/image/correct.svg new file mode 100644 index 0000000..3d45b67 --- /dev/null +++ b/image/correct.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/run.py b/run.py index 5e30e71..d4ac75c 100644 --- a/run.py +++ b/run.py @@ -1,3 +1,4 @@ +from importlib.util import find_spec from logging import getLogger, NOTSET, FileHandler, Formatter, StreamHandler, info from os import environ from pathlib import Path @@ -6,7 +7,6 @@ from time import strftime, localtime, time from PySide6.QtCore import Qt from PySide6.QtWidgets import QApplication from func.Module_mainwindow import MainWindow -import importlib.util if __name__ == '__main__': # 设置日志 @@ -28,7 +28,7 @@ if __name__ == '__main__': info("程序启动") # 解决 Could not find the Qt platform plugin "windows" - spec = importlib.util.find_spec("PySide6") + spec = find_spec("PySide6") if spec and spec.origin: dirname = Path(spec.origin).parent plugin_path = dirname / 'plugins' / 'platforms'