Files
Signal_Label_Reborn/func/Module_label_check.py

1159 lines
59 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from gc import collect
from pathlib import Path
from traceback import format_exc
import matplotlib.pyplot as plt
from PySide6.QtCore import QTimer, QCoreApplication
from PySide6.QtGui import QAction, QFont
from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication, QTableWidgetItem, QTableWidget
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, setdiff1d
from overrides import overrides
from pandas import read_csv, DataFrame
from scipy.signal import find_peaks
from yaml import dump, load, FullLoader
from func.utils.ConfigParams import Filename, Params
from func.utils.PublicFunc import PublicFunc
from func.utils.Constants import Constants
from func.Filters.Preprocessing import data_preprocess_for_label_check
from func.utils.Result import Result
from ui.MainWindow.MainWindow_label_check import Ui_MainWindow_label_check
from ui.setting.label_check_input_setting import Ui_MainWindow_label_check_input_setting
Config = {
}
ButtonState = {
"Default": {
"pushButton_input_setting": True,
"pushButton_input": True,
"pushButton_save": False,
"pushButton_prev_move": False,
"pushButton_pause": False,
"pushButton_next_move": False
},
"Current": {
"pushButton_input_setting": True,
"pushButton_input": True,
"pushButton_save": False,
"pushButton_prev_move": False,
"pushButton_pause": False,
"pushButton_next_move": False
}
}
class SettingWindow(QMainWindow):
def __init__(self, mode, root_path, sampID):
super(SettingWindow, self).__init__()
self.ui = Ui_MainWindow_label_check_input_setting()
self.ui.setupUi(self)
self.mode = mode
self.root_path = root_path
self.sampID = sampID
self.msgBox = QMessageBox()
self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE)
self.config = None
self.__read_config__()
self.__examine_freq__()
self.ui.spinBox_input_freq_signal.valueChanged.connect(self.__update_ui__)
self.ui.spinBox_bandPassOrder.valueChanged.connect(self.__update_ui__)
self.ui.doubleSpinBox_bandPassLow.valueChanged.connect(self.__update_ui__)
self.ui.doubleSpinBox_bandPassHigh.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(Params.LABEL_CHECK_CONFIG_FILE_PATH).exists():
with open(Params.LABEL_CHECK_CONFIG_FILE_PATH, "w") as f:
dump(Params.LABEL_CHECK_CONFIG_NEW_CONTENT, f)
with open(Params.LABEL_CHECK_CONFIG_FILE_PATH, "r") as f:
file_config = load(f.read(), Loader=FullLoader)
Config.update(file_config)
self.config = file_config
if self.mode == "BCG":
Config.update({
"Path": {
"Input_Signal": str((Path(self.root_path) / Filename.PATH_ORGBCG_TEXT /
Path(str(self.sampID)))),
"Input_Peak": str((Path(self.root_path) / Filename.PATH_ORGBCG_TEXT /
Path(str(self.sampID)))),
"Input_Approximately_Align": str((Path(self.root_path) / Filename.PATH_LABEL /
Path(str(self.sampID)))),
"Save": str((Path(self.root_path) / Filename.PATH_ORGBCG_TEXT /
Path(str(self.sampID))))
},
"Mode": self.mode
})
elif self.mode == "ECG":
Config.update({
"Path": {
"Input_Signal": str((Path(self.root_path) / Filename.PATH_PSG_TEXT /
Path(str(self.sampID)))),
"Input_Peak": str((Path(self.root_path) / Filename.PATH_PSG_TEXT /
Path(str(self.sampID)))),
"Input_Approximately_Align": str((Path(self.root_path) / Filename.PATH_LABEL /
Path(str(self.sampID)))),
"Save": str((Path(self.root_path) / Filename.PATH_PSG_TEXT /
Path(str(self.sampID))))
},
"Mode": self.mode
})
else:
raise ValueError("模式不存在")
# 数据回显
self.ui.spinBox_input_freq_signal.setValue(Config["InputConfig"]["Freq"])
self.ui.plainTextEdit_file_path_input_signal.setPlainText(Config["Path"]["Input_Signal"])
self.ui.plainTextEdit_file_path_input_peak.setPlainText(Config["Path"]["Input_Peak"])
self.ui.plainTextEdit_file_path_input_approximately_align.setPlainText(Config["Path"]["Input_Approximately_Align"])
self.ui.plainTextEdit_file_path_save.setPlainText(Config["Path"]["Save"])
if Config["Mode"] == "BCG":
self.ui.spinBox_bandPassOrder.setValue(Config["Filter"]["BCGBandPassOrder"])
self.ui.doubleSpinBox_bandPassLow.setValue(Config["Filter"]["BCGBandPassLow"])
self.ui.doubleSpinBox_bandPassHigh.setValue(Config["Filter"]["BCGBandPassHigh"])
elif Config["Mode"] == "ECG":
self.ui.spinBox_bandPassOrder.setValue(Config["Filter"]["ECGBandPassOrder"])
self.ui.doubleSpinBox_bandPassLow.setValue(Config["Filter"]["ECGBandPassLow"])
self.ui.doubleSpinBox_bandPassHigh.setValue(Config["Filter"]["ECGBandPassHigh"])
else:
raise ValueError("模式不存在")
def __write_config__(self):
# 从界面写入配置
Config["InputConfig"]["Freq"] = self.ui.spinBox_input_freq_signal.value()
Config["Path"]["Input_Signal"] = self.ui.plainTextEdit_file_path_input_signal.toPlainText()
Config["Path"]["Input_Peak"] = self.ui.plainTextEdit_file_path_input_peak.toPlainText()
Config["Path"]["Input_Approximately_Align"] = self.ui.plainTextEdit_file_path_input_approximately_align.toPlainText()
Config["Path"]["Save"] = self.ui.plainTextEdit_file_path_save.toPlainText()
if Config["Mode"] == "BCG":
Config["Filter"]["BCGBandPassOrder"] = self.ui.spinBox_bandPassOrder.value()
Config["Filter"]["BCGBandPassLow"] = self.ui.doubleSpinBox_bandPassLow.value()
Config["Filter"]["BCGBandPassHigh"] = self.ui.doubleSpinBox_bandPassHigh.value()
elif Config["Mode"] == "ECG":
Config["Filter"]["ECGBandPassOrder"] = self.ui.spinBox_bandPassOrder.value()
Config["Filter"]["ECGBandPassLow"] = self.ui.doubleSpinBox_bandPassLow.value()
Config["Filter"]["ECGBandPassHigh"] = self.ui.doubleSpinBox_bandPassHigh.value()
else:
raise ValueError("模式不存在")
# 保存配置到文件
self.config["InputConfig"]["Freq"] = self.ui.spinBox_input_freq_signal.value()
with open(Params.LABEL_CHECK_CONFIG_FILE_PATH, "w") as f:
dump(self.config, f)
self.close()
def __rollback_config__(self):
self.__read_config__()
def __update_ui__(self):
if self.mode == "BCG":
self.ui.plainTextEdit_file_path_input_signal.setPlainText(
str((Path(self.root_path) /
Filename.PATH_ORGBCG_TEXT /
Path(str(self.sampID)) /
Path(Filename.BCG_FILTER +
str(self.ui.spinBox_input_freq_signal.value()) +
Params.ENDSWITH_TXT))))
self.ui.plainTextEdit_file_path_input_peak.setPlainText(
str((Path(self.root_path) /
Filename.PATH_ORGBCG_TEXT /
Path(str(self.sampID)) /
Path(Filename.JPEAK_REVISE +
str(self.ui.spinBox_input_freq_signal.value()) +
Params.ENDSWITH_TXT))))
elif self.mode == "ECG":
self.ui.plainTextEdit_file_path_input_signal.setPlainText(
str((Path(self.root_path) /
Filename.PATH_PSG_TEXT /
Path(str(self.sampID)) /
Path(Filename.ECG_FILTER +
str(self.ui.spinBox_input_freq_signal.value()) +
Params.ENDSWITH_TXT))))
self.ui.plainTextEdit_file_path_input_peak.setPlainText(
str((Path(self.root_path) /
Filename.PATH_PSG_TEXT /
Path(str(self.sampID)) /
Path(Filename.RPEAK_FINAL +
str(self.ui.spinBox_input_freq_signal.value()) +
Params.ENDSWITH_TXT))))
else:
raise ValueError("模式不存在")
def __examine_freq__(self):
if Config["Mode"] == "BCG":
signal = Filename.BCG_FILTER
elif Config["Mode"] == "ECG":
signal = Filename.ECG_FILTER
else:
raise ValueError("模式不存在")
if Path(Config["Path"]["Input_Signal"]).is_file():
Config["Path"]["Input_Signal"] = str(Path(Config["Path"]["Input_Signal"]).parent)
result = PublicFunc.examine_file(Config["Path"]["Input_Signal"], signal, Params.ENDSWITH_TXT)
if result.status:
Config["InputConfig"]["Freq"] = result.data["freq"]
else:
PublicFunc.msgbox_output(self, signal + Constants.FAILURE_REASON["Get_Freq_Not_Correct"], Constants.MSGBOX_TYPE_ERROR)
# 数据回显
self.ui.spinBox_input_freq_signal.setValue(Config["InputConfig"]["Freq"])
class MainWindow_label_check(QMainWindow):
def __init__(self):
super(MainWindow_label_check, self).__init__()
self.ui = Ui_MainWindow_label_check()
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.is_left_button_pressed = None
self.is_right_button_pressed = None
self.point_peak_original = None
self.point_peak_corrected = None
self.annotation_tableWidget = None
# 初始化自动播放定时器
self.autoplay_xlim_start = None
self.autoplay_xlim_end = None
self.timer_autoplay = QTimer()
self.timer_autoplay.timeout.connect(self.autoplay_move_xlim)
Config.update({
"AutoplayArgs": {
"AutoplayMode": "pause",
"MoveLength": int(self.ui.label_moveLength_preset_1.text()),
"MaxRange": int(self.ui.label_maxRange_preset_1.text()),
"MoveSpeed": int(self.ui.label_moveSpeed_preset_1.text())
}
})
self.msgBox = QMessageBox()
self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE)
@overrides
def show(self, mode, root_path, sampID):
super().show()
self.mode = mode
self.root_path = root_path
self.sampID = sampID
self.setting = SettingWindow(mode, 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)
self.ui.verticalLayout_canvas.addWidget(self.canvas)
self.ui.verticalLayout_canvas.addWidget(self.figToolbar)
self.gs = gridspec.GridSpec(2, 1, height_ratios=[1, 1])
self.fig.subplots_adjust(top=0.98, bottom=0.05, right=0.98, left=0.1, hspace=0, wspace=0)
self.ax0 = self.fig.add_subplot(self.gs[0])
self.ax0.grid(True)
self.ax0.xaxis.set_major_formatter(Params.FORMATTER)
self.ax0.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE)
self.ax1 = self.fig.add_subplot(self.gs[1], sharex=self.ax0, sharey=self.ax0)
self.ax1.grid(True)
self.ax1.xaxis.set_major_formatter(Params.FORMATTER)
PublicFunc.__resetAllButton__(self, ButtonState)
self.ui.label_mode.setText(self.mode)
self.ui.doubleSpinBox_findpeaks_min_interval.setValue(Config["FindPeaks"]["MinInterval"])
self.ui.doubleSpinBox_findpeaks_min_height.setValue(Config["FindPeaks"]["MinHeight"])
self.ui.spinBox_moveLength.setValue(Config["CustomAutoplayArgs"]["MoveLength"])
self.ui.spinBox_maxRange.setValue(Config["CustomAutoplayArgs"]["MaxRange"])
self.ui.spinBox_moveSpeed.setValue(Config["CustomAutoplayArgs"]["MoveSpeed"])
self.ui.tableWidget_peak_original.setHorizontalHeaderLabels(['Original'])
self.ui.tableWidget_peak_corrected.setHorizontalHeaderLabels(['Corrected'])
self.ui.tableWidget_peak_original.setEditTriggers(QTableWidget.NoEditTriggers)
self.ui.tableWidget_peak_corrected.setEditTriggers(QTableWidget.NoEditTriggers)
self.ui.tableWidget_peak_original.horizontalHeader().setStretchLastSection(True)
self.ui.tableWidget_peak_corrected.horizontalHeader().setStretchLastSection(True)
self.ui.pushButton_input.clicked.connect(self.__slot_btn_input__)
self.ui.pushButton_input_setting.clicked.connect(self.setting.show)
self.ui.pushButton_save.clicked.connect(self.__slot_btn_save__)
self.ui.pushButton_prev_move.clicked.connect(self.__slot_btn_move__)
self.ui.pushButton_pause.clicked.connect(self.__slot_btn_move__)
self.ui.pushButton_next_move.clicked.connect(self.__slot_btn_move__)
self.ui.radioButton_move_preset_1.toggled.connect(self.__change_autoplay_args__)
self.ui.radioButton_move_preset_2.toggled.connect(self.__change_autoplay_args__)
self.ui.radioButton_move_preset_3.toggled.connect(self.__change_autoplay_args__)
self.ui.radioButton_move_custom.toggled.connect(self.__change_autoplay_args__)
self.ui.tableWidget_peak_original.cellDoubleClicked.connect(
self.__slot_tableWidget_on_cell_double_clicked__)
self.ui.tableWidget_peak_corrected.cellDoubleClicked.connect(
self.__slot_tableWidget_on_cell_double_clicked__)
self.ui.doubleSpinBox_findpeaks_min_interval.editingFinished.connect(self.__update_config__)
self.ui.doubleSpinBox_findpeaks_min_height.editingFinished.connect(self.__update_config__)
self.ui.spinBox_moveLength.editingFinished.connect(self.__update_config__)
self.ui.spinBox_maxRange.editingFinished.connect(self.__update_config__)
self.ui.spinBox_moveSpeed.editingFinished.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.point_peak_original
del self.point_peak_corrected
del self.annotation_tableWidget
if self.ax0 is not None:
self.ax0.clear()
if self.ax1 is not None:
self.ax1.clear()
# 释放资源
del self.data
self.fig.clf()
plt.close(self.fig)
self.deleteLater()
collect()
self.canvas = None
event.accept()
else:
event.ignore()
def __reset__(self):
ButtonState["Current"].update(ButtonState["Default"].copy())
def __plot__(self):
# 清空画框
if self.point_peak_original is not None:
self.point_peak_original.remove()
self.point_peak_original = None
if self.point_peak_corrected is not None:
self.point_peak_corrected.remove()
self.point_peak_corrected = None
if self.annotation_tableWidget is not None:
self.annotation_tableWidget.remove()
self.annotation_tableWidget = None
self.reset_axes()
sender = self.sender()
if sender == self.ui.pushButton_input:
self.ui.spinBox_data_length.setValue(len(self.data.processed_data))
self.ax0.plot(self.data.processed_data,
label=Constants.LABEL_CHECK_PLOT_LABEL_SIGNAL,
color=Constants.PLOT_COLOR_BLUE)
self.ax1.plot(self.data.processed_data,
label=Constants.LABEL_CHECK_PLOT_LABEL_SIGNAL,
color=Constants.PLOT_COLOR_BLUE)
self.ax0.axvline(x=self.data.approximately_align_pos, color=Constants.PLOT_COLOR_BLACK, linestyle="--",
label="Start Line")
self.ax1.axvline(x=self.data.approximately_align_pos, color=Constants.PLOT_COLOR_BLACK, linestyle="--",
label="Start Line")
self.ax0.legend(loc=Constants.PLOT_UPPER_RIGHT)
self.ax1.legend(loc=Constants.PLOT_UPPER_RIGHT)
self.canvas.draw()
self.ax0.autoscale(False)
self.ax1.autoscale(False)
return Result().success(info=Constants.DRAW_FINISHED)
else:
self.canvas.draw()
self.ax0.autoscale(False)
self.ax1.autoscale(False)
return Result().failure(info=Constants.DRAW_FAILURE)
def __plot_peaks__(self):
try:
self.point_peak_original, = self.ax0.plot(self.data.original_peak, self.data.original_peak_y, 'ro',
label=Constants.LABEL_CHECK_PLOT_LABEL_PEAK_ORIGINAL)
self.point_peak_corrected, = self.ax1.plot(self.data.corrected_peak, self.data.corrected_peak_y, 'ro',
label=Constants.LABEL_CHECK_PLOT_LABEL_PEAK_CORRECTED)
self.ax1.callbacks.connect('xlim_changed', lambda ax: self.on_xlim_change(ax))
self.ax0.legend(loc=Constants.PLOT_UPPER_RIGHT)
self.ax1.legend(loc=Constants.PLOT_UPPER_RIGHT)
except Exception as e:
return Result().failure(info=Constants.DRAW_FAILURE + "\n" + format_exc())
self.canvas.draw()
return Result().success(info=Constants.DRAW_FINISHED)
def __redraw_peaks__(self):
self.point_peak_corrected.remove()
self.point_peak_corrected, = self.ax1.plot(self.data.corrected_peak, self.data.corrected_peak_y, 'ro',
label=Constants.LABEL_CHECK_PLOT_LABEL_PEAK_CORRECTED)
self.canvas.draw()
def __update_tableWidget_and_info__(self):
if self.data.original_peak is None or self.data.corrected_peak is None:
return False, Constants.FAILURE_REASON["Data_Not_Exist"]
try:
self.ui.tableWidget_peak_original.setRowCount(len(self.data.original_peak))
self.ui.spinBox_peak_length_original.setValue(len(self.data.original_peak))
for row, value in enumerate(self.data.original_peak):
item = QTableWidgetItem(str(value).strip())
self.ui.tableWidget_peak_original.setItem(row, 0, item)
self.ui.tableWidget_peak_corrected.setRowCount(len(self.data.corrected_peak))
self.ui.spinBox_peak_length_corrected.setValue(len(self.data.corrected_peak))
for row, value in enumerate(self.data.corrected_peak):
item = QTableWidgetItem(str(value).strip())
self.ui.tableWidget_peak_corrected.setItem(row, 0, item)
return Result().success(info=Constants.UPDATE_FINISHED)
except Exception as e:
return Result().failure(info=Constants.UPDATE_FAILURE + "\n" + format_exc())
def __update_config__(self):
Config["FindPeaks"]["MinInterval"] = self.ui.doubleSpinBox_findpeaks_min_interval.value()
Config["FindPeaks"]["MinHeight"] = self.ui.doubleSpinBox_findpeaks_min_height.value()
Config["CustomAutoplayArgs"]["MoveLength"] = self.ui.spinBox_moveLength.value()
Config["CustomAutoplayArgs"]["MaxRange"] = self.ui.spinBox_maxRange.value()
Config["CustomAutoplayArgs"]["MoveSpeed"] = self.ui.spinBox_moveSpeed.value()
def __slot_btn_input__(self):
PublicFunc.__disableAllButton__(self, ButtonState)
# 清空画框
if self.point_peak_original is not None:
self.point_peak_original.remove()
self.point_peak_original = None
if self.point_peak_corrected is not None:
self.point_peak_corrected.remove()
self.point_peak_corrected = None
if self.annotation_tableWidget is not None:
self.annotation_tableWidget.remove()
self.annotation_tableWidget = None
self.reset_axes()
self.canvas.draw()
self.data = Data()
# 导入数据
PublicFunc.progressbar_update(self, 1, 7, Constants.INPUTTING_DATA, 0)
result = self.data.open_file()
if not result.status:
PublicFunc.text_output(self.ui, "(1/7)" + 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/7)" + result.info, Constants.TIPS_TYPE_INFO)
# 获取存档
PublicFunc.progressbar_update(self, 2, 7, Constants.LOADING_ARCHIVE, 20)
result = self.data.get_archive()
if not result.status:
PublicFunc.text_output(self.ui, "(2/7)" + 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/7)" + result.info, Constants.TIPS_TYPE_INFO)
# 保存
PublicFunc.progressbar_update(self, 3, 7, Constants.SAVING_DATA, 25)
result = self.data.save()
if not result.status:
PublicFunc.text_output(self.ui, "(3/7)" + 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/7)" + result.info, Constants.TIPS_TYPE_INFO)
# 数据预处理
PublicFunc.progressbar_update(self, 4, 7, Constants.PREPROCESSING_DATA, 30)
result = self.data.preprocess()
if not result.status:
PublicFunc.text_output(self.ui, "(4/7)" + 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/7)" + result.info, Constants.TIPS_TYPE_INFO)
# 更新表格
PublicFunc.progressbar_update(self, 5, 7, Constants.UPDATING_TABLEWIDGET_AND_INFO, 50)
result = self.__update_tableWidget_and_info__()
if not result.status:
PublicFunc.text_output(self.ui, "(5/7)" + 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/7)" + result.info, Constants.TIPS_TYPE_INFO)
# 绘图
PublicFunc.progressbar_update(self, 6, 7, Constants.DRAWING_DATA, 60)
result = self.__plot__()
if not result.status:
PublicFunc.text_output(self.ui, "(6/7)" + 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/7)" + result.info, Constants.TIPS_TYPE_INFO)
# 绘点
PublicFunc.progressbar_update(self, 7, 7, Constants.DRAWING_DATA, 80)
result = self.__plot_peaks__()
if not result.status:
PublicFunc.text_output(self.ui, "(7/7)" + result.info, Constants.TIPS_TYPE_ERROR)
PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
PublicFunc.finish_operation(self, ButtonState)
return
else:
PublicFunc.text_output(self.ui, "(7/7)" + result.info, Constants.TIPS_TYPE_INFO)
self.__reset__()
self.canvas.mpl_connect("motion_notify_event", self.on_motion)
self.figToolbar.action_Label_Multiple.setEnabled(True)
for action in self.figToolbar._actions.values():
action.setEnabled(True)
ButtonState["Current"]["pushButton_input"] = False
ButtonState["Current"]["pushButton_input_setting"] = False
ButtonState["Current"]["pushButton_save"] = True
ButtonState["Current"]["pushButton_prev_move"] = True
ButtonState["Current"]["pushButton_next_move"] = True
ButtonState["Current"]["pushButton_pause"] = True
PublicFunc.finish_operation(self, ButtonState)
def __slot_btn_save__(self):
PublicFunc.__disableAllButton__(self, ButtonState)
# 保存
PublicFunc.progressbar_update(self, 1, 1, Constants.SAVING_DATA, 0)
result = self.data.save()
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_move__(self):
if self.data is None:
return
sender = self.sender()
if sender == self.ui.pushButton_prev_move:
Config["AutoplayArgs"]["AutoplayMode"] = "prev"
self.autoplay_xlim_start = int(self.ax0.get_xlim()[1] - Config["AutoplayArgs"]["MaxRange"])
self.autoplay_xlim_end = int(self.ax0.get_xlim()[1])
if self.autoplay_xlim_end > len(self.data.processed_data):
self.autoplay_xlim_start = int(len(self.data.processed_data) - Config["AutoplayArgs"]["MaxRange"])
self.autoplay_xlim_end = int(len(self.data.processed_data))
self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end)
self.canvas.draw()
self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"])
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_PREV_MOVE, Constants.TIPS_TYPE_INFO)
elif sender == self.ui.pushButton_next_move:
Config["AutoplayArgs"]["AutoplayMode"] = "next"
self.autoplay_xlim_start = int(self.ax0.get_xlim()[0])
self.autoplay_xlim_end = int(self.ax0.get_xlim()[0] + Config["AutoplayArgs"]["MaxRange"])
if self.autoplay_xlim_start < 0:
self.autoplay_xlim_start = 0
self.autoplay_xlim_end = 0 + Config["AutoplayArgs"]["MaxRange"]
self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end)
self.canvas.draw()
self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"])
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_NEXT_MOVE, Constants.TIPS_TYPE_INFO)
elif sender == self.ui.pushButton_pause:
Config["AutoplayArgs"]["AutoplayMode"] = "pause"
self.timer_autoplay.stop()
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_PAUSE, Constants.TIPS_TYPE_INFO)
def __change_autoplay_args__(self):
sender = self.sender()
if sender == self.ui.radioButton_move_preset_1 and self.ui.radioButton_move_preset_1.isChecked():
Config["AutoplayArgs"]["MoveLength"] = int(self.ui.label_moveLength_preset_1.text())
Config["AutoplayArgs"]["MaxRange"] = int(self.ui.label_maxRange_preset_1.text())
Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.label_moveSpeed_preset_1.text())
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_SWITCH_PRESET_1, Constants.TIPS_TYPE_INFO)
elif sender == self.ui.radioButton_move_preset_2 and self.ui.radioButton_move_preset_2.isChecked():
Config["AutoplayArgs"]["MoveLength"] = int(self.ui.label_moveLength_preset_2.text())
Config["AutoplayArgs"]["MaxRange"] = int(self.ui.label_maxRange_preset_2.text())
Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.label_moveSpeed_preset_2.text())
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_SWITCH_PRESET_2, Constants.TIPS_TYPE_INFO)
elif sender == self.ui.radioButton_move_preset_3 and self.ui.radioButton_move_preset_3.isChecked():
Config["AutoplayArgs"]["MoveLength"] = int(self.ui.label_moveLength_preset_3.text())
Config["AutoplayArgs"]["MaxRange"] = int(self.ui.label_maxRange_preset_3.text())
Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.label_moveSpeed_preset_3.text())
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_SWITCH_PRESET_3, Constants.TIPS_TYPE_INFO)
elif sender == self.ui.radioButton_move_custom and self.ui.radioButton_move_custom.isChecked():
Config["AutoplayArgs"]["MoveLength"] = int(self.ui.spinBox_moveLength.value())
Config["AutoplayArgs"]["MaxRange"] = int(self.ui.spinBox_maxRange.value())
Config["AutoplayArgs"]["MoveSpeed"] = int(self.ui.spinBox_moveSpeed.value())
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_SWITCH_CUSTOM, Constants.TIPS_TYPE_INFO)
if Config["AutoplayArgs"]["AutoplayMode"] == "next":
self.autoplay_xlim_start = int(self.ax0.get_xlim()[0])
self.autoplay_xlim_end = int(self.ax0.get_xlim()[0] + Config["AutoplayArgs"]["MaxRange"])
if self.autoplay_xlim_start < 0:
self.autoplay_xlim_start = 0
self.autoplay_xlim_end = 0 + Config["AutoplayArgs"]["MaxRange"]
self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end)
self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"])
elif Config["AutoplayArgs"]["AutoplayMode"] == "prev":
self.autoplay_xlim_start = int(self.ax0.get_xlim()[1] - Config["AutoplayArgs"]["MaxRange"])
self.autoplay_xlim_end = int(self.ax0.get_xlim()[1])
if self.autoplay_xlim_end > len(self.data.processed_data):
self.autoplay_xlim_start = int(self.data.processed_data) - Config["AutoplayArgs"]["MaxRange"]
self.autoplay_xlim_end = int(self.data.processed_data)
self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end)
self.timer_autoplay.start(Config["AutoplayArgs"]["MoveSpeed"])
elif Config["AutoplayArgs"]["AutoplayMode"] == "pause":
self.timer_autoplay.stop()
def __slot_tableWidget_on_cell_double_clicked__(self, row, col):
if Config["AutoplayArgs"]["AutoplayMode"] != "pause":
self.ui.pushButton_pause.click()
sender = self.sender()
if sender == self.ui.tableWidget_peak_original:
x = float(self.ui.tableWidget_peak_original.item(row, col).text())
elif sender == self.ui.tableWidget_peak_corrected:
x = float(self.ui.tableWidget_peak_corrected.item(row, col).text())
else:
raise ValueError("表格跳转参数不存在")
self.ax0.set_xlim(x - 5000, x + 5000)
self.annotation_tableWidget = self.ax0.annotate(f'x={int(x)}', xy=(int(x), self.ax0.get_ylim()[0]),
xytext=(int(x), self.ax0.get_ylim()[0] + (self.ax0.get_ylim()[1] - self.ax0.get_ylim()[0]) * 0.1),
arrowprops=dict(facecolor=Constants.PLOT_COLOR_BLACK, shrink=0.1))
self.canvas.draw()
PublicFunc.text_output(self.ui, f"{Constants.LABEL_CHECK_JUMP_X_INDEX}{str(int(x))}", Constants.TIPS_TYPE_INFO)
def reset_axes(self):
if self.ax0 is not None:
self.ax0.clear()
self.ax0.grid(True)
self.ax0.xaxis.set_major_formatter(Params.FORMATTER)
self.ax0.tick_params(axis='x', colors=Constants.PLOT_COLOR_WHITE)
if self.ax1 is not None:
self.ax1.clear()
self.ax1.grid(True)
self.ax1.xaxis.set_major_formatter(Params.FORMATTER)
def on_xlim_change(self, event_ax):
try:
if self.annotation_tableWidget is not None:
self.annotation_tableWidget.remove()
self.annotation_tableWidget = None
except AttributeError:
pass
def autoplay_move_xlim(self):
if Config["AutoplayArgs"]["AutoplayMode"] == "prev" and self.autoplay_xlim_start < 0:
Config["AutoplayArgs"]["AutoplayMode"] = "pause"
self.timer_autoplay.stop()
elif Config["AutoplayArgs"]["AutoplayMode"] == "next" and self.autoplay_xlim_end > len(self.data.processed_data):
Config["AutoplayArgs"]["AutoplayMode"] = "pause"
self.timer_autoplay.stop()
else:
if Config["AutoplayArgs"]["AutoplayMode"] == "next":
self.autoplay_xlim_start += Config["AutoplayArgs"]["MoveLength"]
self.autoplay_xlim_end += Config["AutoplayArgs"]["MoveLength"]
elif Config["AutoplayArgs"]["AutoplayMode"] == "prev":
self.autoplay_xlim_start -= Config["AutoplayArgs"]["MoveLength"]
self.autoplay_xlim_end -= Config["AutoplayArgs"]["MoveLength"]
self.ax0.set_xlim(self.autoplay_xlim_start, self.autoplay_xlim_end)
self.canvas.draw()
def on_motion(self, event):
if event.inaxes and self.ui.checkBox_show_reference_line.isChecked():
# Clear previous reference lines and temporary points
for line in self.ax0.lines[1:]:
if (line.get_label() == "vline" or
line.get_label() == "hline"):
line.remove()
for line in self.ax1.lines[1:]:
if (line.get_label() == "vline" or
line.get_label() == "hline"):
line.remove()
# Draw vertical and horizontal reference lines
self.ax0.axvline(event.xdata, color=Constants.PLOT_COLOR_GRAY, linestyle='--', label="vline")
self.ax0.axhline(event.ydata, color=Constants.PLOT_COLOR_GRAY, linestyle='--', label="hline")
self.ax1.axvline(event.xdata, color=Constants.PLOT_COLOR_GRAY, linestyle='--', label="vline")
self.ax1.axhline(event.ydata, color=Constants.PLOT_COLOR_GRAY, linestyle='--', label="hline")
self.canvas.draw()
def toggle_home(self):
if Config["AutoplayArgs"]["AutoplayMode"] != "pause":
self.ui.pushButton_pause.click()
self.ax0.autoscale(True)
self.ax1.autoscale(True)
self.ax1.relim()
self.ax1.autoscale_view()
self.canvas.draw()
self.ax0.autoscale(False)
self.ax1.autoscale(False)
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_RECOVER_SCALE, Constants.TIPS_TYPE_INFO)
def toggle_changeLabel(self, state):
if state:
self.deactivate_figToolbar_buttons()
self.figToolbar.action_Label_Multiple.setChecked(True)
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
self.figToolbar.rect_start_y = event.ydata
# 如果矩形patch已存在先移除
if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None:
self.figToolbar.rect_patch_ax0.remove()
self.figToolbar.rect_patch_ax0 = None
self.figToolbar.rect_patch_ax1.remove()
self.figToolbar.rect_patch_ax1 = None
self.canvas.draw()
def on_release(self, event):
if self.figToolbar.action_Label_Multiple.isChecked():
if self.figToolbar.rect_start_x is not None and self.figToolbar.rect_start_y is not None:
self.figToolbar.rect_end_x = event.xdata
self.figToolbar.rect_end_y = event.ydata
if ((self.figToolbar.rect_start_x is not None and self.figToolbar.rect_end_x is not None) and
(self.figToolbar.rect_start_y is not None and self.figToolbar.rect_end_y 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
if self.figToolbar.rect_start_y < self.figToolbar.rect_end_y:
rect_bottom = self.figToolbar.rect_start_y
rect_top = self.figToolbar.rect_end_y
elif self.figToolbar.rect_start_y > self.figToolbar.rect_end_y:
rect_bottom = self.figToolbar.rect_end_y
rect_top = self.figToolbar.rect_start_y
else:
rect_bottom = self.figToolbar.rect_start_y
rect_top = self.figToolbar.rect_start_y
else:
rect_left = self.figToolbar.rect_start_x
rect_right = self.figToolbar.rect_start_x
rect_bottom = self.figToolbar.rect_start_y
rect_top = self.figToolbar.rect_start_y
if event.button == 1 and self.is_left_button_pressed:
self.is_left_button_pressed = False
if rect_left < 0:
rect_left = 0
elif rect_left >= len(self.data.processed_data):
rect_left = 0
rect_right = 0
if rect_right >= len(self.data.processed_data):
rect_right = len(self.data.processed_data) - 1
elif rect_right < 0:
rect_left = 0
rect_right = 0
selected_area_for_add_points = self.data.processed_data[int(rect_left):int(rect_right)]
# 找到子列表的最小值
min_value = min(selected_area_for_add_points)
# 遍历子列表,将不满足条件的元素替换为最小值
selected_area_for_add_points = [
y if rect_bottom < y < rect_top else min_value
for y in selected_area_for_add_points
]
peaks_idx, _ = find_peaks(selected_area_for_add_points,
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)
else:
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_NO_POINT_IN_THE_INTERVAL,
Constants.TIPS_TYPE_INFO)
self.data.corrected_peak = append(self.data.corrected_peak, peaks_idx)
self.data.corrected_peak_y = append(self.data.corrected_peak_y, self.data.processed_data[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.corrected_peak - rect_left
right_label2_to_delete = self.data.corrected_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.LABEL_CHECK_REMOVE_POINTS_SUCCESSFULLY}{self.data.corrected_peak[left_label2_to_delete_idx:right_label2_to_delete_idx]}",
Constants.TIPS_TYPE_INFO)
else:
PublicFunc.text_output(self.ui, Constants.LABEL_CHECK_NO_POINT_IN_THE_INTERVAL,
Constants.TIPS_TYPE_INFO)
self.data.corrected_peak = delete(self.data.corrected_peak, arange(left_label2_to_delete_idx, right_label2_to_delete_idx))
self.data.corrected_peak_y = delete(self.data.corrected_peak_y, arange(left_label2_to_delete_idx, right_label2_to_delete_idx))
self.__redraw_peaks__()
self.figToolbar.rect_start_x = None
self.figToolbar.rect_end_x = None
self.figToolbar.rect_start_y = None
self.figToolbar.rect_end_y = None
self.data.corrected_peak.sort()
self.data.corrected_peak_y = [self.data.processed_data[x] for x in self.data.corrected_peak]
self.__update_tableWidget_and_info__()
result = self.data.save()
if not result.status:
info = f"未成功保存,错误提示:{result.info},结果已暂存到缓存中,请正确操作后重试。"
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_ERROR)
PublicFunc.msgbox_output(self, info, Constants.MSGBOX_TYPE_ERROR)
else:
info = result.info
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_INFO)
# 移除矩形patch
if self.figToolbar.rect_patch_ax0 is not None and self.figToolbar.rect_patch_ax1 is not None:
self.figToolbar.rect_patch_ax0.remove()
self.figToolbar.rect_patch_ax0 = None
self.figToolbar.rect_patch_ax1.remove()
self.figToolbar.rect_patch_ax1 = None
self.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
if self.figToolbar.rect_start_y is not None and event.ydata is not None:
self.figToolbar.rect_end_y = event.ydata
# 如果矩形patch不存在则创建一个新的
if self.figToolbar.rect_patch_ax0 is None:
if self.is_left_button_pressed:
self.figToolbar.rect_patch_ax0 = patches.Rectangle((0, 0), 1, 1, fill=True,
alpha=Params.LABEL_CHECK_LABEL_TRANSPARENCY,
color=Constants.PLOT_COLOR_PINK)
elif self.is_right_button_pressed:
self.figToolbar.rect_patch_ax0 = patches.Rectangle((0, 0), 1, 1, fill=True,
alpha=Params.LABEL_CHECK_LABEL_TRANSPARENCY,
color=Constants.PLOT_COLOR_RED)
self.ax0.add_patch(self.figToolbar.rect_patch_ax0)
# 如果矩形patch不存在则创建一个新的
if self.figToolbar.rect_patch_ax1 is None:
if self.is_left_button_pressed:
self.figToolbar.rect_patch_ax1 = patches.Rectangle((0, 0), 1, 1, fill=True,
alpha=Params.LABEL_CHECK_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=Params.LABEL_CHECK_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
if self.is_left_button_pressed:
y_start = self.figToolbar.rect_start_y
y_end = self.figToolbar.rect_end_y
self.figToolbar.rect_patch_ax0.set_xy((min(x_start, x_end), min(y_start, y_end)))
self.figToolbar.rect_patch_ax0.set_height(abs(y_end - y_start))
self.figToolbar.rect_patch_ax1.set_xy((min(x_start, x_end), min(y_start, y_end)))
self.figToolbar.rect_patch_ax1.set_height(abs(y_end - y_start))
elif self.is_right_button_pressed:
y_start = min(self.ax0.get_ylim()[0], self.ax1.get_ylim()[0]) - 1000
y_end = max(self.ax0.get_ylim()[1], self.ax1.get_ylim()[1]) + 1000
self.figToolbar.rect_patch_ax0.set_xy((min(x_start, x_end), y_start))
self.figToolbar.rect_patch_ax0.set_height(abs(y_end - y_start))
self.figToolbar.rect_patch_ax1.set_xy((min(x_start, x_end), y_start))
self.figToolbar.rect_patch_ax1.set_height(abs(y_end - y_start))
self.figToolbar.rect_patch_ax0.set_width(abs(x_end - x_start))
self.figToolbar.rect_patch_ax1.set_width(abs(x_end - x_start))
self.canvas.draw()
class Data:
def __init__(self):
self.raw_data = None
self.processed_data = None
self.original_peak = None
self.original_peak_y = None
self.corrected_peak = None
self.corrected_peak_y = None
self.approximately_align_pos = None
def open_file(self):
if Config["Mode"] == "BCG":
signal = Filename.BCG_FILTER
peak = Filename.JPEAK_REVISE
save = Filename.JPEAK_REVISE_CORRECTED
elif Config["Mode"] == "ECG":
signal = Filename.ECG_FILTER
peak = Filename.RPEAK_FINAL
save = Filename.RPEAK_FINAL_CORRECTED
else:
raise ValueError("模式不存在")
if Path(Config["Path"]["Input_Signal"]).is_file():
Config["Path"]["Input_Signal"] = str(Path(Config["Path"]["Input_Signal"]).parent)
if Path(Config["Path"]["Input_Peak"]).is_file():
Config["Path"]["Input_Peak"] = str(Path(Config["Path"]["Input_Peak"]).parent)
if Path(Config["Path"]["Input_Approximately_Align"]).is_file():
Config["Path"]["Input_Approximately_Align"] = str(Path(Config["Path"]["Input_Approximately_Align"]).parent)
result = PublicFunc.examine_file(Config["Path"]["Input_Signal"], signal, Params.ENDSWITH_TXT)
if result.status:
Config["Path"]["Input_Signal"] = result.data["path"]
Config["InputConfig"]["Freq"] = result.data["freq"]
else:
return result
Config["Path"]["Input_Peak"] = str(
Path(Config["Path"]["Input_Peak"]) / Path(peak + str(Config["InputConfig"]["Freq"]) + Params.ENDSWITH_TXT))
Config["Path"]["Input_Approximately_Align"] = str(
Path(Config["Path"]["Input_Approximately_Align"]) / Path(
Filename.APPROXIMATELY_ALIGN_INFO + Params.ENDSWITH_CSV))
Config["Path"]["Save"] = str(
Path(Config["Path"]["Save"]) / Path(save + str(Config["InputConfig"]["Freq"]) + Params.ENDSWITH_TXT))
if not Path(Config["Path"]["Input_Peak"]).exists():
return Result().failure(info=Constants.INPUT_FAILURE + "\n" +
peak + "" +
Config["Path"]["Input_Peak"] +
Constants.FAILURE_REASON["Path_Not_Exist"])
if not Path(Config["Path"]["Input_Approximately_Align"]).exists():
return Result().failure(info=Constants.INPUT_FAILURE + "\n" +
Filename.APPROXIMATELY_ALIGN_INFO + "" +
Config["Path"]["Input_Approximately_Align"] +
Constants.FAILURE_REASON["Path_Not_Exist"])
try:
self.raw_data = read_csv(Config["Path"]["Input_Signal"],
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.original_peak = read_csv(Config["Path"]["Input_Peak"],
encoding=Params.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:
df = read_csv(Config["Path"]["Input_Approximately_Align"])
pos = df["pos"].values[-1]
ApplyFrequency = df["ApplyFrequency"].values[-1]
self.approximately_align_pos = int(pos * (Config["InputConfig"]["Freq"] / ApplyFrequency))
if Config["Mode"] == "BCG":
if self.approximately_align_pos > 0:
self.approximately_align_pos = 0
else:
self.approximately_align_pos = - self.approximately_align_pos
elif Config["Mode"] == "ECG":
if self.approximately_align_pos < 0:
self.approximately_align_pos = 0
else:
raise ValueError("模式不存在")
except Exception:
self.approximately_align_pos = 0
return Result().success(info=Constants.INPUT_FINISHED)
def get_archive(self):
if not Path(Config["Path"]["Save"]).exists():
self.corrected_peak = self.original_peak
return Result().success(info=Constants.ARCHIVE_NOT_EXIST)
else:
self.corrected_peak = read_csv(Config["Path"]["Save"],
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
return Result().success(info=Constants.ARCHIVE_EXIST)
def preprocess(self):
if self.raw_data is None:
return Result().failure(info=Constants.PREPROCESS_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
try:
if Config["Mode"] == "BCG":
if Config["Filter"]["BCGBandPassOrder"] == 0:
self.processed_data = self.raw_data
else:
if ((Config["Filter"]["BCGBandPassLow"] >= Config["Filter"]["BCGBandPassHigh"]) or
(Config["Filter"]["BCGBandPassLow"] <= 0) or (Config["Filter"]["BCGBandPassHigh"] <= 0)):
return Result().failure(
info=Constants.PREPROCESS_FAILURE + Constants.FAILURE_REASON["Filter_Args_Not_Correct"])
self.processed_data = data_preprocess_for_label_check(self.raw_data,
Config["Filter"]["BCGBandPassOrder"],
Config["Filter"]["BCGBandPassLow"],
Config["Filter"]["BCGBandPassHigh"],
Config["InputConfig"]["Freq"])
elif Config["Mode"] == "ECG":
if Config["Filter"]["ECGBandPassOrder"] == 0:
self.processed_data = self.raw_data
else:
if ((Config["Filter"]["ECGBandPassLow"] >= Config["Filter"]["ECGBandPassHigh"]) or
(Config["Filter"]["ECGBandPassLow"] <= 0) or (Config["Filter"]["ECGBandPassHigh"] <= 0)):
return Result().failure(
info=Constants.PREPROCESS_FAILURE + Constants.FAILURE_REASON["Filter_Args_Not_Correct"])
self.processed_data = data_preprocess_for_label_check(self.raw_data,
Config["Filter"]["ECGBandPassOrder"],
Config["Filter"]["ECGBandPassLow"],
Config["Filter"]["ECGBandPassHigh"],
Config["InputConfig"]["Freq"])
else:
raise ValueError("模式不存在")
self.original_peak_y = [self.processed_data[x] for x in self.original_peak]
self.corrected_peak_y = [self.processed_data[x] for x in self.corrected_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 save(self):
if self.corrected_peak is None:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Data_Not_Exist"])
try:
DataFrame(self.corrected_peak).to_csv(Config["Path"]["Save"], index=False, header=False)
except PermissionError as e:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_Permission_Denied"])
except FileNotFoundError as e:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_File_Not_Found"])
except Exception as e:
return Result().failure(info=Constants.SAVE_FAILURE +
Constants.FAILURE_REASON["Save_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.SAVE_FINISHED)
class CustomNavigationToolbar(NavigationToolbar2QT):
def __init__(self, canvas, parent):
super().__init__(canvas, parent)
# 初始化画框工具栏
self.action_Label_Multiple = QAction(Constants.LABEL_CHECK_ACTION_LABEL_MULTIPLE_NAME, self)
self.action_Label_Multiple.setFont(QFont(Params.FONT, 14))
self.action_Label_Multiple.setCheckable(True)
self.action_Label_Multiple.setShortcut(QCoreApplication.translate(
"MainWindow",
Params.LABEL_CHECK_ACTION_LABEL_MULTIPLE_SHORTCUT_KEY))
self.insertAction(self._actions['pan'], self.action_Label_Multiple)
self._actions['pan'].setShortcut(QCoreApplication.translate(
"MainWindow",
Params.ACTION_PAN_SHORTCUT_KEY))
self._actions['zoom'].setShortcut(QCoreApplication.translate(
"MainWindow",
Params.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_start_y = None
self.rect_end_y = None
self.rect_patch_ax0 = None # 用于绘制矩形的patch
self.rect_patch_ax1 = None # 用于绘制矩形的patch
def home(self, *args):
pass
def zoom(self, *args):
super().zoom(*args)
self.deactivate_figToorbar_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