From b57bd9e7fae781e532e3660331683962991374a3 Mon Sep 17 00:00:00 2001
From: Yorusora <2944763079@qq.com>
Date: Thu, 22 May 2025 15:58:29 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BA=86<=E5=91=BC=E5=90=B8?=
=?UTF-8?q?=E5=8F=AF=E7=94=A8=E6=80=A7=E5=8F=8A=E9=97=B4=E6=9C=9F=E6=A0=87?=
=?UTF-8?q?=E6=B3=A8>=E7=9A=84=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84=20?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86<=E4=BA=BA=E5=B7=A5=E7=BA=A0?=
=?UTF-8?q?=E6=AD=A3>=E4=B8=AD=E5=A4=9A=E6=AC=A1=E5=AF=BB=E5=B3=B0?=
=?UTF-8?q?=E6=97=B6=E4=BC=9A=E4=BF=9D=E5=AD=98=E5=A4=9A=E4=B8=AA=E7=9B=B8?=
=?UTF-8?q?=E5=90=8C=E5=B3=B0=E5=80=BC=E6=A8=AA=E5=9D=90=E6=A0=87=E7=9A=84?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
func/Module_label_check.py | 3 +-
func/Module_resp_quality_label.py | 1552 +++++++++++++++++
func/utils/ConfigParams.py | 3 +-
func/utils/Constants.py | 89 +-
.../MainWindow_resp_quality_label.py | 27 +-
.../MainWindow_resp_quality_label.ui | 25 +-
.../resp_quality_label_input_setting.py | 10 +-
.../resp_quality_label_input_setting.ui | 2 +-
8 files changed, 1637 insertions(+), 74 deletions(-)
create mode 100644 func/Module_resp_quality_label.py
diff --git a/func/Module_label_check.py b/func/Module_label_check.py
index 962c031..0a7cbf2 100644
--- a/func/Module_label_check.py
+++ b/func/Module_label_check.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 append, delete, arange
+from numpy import append, delete, arange, setdiff1d
from overrides import overrides
from pandas import read_csv, DataFrame
from scipy.signal import find_peaks
@@ -818,6 +818,7 @@ class MainWindow_label_check(QMainWindow):
height=Config["FindPeaks"]["MinHeight"],
distance=Config["FindPeaks"]["MinInterval"])
peaks_idx = peaks_idx + int(rect_left)
+ peaks_idx = setdiff1d(peaks_idx, self.data.corrected_peak)
if len(peaks_idx) != 0:
PublicFunc.text_output(self.ui, f"{Constants.LABEL_CHECK_ADD_POINTS_SUCCESSFULLY}{peaks_idx}",
Constants.TIPS_TYPE_INFO)
diff --git a/func/Module_resp_quality_label.py b/func/Module_resp_quality_label.py
new file mode 100644
index 0000000..38d21b7
--- /dev/null
+++ b/func/Module_resp_quality_label.py
@@ -0,0 +1,1552 @@
+from gc import collect
+from pathlib import Path
+from traceback import format_exc
+
+import matplotlib.pyplot as plt
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtGui import QAction, QFont
+from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication, QTableWidget, QTableWidgetItem
+from matplotlib import gridspec, patches
+from matplotlib.backends.backend_qt import NavigationToolbar2QT
+from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
+from numpy import array, full, int64, append, zeros, where, arange, float64, nan, place, delete, setdiff1d
+from overrides import overrides
+from pandas import read_csv, DataFrame
+from scipy.signal import find_peaks, resample
+from yaml import dump, load, FullLoader
+
+from func.utils.PublicFunc import PublicFunc
+from func.utils.Constants import Constants, ConfigParams
+from func.Filters.Preprocessing import Butterworth_for_BCG_PreProcess, Butterworth_for_ECG_PreProcess
+from func.utils.Result import Result
+from func.utils.resp_quality_label import pre_process, get_slice, evaluate_quality
+from func.utils.resp_quality_label_filter import get_bandpass_bcgsignal
+
+from ui.MainWindow.MainWindow_resp_quality_label import Ui_MainWindow_resp_quality_label
+from ui.setting.resp_quality_label_input_setting import Ui_MainWindow_resp_quality_label_input_setting
+
+
+Config = {
+
+}
+
+ButtonState = {
+ "Default": {
+ "pushButton_input_setting": True,
+ "pushButton_input_and_calculate_peaks": True,
+ "pushButton_calculate_peaks": False,
+ "pushButton_calculate_peaks_save": False,
+ "pushButton_input_data_and_label": True,
+ "pushButton_autoqualitylabel_recalculate": False,
+ "pushButton_refilter_orgBcg": False,
+ "pushButton_valid": False,
+ "pushButton_invalid": False,
+ "pushButton_reset": False,
+ "pushButton_prev": False,
+ "pushButton_next": False,
+ "pushButton_save": False
+ },
+ "Current": {
+ "pushButton_input_setting": True,
+ "pushButton_input_and_calculate_peaks": True,
+ "pushButton_calculate_peaks": False,
+ "pushButton_calculate_peaks_save": False,
+ "pushButton_input_data_and_label": True,
+ "pushButton_autoqualitylabel_recalculate": False,
+ "pushButton_refilter_orgBcg": False,
+ "pushButton_valid": False,
+ "pushButton_invalid": False,
+ "pushButton_reset": False,
+ "pushButton_prev": False,
+ "pushButton_next": False,
+ "pushButton_save": False
+ }
+}
+
+
+class SettingWindow(QMainWindow):
+
+ def __init__(self, root_path, sampID):
+ super(SettingWindow, self).__init__()
+ self.ui = Ui_MainWindow_resp_quality_label_input_setting()
+ self.ui.setupUi(self)
+
+ self.root_path = root_path
+ self.sampID = sampID
+
+ self.config = None
+ self.__read_config__()
+
+ self.ui.spinBox_input_freq_signal_OrgBCG.valueChanged.connect(self.__update_ui__)
+ self.ui.spinBox_input_freq_signal_Tho.valueChanged.connect(self.__update_ui__)
+ self.ui.pushButton_confirm.clicked.connect(self.__write_config__)
+ self.ui.pushButton_cancel.clicked.connect(self.__rollback_config__)
+ self.ui.pushButton_cancel.clicked.connect(self.close)
+
+ def __read_config__(self):
+ if not Path(ConfigParams.RESP_QUALITY_LABEL_CONFIG_FILE_PATH).exists():
+ with open(ConfigParams.RESP_QUALITY_LABEL_CONFIG_FILE_PATH, "w") as f:
+ dump(ConfigParams.RESP_QUALITY_LABEL_CONFIG_NEW_CONTENT, f)
+
+ with open(ConfigParams.RESP_QUALITY_LABEL_CONFIG_FILE_PATH, "r") as f:
+ file_config = load(f.read(), Loader=FullLoader)
+ Config.update(file_config)
+ self.config = file_config
+
+ Config.update({
+ "Path": {
+ "Input_OrgBCG": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_ORGBCG_ALIGNED /
+ Path(str(self.sampID)))),
+ "Input_Tho": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_PSG_ALIGNED /
+ Path(str(self.sampID)))),
+ "Input_Artifact": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_LABEL /
+ Path(str(self.sampID)))),
+ "Save_Resp_quality_label": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_LABEL /
+ Path(str(self.sampID)))),
+ "Save_Tho_peak": str((Path(self.root_path) / ConfigParams.PUBLIC_PATH_LABEL /
+ Path(str(self.sampID))))
+ },
+ "CurrentPartNum": 1,
+ "DataPartNum": 0,
+ "CurrentOrgBCGIndex": 0,
+ "CurrentThoIndex": 0
+ })
+
+ # 数据回显
+ self.ui.spinBox_input_freq_signal_OrgBCG.setValue(Config["InputConfig"]["OrgBCGFreq"])
+ self.ui.spinBox_input_freq_signal_Tho.setValue(Config["InputConfig"]["ThoFreq"])
+ self.ui.plainTextEdit_file_path_input_signal_OrgBCG.setPlainText(Config["Path"]["Input_OrgBCG"])
+ self.ui.plainTextEdit_file_path_input_signal_Tho.setPlainText(Config["Path"]["Input_Tho"])
+ self.ui.plainTextEdit_file_path_input_artifact.setPlainText(Config["Path"]["Input_Artifact"])
+ self.ui.plainTextEdit_file_path_save_Resp_quality_label.setPlainText(Config["Path"]["Save_Resp_quality_label"])
+ self.ui.plainTextEdit_file_path_save_Tho_peak.setPlainText(Config["Path"]["Save_Tho_peak"])
+
+ def __write_config__(self):
+ # 从界面写入配置
+ Config["InputConfig"]["OrgBCGFreq"] = self.ui.spinBox_input_freq_signal_OrgBCG.value()
+ Config["InputConfig"]["ThoFreq"] = self.ui.spinBox_input_freq_signal_Tho.value()
+ Config["Path"]["Input_OrgBCG"] = self.ui.plainTextEdit_file_path_input_signal_OrgBCG.toPlainText()
+ Config["Path"]["Input_Tho"] = self.ui.plainTextEdit_file_path_input_signal_Tho.toPlainText()
+ Config["Path"]["Input_Artifact"] = self.ui.plainTextEdit_file_path_input_artifact.toPlainText()
+ Config["Path"]["Save_Resp_quality_label"] = self.ui.plainTextEdit_file_path_save_Resp_quality_label.toPlainText()
+ Config["Path"]["Save_Tho_peak"] = self.ui.plainTextEdit_file_path_save_Tho_peak.toPlainText()
+
+ # 保存配置到文件
+ self.config["InputConfig"]["OrgBCGFreq"] = self.ui.spinBox_input_freq_signal_OrgBCG.value()
+ self.config["InputConfig"]["ThoFreq"] = self.ui.spinBox_input_freq_signal_Tho.value()
+
+ with open(ConfigParams.RESP_QUALITY_LABEL_CONFIG_FILE_PATH, "w") as f:
+ dump(self.config, f)
+
+ self.close()
+
+ def __rollback_config__(self):
+ self.__read_config__()
+
+ def __update_ui__(self):
+ self.ui.plainTextEdit_file_path_input_signal_OrgBCG.setPlainText(
+ str((Path(self.root_path) /
+ ConfigParams.PUBLIC_PATH_ORGBCG_ALIGNED /
+ Path(str(self.sampID)) /
+ Path(ConfigParams.ORGBCG_SYNC +
+ str(self.ui.spinBox_input_freq_signal_OrgBCG.value()) +
+ ConfigParams.ENDSWITH_TXT))))
+ self.ui.plainTextEdit_file_path_input_signal_Tho.setPlainText(
+ str((Path(self.root_path) /
+ ConfigParams.PUBLIC_PATH_PSG_ALIGNED /
+ Path(str(self.sampID)) /
+ Path(ConfigParams.THO_SYNC +
+ str(self.ui.spinBox_input_freq_signal_Tho.value()) +
+ ConfigParams.ENDSWITH_TXT))))
+
+
+class MainWindow_resp_quality_label(QMainWindow):
+
+ def __init__(self):
+ super(MainWindow_resp_quality_label, self).__init__()
+ self.ui = Ui_MainWindow_resp_quality_label()
+ self.ui.setupUi(self)
+
+ self.mode = None
+ self.root_path = None
+ self.sampID = None
+
+ self.data = None
+
+ self.setting = None
+
+ # 初始化进度条
+ self.progressbar = None
+ PublicFunc.add_progressbar(self)
+
+ #初始化画框
+ self.fig = None
+ self.canvas = None
+ self.figToolbar = None
+ self.gs = None
+ self.ax0 = None
+ self.ax1 = None
+ self.fig_spectrum = None
+ self.canvas_spectrum = None
+ self.figToolbar_spectrum = None
+ self.gs_spectrum = None
+ self.ax0_spectrum = None
+ self.ax1_spectrum = None
+ self.is_left_button_pressed = None
+ self.is_right_button_pressed = None
+
+ self.tho_peak_point = None
+
+ self.cid1 = None
+ self.cid2 = None
+
+ self.msgBox = QMessageBox()
+ self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE)
+
+ @overrides
+ def show(self, root_path, sampID):
+ super().show()
+ self.root_path = root_path
+ self.sampID = sampID
+
+ self.setting = SettingWindow(root_path, sampID)
+
+ # 初始化画框
+ self.fig = plt.figure(figsize=(12, 9), dpi=100)
+ self.canvas = FigureCanvasQTAgg(self.fig)
+ self.figToolbar = CustomNavigationToolbar(self.canvas, self)
+ self.figToolbar.action_Label_Multiple.setEnabled(False)
+ for action in self.figToolbar._actions.values():
+ action.setEnabled(False)
+ for action in self.figToolbar.actions():
+ if action.text() == "Subplots" or action.text() == "Customize":
+ self.figToolbar.removeAction(action)
+ self.figToolbar._actions['home'].triggered.connect(self.toggle_home)
+ self.figToolbar.action_Label_Multiple.triggered.connect(self.toggle_changeLabel_Multiple_mode)
+ self.ui.verticalLayout_canvas.addWidget(self.canvas)
+ self.ui.verticalLayout_canvas.addWidget(self.figToolbar)
+ self.gs = gridspec.GridSpec(2, 1, height_ratios=[1, 1])
+ self.fig.subplots_adjust(top=0.98, bottom=0.05, right=0.98, left=0.1, hspace=0.1, wspace=0)
+ self.ax0 = self.fig.add_subplot(self.gs[0])
+ self.ax0.grid(True)
+ self.ax0.xaxis.set_major_formatter(ConfigParams.FORMATTER)
+ self.ax1 = self.fig.add_subplot(self.gs[1])
+ self.ax1.grid(True)
+ self.ax1.xaxis.set_major_formatter(ConfigParams.FORMATTER)
+
+ # 初始化频谱画框
+ self.fig_spectrum = plt.figure(figsize=(12, 9), dpi=100)
+ self.canvas_spectrum = FigureCanvasQTAgg(self.fig_spectrum)
+ self.figToolbar_spectrum = NavigationToolbar2QT(self.canvas_spectrum)
+ self.figToolbar_spectrum.setStyleSheet(Constants.RESP_QUALITY_LABEL_FIGTOOLBAR_SPECTRUM_STYLESHEET)
+ for action in self.figToolbar_spectrum._actions.values():
+ action.setEnabled(False)
+ for action in self.figToolbar_spectrum.actions():
+ if action.text() == "Subplots" or action.text() == "Customize":
+ self.figToolbar_spectrum.removeAction(action)
+ self.ui.verticalLayout_spectrum_display.addWidget(self.canvas_spectrum)
+ self.ui.verticalLayout_spectrum_display.addWidget(self.figToolbar_spectrum)
+ self.gs_spectrum = gridspec.GridSpec(2, 1, height_ratios=[1, 1])
+ self.fig_spectrum.subplots_adjust(top=0.95, bottom=0.05, right=0.98, left=0.1, hspace=0.2, wspace=0)
+ self.ax0_spectrum = self.fig_spectrum.add_subplot(self.gs_spectrum[0])
+ self.ax0_spectrum.grid(True)
+ self.ax0_spectrum.set_title(Constants.RESP_QUALITY_LABEL_SPECTRUM_BDR_TITLE, fontsize=8)
+ self.ax1_spectrum = self.fig_spectrum.add_subplot(self.gs_spectrum[1])
+ self.ax1_spectrum.grid(True)
+ self.ax1_spectrum.set_title(Constants.RESP_QUALITY_LABEL_SPECTRUM_THO_TITLE, fontsize=8)
+
+ PublicFunc.__resetAllButton__(self, ButtonState)
+
+ self.ui.doubleSpinBox_quality_threshold1.setValue(Config["Threshold"]["Low"])
+ self.ui.doubleSpinBox_quality_threshold2.setValue(Config["Threshold"]["High"])
+ self.ui.doubleSpinBox_findpeaks_min_interval.setValue(Config["FindPeaks"]["MinInterval"])
+ self.ui.doubleSpinBox_findpeaks_min_height.setValue(Config["FindPeaks"]["MinHeight"])
+ self.ui.doubleSpinBox_fillterMode_custom_low.setValue(Config["Filter"]["BandPassLow"])
+ self.ui.doubleSpinBox_fillterMode_custom_high.setValue(Config["Filter"]["BandPassHigh"])
+
+ self.ui.tableWidget_labeled.setHorizontalHeaderLabels(['信号片段', '质量类型'])
+ self.ui.tableWidget_tobelabeled.setHorizontalHeaderLabels(['信号片段', '质量类型'])
+ self.ui.tableWidget_labeled.setEditTriggers(QTableWidget.NoEditTriggers)
+ self.ui.tableWidget_tobelabeled.setEditTriggers(QTableWidget.NoEditTriggers)
+ self.ui.tableWidget_labeled.horizontalHeader().setStretchLastSection(True)
+ self.ui.tableWidget_tobelabeled.horizontalHeader().setStretchLastSection(True)
+
+ self.ui.pushButton_input_setting.clicked.connect(self.setting.show)
+ self.ui.pushButton_input_and_calculate_peaks.clicked.connect(self.__slot_btn_input_and_calculate_peaks__)
+ self.ui.pushButton_calculate_peaks.clicked.connect(self.__slot_btn_calculate_peaks__)
+ self.ui.pushButton_calculate_peaks_save.clicked.connect(self.__slot_btn_save_calculate_peak__)
+ self.ui.pushButton_input_and_label.clicked.connect(self.__slot_btn_input_and_label__)
+ self.ui.pushButton_autoqualitylabel_recalculate.clicked.connect(self.__slot_btn_autoqualitylabel_recalculate__)
+ self.ui.pushButton_refilter_orgBcg.clicked.connect(self.__slot_btn_refilter_orgBcg__)
+ self.ui.pushButton_prev.clicked.connect(self.__slot_btn_move__)
+ self.ui.pushButton_next.clicked.connect(self.__slot_btn_move__)
+ self.ui.tableWidget_labeled.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__)
+ self.ui.tableWidget_tobelabeled.cellDoubleClicked.connect(self.__slot_tableWidget_on_cell_double_clicked__)
+ self.ui.pushButton_valid.clicked.connect(self.__slot_btn_label__)
+ self.ui.pushButton_invalid.clicked.connect(self.__slot_btn_label__)
+ self.ui.pushButton_reset.clicked.connect(self.__slot_btn_label__)
+ self.ui.pushButton_save.clicked.connect(self.__slot_btn_save__)
+
+ self.ui.lineEdit_filter_labeled.textChanged.connect(self.__slot_lineEdit_filter__)
+ self.ui.lineEdit_filter_tobelabeled.textChanged.connect(self.__slot_lineEdit_filter__)
+ self.ui.doubleSpinBox_quality_threshold1.valueChanged.connect(self.update_config)
+ self.ui.doubleSpinBox_quality_threshold2.valueChanged.connect(self.update_config)
+ self.ui.doubleSpinBox_findpeaks_min_interval.valueChanged.connect(self.update_config)
+ self.ui.doubleSpinBox_findpeaks_min_height.valueChanged.connect(self.update_config)
+ self.ui.doubleSpinBox_fillterMode_custom_low.valueChanged.connect(self.update_config)
+ self.ui.doubleSpinBox_fillterMode_custom_high.valueChanged.connect(self.update_config)
+
+ @overrides
+ def closeEvent(self, event):
+ reply = QMessageBox.question(self, '确认', '确认退出吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
+ if reply == QMessageBox.Yes:
+
+ PublicFunc.__disableAllButton__(self, ButtonState)
+
+ PublicFunc.statusbar_show_msg(self, PublicFunc.format_status_msg(Constants.SHUTTING_DOWN))
+ QApplication.processEvents()
+
+ # 清空画框
+ del self.tho_peak_point
+ if self.ax0 is not None:
+ self.ax0.clear()
+ if self.ax1 is not None:
+ self.ax1.clear()
+ if self.ax0_spectrum is not None:
+ self.ax0_spectrum.clear()
+ if self.ax1_spectrum is not None:
+ self.ax1_spectrum.clear()
+
+ # 释放资源
+ del self.data
+ self.fig.clf()
+ plt.close(self.fig)
+ self.deleteLater()
+ collect()
+ self.canvas = None
+ event.accept()
+ else:
+ event.ignore()
+
+ def __reset__(self):
+ ButtonState["Current"].update(ButtonState["Default"].copy())
+
+ def __plot__(self):
+ # 清空画框
+ self.reset_axes()
+
+ sender = self.sender()
+
+ if sender == self.ui.pushButton_calculate_peaks:
+ try:
+ self.ax1.plot(self.data.Tho_Processed,
+ label=Constants.RESP_QUALITY_LABEL_PLOT_LABEL_THO, color=Constants.PLOT_COLOR_BLUE)
+ self.tho_peak_point, = self.ax1.plot(self.data.Tho_peak, self.data.Tho_peak_y, 'ro',
+ label=Constants.RESP_QUALITY_LABEL_PLOT_LABEL_THO_PEAKS)
+ self.ax1.legend(loc=Constants.PLOT_UPPER_RIGHT)
+ self.canvas.draw()
+ except Exception as e:
+ return Result().failure(info=Constants.DRAW_FAILURE + "\n" + format_exc())
+ return Result().success(info=Constants.DRAW_FINISHED)
+ elif (sender == self.ui.pushButton_input_and_label or
+ sender == self.ui.pushButton_refilter_orgBcg or
+ sender == self.ui.pushButton_prev or
+ sender == self.ui.pushButton_next or
+ sender == self.ui.tableWidget_labeled or
+ sender == self.ui.tableWidget_tobelabeled):
+ try:
+ if Config["CurrentPartNum"] != Config["DataPartNum"]:
+ begin_OrgBCG = Config["CurrentOrgBCGIndex"]
+ end_OrgBCG = (Config["CurrentOrgBCGIndex"] + ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["OrgBCGUseFreq"]))
+ begin_Tho = Config["CurrentThoIndex"]
+ end_Tho = (Config["CurrentThoIndex"] + ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]))
+ else:
+ begin_OrgBCG = Config["CurrentOrgBCGIndex"]
+ end_OrgBCG = len(self.data.OrgBCG_Processed)
+ begin_Tho = Config["CurrentThoIndex"]
+ end_Tho = len(self.data.Tho_Processed)
+
+ if self.ui.radioButton_orgBcg_fillterMode_preset.isChecked():
+ mode = "preset"
+ elif self.ui.radioButton_orgBcg_fillterMode_custom.isChecked():
+ mode = "custom"
+ else:
+ raise ValueError("模式不存在")
+ BDR, band_low, band_high, bcg_spectrum, bcg_freq, tho_spectrum, tho_freq = (
+ self.data.preprocess_getBDR(self.data.OrgBCG_Processed[begin_OrgBCG:end_OrgBCG],
+ self.data.Tho_Processed[begin_Tho:end_Tho], mode))
+ # 绘制数据
+ self.ax0.plot(arange(begin_OrgBCG, end_OrgBCG), BDR,
+ label=Constants.RESP_QUALITY_LABEL_PLOT_LABEL_ORGBCG,
+ color=Constants.PLOT_COLOR_BLUE)
+ self.ax1.plot(arange(begin_Tho, end_Tho), self.data.Tho_Processed[begin_Tho:end_Tho],
+ label=Constants.RESP_QUALITY_LABEL_PLOT_LABEL_THO,
+ color=Constants.PLOT_COLOR_BLUE)
+ self.tho_peak_point, = self.ax1.plot(
+ [value for value in self.data.Tho_peak if begin_Tho <= value <= end_Tho],
+ [self.data.Tho_peak_y[x] for x in
+ [index for index, value in enumerate(self.data.Tho_peak) if begin_Tho <= value <= end_Tho]], 'ro',
+ label=Constants.RESP_QUALITY_LABEL_PLOT_LABEL_THO_PEAKS)
+ self.ax0.legend(loc=Constants.PLOT_UPPER_RIGHT)
+ self.ax1.legend(loc=Constants.PLOT_UPPER_RIGHT)
+ # 绘制体动
+ mask = self.data.artifact_mask[begin_OrgBCG: end_OrgBCG] == 1
+ mask = (BDR * mask).astype(float64)
+ place(mask, mask == 0, nan)
+ self.ax0.plot(arange(begin_OrgBCG, end_OrgBCG), mask,
+ label=f"{Constants.RESP_QUALITY_LABEL_PLOT_LABEL_ARTIFACT}",
+ color=Constants.PLOT_COLOR_PINK, linestyle="-")
+ # 绘制频谱
+ self.ax0_spectrum.plot(bcg_freq, bcg_spectrum,
+ label=Constants.RESP_QUALITY_LABEL_SPECTRUM_ORGBCG_LABEL,
+ color=Constants.PLOT_COLOR_BLUE)
+ self.ax0_spectrum.axvline(x=band_low,
+ label=Constants.RESP_QUALITY_LABEL_SPECTRUM_BDR_LABEL,
+ color=Constants.PLOT_COLOR_ORANGE,
+ linestyle='--')
+ self.ax0_spectrum.axvline(x=band_high,
+ color=Constants.PLOT_COLOR_ORANGE,
+ linestyle='--')
+ self.ax1_spectrum.plot(tho_freq, tho_spectrum,
+ label=Constants.RESP_QUALITY_LABEL_SPECTRUM_THO_LABEL,
+ color=Constants.PLOT_COLOR_BLUE)
+ self.ax0_spectrum.legend(loc=Constants.PLOT_UPPER_RIGHT)
+ self.ax1_spectrum.legend(loc=Constants.PLOT_UPPER_RIGHT)
+
+ self.canvas.draw()
+ self.canvas_spectrum.draw()
+ self.cid1 = self.ax0.callbacks.connect('xlim_changed', self.on_xlim_changed)
+ self.cid2 = self.ax1.callbacks.connect('xlim_changed', self.on_xlim_changed)
+ except Exception as e:
+ return Result().failure(info=Constants.DRAW_FAILURE + "\n" + format_exc())
+ return Result().success(info=Constants.DRAW_FINISHED)
+ else:
+ self.canvas.draw()
+ self.ax0.autoscale(False)
+ self.ax1.autoscale(False)
+ return Result().failure(info=Constants.DRAW_FAILURE)
+
+ def __redraw_peaks__(self):
+ try:
+ if Config["CurrentPartNum"] != Config["DataPartNum"]:
+ begin_Tho = Config["CurrentThoIndex"]
+ end_Tho = (Config["CurrentThoIndex"] + ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]))
+ elif Config["CurrentPartNum"] == Config["DataPartNum"]:
+ begin_Tho = Config["CurrentThoIndex"]
+ end_Tho = len(self.data.Tho_Processed)
+
+ self.tho_peak_point.remove()
+ self.tho_peak_point, = self.ax1.plot(
+ [value for value in self.data.Tho_peak if begin_Tho <= value <= end_Tho],
+ [self.data.Tho_peak_y[x] for x in
+ [index for index, value in enumerate(self.data.Tho_peak) if begin_Tho <= value <= end_Tho]], 'ro',
+ label=Constants.RESP_QUALITY_LABEL_PLOT_LABEL_THO_PEAKS)
+
+ self.ax1.autoscale(False)
+ self.canvas.draw()
+ return Result().success(info=Constants.DRAW_FINISHED)
+ except Exception as e:
+ self.canvas.draw()
+ self.ax0.autoscale(False)
+ self.ax1.autoscale(False)
+ return Result().failure(info=Constants.DRAW_FAILURE)
+
+ def update_tableWidget(self):
+ label_list = {
+ "已标注":
+ where((self.data.resp_quality_label == Constants.RESP_QUALITY_LABEL_A_QUALITY) |
+ (self.data.resp_quality_label == Constants.RESP_QUALITY_LABEL_B_QUALITY))[0] + 1,
+ "未标注":
+ where(self.data.resp_quality_label == Constants.RESP_QUALITY_LABEL_C_QUALITY)[0] + 1
+ }
+ self.ui.tableWidget_labeled.setRowCount(
+ label_list["已标注"].__len__())
+ self.ui.tableWidget_tobelabeled.setRowCount(
+ label_list["未标注"].__len__())
+ for label_name, label in label_list.items():
+ if label_name == "已标注":
+ tableWidget = self.ui.tableWidget_labeled
+ elif label_name == "未标注":
+ tableWidget = self.ui.tableWidget_tobelabeled
+ else:
+ raise ValueError("label_name不存在")
+ for row, value in enumerate(label):
+ item = QTableWidgetItem(str(value).strip())
+ tableWidget.setItem(row, 0, item)
+ item = QTableWidgetItem(Constants.RESP_QUALITY_LABEL_KEY_VALUE[self.data.resp_quality_label[value - 1]])
+ tableWidget.setItem(row, 1, item)
+ self.ui.tableWidget_labeled.verticalScrollBar().setValue(self.ui.tableWidget_labeled.verticalScrollBar().maximum())
+ self.ui.tableWidget_tobelabeled.verticalScrollBar().setValue(self.ui.tableWidget_tobelabeled.verticalScrollBar().maximum())
+
+ def update_info(self):
+ try:
+ self.ui.lineEdit_current_part_num.setText("{} / {}".format(Config["CurrentPartNum"], Config["DataPartNum"]))
+ self.ui.lineEdit_current_part_label.setText(
+ str(Constants.RESP_QUALITY_LABEL_KEY_VALUE[self.data.resp_quality_label[Config["CurrentPartNum"] - 1]]))
+ except Exception as e:
+ return Result().failure(info=Constants.UPDATE_FAILURE +
+ Constants.FAILURE_REASON["Update_Info_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=Constants.UPDATE_FINISHED)
+
+ def __slot_btn_input_and_calculate_peaks__(self):
+ PublicFunc.__disableAllButton__(self, ButtonState)
+
+ self.reset_axes()
+ self.canvas.draw()
+
+ self.data = Data()
+
+ # 导入数据
+ PublicFunc.progressbar_update(self, 1, 3, Constants.INPUTTING_DATA, 0)
+ result = self.data.open_file_calculate_peaks()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(1/3)" + 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/3)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 重采样
+ PublicFunc.progressbar_update(self, 2, 3, Constants.RESAMPLING_DATA, 40)
+ result = self.data.resample_tho()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(2/3)" + 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/3)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 数据预处理
+ PublicFunc.progressbar_update(self, 3, 3, Constants.PREPROCESSING_DATA, 70)
+ result = self.data.preprocess_tho()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(3/3)" + 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/3)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ self.__reset__()
+ ButtonState["Current"]["pushButton_input_setting"] = True
+ ButtonState["Current"]["pushButton_input_and_calculate_peaks"] = True
+ ButtonState["Current"]["pushButton_calculate_peaks"] = True
+ ButtonState["Current"]["pushButton_calculate_peaks_save"] = True
+ ButtonState["Current"]["pushButton_input_data_and_label"] = True
+ ButtonState["Current"]["pushButton_autoqualitylabel_recalculate"] = False
+ ButtonState["Current"]["pushButton_refilter_orgBcg"]: False
+ ButtonState["Current"]["pushButton_valid"] = False
+ ButtonState["Current"]["pushButton_invalid"] = False
+ ButtonState["Current"]["pushButton_reset"] = False
+ ButtonState["Current"]["pushButton_prev"] = False
+ ButtonState["Current"]["pushButton_next"] = False
+ ButtonState["Current"]["pushButton_save"] = False
+ for action in self.figToolbar._actions.values():
+ action.setEnabled(True)
+ self.figToolbar.action_Label_Multiple.setEnabled(False)
+
+ PublicFunc.finish_operation(self, ButtonState)
+
+ def __slot_btn_calculate_peaks__(self):
+ PublicFunc.__disableAllButton__(self, ButtonState)
+
+ self.reset_axes()
+ self.canvas.draw()
+
+ # 计算峰值
+ PublicFunc.progressbar_update(self, 1, 2, Constants.RESP_QUALITY_LABEL_CALCULATING_PEAK, 0)
+ result = self.data.calculate_tho_peak()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(1/2)" + 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/2)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 绘图
+ PublicFunc.progressbar_update(self, 2, 2, Constants.DRAWING_DATA, 50)
+ result = self.__plot__()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(2/2)" + 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/2)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ ButtonState["Current"]["pushButton_input_setting"] = True
+ ButtonState["Current"]["pushButton_input_and_calculate_peaks"] = True
+ ButtonState["Current"]["pushButton_calculate_peaks"] = True
+ ButtonState["Current"]["pushButton_calculate_peaks_save"] = True
+ ButtonState["Current"]["pushButton_input_data_and_label"] = True
+ ButtonState["Current"]["pushButton_autoqualitylabel_recalculate"] = False
+ ButtonState["Current"]["pushButton_refilter_orgBcg"]: False
+ ButtonState["Current"]["pushButton_valid"] = False
+ ButtonState["Current"]["pushButton_invalid"] = False
+ ButtonState["Current"]["pushButton_reset"] = False
+ ButtonState["Current"]["pushButton_prev"] = False
+ ButtonState["Current"]["pushButton_next"] = False
+ ButtonState["Current"]["pushButton_save"] = False
+
+ PublicFunc.finish_operation(self, ButtonState)
+
+ def __slot_btn_save_calculate_peak__(self):
+ reply = QMessageBox.question(self, Constants.QUESTION_TITLE,
+ Constants.QUESTION_CONTENT + Config["Path"]["Save_Tho_peak"],
+ QMessageBox.Yes | QMessageBox.No,
+ QMessageBox.Yes)
+ if reply == QMessageBox.Yes:
+ PublicFunc.__disableAllButton__(self, ButtonState)
+
+ # 保存
+ PublicFunc.progressbar_update(self, 1, 1, Constants.SAVING_DATA, 0)
+ result = self.data.save_tho_peak()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(1/1)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(1/1)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ PublicFunc.msgbox_output(self, result.info, Constants.TIPS_TYPE_INFO)
+ PublicFunc.finish_operation(self, ButtonState)
+
+ def __slot_btn_input_and_label__(self):
+ PublicFunc.__disableAllButton__(self, ButtonState)
+
+ self.reset_axes()
+ self.canvas.draw()
+
+ self.data = Data()
+
+ # 导入数据
+ PublicFunc.progressbar_update(self, 1, 6, Constants.INPUTTING_DATA, 0)
+ result = self.data.open_file_label()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(1/6)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(1/6)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 获取标签存档
+ PublicFunc.progressbar_update(self, 2, 6, Constants.LOADING_ARCHIVE, 30)
+ result = self.data.get_archive_resp_quality()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(2/6)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(2/6)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 获取峰值存档
+ PublicFunc.progressbar_update(self, 3, 6, Constants.LOADING_ARCHIVE, 30)
+ result = self.data.get_archive_tho_peak()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(3/6)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(3/6)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 重采样
+ PublicFunc.progressbar_update(self, 4, 6, Constants.RESAMPLING_DATA, 40)
+ result = self.data.resample_tho_and_OrgBCG()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(4/6)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(4/6)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 数据预处理
+ PublicFunc.progressbar_update(self, 5, 6, Constants.PREPROCESSING_DATA, 70)
+ result = self.data.preprocess_tho_and_OrgBCG()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(5/6)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(5/6)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ # 绘图
+ PublicFunc.progressbar_update(self, 6, 6, Constants.DRAWING_DATA, 90)
+ self.ax0.autoscale(True)
+ self.ax1.autoscale(True)
+ self.ax1.relim()
+ self.ax1.autoscale_view()
+ result = self.__plot__()
+ self.ax0.autoscale(False)
+ self.ax1.autoscale(False)
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(6/6)" + result.info, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
+ PublicFunc.finish_operation(self, ButtonState)
+ return
+ else:
+ PublicFunc.text_output(self.ui, "(6/6)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ Config["CurrentPartNum"] = 1
+ self.update_info()
+ self.update_tableWidget()
+ self.__reset__()
+ ButtonState["Current"]["pushButton_input_setting"] = False
+ ButtonState["Current"]["pushButton_input_and_calculate_peaks"] = False
+ ButtonState["Current"]["pushButton_calculate_peaks"] = False
+ ButtonState["Current"]["pushButton_calculate_peaks_save"] = False
+ ButtonState["Current"]["pushButton_input_data_and_label"] = False
+ ButtonState["Current"]["pushButton_autoqualitylabel_recalculate"] = True
+ ButtonState["Current"]["pushButton_refilter_orgBcg"] = True
+ ButtonState["Current"]["pushButton_valid"] = True
+ ButtonState["Current"]["pushButton_invalid"] = True
+ ButtonState["Current"]["pushButton_reset"] = True
+ ButtonState["Current"]["pushButton_prev"] = True
+ ButtonState["Current"]["pushButton_next"] = True
+ ButtonState["Current"]["pushButton_save"] = True
+ for action in self.figToolbar._actions.values():
+ action.setEnabled(True)
+ for action in self.figToolbar_spectrum._actions.values():
+ action.setEnabled(True)
+ self.figToolbar.action_Label_Multiple.setEnabled(True)
+
+ PublicFunc.finish_operation(self, ButtonState)
+
+ def __slot_btn_autoqualitylabel_recalculate__(self):
+ reply = QMessageBox.question(self, '确认', Constants.RESP_QUALITY_LABEL_CHECK_ARGS_QUESTION_CONTENT,
+ QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
+ if reply == QMessageBox.Yes:
+ if self.check_autolabel_args() is False:
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_AUTOLABEL_ARGS_ERROR, Constants.MSGBOX_TYPE_ERROR)
+ return
+
+ try:
+ # 确定带体动的片段
+ artifact_indices = []
+ for i in range(0, len(self.data.Artifact_a)):
+ if i + 3 < len(self.data.Artifact_a): # 防止索引越界
+ index0 = self.data.Artifact_a[i + 2] // (Config["InputConfig"]["OrgBCGUseFreq"] * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC) # 第三行(索引+2)
+ index1 = self.data.Artifact_a[i + 3] // (Config["InputConfig"]["OrgBCGUseFreq"] * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC) # 第四行(索引+3)
+ if index0 == index1:
+ artifact_indices.append(index0)
+ else:
+ artifact_indices.append(index0)
+ artifact_indices.append(index1)
+ artifact_indices = list(dict.fromkeys(artifact_indices))
+ for seg_idx in range(1, Config["DataPartNum"] - 1):
+ # 数据切片预处理
+ BCG = pre_process(
+ get_slice(
+ self.data.OrgBCG_Processed, seg_idx, ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC, Config["InputConfig"]["OrgBCGUseFreq"]), Config["InputConfig"]["OrgBCGUseFreq"], ConfigParams.RESP_QUALITY_LABEL_PREPROCESS_FC)
+ THO = pre_process(
+ get_slice(
+ self.data.Tho_Processed, seg_idx, ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC, Config["InputConfig"]["ThoUseFreq"]), Config["InputConfig"]["ThoUseFreq"], ConfigParams.RESP_QUALITY_LABEL_PREPROCESS_FC)
+
+ # 质量评估
+ # 有体动:1 无体动:0
+ artifact_flag = 1 if (seg_idx - 1) in artifact_indices else 0
+
+ quality = evaluate_quality(
+ BCG, THO, Config["InputConfig"]["OrgBCGUseFreq"], Config["InputConfig"]["ThoUseFreq"]
+ , artifact_flag, [float(Config["Threshold"]["Low"]), float(Config["Threshold"]["High"])]
+ )
+ self.data.resp_quality_label[seg_idx - 1] = quality
+
+ self.update_tableWidget()
+ result = self.update_info()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ PublicFunc.text_output(self.ui, Constants.OPERATION_FINISHED, Constants.TIPS_TYPE_INFO)
+ PublicFunc.msgbox_output(self, Constants.OPERATION_FINISHED, Constants.MSGBOX_TYPE_INFO)
+ except Exception as e:
+ PublicFunc.text_output(self.ui, Constants.OPERATION_FAILURE, Constants.TIPS_TYPE_ERROR)
+ PublicFunc.msgbox_output(self, Constants.OPERATION_FAILURE, Constants.MSGBOX_TYPE_ERROR)
+
+ def __slot_btn_refilter_orgBcg__(self):
+ PublicFunc.__disableAllButton__(self, ButtonState)
+ if len(self.data.OrgBCG_Processed) == 0:
+ Result().failure(info=Constants.RESAMPLE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+ return
+
+ if self.ui.radioButton_orgBcg_fillterMode_custom.isChecked():
+ if self.check_filter_args() is False:
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_CUSTOM_FILTER_ARGS_ERROR, Constants.MSGBOX_TYPE_ERROR)
+ return
+ result = self.__plot__()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+
+ def __slot_btn_move__(self):
+ sender = self.sender()
+
+ if sender == self.ui.pushButton_prev:
+ if self.check_filter_args() is False:
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_CUSTOM_FILTER_ARGS_ERROR, Constants.MSGBOX_TYPE_ERROR)
+ return
+ if Config["CurrentPartNum"] == 1:
+ PublicFunc.text_output(self.ui, Constants.RESP_QUALITY_LABEL_VIEWING_THE_FIRST_PART, Constants.MSGBOX_TYPE_INFO)
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_VIEWING_THE_FIRST_PART, Constants.MSGBOX_TYPE_INFO)
+ return
+ Config["CurrentPartNum"] = Config["CurrentPartNum"] - 1
+ Config["CurrentOrgBCGIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["OrgBCGUseFreq"]))
+ Config["CurrentThoIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]))
+ result = self.__plot__()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ result = self.update_info()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ PublicFunc.text_output(self.ui, Constants.RESP_QUALITY_LABEL_PREV_PART + str(Config["CurrentPartNum"]),
+ Constants.TIPS_TYPE_INFO)
+ elif sender == self.ui.pushButton_next:
+ if self.check_filter_args() is False:
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_CUSTOM_FILTER_ARGS_ERROR, Constants.MSGBOX_TYPE_ERROR)
+ return
+ if Config["CurrentPartNum"] == Config["DataPartNum"]:
+ PublicFunc.text_output(self.ui, Constants.RESP_QUALITY_LABEL_VIEWING_THE_LAST_PART, Constants.MSGBOX_TYPE_INFO)
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_VIEWING_THE_LAST_PART, Constants.MSGBOX_TYPE_INFO)
+ return
+ Config["CurrentPartNum"] = Config["CurrentPartNum"] + 1
+ Config["CurrentOrgBCGIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["OrgBCGUseFreq"]))
+ Config["CurrentThoIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]))
+ result = self.__plot__()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ result = self.update_info()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ PublicFunc.text_output(self.ui, Constants.RESP_QUALITY_LABEL_NEXT_PART + str(Config["CurrentPartNum"]),
+ Constants.TIPS_TYPE_INFO)
+ else:
+ raise ValueError("发射信号不存在")
+
+ def __slot_tableWidget_on_cell_double_clicked__(self, row, column):
+ sender = self.sender()
+
+ if self.check_filter_args() is False:
+ PublicFunc.msgbox_output(self, Constants.RESP_QUALITY_LABEL_CUSTOM_FILTER_ARGS_ERROR, Constants.MSGBOX_TYPE_ERROR)
+ return
+ if sender == self.ui.tableWidget_labeled:
+ Config["CurrentPartNum"] = int(
+ self.ui.tableWidget_labeled.item(row, 0).text())
+ Config["CurrentOrgBCGIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["OrgBCGUseFreq"]))
+ Config["CurrentThoIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]))
+ elif sender == self.ui.tableWidget_tobelabeled:
+ Config["CurrentPartNum"] = int(
+ self.ui.tableWidget_tobelabeled.item(row, 0).text())
+ Config["CurrentOrgBCGIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["OrgBCGUseFreq"]))
+ Config["CurrentThoIndex"] = ((Config["CurrentPartNum"] - 1) * ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]))
+ else:
+ raise ValueError("发射信号不存在")
+ result = self.__plot__()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ result = self.update_info()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ PublicFunc.text_output(self.ui, Constants.RESP_QUALITY_LABEL_JUMP + str(Config["CurrentPartNum"]),
+ Constants.TIPS_TYPE_INFO)
+
+ def __slot_btn_label__(self):
+ sender = self.sender()
+
+ if sender == self.ui.pushButton_valid:
+ self.data.resp_quality_label[Config["CurrentPartNum"] - 1] = 1
+ elif sender == self.ui.pushButton_invalid:
+ self.data.resp_quality_label[Config["CurrentPartNum"] - 1] = 0
+ elif sender == self.ui.pushButton_reset:
+ self.data.resp_quality_label[Config["CurrentPartNum"] - 1] = -1
+ else:
+ raise ValueError("发射信号不存在")
+ result = self.data.save_resp_quality_label()
+ self.update_tableWidget()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+ PublicFunc.text_output(self.ui, str(Config["CurrentPartNum"]) +
+ Constants.RESP_QUALITY_LABEL_LABEL_SUCCESSFULLY +
+ "," + Constants.RESP_QUALITY_LABEL_LABEL_TYPE +
+ Constants.RESP_QUALITY_LABEL_KEY_VALUE[self.data.resp_quality_label[Config["CurrentPartNum"] - 1]],
+ Constants.TIPS_TYPE_INFO)
+
+ def __slot_btn_save__(self):
+ PublicFunc.__disableAllButton__(self, ButtonState)
+
+ # 保存
+ PublicFunc.progressbar_update(self, 1, 2, Constants.SAVING_DATA + ConfigParams.RESP_QUALITY_LABEL, 0)
+ result = self.data.save_resp_quality_label()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(1/2)" + 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/2)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ PublicFunc.progressbar_update(self, 2, 2, Constants.SAVING_DATA + ConfigParams.THO_PEAK, 50)
+ result = self.data.save_tho_peak()
+ if not result.status:
+ PublicFunc.text_output(self.ui, "(2/2)" + 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/2)" + result.info, Constants.TIPS_TYPE_INFO)
+
+ PublicFunc.msgbox_output(self, Constants.SAVE_FINISHED, Constants.TIPS_TYPE_INFO)
+ PublicFunc.finish_operation(self, ButtonState)
+
+ def __slot_lineEdit_filter__(self, filter_text):
+ sender = self.sender()
+
+ if sender == self.ui.lineEdit_filter_labeled:
+ for row in range(self.ui.tableWidget_labeled.rowCount()):
+ match = False
+ for col in range(self.ui.tableWidget_labeled.columnCount()):
+ item = self.ui.tableWidget_labeled.item(row, col)
+ if filter_text.lower() in item.text().lower():
+ match = True
+ break
+ self.ui.tableWidget_labeled.setRowHidden(row, not match)
+ elif sender == self.ui.lineEdit_filter_tobelabeled:
+ for row in range(self.ui.tableWidget_tobelabeled.rowCount()):
+ match = False
+ for col in range(self.ui.tableWidget_tobelabeled.columnCount()):
+ item = self.ui.tableWidget_tobelabeled.item(row, col)
+ if filter_text.lower() in item.text().lower():
+ match = True
+ break
+ self.ui.tableWidget_tobelabeled.setRowHidden(row, not match)
+ else:
+ raise ValueError("发生信号不存在")
+
+ def reset_axes(self):
+ if self.ax0 is not None:
+ self.ax0.clear()
+ self.ax0.grid(True)
+ self.ax0.xaxis.set_major_formatter(ConfigParams.FORMATTER)
+ if self.ax1 is not None:
+ self.ax1.clear()
+ self.ax1.grid(True)
+ self.ax1.xaxis.set_major_formatter(ConfigParams.FORMATTER)
+ if self.ax0_spectrum is not None:
+ self.ax0_spectrum.clear()
+ self.ax0_spectrum.grid(True)
+ self.ax0_spectrum.set_title(Constants.RESP_QUALITY_LABEL_SPECTRUM_BDR_TITLE, fontsize=8)
+ if self.ax1_spectrum is not None:
+ self.ax1_spectrum.clear()
+ self.ax1_spectrum.grid(True)
+ self.ax1_spectrum.set_title(Constants.RESP_QUALITY_LABEL_SPECTRUM_THO_TITLE, fontsize=8)
+
+ def check_filter_args(self):
+ if (float(Config["Filter"]["BandPassLow"]) >= float(Config["Filter"]["BandPassHigh"])):
+ return False
+ return True
+
+ def check_autolabel_args(self):
+ if (float(Config["Threshold"]["Low"]) >= float(Config["Threshold"]["High"])):
+ return False
+ return True
+
+ def on_xlim_changed(self, ax):
+ # 获取触发事件的轴
+ if ax == self.ax0:
+ # 获取第一个子图的X轴范围
+ xlim1 = self.ax0.get_xlim()
+ # 计算对应的第二个子图的X轴范围
+ ratio = (int(Config["InputConfig"]["ThoUseFreq"]) /
+ int(Config["InputConfig"]["OrgBCGUseFreq"])) # 信号B的样本点数与信号A的样本点数的比例
+ xlim2 = (xlim1[0] * ratio, xlim1[1] * ratio)
+
+ # 禁用事件监听
+ self.ax0.callbacks.disconnect(self.cid2)
+ # 设置第二个子图的X轴范围
+ self.ax1.set_xlim(xlim2)
+ # 重新启用事件监听
+ self.cid2 = self.ax1.callbacks.connect('xlim_changed', self.on_xlim_changed)
+
+ elif ax == self.ax1:
+ # 获取第二个子图的X轴范围
+ xlim2 = self.ax1.get_xlim()
+ # 计算对应的第一个子图的X轴范围
+ ratio = (int(Config["InputConfig"]["OrgBCGUseFreq"]) /
+ int(Config["InputConfig"]["ThoUseFreq"])) # 信号A的样本点数与信号B的样本点数的比例
+ xlim1 = (xlim2[0] * ratio, xlim2[1] * ratio)
+
+ # 禁用事件监听
+ self.ax0.callbacks.disconnect(self.cid1)
+ # 设置第一个子图的X轴范围
+ self.ax0.set_xlim(xlim1)
+ # 重新启用事件监听
+ self.cid1 = self.ax0.callbacks.connect('xlim_changed', self.on_xlim_changed)
+
+ def update_config(self):
+ Config["Threshold"]["Low"] = self.ui.doubleSpinBox_quality_threshold1.value()
+ Config["Threshold"]["High"] = self.ui.doubleSpinBox_quality_threshold2.value()
+ Config["FindPeaks"]["MinInterval"] = self.ui.doubleSpinBox_findpeaks_min_interval.value()
+ Config["FindPeaks"]["MinHeight"] = self.ui.doubleSpinBox_findpeaks_min_height.value()
+ Config["Filter"]["BandPassLow"] = self.ui.doubleSpinBox_fillterMode_custom_low.value()
+ Config["Filter"]["BandPassHigh"] = self.ui.doubleSpinBox_fillterMode_custom_high.value()
+
+ def toggle_home(self):
+ if Config["CurrentPartNum"] is None:
+ self.ax1.autoscale(True)
+ self.ax1.relim()
+ self.ax1.autoscale_view()
+ self.canvas.draw()
+ self.ax1.autoscale(False)
+
+ def toggle_changeLabel_Multiple_mode(self, state):
+
+ if state:
+ self.deactivate_figToolbar_buttons()
+ self.figToolbar.action_Label_Multiple.setChecked(True)
+ self.figToolbar.cid_mouse_press = self.canvas.mpl_connect(
+ "button_press_event", self.on_click)
+ self.figToolbar.cid_mouse_release = self.canvas.mpl_connect(
+ "button_release_event", self.on_release)
+ self.figToolbar.cid_mouse_hold = self.canvas.mpl_connect(
+ "motion_notify_event", self.on_hold)
+ else:
+ if self.figToolbar.cid_mouse_press is not None:
+ self.canvas.mpl_disconnect(self.figToolbar.cid_mouse_press)
+ self.figToolbar.cid_mouse_press = None
+ if self.figToolbar.cid_mouse_release is not None:
+ self.canvas.mpl_disconnect(self.figToolbar.cid_mouse_release)
+ self.figToolbar.cid_mouse_release = None
+ if self.figToolbar.cid_mouse_hold:
+ self.canvas.mpl_disconnect(self.figToolbar.cid_mouse_hold)
+ self.figToolbar.cid_mouse_hold = None
+
+ def deactivate_figToolbar_buttons(self):
+ for action in self.figToolbar._actions.values():
+ if action.isChecked() == True:
+ if action == self.figToolbar._actions['pan']:
+ self.figToolbar.pan()
+ if action == self.figToolbar._actions['zoom']:
+ self.figToolbar.zoom()
+
+ def on_click(self, event):
+ if self.figToolbar.action_Label_Multiple.isChecked():
+ if event.button == 1 or event.button == 3: # 左键或右键
+ if event.button == 1:
+ self.is_left_button_pressed = True
+ elif event.button == 3:
+ self.is_right_button_pressed = True
+ self.figToolbar.rect_start_x = event.xdata
+ # 如果矩形patch已存在,先移除
+ if self.figToolbar.rect_patch_ax1 is not None:
+ self.figToolbar.rect_patch_ax1.remove()
+ self.figToolbar.rect_patch_ax1 = None
+ 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
+ if self.figToolbar.rect_start_x is not None and self.figToolbar.rect_end_x is not None:
+ if self.figToolbar.rect_start_x < self.figToolbar.rect_end_x:
+ rect_left = self.figToolbar.rect_start_x
+ rect_right = self.figToolbar.rect_end_x
+ elif self.figToolbar.rect_start_x > self.figToolbar.rect_end_x:
+ rect_left = self.figToolbar.rect_end_x
+ rect_right = self.figToolbar.rect_start_x
+ else:
+ rect_left = self.figToolbar.rect_start_x
+ rect_right = self.figToolbar.rect_start_x
+ else:
+ rect_left = self.figToolbar.rect_start_x
+ rect_right = self.figToolbar.rect_start_x
+ if event.button == 1 and self.is_left_button_pressed:
+ self.is_left_button_pressed = False
+ if rect_left < Config["CurrentThoIndex"]:
+ rect_left = Config["CurrentThoIndex"]
+ elif rect_left >= (Config["CurrentThoIndex"] + ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"])):
+ rect_left = 0
+ rect_right = 0
+ if (rect_right >= Config["CurrentThoIndex"] + ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"])):
+ rect_right = (Config["CurrentThoIndex"] + ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ int(Config["InputConfig"]["ThoUseFreq"]) - 1)
+ elif rect_right < Config["CurrentThoIndex"]:
+ rect_left = 0
+ rect_right = 0
+ selected_area_for_add_points = self.data.Tho_Processed[int(rect_left):int(rect_right)]
+ peaks_idx, _ = find_peaks(selected_area_for_add_points,
+ height=float(Config["FindPeaks"]["MinHeight"]),
+ distance=float(Config["FindPeaks"]["MinInterval"]))
+ peaks_idx = peaks_idx + int(rect_left)
+ peaks_idx = setdiff1d(peaks_idx, self.data.Tho_peak)
+ if len(peaks_idx) != 0:
+ PublicFunc.text_output(self.ui,
+ f"{Constants.RESP_QUALITY_LABEL_ADD_POINTS_SUCCESSFULLY}{peaks_idx}",
+ Constants.TIPS_TYPE_INFO)
+ else:
+ PublicFunc.text_output(self.ui,
+ Constants.RESP_QUALITY_LABEL_NO_POINT_IN_THE_INTERVAL,
+ Constants.TIPS_TYPE_INFO)
+ self.data.Tho_peak = append(self.data.Tho_peak, peaks_idx)
+ self.data.Tho_peak_y = append(self.data.Tho_peak_y, self.data.Tho_Processed[peaks_idx])
+ self.__redraw_peaks__()
+ elif event.button == 3 and self.is_right_button_pressed:
+ self.is_right_button_pressed = False
+ left_label2_to_delete = self.data.Tho_peak - rect_left
+ right_label2_to_delete = self.data.Tho_peak - rect_right
+ left_label2_to_delete_idx = len(left_label2_to_delete[left_label2_to_delete < 0])
+ right_label2_to_delete_idx = len(right_label2_to_delete[right_label2_to_delete < 0])
+ if left_label2_to_delete_idx != right_label2_to_delete_idx:
+ PublicFunc.text_output(self.ui,
+ f"{Constants.RESP_QUALITY_LABEL_REMOVE_POINTS_SUCCESSFULLY}{self.data.Tho_peak[left_label2_to_delete_idx:right_label2_to_delete_idx]}",
+ Constants.TIPS_TYPE_INFO)
+ else:
+ PublicFunc.text_output(self.ui,
+ Constants.RESP_QUALITY_LABEL_NO_POINT_IN_THE_INTERVAL,
+ Constants.TIPS_TYPE_INFO)
+ self.data.Tho_peak = delete(self.data.Tho_peak,
+ arange(left_label2_to_delete_idx, right_label2_to_delete_idx))
+ self.data.Tho_peak_y = delete(self.data.Tho_peak_y, arange(left_label2_to_delete_idx, right_label2_to_delete_idx))
+ self.__redraw_peaks__()
+ self.data.Tho_peak.sort()
+ self.data.Tho_peak_y = [self.data.Tho_Processed[x] for x in self.data.Tho_peak]
+ self.figToolbar.rect_start_x = None
+ self.figToolbar.rect_end_x = None
+
+ result = self.data.save_tho_peak()
+ if not result.status:
+ PublicFunc.text_output(self.ui, 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, result.info, Constants.TIPS_TYPE_INFO)
+
+ # 移除矩形patch
+ if self.figToolbar.rect_patch_ax1 is not None:
+ self.figToolbar.rect_patch_ax1.remove()
+ self.figToolbar.rect_patch_ax1 = None
+ self.canvas.draw()
+
+ def on_hold(self, event):
+ if self.figToolbar.rect_start_x is not None and event.xdata is not None:
+ self.figToolbar.rect_end_x = event.xdata
+
+ # 如果矩形patch不存在,则创建一个新的
+ if self.figToolbar.rect_patch_ax1 is None:
+ if self.is_left_button_pressed:
+ self.figToolbar.rect_patch_ax1 = patches.Rectangle((0, 0), 1, 1, fill=True,
+ alpha=ConfigParams.RESP_QUALITY_LABEL_LABEL_TRANSPARENCY,
+ color=Constants.PLOT_COLOR_PINK)
+ elif self.is_right_button_pressed:
+ self.figToolbar.rect_patch_ax1 = patches.Rectangle((0, 0), 1, 1, fill=True,
+ alpha=ConfigParams.RESP_QUALITY_LABEL_LABEL_TRANSPARENCY,
+ color=Constants.PLOT_COLOR_RED)
+ self.ax1.add_patch(self.figToolbar.rect_patch_ax1)
+
+ # 更新矩形patch的位置和大小
+ x_start = self.figToolbar.rect_start_x
+ x_end = self.figToolbar.rect_end_x
+ rect_down = self.ax1.get_ylim()[0] - 1000
+ rect_up = self.ax1.get_ylim()[1] + 1000
+ self.figToolbar.rect_patch_ax1.set_xy((min(x_start, x_end), rect_down))
+ self.figToolbar.rect_patch_ax1.set_width(abs(x_end - x_start))
+ self.figToolbar.rect_patch_ax1.set_height(rect_up - rect_down)
+
+ self.canvas.draw()
+
+
+class Data():
+
+ def __init__(self):
+ self.OrgBCG = None
+ self.Tho = None
+ self.OrgBCG_Processed = None
+ self.Tho_Processed = None
+ self.Tho_peak = None
+ self.resp_quality_label = None
+ self.Artifact_a = None
+ self.Tho_peak_y = None
+
+ self.artifact_number = array([]).astype(int64)
+ self.artifact_type = array([]).astype(int64)
+ self.artifact_mask = array([]).astype(int64)
+
+ def open_file_calculate_peaks(self):
+ if Path(Config["Path"]["Input_OrgBCG"]).is_file():
+ Config["Path"]["Input_OrgBCG"] = str(Path(Config["Path"]["Input_OrgBCG"]).parent)
+ if Path(Config["Path"]["Input_Tho"]).is_file():
+ Config["Path"]["Input_Tho"] = str(Path(Config["Path"]["Input_Tho"]).parent)
+ if Path(Config["Path"]["Input_Artifact"]).is_file():
+ Config["Path"]["Input_Artifact"] = str(Path(Config["Path"]["Input_Artifact"]).parent)
+ if Path(Config["Path"]["Save_Resp_quality_label"]).is_file():
+ Config["Path"]["Save_Resp_quality_label"] = str(Path(Config["Path"]["Save_Resp_quality_label"]).parent)
+ if Path(Config["Path"]["Save_Tho_peak"]).is_file():
+ Config["Path"]["Save_Tho_peak"] = str(Path(Config["Path"]["Save_Tho_peak"]).parent)
+
+ result = PublicFunc.examine_file(Config["Path"]["Input_Tho"], ConfigParams.THO_SYNC)
+ if result.status:
+ Config["Path"]["Input_Tho"] = result.data["path"]
+ Config["InputConfig"]["ThoFreq"] = result.data["freq"]
+ else:
+ return result
+
+ Config["Path"]["Save_Tho_peak"] = str(
+ Path(Config["Path"]["Save_Tho_peak"]) / Path(ConfigParams.THO_PEAK + str(Config["InputConfig"]["ThoUseFreq"]) + ConfigParams.ENDSWITH_TXT))
+
+ try:
+ self.Tho = read_csv(Config["Path"]["Input_Tho"],
+ encoding=ConfigParams.UTF8_ENCODING,
+ header=None).to_numpy().reshape(-1)
+ except Exception as e:
+ return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON["Open_Data_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=Constants.INPUT_FINISHED)
+
+ def resample_tho(self):
+ if self.Tho is None:
+ Result().failure(info=Constants.RESAMPLE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ if Config["InputConfig"]["ThoFreq"] != Config["InputConfig"]["ThoUseFreq"]:
+ self.Tho = resample(self.Tho,
+ int(len(self.Tho) *
+ (Config["InputConfig"]["ThoUseFreq"] / Config["InputConfig"]["ThoFreq"])))
+ else:
+ return Result().success(info=Constants.RESAMPLE_NO_NEED)
+ except Exception as e:
+ Result().failure(info=Constants.RESAMPLE_FAILURE +
+ Constants.FAILURE_REASON["Resample_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=Constants.RESAMPLE_FINISHED)
+
+ def preprocess_tho(self):
+ if self.Tho is None:
+ return Result().failure(info=Constants.PREPROCESS_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ self.Tho_Processed = pre_process(self.Tho, Config["InputConfig"]["ThoUseFreq"], ConfigParams.RESP_QUALITY_LABEL_PREPROCESS_FC)
+ 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 calculate_tho_peak(self):
+ if self.Tho_Processed is None:
+ return Result().failure(info=Constants.RESP_QUALITY_LABEL_CALCULATE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ self.Tho_peak, _ = find_peaks(self.Tho_Processed,
+ height=float(Config["FindPeaks"]["MinHeight"]),
+ distance=float(Config["FindPeaks"]["MinInterval"]))
+ self.Tho_peak_y = [self.Tho_Processed[x] for x in (self.Tho_peak)]
+ except Exception as e:
+ return Result().failure(info=Constants.RESP_QUALITY_LABEL_CALCULATE_FAILURE +
+ Constants.FAILURE_REASON["Calculate_Peak_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=Constants.RESP_QUALITY_LABEL_CALCULATE_FINISHED)
+
+ def save_tho_peak(self):
+ if (not Path(Config["Path"]["Save_Tho_peak"]).parent.exists()) or (not Path(Config["Path"]["Save_Tho_peak"]).parent.is_dir()):
+ Path(Config["Path"]["Save_Tho_peak"]).parent.mkdir(parents=True, exist_ok=True)
+
+ if self.Tho_peak is None:
+
+ return Result().failure(info=ConfigParams.THO_PEAK + Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ DataFrame(self.Tho_peak).to_csv(Path(Config["Path"]["Save_Tho_peak"]), mode='w', index=False, header=False)
+ except Exception as e:
+ return Result().failure(info=ConfigParams.THO_PEAK + Constants.SAVE_FAILURE +
+ Constants.FAILURE_REASON["Save_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=ConfigParams.THO_PEAK + Constants.SAVE_FINISHED)
+
+ def open_file_label(self):
+ if Path(Config["Path"]["Input_OrgBCG"]).is_file():
+ Config["Path"]["Input_OrgBCG"] = str(Path(Config["Path"]["Input_OrgBCG"]).parent)
+ if Path(Config["Path"]["Input_Tho"]).is_file():
+ Config["Path"]["Input_Tho"] = str(Path(Config["Path"]["Input_Tho"]).parent)
+ if Path(Config["Path"]["Input_Artifact"]).is_file():
+ Config["Path"]["Input_Artifact"] = str(Path(Config["Path"]["Input_Artifact"]).parent)
+ if Path(Config["Path"]["Save_Resp_quality_label"]).is_file():
+ Config["Path"]["Save_Resp_quality_label"] = str(Path(Config["Path"]["Save_Resp_quality_label"]).parent)
+ if Path(Config["Path"]["Save_Tho_peak"]).is_file():
+ Config["Path"]["Save_Tho_peak"] = str(Path(Config["Path"]["Save_Tho_peak"]).parent)
+
+ result = PublicFunc.examine_file(Config["Path"]["Input_OrgBCG"], ConfigParams.ORGBCG_SYNC)
+ if result.status:
+ Config["Path"]["Input_OrgBCG"] = result.data["path"]
+ Config["InputConfig"]["OrgBCGFreq"] = result.data["freq"]
+ else:
+ return result
+
+ result = PublicFunc.examine_file(Config["Path"]["Input_Tho"], ConfigParams.THO_SYNC)
+ if result.status:
+ Config["Path"]["Input_Tho"] = result.data["path"]
+ Config["InputConfig"]["ThoFreq"] = result.data["freq"]
+ else:
+ return result
+
+ Config["Path"]["Input_Artifact"] = str(
+ Path(Config["Path"]["Input_Artifact"]) / Path(ConfigParams.ARTIFACT_A + ConfigParams.ENDSWITH_TXT))
+ Config["Path"]["Save_Resp_quality_label"] = str(
+ Path(Config["Path"]["Save_Resp_quality_label"]) / Path(
+ ConfigParams.RESP_QUALITY_LABEL + ConfigParams.ENDSWITH_TXT))
+ Config["Path"]["Save_Tho_peak"] = str(
+ Path(Config["Path"]["Save_Tho_peak"]) / Path(
+ ConfigParams.THO_PEAK + str(Config["InputConfig"]["ThoUseFreq"]) + ConfigParams.ENDSWITH_TXT))
+
+ try:
+ self.OrgBCG = read_csv(Config["Path"]["Input_OrgBCG"],
+ encoding=ConfigParams.UTF8_ENCODING,
+ header=None).to_numpy().reshape(-1)
+ self.Tho = read_csv(Config["Path"]["Input_Tho"],
+ encoding=ConfigParams.UTF8_ENCODING,
+ header=None).to_numpy().reshape(-1)
+ self.Artifact_a = read_csv(Config["Path"]["Input_Artifact"],
+ encoding=ConfigParams.UTF8_ENCODING,
+ header=None).to_numpy().reshape(-1)
+ except Exception as e:
+ return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON["Open_Data_Exception"] + "\n" + format_exc())
+
+ try:
+ Config["DataPartNum"] = len(self.OrgBCG) // (ConfigParams.RESP_QUALITY_LABEL_PARTS_TIME_SEC *
+ Config["InputConfig"]["OrgBCGFreq"]) + 1
+ if Config["DataPartNum"] == 0:
+ return Result().failure(info=Constants.INPUT_FAILURE + Constants.FAILURE_REASON["Data_Length_Not_Correct"])
+ 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_a)
+ # 定义绘制体动所需要的数组
+ artifact_start = array([])
+ artifact_start = artifact_start.astype(int64)
+ artifact_end = array([])
+ artifact_end = artifact_end.astype(int64)
+ for i in range(0, len(self.Artifact_a), 4):
+ self.artifact_number = append(self.artifact_number, self.Artifact_a[i])
+ for i in range(1, len(self.Artifact_a), 4):
+ self.artifact_type = append(self.artifact_type, self.Artifact_a[i])
+ for i in range(2, len(self.Artifact_a), 4):
+ artifact_start = append(artifact_start, self.Artifact_a[i])
+ for i in range(3, len(self.Artifact_a), 4):
+ artifact_end = append(artifact_end, self.Artifact_a[i])
+ self.artifact_mask = zeros(len(self.OrgBCG))
+ for i in range(0, len(self.artifact_number)):
+ self.artifact_mask[artifact_start[i]: artifact_end[i] + 1] = 1
+ except Exception as e:
+ return Result().failure(info=Constants.INPUT_FAILURE +
+ Constants.FAILURE_REASON["Get_Artifact_Format_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=Constants.INPUT_FINISHED)
+
+ def get_archive_resp_quality(self):
+ if not Path(Config["Path"]["Save_Resp_quality_label"]).exists():
+ self.resp_quality_label = full(Config["DataPartNum"], -1)
+ return Result().success(info=ConfigParams.RESP_QUALITY_LABEL + ":" + Constants.ARCHIVE_NOT_EXIST)
+ else:
+ self.resp_quality_label = read_csv(Config["Path"]["Save_Resp_quality_label"],
+ encoding=ConfigParams.UTF8_ENCODING,
+ header=None).to_numpy().reshape(-1)
+ return Result().success(info=ConfigParams.RESP_QUALITY_LABEL + ":" + Constants.ARCHIVE_EXIST)
+
+ def get_archive_tho_peak(self):
+ if not Path(Config["Path"]["Save_Tho_peak"]).exists():
+ self.Tho_peak = array([]).astype(int)
+ self.Tho_peak_y = array([]).astype(int)
+ return Result().success(info=ConfigParams.RESP_QUALITY_LABEL + ":" + Constants.ARCHIVE_NOT_EXIST)
+ else:
+ self.Tho_peak = read_csv(Config["Path"]["Save_Tho_peak"],
+ encoding=ConfigParams.UTF8_ENCODING,
+ header=None).to_numpy().reshape(-1)
+
+ return Result().success(info=ConfigParams.THO_PEAK + ":" + Constants.ARCHIVE_EXIST)
+
+ def resample_tho_and_OrgBCG(self):
+ if (self.OrgBCG is None) or (self.Tho is None):
+ Result().failure(info=Constants.RESAMPLE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ if ((Config["InputConfig"]["OrgBCGFreq"] != Config["InputConfig"]["OrgBCGUseFreq"])
+ or (Config["InputConfig"]["ThoFreq"] != Config["InputConfig"]["ThoUseFreq"])):
+
+ if Config["InputConfig"]["OrgBCGFreq"] != Config["InputConfig"]["OrgBCGUseFreq"]:
+ self.OrgBCG = resample(self.OrgBCG,
+ int(len(self.OrgBCG) *
+ (Config["InputConfig"]["OrgBCGUseFreq"] / Config["InputConfig"]["OrgBCGFreq"])))
+ if Config["InputConfig"]["ThoFreq"] != Config["InputConfig"]["ThoUseFreq"]:
+ self.Tho = resample(self.Tho,
+ int(len(self.Tho) *
+ (Config["InputConfig"]["ThoUseFreq"] / Config["InputConfig"]["ThoFreq"])))
+ else:
+ return Result().success(info=Constants.RESAMPLE_NO_NEED)
+ except Exception as e:
+ Result().failure(info=Constants.RESAMPLE_FAILURE +
+ Constants.FAILURE_REASON["Resample_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=Constants.RESAMPLE_FINISHED)
+
+ def preprocess_tho_and_OrgBCG(self):
+ if (self.OrgBCG is None) or (self.Tho is None):
+ return Result().failure(info=Constants.PREPROCESS_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ self.OrgBCG_Processed = self.OrgBCG.copy()
+ self.Tho_Processed = pre_process(self.Tho, Config["InputConfig"]["ThoUseFreq"], ConfigParams.RESP_QUALITY_LABEL_PREPROCESS_FC)
+ self.Tho_peak_y = [self.Tho_Processed[x] for x in (self.Tho_peak)]
+ 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 preprocess_getBDR(self, orgBcg_slice, THO_slice, mode):
+ orgBcg = pre_process(orgBcg_slice, int(Config["InputConfig"]["OrgBCGUseFreq"]),
+ ConfigParams.RESP_QUALITY_LABEL_PREPROCESS_FC)
+ if mode == "preset":
+ BDR, band_low, band_high, bcg_spectrum, bcg_freq, tho_spectrum, tho_freq = (
+ get_bandpass_bcgsignal(orgBcg, THO_slice, int(Config["InputConfig"]["OrgBCGUseFreq"]),
+ int(Config["InputConfig"]["ThoUseFreq"]), use_custom_band=False))
+ elif mode == "custom":
+ BDR, band_low, band_high, bcg_spectrum, bcg_freq, tho_spectrum, tho_freq = (
+ get_bandpass_bcgsignal(orgBcg, THO_slice, int(Config["InputConfig"]["OrgBCGUseFreq"]),
+ int(Config["InputConfig"]["ThoUseFreq"]),
+ use_custom_band=True,
+ low_cutoff=float(Config["Filter"]["BandPassLow"]),
+ high_cutoff=float(Config["Filter"]["BandPassHigh"])))
+ else:
+ raise ValueError("模式不存在")
+ return BDR, band_low, band_high, bcg_spectrum, bcg_freq, tho_spectrum, tho_freq
+
+ def save_resp_quality_label(self):
+ if (not Path(Config["Path"]["Save_Resp_quality_label"]).parent.exists()) or (not Path(Config["Path"]["Save_Resp_quality_label"]).parent.is_dir()):
+ Path(Config["Path"]["Save_Resp_quality_label"]).parent.mkdir(parents=True, exist_ok=True)
+
+ if self.resp_quality_label is None:
+
+ return Result().failure(info=ConfigParams.RESP_QUALITY_LABEL + Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
+
+ try:
+ DataFrame(self.resp_quality_label).to_csv(Path(Config["Path"]["Save_Resp_quality_label"]), mode='w', index=False, header=False)
+ except Exception as e:
+ return Result().failure(info=ConfigParams.RESP_QUALITY_LABEL + Constants.SAVE_FAILURE +
+ Constants.FAILURE_REASON["Save_Exception"] + "\n" + format_exc())
+
+ return Result().success(info=ConfigParams.RESP_QUALITY_LABEL + Constants.SAVE_FINISHED)
+
+
+class CustomNavigationToolbar(NavigationToolbar2QT):
+
+ def __init__(self, canvas, parent):
+ super().__init__(canvas, parent)
+ # 初始化画框工具栏
+ self.action_Label_Multiple = QAction(Constants.RESP_QUALITY_LABEL_ACTION_LABEL_MULTIPLE_NAME, self)
+ self.action_Label_Multiple.setFont(QFont(ConfigParams.FONT, 14))
+ self.action_Label_Multiple.setCheckable(True)
+ self.action_Label_Multiple.setShortcut(QCoreApplication.translate("MainWindow",
+ ConfigParams.RESP_QUALITY_LABEL_ACTION_LABEL_MULTIPLE_SHORTCUT_KEY))
+ self.insertAction(self._actions['pan'], self.action_Label_Multiple)
+
+ self._actions['pan'].setShortcut(QCoreApplication.translate("MainWindow", ConfigParams.ACTION_PAN_SHORTCUT_KEY))
+ self._actions['zoom'].setShortcut(QCoreApplication.translate("MainWindow", ConfigParams.ACTION_ZOOM_SHORTCUT_KEY))
+
+ # 用于存储事件连接ID
+ self.cid_mouse_press = None
+ self.cid_mouse_release = None
+ self.cid_mouse_hold = None
+
+ # 初始化矩形选择区域
+ self.rect_start_x = None
+ self.rect_end_x = None
+ self.rect_patch_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:
+ self.canvas.mpl_disconnect(self.cid_mouse_press)
+ self.cid_mouse_press = None
+ if self.cid_mouse_release is not None:
+ self.canvas.mpl_disconnect(self.cid_mouse_release)
+ self.cid_mouse_release = None
+ if self.cid_mouse_hold is not None:
+ self.canvas.mpl_disconnect(self.cid_mouse_hold)
+ self.cid_mouse_hold = None
\ No newline at end of file
diff --git a/func/utils/ConfigParams.py b/func/utils/ConfigParams.py
index 04582c6..404ada5 100644
--- a/func/utils/ConfigParams.py
+++ b/func/utils/ConfigParams.py
@@ -275,6 +275,7 @@ class ConfigParams:
}
}
RESP_QUALITY_LABEL_PREPROCESS_FC: int = 1
+ RESP_QUALITY_LABEL_PARTS_TIME_SEC: int = 30
RESP_QUALITY_LABEL_LABEL_TRANSPARENCY: float = 0.2
RESP_QUALITY_LABEL_ACTION_LABEL_MULTIPLE_SHORTCUT_KEY: str = "Z"
@@ -341,7 +342,7 @@ class ConfigParams:
RESP_QUALITY_LABEL_INPUT_XINXIAO_DEFAULT_FS: int = 1000
RESP_QUALITY_LABEL_INPUT_THO_DEFAULT_FS: int = 200
- RESP_QUALITY_LABEL_PARTS_TIME_SEC: int = 30
+
RESP_QUALITY_LABEL_THRESHOLD1_DEFAULT: float = 0.65
diff --git a/func/utils/Constants.py b/func/utils/Constants.py
index ad4749a..f8a2a0e 100644
--- a/func/utils/Constants.py
+++ b/func/utils/Constants.py
@@ -103,7 +103,7 @@ class Constants:
"Method_Not_Exist": "(检测方法不存在)",
"Data_Length_not_Correct": "(orgBcg和BCG长度不匹配)",
"Artifact_Format_Not_Correct": "(体动长度或格式不正确)",
- "Get_Artifact_Format_Exception": "(获取体动长度或格式异常)",
+ "Data_Length_Not_Correct": "(信号长度不正确)",
"Open_Data_Exception": "(打开数据异常)",
"Process_Exception": "(处理异常)",
@@ -132,6 +132,7 @@ class Constants:
"Get_File_and_Freq_Excepetion": "(检查文件是否存在并获取其数据采样率异常)",
"Update_tableWidget_Exception": "(更新表格异常)",
"Update_Info_Exception": "(更新信息异常)",
+ "Get_Artifact_Format_Exception": "(获取体动长度或格式异常)",
"Label_Format_Exception": "(获取标签格式异常)",
"Calculate_Peak_Exception": "(计算峰值异常)",
@@ -389,11 +390,45 @@ class Constants:
RESP_QUALITY_LABEL_PLOT_LABEL_ARTIFACT: str = "Artifact"
RESP_QUALITY_LABEL_SPECTRUM_BDR_TITLE: str = "Spectrum of BDR_sync by filter OrgBCG_Sync"
RESP_QUALITY_LABEL_SPECTRUM_THO_TITLE: str = "Spectrum of THO_sync after preprocess"
- RESP_QUALITY_LABEL_SPECTRUM_ORGBCG_LABEL: str = "orgBcg"
+ RESP_QUALITY_LABEL_SPECTRUM_ORGBCG_LABEL: str = "OrgBCG"
RESP_QUALITY_LABEL_SPECTRUM_BDR_LABEL: str = "BDR"
- RESP_QUALITY_LABEL_SPECTRUM_THO_LABEL: str = "THO"
-
+ RESP_QUALITY_LABEL_SPECTRUM_THO_LABEL: str = "Tho"
+ RESP_QUALITY_LABEL_VIEWING_THE_FIRST_PART: str = "你正在查看第1段信号"
+ RESP_QUALITY_LABEL_VIEWING_THE_LAST_PART: str = "你正在查看最后1段信号"
+ RESP_QUALITY_LABEL_PREV_PART: str = "上一个片段,index "
+ RESP_QUALITY_LABEL_NEXT_PART: str = "下一个片段,index "
+ RESP_QUALITY_LABEL_JUMP: str = "跳转到片段,index "
+ RESP_QUALITY_LABEL_LABEL_SUCCESSFULLY: str = "片段标注成功"
+ RESP_QUALITY_LABEL_LABEL_TYPE: str = "标注类型为:"
+ RESP_QUALITY_LABEL_ADD_POINTS_SUCCESSFULLY: str = "成功新增点,横坐标:"
+ RESP_QUALITY_LABEL_REMOVE_POINTS_SUCCESSFULLY: str = "成功删除点,横坐标:"
+ RESP_QUALITY_LABEL_NO_POINT_IN_THE_INTERVAL: str = "所选区间内无新增或删除点"
+ RESP_QUALITY_LABEL_CUSTOM_FILTER_ARGS_ERROR: str = "OrgBCG带通滤波频率设置范围应为数字,范围是0~1"
+ RESP_QUALITY_LABEL_AUTOLABEL_ARGS_ERROR: str = "人工标注阈值设置范围应为数字,范围是0~1"
+ RESP_QUALITY_LABEL_CHECK_ARGS_QUESTION_CONTENT: str = "你确定要执行此操作吗,请确保参数输入正确"
RESP_QUALITY_LABEL_ACTION_LABEL_MULTIPLE_NAME: str = f"批量更改标签({ConfigParams.RESP_QUALITY_LABEL_ACTION_LABEL_MULTIPLE_SHORTCUT_KEY})"
+ RESP_QUALITY_LABEL_A_QUALITY: int = 1
+ RESP_QUALITY_LABEL_B_QUALITY: int = 0
+ RESP_QUALITY_LABEL_C_QUALITY: int = -1
+ RESP_QUALITY_LABEL_KEY_VALUE = {
+ 1: "Good",
+ 0: "Bad",
+ -1: "None"
+ }
+ RESP_QUALITY_LABEL_FIGTOOLBAR_SPECTRUM_STYLESHEET: str = """
+ QToolBar {
+ border: 1px;
+ spacing: 2px; /* 设置工具栏按钮之间的间距 */
+ }
+ QToolButton {
+ height: 20px; /* 设置工具栏按钮的高度 */
+ width: 20px; /* 设置工具栏按钮的宽度 */
+ font-size: 8px; /* 设置按钮文字大小 */
+ }
+ QToolButton::menu-button {
+ width: 0px; /* 隐藏下拉菜单按钮 */
+ }
+ """
# 睡眠呼吸暂停事件标注
SA_LABEL_JUMP: str = "跳转到事件"
@@ -483,52 +518,6 @@ class Constants:
background-color: yellow; /* 鼠标悬停时的背景颜色 */
}"""
-
- # 呼吸可用性及间期标注
- RESP_QUALITY_LABEL_FILES_NOT_FOUND: str = f"无法找到{ConfigParams.RESP_QUALITY_LABEL_INPUT_XINXIAO_FILENAME}{ConfigParams.ENDSWITH_TXT}或{ConfigParams.RESP_QUALITY_LABEL_INPUT_THO_FILENAME}{ConfigParams.ENDSWITH_TXT}或{ConfigParams.RESP_QUALITY_LABEL_INPUT_ARTIFACT_FILENAME}{ConfigParams.ENDSWITH_TXT},无法执行<呼吸可用性及间期标注>"
- RESP_QUALITY_LABEL_FILES_FOUND: str = f"找到{ConfigParams.RESP_QUALITY_LABEL_INPUT_XINXIAO_FILENAME}{ConfigParams.ENDSWITH_TXT}和{ConfigParams.RESP_QUALITY_LABEL_INPUT_THO_FILENAME}{ConfigParams.ENDSWITH_TXT}和{ConfigParams.RESP_QUALITY_LABEL_INPUT_ARTIFACT_FILENAME}{ConfigParams.ENDSWITH_TXT}"
- RESP_QUALITY_LABEL_HISTORICAL_SAVE1_FOUND: str = f"找到历史存档文件{ConfigParams.RESP_QUALITY_LABEL_SAVE_RESP_QUALITY_LABNEL_FILENAME}{ConfigParams.ENDSWITH_TXT},已成功读取"
- RESP_QUALITY_LABEL_HISTORICAL_SAVE2_FOUND: str = f"找到历史存档文件{ConfigParams.RESP_QUALITY_LABEL_SAVE_THO_PEAK_FILENAME}{ConfigParams.ENDSWITH_TXT},已成功读取"
- RESP_QUALITY_LABEL_INPUT_SIGNAL_FAILURE: str = "导入信号失败,请检查信号长度"
- RESP_QUALITY_LABEL_INPUT_SUCCESSFULLY: str = "导入数据成功"
- RESP_QUALITY_LABEL_PREPROCESS_SUCCESSFULLY: str = "导入数据成功"
- RESP_QUALITY_LABEL_INPUT_ARTIFACT_FAILURE_FORMAT: str = "导入体动失败,请检查体动标签格式"
- RESP_QUALITY_LABEL_INPUT_ARTIFACT_FAILURE_LENGTH: str = "导入体动失败,请检查体动长度是否为4的倍数"
-
- RESP_QUALITY_LABEL_VIEWING_THE_FIRST_PART: str = "你正在查看第1段信号"
- RESP_QUALITY_LABEL_VIEWING_THE_LAST_PART: str = "你正在查看最后1段信号"
- RESP_QUALITY_LABEL_CUSTOM_NAVIGATIONTOOLBAR_WIDGET_NAME: str = "MainWindow"
- RESP_QUALITY_LABEL_BUTTON_PRESS_EVENT: str = "button_press_event"
- RESP_QUALITY_LABEL_BUTTON_RELEASE_EVENT: str = "button_release_event"
- RESP_QUALITY_LABEL_MOTION_NOTIFY_EVENT: str = "motion_notify_event"
- RESP_QUALITY_LABEL_ADD_POINTS_SUCCESSFULLY: str = "成功新增点,横坐标:"
- RESP_QUALITY_LABEL_REMOVE_POINTS_SUCCESSFULLY: str = "成功删除点,横坐标:"
- RESP_QUALITY_LABEL_NO_POINT_IN_THE_INTERVAL: str = "所选区间内无新增或删除点"
- RESP_QUALITY_LABEL_SAVE_PEAKS_SUCCESSFULLY: str = "保存峰值成功"
- RESP_QUALITY_LABEL_DATA_NOT_FOUND: str = "数据未导入"
- RESP_QUALITY_LABEL_LABEL_SUCCESSFULLY: str = "片段标注并保存成功"
- RESP_QUALITY_LABEL_RESET_SUCCESSFULLY: str = "片段重置并保存成功"
- RESP_QUALITY_LABEL_PLOT_LABEL_VLINE: str = "vline"
- RESP_QUALITY_LABEL_PLOT_LABEL_HLINE: str = "hline"
- RESP_QUALITY_LABEL_A_QUALITY: int = 1
- RESP_QUALITY_LABEL_B_QUALITY: int = 0
- RESP_QUALITY_LABEL_C_QUALITY: int = -1
- RESP_QUALITY_LABEL_LABELED: str = "已标注"
- RESP_QUALITY_LABEL_TOBELABELED: str = "未标注"
-
- RESP_QUALITY_LABEL_CUSTOM_FILTER_ARGS_ERROR: str = "orgBcg带通滤波频率设置范围应为数字,范围是0~1"
- RESP_QUALITY_LABEL_AUTOLABEL_ARGS_ERROR: str = "人工标注阈值设置范围应为数字,范围是0~1"
- RESP_QUALITY_LABEL_CHECK_ARGS_QUESTION_CONTENT: str = "你确定要执行此操作吗,请确保参数输入正确"
- RESP_QUALITY_LABEL_KEY_VALUE = {
- 1: "Good",
- 0: "Bad",
- -1: "None"
- }
-
-
-
-
-
# 禁止实例化
def __new__(cls):
raise TypeError("Constants class cannot be instantiated")
diff --git a/ui/MainWindow/MainWindow_resp_quality_label.py b/ui/MainWindow/MainWindow_resp_quality_label.py
index 4367465..146c77c 100644
--- a/ui/MainWindow/MainWindow_resp_quality_label.py
+++ b/ui/MainWindow/MainWindow_resp_quality_label.py
@@ -94,13 +94,13 @@ class Ui_MainWindow_resp_quality_label(object):
self.gridLayout_5.addWidget(self.pushButton_calculate_peaks_save, 2, 0, 1, 1)
- self.pushButton_input_data_and_label = QPushButton(self.groupBox_left)
- self.pushButton_input_data_and_label.setObjectName(u"pushButton_input_data_and_label")
- sizePolicy.setHeightForWidth(self.pushButton_input_data_and_label.sizePolicy().hasHeightForWidth())
- self.pushButton_input_data_and_label.setSizePolicy(sizePolicy)
- self.pushButton_input_data_and_label.setFont(font1)
+ self.pushButton_input_and_label = QPushButton(self.groupBox_left)
+ self.pushButton_input_and_label.setObjectName(u"pushButton_input_and_label")
+ sizePolicy.setHeightForWidth(self.pushButton_input_and_label.sizePolicy().hasHeightForWidth())
+ self.pushButton_input_and_label.setSizePolicy(sizePolicy)
+ self.pushButton_input_and_label.setFont(font1)
- self.gridLayout_5.addWidget(self.pushButton_input_data_and_label, 0, 1, 3, 1)
+ self.gridLayout_5.addWidget(self.pushButton_input_and_label, 0, 1, 3, 1)
self.verticalLayout_2.addLayout(self.gridLayout_5)
@@ -135,14 +135,16 @@ class Ui_MainWindow_resp_quality_label(object):
self.doubleSpinBox_quality_threshold1 = QDoubleSpinBox(self.groupBox_autoqualitylabel_options)
self.doubleSpinBox_quality_threshold1.setObjectName(u"doubleSpinBox_quality_threshold1")
self.doubleSpinBox_quality_threshold1.setFont(font1)
- self.doubleSpinBox_quality_threshold1.setMaximum(10000.000000000000000)
+ self.doubleSpinBox_quality_threshold1.setMaximum(1.000000000000000)
+ self.doubleSpinBox_quality_threshold1.setSingleStep(0.100000000000000)
self.gridLayout_3.addWidget(self.doubleSpinBox_quality_threshold1, 0, 1, 1, 1)
self.doubleSpinBox_quality_threshold2 = QDoubleSpinBox(self.groupBox_autoqualitylabel_options)
self.doubleSpinBox_quality_threshold2.setObjectName(u"doubleSpinBox_quality_threshold2")
self.doubleSpinBox_quality_threshold2.setFont(font1)
- self.doubleSpinBox_quality_threshold2.setMaximum(10000.000000000000000)
+ self.doubleSpinBox_quality_threshold2.setMaximum(1.000000000000000)
+ self.doubleSpinBox_quality_threshold2.setSingleStep(0.100000000000000)
self.gridLayout_3.addWidget(self.doubleSpinBox_quality_threshold2, 1, 1, 1, 1)
@@ -201,14 +203,16 @@ class Ui_MainWindow_resp_quality_label(object):
self.doubleSpinBox_fillterMode_custom_low = QDoubleSpinBox(self.groupBox_threshold_setting)
self.doubleSpinBox_fillterMode_custom_low.setObjectName(u"doubleSpinBox_fillterMode_custom_low")
self.doubleSpinBox_fillterMode_custom_low.setFont(font1)
- self.doubleSpinBox_fillterMode_custom_low.setMaximum(10000.000000000000000)
+ self.doubleSpinBox_fillterMode_custom_low.setMaximum(1.000000000000000)
+ self.doubleSpinBox_fillterMode_custom_low.setSingleStep(0.100000000000000)
self.gridLayout_6.addWidget(self.doubleSpinBox_fillterMode_custom_low, 3, 1, 1, 1)
self.doubleSpinBox_fillterMode_custom_high = QDoubleSpinBox(self.groupBox_threshold_setting)
self.doubleSpinBox_fillterMode_custom_high.setObjectName(u"doubleSpinBox_fillterMode_custom_high")
self.doubleSpinBox_fillterMode_custom_high.setFont(font1)
- self.doubleSpinBox_fillterMode_custom_high.setMaximum(10000.000000000000000)
+ self.doubleSpinBox_fillterMode_custom_high.setMaximum(1.000000000000000)
+ self.doubleSpinBox_fillterMode_custom_high.setSingleStep(0.100000000000000)
self.gridLayout_6.addWidget(self.doubleSpinBox_fillterMode_custom_high, 3, 3, 1, 1)
@@ -229,6 +233,7 @@ class Ui_MainWindow_resp_quality_label(object):
self.radioButton_orgBcg_fillterMode_preset = QRadioButton(self.groupBox_threshold_setting)
self.radioButton_orgBcg_fillterMode_preset.setObjectName(u"radioButton_orgBcg_fillterMode_preset")
self.radioButton_orgBcg_fillterMode_preset.setFont(font1)
+ self.radioButton_orgBcg_fillterMode_preset.setChecked(True)
self.gridLayout_6.addWidget(self.radioButton_orgBcg_fillterMode_preset, 0, 0, 1, 1)
@@ -471,7 +476,7 @@ class Ui_MainWindow_resp_quality_label(object):
self.pushButton_calculate_peaks.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"\u8ba1\u7b97\u5cf0\u503c\u5e76\u7ed8\u5236", None))
self.pushButton_input_and_calculate_peaks.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"\u5bfc\u5165\u5e76\u7b97\u6cd5\u5b9a\u4f4d\u5cf0\u503c", None))
self.pushButton_calculate_peaks_save.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"\u4fdd\u5b58\u5cf0\u503c\u7ed3\u679c", None))
- self.pushButton_input_data_and_label.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"\u5bfc\u5165\u5e76\u5f00\u59cb\u6807\u6ce8", None))
+ self.pushButton_input_and_label.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"\u5bfc\u5165\u5e76\u5f00\u59cb\u6807\u6ce8", None))
self.groupBox_autoqualitylabel_options.setTitle(QCoreApplication.translate("MainWindow_resp_quality_label", u"\u4eba\u5de5\u6807\u6ce8\u9608\u503c\u8bbe\u7f6e", None))
self.label_6.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"threshold[1]", None))
self.label_5.setText(QCoreApplication.translate("MainWindow_resp_quality_label", u"threshold[0]", None))
diff --git a/ui/MainWindow/MainWindow_resp_quality_label.ui b/ui/MainWindow/MainWindow_resp_quality_label.ui
index a0213a7..c65e326 100644
--- a/ui/MainWindow/MainWindow_resp_quality_label.ui
+++ b/ui/MainWindow/MainWindow_resp_quality_label.ui
@@ -122,7 +122,7 @@
-
-
+
0
@@ -204,7 +204,10 @@
- 10000.000000000000000
+ 1.000000000000000
+
+
+ 0.100000000000000
@@ -216,7 +219,10 @@
- 10000.000000000000000
+ 1.000000000000000
+
+
+ 0.100000000000000
@@ -324,7 +330,10 @@
- 10000.000000000000000
+ 1.000000000000000
+
+
+ 0.100000000000000
@@ -336,7 +345,10 @@
- 10000.000000000000000
+ 1.000000000000000
+
+
+ 0.100000000000000
@@ -380,6 +392,9 @@
预设
+
+ true
+
-
diff --git a/ui/setting/resp_quality_label_input_setting.py b/ui/setting/resp_quality_label_input_setting.py
index b8f4b5d..fc6b9b3 100644
--- a/ui/setting/resp_quality_label_input_setting.py
+++ b/ui/setting/resp_quality_label_input_setting.py
@@ -100,10 +100,10 @@ class Ui_MainWindow_resp_quality_label_input_setting(object):
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
- self.plainTextEdit_file_path_input_Tho = QPlainTextEdit(self.groupBox_file_path_input_Tho)
- self.plainTextEdit_file_path_input_Tho.setObjectName(u"plainTextEdit_file_path_input_Tho")
+ self.plainTextEdit_file_path_input_signal_Tho = QPlainTextEdit(self.groupBox_file_path_input_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_Tho)
+ self.verticalLayout_3.addWidget(self.plainTextEdit_file_path_input_signal_Tho)
self.verticalLayout_3.setStretch(0, 1)
self.verticalLayout_3.setStretch(1, 2)
@@ -181,8 +181,8 @@ class Ui_MainWindow_resp_quality_label_input_setting(object):
self.plainTextEdit_file_path_input_signal_OrgBCG.setPlaceholderText(QCoreApplication.translate("MainWindow_resp_quality_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None))
self.groupBox_file_path_input_Tho.setTitle(QCoreApplication.translate("MainWindow_resp_quality_label_input_setting", u"\u540c\u6b65\u540e\u7684Effort Tho\u8def\u5f84", None))
self.label_3.setText(QCoreApplication.translate("MainWindow_resp_quality_label_input_setting", u"\u91c7\u6837\u7387(Hz)\uff1a", None))
- self.plainTextEdit_file_path_input_Tho.setPlainText("")
- self.plainTextEdit_file_path_input_Tho.setPlaceholderText(QCoreApplication.translate("MainWindow_resp_quality_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None))
+ self.plainTextEdit_file_path_input_signal_Tho.setPlainText("")
+ self.plainTextEdit_file_path_input_signal_Tho.setPlaceholderText(QCoreApplication.translate("MainWindow_resp_quality_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None))
self.groupBox_file_path_input_artifact.setTitle(QCoreApplication.translate("MainWindow_resp_quality_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_resp_quality_label_input_setting", u"\u6587\u4ef6\u8def\u5f84", None))
diff --git a/ui/setting/resp_quality_label_input_setting.ui b/ui/setting/resp_quality_label_input_setting.ui
index cb34972..9b91416 100644
--- a/ui/setting/resp_quality_label_input_setting.ui
+++ b/ui/setting/resp_quality_label_input_setting.ui
@@ -126,7 +126,7 @@
-
-
+