重构数据切割和标签映射模块,新增OrgBCG和ECG通道支持,优化文件路径和异常处理逻辑,调整UI组件名称和布局

This commit is contained in:
2025-12-18 20:00:45 +08:00
parent 860bddf0a3
commit 6c688f26cf
7 changed files with 758 additions and 461 deletions

View File

@ -1,358 +0,0 @@
from ast import literal_eval
from gc import collect
from math import floor
from pathlib import Path
from traceback import format_exc
from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication
from numpy import array
from overrides import overrides
from pandas import read_csv, DataFrame
from yaml import dump, load, FullLoader
from func.utils.ConfigParams import Filename, Params
from func.utils.PublicFunc import PublicFunc
from func.utils.Constants import Constants
from func.utils.Result import Result
from ui.MainWindow.MainWindow_cut_PSG import Ui_MainWindow_cut_PSG
Config = {
}
ButtonState = {
"Default": {
"pushButton_execute": True
},
"Current": {
"pushButton_execute": True
}
}
class MainWindow_cut_PSG(QMainWindow):
def __init__(self):
super(MainWindow_cut_PSG, self).__init__()
self.ui = Ui_MainWindow_cut_PSG()
self.ui.setupUi(self)
self.root_path = None
self.sampID = None
self.__read_config__()
self.data = None
self.ui.textBrowser_info.setStyleSheet("QTextBrowser { background-color: rgb(255, 255, 200); }")
PublicFunc.__styleAllButton__(self, ButtonState)
# 初始化进度条
self.ui.progressbar.setStyleSheet(Constants.PROGRESSBAR_STYLE)
self.progressbar = self.ui.progressbar
self.msgBox = QMessageBox()
self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE)
@overrides
def show(self, root_path, sampID):
super().show()
self.root_path = root_path
self.sampID = sampID
PublicFunc.__resetAllButton__(self, ButtonState)
Config.update({
"Path": {
"InputFolder": str(Path(self.root_path) / Filename.PATH_PSG_TEXT / Path(str(self.sampID))),
"SaveFolder": str(Path(self.root_path) / Filename.PATH_PSG_ALIGNED / Path(str(self.sampID))),
"InputAlignInfo": str(Path(self.root_path) / Filename.PATH_LABEL / Path(str(self.sampID)))
}
})
self.ui.plainTextEdit_channel.setPlainText(', '.join(Config["ChannelInput"].keys()))
self.ui.plainTextEdit_label.setPlainText(', '.join(Config["LabelInput"].keys()))
self.ui.pushButton_execute.clicked.connect(self.__slot_btn_execute__)
@overrides
def closeEvent(self, event):
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.data
self.deleteLater()
collect()
event.accept()
else:
event.ignore()
def __reset__(self):
ButtonState["Current"].update(ButtonState["Default"].copy())
def __read_config__(self):
if not Path(Params.CUT_PSG_CONFIG_FILE_PATH).exists():
with open(Params.CUT_PSG_CONFIG_FILE_PATH, "w") as f:
dump(Params.CUT_PSG_CONFIG_NEW_CONTENT, f)
with open(Params.CUT_PSG_CONFIG_FILE_PATH, "r") as f:
file_config = load(f.read(), Loader=FullLoader)
Config.update(file_config)
# 数据回显
self.ui.spinBox_ECGFreq.setValue(Config["ECGFreq"])
def __slot_btn_execute__(self):
PublicFunc.__disableAllButton__(self, ButtonState)
self.data = Data(self.root_path, self.sampID)
Config["ECGFreq"] = self.ui.spinBox_ECGFreq.value()
# 检查文件是否存在并获取其数据采样率
PublicFunc.progressbar_update(self, 1, 5, Constants.CUT_PSG_GETTING_FILE_AND_FREQ, 0)
result = self.data.get_file_and_freq()
if not result.status:
PublicFunc.text_output(self.ui, "(1/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 导入数据
PublicFunc.progressbar_update(self, 2, 5, Constants.INPUTTING_DATA, 10)
result = self.data.open_file()
if not result.status:
PublicFunc.text_output(self.ui, "(2/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 切割数据
PublicFunc.progressbar_update(self, 3, 5, Constants.CUT_PSG_CUTTING_DATA, 40)
result = self.data.cut_data()
if not result.status:
PublicFunc.text_output(self.ui, "(3/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 标签映射
PublicFunc.progressbar_update(self, 4, 5, Constants.CUT_PSG_ALIGNING_LABEL, 60)
result = self.data.align_label()
if not result.status:
PublicFunc.text_output(self.ui, "(4/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 保存数据
PublicFunc.progressbar_update(self, 5, 5, Constants.SAVING_DATA, 70)
result = self.data.save()
if not result.status:
PublicFunc.text_output(self.ui, "(5/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
for key, raw in self.data.raw.items():
info = "保存{}的长度为{},采样率为{}Hz".format(key, str(len(raw)), str(self.data.freq[key]))
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_INFO)
QApplication.processEvents()
PublicFunc.msgbox_output(self, result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
class Data:
def __init__(self, root_path, sampID):
self.alignInfo = None
self.raw = {key: array([]) for key in Config["ChannelInput"]}
self.freq = {key: 0 for key in Config["ChannelInput"]}
self.SALabel = None
self.startTime = None
self.root_path = root_path
self.sampID = sampID
def get_file_and_freq(self):
try:
for file_path in Path(Config["Path"]["InputFolder"]).glob('*'):
if file_path.is_file():
file_stem = Path(file_path).stem
for key, prefix in Config["ChannelInput"].items():
if file_stem.startswith(prefix):
freq_str = file_stem.rsplit('_', 1)[1]
try:
freq = int(freq_str)
self.freq[key] = freq
except ValueError:
return Result().failure(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Filename_Format_not_Correct"] + f"\n{Config['ChannelInput']}")
for value in self.freq.values():
if value == 0:
return Result().failure(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Filename_Format_not_Correct"] + f"\n{Config['ChannelInput']}")
if not any((Config["LabelInput"]["SA Label"] + Config["EndWith"]["SA Label"]) in str(file) for file in Path(Config["Path"]["InputFolder"]).glob('*')):
return Result().failure(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["File_Not_Exist"])
if not any((Config["StartTime"] + Config["EndWith"]["StartTime"]) in str(file) for file in Path(Config["Path"]["InputFolder"]).glob('*')):
return Result().failure(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["File_Not_Exist"])
if not Path(Config["Path"]["InputAlignInfo"]).exists():
return Result().failure(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["File_Not_Exist"])
except Exception as e:
return Result().failure(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Get_File_and_Freq_Excepetion"] + "\n" + format_exc())
return Result().success(info=Constants.CUT_PSG_GET_FILE_AND_FREQ_FINISHED)
def open_file(self):
path = str(Path(self.root_path) / Filename.PATH_PSG_TEXT / Path(str(self.sampID)))
for value in Config["ChannelInput"].values():
result = PublicFunc.examine_file(path, value, Params.ENDSWITH_TXT)
if not result.status:
return result
if Path(Config["Path"]["InputAlignInfo"]).is_file():
Config["Path"]["InputAlignInfo"] = str(Path(Config["Path"]["InputAlignInfo"]).parent)
Config["Path"]["InputAlignInfo"] = str(
Path(Config["Path"]["InputAlignInfo"]) / Path(
Filename.PRECISELY_ALIGN_INFO + Params.ENDSWITH_TXT))
try:
for key in Config["ChannelInput"].keys():
self.raw[key] = read_csv(Path(Config["Path"]["InputFolder"]) / Path((Config["ChannelInput"][key] + str(self.freq[key]) + Config["EndWith"][key])),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.SALabel = read_csv(Path(Config["Path"]["InputFolder"]) / Path((Config["LabelInput"]["SA Label"] + Config["EndWith"]["SA Label"])),
encoding=Params.GBK_ENCODING)
self.startTime = read_csv(Path(Config["Path"]["InputFolder"]) / Path((Config["StartTime"] + Config["EndWith"]["StartTime"])),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.alignInfo = read_csv(Path(Config["Path"]["InputAlignInfo"]),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.alignInfo = literal_eval(self.alignInfo[0])
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 cut_data(self):
try:
for key, raw in self.raw.items():
# 转换切割点
ECG_freq = Config["ECGFreq"]
raw_freq = self.freq[key]
duration_second = ((self.alignInfo["cut_index"]["back_ECG"] - self.alignInfo["cut_index"]["front_ECG"]) // ECG_freq) + 1
start_index_cut = floor(self.alignInfo["cut_index"]["front_ECG"] * (raw_freq / ECG_freq))
end_index_cut = start_index_cut + (duration_second * raw_freq)
try:
# 切割信号
self.raw[key] = self.raw[key][start_index_cut:end_index_cut]
except Exception:
return Result().failure(info=Constants.CUT_PSG_CUT_DATA_FAILURE +
Constants.FAILURE_REASON["Cut_Data_Length_not_Correct"])
except Exception as e:
return Result().failure(info=Constants.CUT_PSG_CUT_DATA_FAILURE +
Constants.FAILURE_REASON["Cut_Data_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.CUT_PSG_CUT_DATA_FINISHED)
def align_label(self):
try:
# 读取SA标签
self.SALabel = self.SALabel.loc[:, ~self.SALabel.columns.str.contains("^Unnamed")]
self.SALabel = self.SALabel[self.SALabel["Event type"].isin(Params.CUT_PSG_SALABEL_EVENT)]
self.SALabel["Duration"] = self.SALabel["Duration"].astype(str)
self.SALabel["Duration"] = self.SALabel["Duration"].str.replace(r' \(.*?\)', '', regex=True)
except Exception:
return Result().failure(info=Constants.CUT_PSG_ALIGN_LABEL_FAILURE +
Constants.FAILURE_REASON["Align_Label_SALabel_Format_not_Correct"])
try:
# 获取记录开始时间
start_time = str(self.startTime[0]).split(" ")[1]
start_time = Data.get_time_to_seconds(start_time)
# 计算起始时间秒数和终止时间秒数
self.SALabel["Start"] = (self.SALabel["Time"].apply(self.get_time_to_seconds) - start_time).apply(
lambda x: x + 24 * 3600 if x < 0 else x).astype(int)
self.SALabel["End"] = self.SALabel["Start"] + self.SALabel["Duration"].astype(float).round(0).astype(int)
# 标签映射
ECG_length = self.alignInfo["cut_index"]["back_ECG"] - self.alignInfo["cut_index"]["front_ECG"]
self.SALabel["Start"] = self.SALabel["Start"] - round((self.alignInfo["cut_index"]["front_ECG"] / 1000))
self.SALabel["End"] = self.SALabel["End"] - round((self.alignInfo["cut_index"]["front_ECG"] / 1000))
self.SALabel = self.SALabel[self.SALabel["End"] >= 0]
self.SALabel.loc[self.SALabel["Start"] < 0, "Start"] = 0
self.SALabel = self.SALabel[self.SALabel["Start"] < ECG_length]
self.SALabel.loc[self.SALabel["End"] >= ECG_length, "End"] = ECG_length - 1
except Exception as e:
return Result().failure(info=Constants.CUT_PSG_ALIGN_LABEL_FAILURE +
Constants.FAILURE_REASON["Align_Label_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.CUT_PSG_ALIGN_LABEL_FINISHED)
def save(self):
for raw in self.raw.values():
if len(raw) == 0:
return Result().failure(info=Constants.SAVE_FAILURE +
Constants.FAILURE_REASON["Data_not_Exist"])
try:
for key, raw in self.raw.items():
DataFrame(raw.reshape(-1)).to_csv(Path(Config["Path"]["SaveFolder"]) / Path((Config["ChannelSave"][key] + str(self.freq[key]) + Config["EndWith"][key])),
index=False, header=False)
# 重排index从1开始并给index命名
self.SALabel.sort_values(by=["Start"], inplace=True)
self.SALabel.reset_index(drop=True, inplace=True)
self.SALabel.index = self.SALabel.index + 1
self.SALabel.index.name = "Index"
self.SALabel.to_csv(Path(Config["Path"]["SaveFolder"]) / Path((Config["LabelSave"]["SA Label"] + Config["EndWith"]["SA Label"])),
encoding="gbk")
except PermissionError as e:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_Permission_Denied"])
except FileNotFoundError as e:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_File_Not_Found"])
except Exception as e:
return Result().failure(info=Constants.SAVE_FAILURE +
Constants.FAILURE_REASON["Save_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.SAVE_FINISHED)
@staticmethod
def get_time_to_seconds(time_str):
h, m, s = map(int, time_str.split(":"))
return h * 3600 + m * 60 + s

View File

@ -0,0 +1,564 @@
from ast import literal_eval
from gc import collect
from math import floor
from pathlib import Path
from traceback import format_exc
from PySide6.QtWidgets import QMessageBox, QMainWindow, QApplication
from numpy import array
from overrides import overrides
from pandas import read_csv, DataFrame
from yaml import dump, load, FullLoader
from func.utils.ConfigParams import Filename, Params
from func.utils.PublicFunc import PublicFunc
from func.utils.Constants import Constants
from func.utils.Result import Result
from ui.MainWindow.MainWindow_cut_PAIR_FILE import Ui_MainWindow_cut_PAIR_FILE
Config = {
}
ButtonState = {
"Default": {
"checkBox_roughCut": False,
"pushButton_deleteRoughCut": False,
"pushButton_execute": True
},
"Current": {
"checkBox_roughCut": False,
"pushButton_deleteRoughCut": False,
"pushButton_execute": True
}
}
class MainWindow_cut_PAIR_FILE(QMainWindow):
def __init__(self):
super(MainWindow_cut_PAIR_FILE, self).__init__()
self.ui = Ui_MainWindow_cut_PAIR_FILE()
self.ui.setupUi(self)
self.root_path = None
self.sampID = None
self.__read_config__()
self.data = None
self.ui.textBrowser_info.setStyleSheet("QTextBrowser { background-color: rgb(255, 255, 200); }")
PublicFunc.__styleAllButton__(self, ButtonState)
# 初始化进度条
self.ui.progressbar.setStyleSheet(Constants.PROGRESSBAR_STYLE)
self.progressbar = self.ui.progressbar
self.msgBox = QMessageBox()
self.msgBox.setWindowTitle(Constants.MAINWINDOW_MSGBOX_TITLE)
@overrides
def show(self, root_path, sampID):
super().show()
self.root_path = root_path
self.sampID = sampID
PublicFunc.__resetAllButton__(self, ButtonState)
Config.update({
"Path": {
"InputPSGFolder": str(Path(self.root_path) / Filename.PATH_PSG_TEXT / Path(str(self.sampID))),
"SavePSGFolder": str(Path(self.root_path) / Filename.PATH_PSG_ALIGNED / Path(str(self.sampID))),
"InputAlignInfo": str(Path(self.root_path) / Filename.PATH_LABEL / Path(str(self.sampID))),
"InputOrgBCGFolder": str(Path(self.root_path) / Filename.PATH_ORGBCG_TEXT / Path(str(self.sampID))),
"SaveOrgBCGFolder": str(Path(self.root_path) / Filename.PATH_ORGBCG_ALIGNED / Path(str(self.sampID)))
}
})
self.ui.plainTextEdit_channel.setPlainText(', '.join(Config["ChannelInput"].keys()))
self.ui.plainTextEdit_label.setPlainText(', '.join(Config["LabelInput"].keys()))
self.ui.pushButton_execute.clicked.connect(self.__slot_btn_execute__)
self.ui.checkBox_roughCut.stateChanged.connect(self.__change_approximate_align_mode__)
@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.data
self.deleteLater()
collect()
event.accept()
else:
event.ignore()
def __reset__(self):
ButtonState["Current"].update(ButtonState["Default"].copy())
def __read_config__(self):
if not Path(Params.CUT_PAIR_FILE_CONFIG_FILE_PATH).exists():
with open(Params.CUT_PAIR_FILE_CONFIG_FILE_PATH, "w") as f:
dump(Params.CUT_PAIR_FILE_CONFIG_NEW_CONTENT, f)
with open(Params.CUT_PAIR_FILE_CONFIG_FILE_PATH, "r") as f:
file_config = load(f.read(), Loader=FullLoader)
Config.update(file_config)
# 数据回显
self.ui.spinBox_ECGFreq.setValue(Config["ECGFreq"])
def __change_approximate_align_mode__(self):
# ChannelInput 添加OrgBCGCHannelInput
if self.ui.checkBox_roughCut.isChecked():
Config["ChannelInput"].update(Config["OrgBCGChannelInput"])
Config["ChannelInput"].update(Config["ECGChannelInput"])
# 修改ChannelSave中的Sync为RoughCut
for key in Config["ChannelSave"].keys():
if "Sync" in Config["ChannelSave"][key]:
Config["ChannelSave"][key] = Config["ChannelSave"][key].replace("Sync", "RoughCut")
ButtonState["Default"]["pushButton_deleteRoughCut"] = True
self.ui.plainTextEdit_channel.setPlainText(', '.join(Config["ChannelInput"].keys()))
else:
# ChannelInput 移除OrgBCGCHannelInput
for key in Config["OrgBCGChannelInput"].keys():
if key in Config["ChannelInput"]:
Config["ChannelInput"].pop(key)
for key in Config["ECGChannelInput"].keys():
if key in Config["ChannelInput"]:
Config["ChannelInput"].pop(key)
# print(Config["ChannelInput"])
# 修改ChannelSave中的RoughCut为Sync
for key in Config["ChannelSave"].keys():
if "RoughCut" in Config["ChannelSave"][key]:
Config["ChannelSave"][key] = Config["ChannelSave"][key].replace("RoughCut", "Sync")
ButtonState["Default"]["pushButton_deleteRoughCut"] = False
self.ui.plainTextEdit_channel.setPlainText(', '.join(Config["ChannelInput"].keys()))
def __get_approximately_align_info__(self):
time_bias = self.data.get_approximately_align_info()
if time_bias is not None:
info = "已成功获取粗对齐信息,时间偏移量为{:.3f}".format(time_bias)
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_INFO)
QApplication.processEvents()
return True
else:
info = "获取粗对齐信息失败,系统将尝试计算粗对齐信息"
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_WARNING)
QApplication.processEvents()
align_info = self.data.calc_approximately_align_info()
if align_info is not None:
info = "已成功计算粗对齐信息切割点为ECG前端去除{}样本点ECG后端去除{}样本点BCG前端去除{}样本点BCG后端去除{}样本点".format(
align_info["cut_index"]["front_ECG"],
align_info["cut_index"]["back_ECG"],
align_info["cut_index"]["front_BCG"],
align_info["cut_index"]["back_BCG"]
)
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_INFO)
QApplication.processEvents()
return True
else:
info = "计算粗对齐信息失败,无法进行后续操作"
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_ERROR)
QApplication.processEvents()
return False
def __slot_btn_execute__(self):
PublicFunc.__disableAllButton__(self, ButtonState)
self.data = Data(self.root_path, self.sampID)
Config["ECGFreq"] = self.ui.spinBox_ECGFreq.value()
# 检查文件是否存在并获取其数据采样率
PublicFunc.progressbar_update(self, 1, 5, Constants.CUT_PAIR_FILE_GETTING_FILE_AND_FREQ, 0)
result = self.data.get_file_and_freq()
if not result.status:
PublicFunc.text_output(self.ui, "(1/5)" + result.info, Constants.TIPS_TYPE_ERROR)
PublicFunc.msgbox_output(self, result.info, Constants.MSGBOX_TYPE_ERROR)
PublicFunc.finish_operation(self, ButtonState)
return
else:
if self.ui.checkBox_roughCut.isChecked():
Config["BCGFreq"] = self.data.freq["OrgBCG"]
PublicFunc.text_output(self.ui, "(1/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 导入数据
PublicFunc.progressbar_update(self, 2, 5, Constants.INPUTTING_DATA, 10)
result = self.data.open_file()
if not result.status:
PublicFunc.text_output(self.ui, "(2/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
if self.ui.checkBox_roughCut.isChecked():
# 获取或计算粗对齐信息
PublicFunc.progressbar_update(self, 3, 5, Constants.CUT_PAIR_FILE_GETTING_APPROXIMATE_ALIGN_INFO, 20)
result_approximate = self.__get_approximately_align_info__()
if not result_approximate:
PublicFunc.msgbox_output(self, Constants.CUT_PAIR_FILE_GETTING_APPROXIMATE_ALIGN_INFO_FAILURE, Constants.MSGBOX_TYPE_ERROR)
PublicFunc.finish_operation(self, ButtonState)
return
else:
PublicFunc.text_output(self.ui, "(3/5)" + Constants.CUT_PAIR_FILE_GETTING_APPROXIMATE_ALIGN_INFO_FINISHED, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 切割数据
PublicFunc.progressbar_update(self, 3, 5, Constants.CUT_PAIR_FILE_CUTTING_DATA, 40)
result = self.data.cut_data()
if not result.status:
PublicFunc.text_output(self.ui, "(3/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 标签映射
PublicFunc.progressbar_update(self, 4, 5, Constants.CUT_PAIR_FILE_ALIGNING_LABEL, 60)
result = self.data.align_label()
if not result.status:
PublicFunc.text_output(self.ui, "(4/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
# 保存数据
PublicFunc.progressbar_update(self, 5, 5, Constants.SAVING_DATA, 70)
result = self.data.save()
if not result.status:
PublicFunc.text_output(self.ui, "(5/5)" + 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/5)" + result.info, Constants.TIPS_TYPE_INFO)
for key, raw in self.data.raw.items():
info = "保存{}的长度为{},采样率为{}Hz".format(key, str(len(raw)), str(self.data.freq[key]))
PublicFunc.text_output(self.ui, info, Constants.TIPS_TYPE_INFO)
QApplication.processEvents()
PublicFunc.msgbox_output(self, result.info, Constants.TIPS_TYPE_INFO)
PublicFunc.finish_operation(self, ButtonState)
class Data:
def __init__(self, root_path, sampID):
self.TimeBiasSecond = None
self.alignInfo = None
self.raw = {key: array([]) for key in Config["ChannelInput"]}
self.freq = {key: 0 for key in Config["ChannelInput"]}
self.SALabel = None
self.startTime = None
self.root_path = root_path
self.sampID = sampID
def get_file_and_freq(self):
try:
for file_path in Path(Config["Path"]["InputPSGFolder"]).glob('*'):
if file_path.is_file():
file_stem = Path(file_path).stem
for key, prefix in Config["ChannelInput"].items():
if not prefix.startswith("PSG:"):
continue
prefix = prefix[len("PSG:"):]
if file_stem.startswith(prefix):
freq_str = file_stem.rsplit('_', 1)[1]
try:
freq = int(freq_str)
self.freq[key] = freq
except ValueError:
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Filename_Format_not_Correct"] + f"\n{Config['ChannelInput']}")
for file_path in Path(Config["Path"]["InputOrgBCGFolder"]).glob('*'):
if file_path.is_file():
file_stem = Path(file_path).stem
for key, prefix in Config["ChannelInput"].items():
if not prefix.startswith("OrgBCG:"):
continue
prefix = prefix[len("OrgBCG:"):]
if file_stem.startswith(prefix):
freq_str = file_stem.rsplit('_', 1)[1]
try:
freq = int(freq_str)
self.freq[key] = freq
except ValueError:
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Filename_Format_not_Correct"] + f"\n{Config['ChannelInput']}")
for value in self.freq.values():
if value == 0:
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Filename_Format_not_Correct"] + f"\n{Config['ChannelInput']}")
if not any((Config["LabelInput"]["SA Label"] + Config["EndWith"]["SA Label"]) in str(file) for file in Path(Config["Path"]["InputPSGFolder"]).glob('*')):
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["File_Not_Exist"])
if not any((Config["StartTime"] + Config["EndWith"]["StartTime"]) in str(file) for file in Path(Config["Path"]["InputPSGFolder"]).glob('*')):
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["File_Not_Exist"])
if not Path(Config["Path"]["InputAlignInfo"]).exists():
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["File_Not_Exist"])
except Exception as e:
return Result().failure(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE +
Constants.FAILURE_REASON["Get_File_and_Freq_Excepetion"] + "\n" + format_exc())
return Result().success(info=Constants.CUT_PAIR_FILE_GET_FILE_AND_FREQ_FINISHED)
def open_file(self):
psg_path = str(Path(self.root_path) / Filename.PATH_PSG_TEXT / Path(str(self.sampID)))
bcg_path = str(Path(self.root_path) / Filename.PATH_ORGBCG_TEXT / Path(str(self.sampID)))
for value in Config["ChannelInput"].values():
if value.startswith("PSG:"):
value = value[len("PSG:"):]
result = PublicFunc.examine_file(psg_path, value, Params.ENDSWITH_TXT)
if not result.status:
return result
elif value.startswith("OrgBCG:"):
value = value[len("OrgBCG:"):]
result = PublicFunc.examine_file(bcg_path, value, Params.ENDSWITH_TXT)
if not result.status:
return result
if Path(Config["Path"]["InputAlignInfo"]).is_file():
Config["Path"]["InputAlignInfo"] = str(Path(Config["Path"]["InputAlignInfo"]).parent)
Config["Path"]["Input_Approximately_Align"] = str(
Path(Config["Path"]["InputAlignInfo"]) / Path(
Filename.APPROXIMATELY_ALIGN_INFO + Params.ENDSWITH_CSV))
Config["Path"]["InputAlignInfo"] = str(
Path(Config["Path"]["InputAlignInfo"]) / Path(
Filename.PRECISELY_ALIGN_INFO + Params.ENDSWITH_TXT))
try:
for key in Config["ChannelInput"].keys():
if not Config["ChannelInput"][key].startswith("OrgBCG"):
self.raw[key] = read_csv(Path(Config["Path"]["InputPSGFolder"]) / Path((Config["ChannelInput"][key][len("PSG:"):] + str(self.freq[key]) + Config["EndWith"][key])),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
elif Config["ChannelInput"][key].startswith("OrgBCG"):
self.raw[key] = read_csv(Path(Config["Path"]["InputOrgBCGFolder"]) / Path((Config["ChannelInput"][key][len("OrgBCG:"):] + str(self.freq[key]) + Config["EndWith"][key])),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.SALabel = read_csv(Path(Config["Path"]["InputPSGFolder"]) / Path((Config["LabelInput"]["SA Label"] + Config["EndWith"]["SA Label"])),
encoding=Params.GBK_ENCODING)
self.startTime = read_csv(Path(Config["Path"]["InputPSGFolder"]) / Path((Config["StartTime"] + Config["EndWith"]["StartTime"])),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.alignInfo = read_csv(Path(Config["Path"]["InputAlignInfo"]),
encoding=Params.UTF8_ENCODING,
header=None).to_numpy().reshape(-1)
self.alignInfo = literal_eval(self.alignInfo[0])
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 get_approximately_align_info(self):
try:
df = read_csv(Config["Path"]["Input_Approximately_Align"])
pos = df["pos"].values[-1]
ApplyFrequency = df["ApplyFrequency"].values[-1]
self.TimeBiasSecond = pos / ApplyFrequency
return self.TimeBiasSecond
except Exception as e:
self.TimeBiasSecond = 0
return None
def calc_approximately_align_info(self):
try:
# 获取BCG长度
BCG_freq = Config["BCGFreq"]
BCG_second = len(self.raw["BCG"]) // BCG_freq
# 计算ECG长度
ECG_freq = Config["ECGFreq"]
ECG_second = len(self.raw["ECG"]) // ECG_freq
pos = self.TimeBiasSecond
# 如果pos<0表示BCG信号比ECG信号提前需要在开头去除掉一部分BCG信号
if pos < 0:
front_BCG = floor(-pos)
front_ECG = 0
else:
front_BCG = 0
front_ECG = floor(pos)
# 计算结束位置
if (BCG_second - front_BCG) > (ECG_second - front_ECG):
back_ECG = ECG_second + front_ECG
back_BCG = back_ECG - floor(pos)
else:
back_BCG = BCG_second + front_BCG
back_ECG = back_BCG + floor(pos)
self.alignInfo = {
"cut_index": {
"front_ECG": front_ECG * ECG_freq,
"back_ECG": back_ECG * ECG_freq,
"front_BCG": front_BCG * BCG_freq,
"back_BCG": back_BCG * BCG_freq
}
}
return self.alignInfo
except Exception as e:
return None
def cut_data(self):
try:
for key, raw in self.raw.items():
if Config["ChannelInput"][key].startswith("PSG:"):
# 转换切割点
ECG_freq = Config["ECGFreq"]
raw_freq = self.freq[key]
duration_second = ((self.alignInfo["cut_index"]["back_ECG"] - self.alignInfo["cut_index"]["front_ECG"]) // ECG_freq) + 1
start_index_cut = floor(self.alignInfo["cut_index"]["front_ECG"] * (raw_freq / ECG_freq))
end_index_cut = start_index_cut + (duration_second * raw_freq)
try:
# 切割信号
self.raw[key] = self.raw[key][start_index_cut:end_index_cut]
except Exception:
return Result().failure(info=Constants.CUT_PAIR_FILE_CUT_DATA_FAILURE +
Constants.FAILURE_REASON["Cut_Data_Length_not_Correct"])
elif Config["ChannelInput"][key].startswith("OrgBCG:"):
# 转换切割点
BCG_freq = Config["BCGFreq"]
raw_freq = self.freq[key]
duration_second = ((self.alignInfo["cut_index"]["back_BCG"] - self.alignInfo["cut_index"]["front_BCG"]) // BCG_freq) + 1
start_index_cut = floor(self.alignInfo["cut_index"]["front_BCG"] * (raw_freq / BCG_freq))
end_index_cut = start_index_cut + (duration_second * raw_freq)
try:
# 切割信号
self.raw[key] = self.raw[key][start_index_cut:end_index_cut]
except Exception:
return Result().failure(info=Constants.CUT_PAIR_FILE_CUT_DATA_FAILURE +
Constants.FAILURE_REASON["Cut_Data_Length_not_Correct"])
except Exception as e:
return Result().failure(info=Constants.CUT_PAIR_FILE_CUT_DATA_FAILURE +
Constants.FAILURE_REASON["Cut_Data_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.CUT_PAIR_FILE_CUT_DATA_FINISHED)
def align_label(self):
try:
# 读取SA标签
self.SALabel = self.SALabel.loc[:, ~self.SALabel.columns.str.contains("^Unnamed")]
self.SALabel = self.SALabel[self.SALabel["Event type"].isin(Params.CUT_PAIR_FILE_SALABEL_EVENT)]
self.SALabel["Duration"] = self.SALabel["Duration"].astype(str)
self.SALabel["Duration"] = self.SALabel["Duration"].str.replace(r' \(.*?\)', '', regex=True)
except Exception:
return Result().failure(info=Constants.CUT_PAIR_FILE_ALIGN_LABEL_FAILURE +
Constants.FAILURE_REASON["Align_Label_SALabel_Format_not_Correct"])
try:
# 获取记录开始时间
start_time = str(self.startTime[0]).split(" ")[1]
start_time = Data.get_time_to_seconds(start_time)
# 计算起始时间秒数和终止时间秒数
self.SALabel["Start"] = (self.SALabel["Time"].apply(self.get_time_to_seconds) - start_time).apply(
lambda x: x + 24 * 3600 if x < 0 else x).astype(int)
self.SALabel["End"] = self.SALabel["Start"] + self.SALabel["Duration"].astype(float).round(0).astype(int)
# 标签映射
ECG_length = self.alignInfo["cut_index"]["back_ECG"] - self.alignInfo["cut_index"]["front_ECG"]
self.SALabel["Start"] = self.SALabel["Start"] - round((self.alignInfo["cut_index"]["front_ECG"] / 1000))
self.SALabel["End"] = self.SALabel["End"] - round((self.alignInfo["cut_index"]["front_ECG"] / 1000))
self.SALabel = self.SALabel[self.SALabel["End"] >= 0]
self.SALabel.loc[self.SALabel["Start"] < 0, "Start"] = 0
self.SALabel = self.SALabel[self.SALabel["Start"] < ECG_length]
self.SALabel.loc[self.SALabel["End"] >= ECG_length, "End"] = ECG_length - 1
except Exception as e:
return Result().failure(info=Constants.CUT_PAIR_FILE_ALIGN_LABEL_FAILURE +
Constants.FAILURE_REASON["Align_Label_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.CUT_PAIR_FILE_ALIGN_LABEL_FINISHED)
def save(self):
for raw in self.raw.values():
if len(raw) == 0:
return Result().failure(info=Constants.SAVE_FAILURE +
Constants.FAILURE_REASON["Data_not_Exist"])
try:
for key, raw in self.raw.items():
if Config["ChannelInput"][key].startswith("PSG:"):
# print(f"Saving PSG channel: {key} to {Config['Path']['SavePSGFolder']} / {Config['ChannelSave'][key] + str(self.freq[key]) + Config['EndWith'][key]}")
DataFrame(raw.reshape(-1)).to_csv(Path(Config["Path"]["SavePSGFolder"]) / Path((Config["ChannelSave"][key][len("PSG:"):] + str(self.freq[key]) + Config["EndWith"][key])),
index=False, header=False)
elif Config["ChannelInput"][key].startswith("OrgBCG:"):
# print(f"Saving OrgBCG channel: {key} to {Config['Path']['SaveOrgBCGFolder']} / {Config['ChannelSave'][key] + str(self.freq[key]) + Config['EndWith'][key]}")
DataFrame(raw.reshape(-1)).to_csv(Path(Config["Path"]["SaveOrgBCGFolder"]) / Path((Config["ChannelSave"][key][len("OrgBCG:"):] + str(self.freq[key]) + Config["EndWith"][key])),
index=False, header=False)
# 重排index从1开始并给index命名
self.SALabel.sort_values(by=["Start"], inplace=True)
self.SALabel.reset_index(drop=True, inplace=True)
self.SALabel.index = self.SALabel.index + 1
self.SALabel.index.name = "Index"
self.SALabel.to_csv(Path(Config["Path"]["SavePSGFolder"]) / Path((Config["LabelSave"]["SA Label"] + Config["EndWith"]["SA Label"])),
encoding="gbk")
except PermissionError as e:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_Permission_Denied"])
except FileNotFoundError as e:
return Result().failure(info=Constants.SAVE_FAILURE + Constants.FAILURE_REASON["Save_File_Not_Found"])
except Exception as e:
return Result().failure(info=Constants.SAVE_FAILURE +
Constants.FAILURE_REASON["Save_Exception"] + "\n" + format_exc())
return Result().success(info=Constants.SAVE_FINISHED)
@staticmethod
def get_time_to_seconds(time_str):
h, m, s = map(int, time_str.split(":"))
return h * 3600 + m * 60 + s

View File

@ -16,7 +16,7 @@ from func.Module_detect_Jpeak import MainWindow_detect_Jpeak
from func.Module_detect_Rpeak import MainWindow_detect_Rpeak
from func.Module_label_check import MainWindow_label_check
from func.Module_precisely_align import MainWindow_precisely_align
from func.Module_cut_PSG import MainWindow_cut_PSG
from func.Module_cut_pair_file import MainWindow_cut_PAIR_FILE
from func.Module_artifact_label import MainWindow_artifact_label
from func.Module_bcg_quality_label import MainWindow_bcg_quality_label
from func.Module_resp_quality_label import MainWindow_resp_quality_label
@ -266,7 +266,7 @@ class MainWindow(QMainWindow, Ui_Signal_Label):
self.check_save_path_and_mkdir(root_path, sampID)
def __slot_btn_cut_PSG__(self):
self.cut_PSG = MainWindow_cut_PSG()
self.cut_PSG = MainWindow_cut_PAIR_FILE()
root_path = self.ui.plainTextEdit_root_path.toPlainText()
sampID = self.ui.comboBox_sampID.currentText()
if not self.check_root_path():

View File

@ -220,30 +220,39 @@ class Params:
PRECISELY_ALIGN_LABEL_TRANSPARENCY: float = 0.2
# 冗余数据切割和标签映射
CUT_PSG_CONFIG_FILE_PATH: str = "./config/Config_cut_PSG.yaml"
CUT_PSG_CONFIG_NEW_CONTENT: dict = {
CUT_PAIR_FILE_CONFIG_FILE_PATH: str = "./config/Config_cut_PAIR_FILE.yaml"
CUT_PAIR_FILE_CONFIG_NEW_CONTENT: dict = {
"ECGFreq": 1000,
"BCGFreq": 1000,
"ChannelInput": {
"Effort Tho": Filename.THO_RAW,
"Effort Abd": Filename.ABD_RAW,
"Flow T": Filename.FLOWT_RAW,
"Flow P": Filename.FLOWP_RAW,
"Snore": Filename.SNORE_RAW,
"SpO2": Filename.SPO2_RAW,
"5_class": Filename.FIVE_CLASS_RAW
"Effort Tho": "PSG:" + Filename.THO_RAW,
"Effort Abd": "PSG:" + Filename.ABD_RAW,
"Flow T": "PSG:" + Filename.FLOWT_RAW,
"Flow P": "PSG:" + Filename.FLOWP_RAW,
"Snore": "PSG:" + Filename.SNORE_RAW,
"SpO2": "PSG:" + Filename.SPO2_RAW,
"5_class": "PSG:" + Filename.FIVE_CLASS_RAW
},
"OrgBCGChannelInput":{
"OrgBCG": "OrgBCG:OrgBCG_Raw_"
},
"ECGChannelInput": {
"ECG": "PSG:" + Filename.ECG_RAW
},
"LabelInput": {
"SA Label": Filename.SA_LABEL_RAW
},
"StartTime": Filename.STARTTIME_RAW,
"ChannelSave": {
"Effort Tho": Filename.THO_SYNC,
"Effort Abd": Filename.ABD_SYNC,
"Flow T": Filename.FLOWT_SYNC,
"Flow P": Filename.FLOWP_SYNC,
"Snore": Filename.SNORE_SYNC,
"SpO2": Filename.SPO2_SYNC,
"5_class": Filename.FIVE_CLASS_SYNC
"Effort Tho": "PSG:" + Filename.THO_SYNC,
"Effort Abd": "PSG:" + Filename.ABD_SYNC,
"Flow T": "PSG:" + Filename.FLOWT_SYNC,
"Flow P": "PSG:" + Filename.FLOWP_SYNC,
"Snore": "PSG:" + Filename.SNORE_SYNC,
"SpO2": "PSG:" + Filename.SPO2_SYNC,
"5_class": "PSG:" + Filename.FIVE_CLASS_SYNC,
"OrgBCG": "OrgBCG:" + Filename.ORGBCG_SYNC,
"ECG": "PSG:" + Filename.ECG_SYNC
},
"LabelSave": {
"SA Label": Filename.SA_LABEL_SYNC
@ -258,10 +267,12 @@ class Params:
"SpO2": ENDSWITH_TXT,
"5_class": ENDSWITH_TXT,
"SA Label": ENDSWITH_CSV,
"StartTime": ENDSWITH_TXT
"StartTime": ENDSWITH_TXT,
"OrgBCG": ENDSWITH_TXT,
"ECG": ENDSWITH_TXT
},
}
CUT_PSG_SALABEL_EVENT: list = ["Hypopnea", "Central apnea", "Obstructive apnea", "Mixed apnea"]
CUT_PAIR_FILE_SALABEL_EVENT: list = ["Hypopnea", "Central apnea", "Obstructive apnea", "Mixed apnea"]
# 体动标注
ARTIFACT_LABEL_CONFIG_FILE_PATH: str = "./config/Config_artifact_label.yaml"

View File

@ -5,6 +5,7 @@ class Constants:
# 公共
TIPS_TYPE_INFO: str = "Info"
TIPS_TYPE_WARNING: str = "Warning"
TIPS_TYPE_ERROR: str = "Error"
MSGBOX_TYPE_INFO: str = "Info"
MSGBOX_TYPE_WARNING: str = "Warning"
@ -352,17 +353,22 @@ class Constants:
PRECISELY_ALIGN_ACTION_GET_RANGE_NAME: str = f"设置范围({Params.PRECISELY_ALIGN_ACTION_GET_RANGE_SHORTCUT_KEY})"
# 冗余数据切割和标签映射
CUT_PSG_GETTING_FILE_AND_FREQ: str = "正在获取文件及其采样率"
CUT_PSG_GET_FILE_AND_FREQ_FINISHED: str = "获取文件及其采样率完成"
CUT_PSG_GET_FILE_AND_FREQ_FAILURE: str = "获取文件及其采样率失败"
CUT_PAIR_FILE_GETTING_FILE_AND_FREQ: str = "正在获取文件及其采样率"
CUT_PAIR_FILE_GET_FILE_AND_FREQ_FINISHED: str = "获取文件及其采样率完成"
CUT_PAIR_FILE_GET_FILE_AND_FREQ_FAILURE: str = "获取文件及其采样率失败"
CUT_PSG_CUTTING_DATA: str = "正在切割数据"
CUT_PSG_CUT_DATA_FINISHED: str = "切割数据完成"
CUT_PSG_CUT_DATA_FAILURE: str = "切割数据失败"
CUT_PAIR_FILE_CUTTING_DATA: str = "正在切割数据"
CUT_PAIR_FILE_CUT_DATA_FINISHED: str = "切割数据完成"
CUT_PAIR_FILE_CUT_DATA_FAILURE: str = "切割数据失败"
CUT_PAIR_FILE_ALIGNING_LABEL: str = "正在映射标签"
CUT_PAIR_FILE_ALIGN_LABEL_FINISHED: str = "映射标签完成"
CUT_PAIR_FILE_ALIGN_LABEL_FAILURE: str = "映射标签失败"
CUT_PAIR_FILE_GETTING_APPROXIMATE_ALIGN_INFO: str = "正在获取粗对齐信息"
CUT_PAIR_FILE_GETTING_APPROXIMATE_ALIGN_INFO_FAILURE: str = "获取粗对齐信息失败"
CUT_PAIR_FILE_GETTING_APPROXIMATE_ALIGN_INFO_FINISHED: str = "获取粗对齐信息完成"
CUT_PSG_ALIGNING_LABEL: str = "正在映射标签"
CUT_PSG_ALIGN_LABEL_FINISHED: str = "映射标签完成"
CUT_PSG_ALIGN_LABEL_FAILURE: str = "映射标签失败"
# 体动标注
ARTIFACT_LABEL_PLOT_LABEL_ORGBCG_SYNC: str = "OrgBCG_Sync"

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'MainWindow_cut_PSG.ui'
## Form generated from reading UI file 'MainWindow_cut_PAIR_FILE.ui'
##
## Created by: Qt User Interface Compiler version 6.8.2
## Created by: Qt User Interface Compiler version 6.9.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@ -16,35 +16,36 @@ from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QGridLayout, QGroupBox, QHBoxLayout,
QLabel, QMainWindow, QPlainTextEdit, QProgressBar,
QPushButton, QSizePolicy, QSpacerItem, QSpinBox,
QStatusBar, QTextBrowser, QVBoxLayout, QWidget)
from PySide6.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox,
QHBoxLayout, QLabel, QMainWindow, QPlainTextEdit,
QProgressBar, QPushButton, QSizePolicy, QSpacerItem,
QSpinBox, QStatusBar, QTextBrowser, QVBoxLayout,
QWidget)
class Ui_MainWindow_cut_PSG(object):
def setupUi(self, MainWindow_cut_PSG):
if not MainWindow_cut_PSG.objectName():
MainWindow_cut_PSG.setObjectName(u"MainWindow_cut_PSG")
MainWindow_cut_PSG.setEnabled(True)
MainWindow_cut_PSG.resize(540, 720)
class Ui_MainWindow_cut_PAIR_FILE(object):
def setupUi(self, MainWindow_cut_PAIR_FILE):
if not MainWindow_cut_PAIR_FILE.objectName():
MainWindow_cut_PAIR_FILE.setObjectName(u"MainWindow_cut_PAIR_FILE")
MainWindow_cut_PAIR_FILE.setEnabled(True)
MainWindow_cut_PAIR_FILE.resize(548, 739)
sizePolicy = QSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(MainWindow_cut_PSG.sizePolicy().hasHeightForWidth())
MainWindow_cut_PSG.setSizePolicy(sizePolicy)
sizePolicy.setHeightForWidth(MainWindow_cut_PAIR_FILE.sizePolicy().hasHeightForWidth())
MainWindow_cut_PAIR_FILE.setSizePolicy(sizePolicy)
font = QFont()
font.setPointSize(12)
MainWindow_cut_PSG.setFont(font)
self.action_selectPath = QAction(MainWindow_cut_PSG)
MainWindow_cut_PAIR_FILE.setFont(font)
self.action_selectPath = QAction(MainWindow_cut_PAIR_FILE)
self.action_selectPath.setObjectName(u"action_selectPath")
font1 = QFont()
font1.setFamilies([u"\u9ed1\u4f53"])
font1.setPointSize(14)
self.action_selectPath.setFont(font1)
self.action = QAction(MainWindow_cut_PSG)
self.action = QAction(MainWindow_cut_PAIR_FILE)
self.action.setObjectName(u"action")
self.action.setFont(font1)
self.centralwidget = QWidget(MainWindow_cut_PSG)
self.centralwidget = QWidget(MainWindow_cut_PAIR_FILE)
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget)
self.gridLayout.setObjectName(u"gridLayout")
@ -55,22 +56,27 @@ class Ui_MainWindow_cut_PSG(object):
self.groupBox_3.setFont(font2)
self.gridLayout_2 = QGridLayout(self.groupBox_3)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.groupBox = QGroupBox(self.groupBox_3)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_6 = QVBoxLayout(self.groupBox)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.textBrowser_info = QTextBrowser(self.groupBox)
self.textBrowser_info.setObjectName(u"textBrowser_info")
self.verticalLayout_6.addWidget(self.textBrowser_info)
self.gridLayout_2.addWidget(self.groupBox, 1, 0, 1, 1)
self.groupBox_2 = QGroupBox(self.groupBox_3)
self.groupBox_2.setObjectName(u"groupBox_2")
self.verticalLayout_5 = QVBoxLayout(self.groupBox_2)
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.checkBox_roughCut = QCheckBox(self.groupBox_2)
self.checkBox_roughCut.setObjectName(u"checkBox_roughCut")
self.checkBox_roughCut.setFont(font)
self.horizontalLayout_3.addWidget(self.checkBox_roughCut)
self.pushButton_deteleRougnCut = QPushButton(self.groupBox_2)
self.pushButton_deteleRougnCut.setObjectName(u"pushButton_deteleRougnCut")
self.pushButton_deteleRougnCut.setFont(font)
self.horizontalLayout_3.addWidget(self.pushButton_deteleRougnCut)
self.verticalLayout_5.addLayout(self.horizontalLayout_3)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.label_2 = QLabel(self.groupBox_2)
@ -159,15 +165,28 @@ class Ui_MainWindow_cut_PSG(object):
self.verticalLayout_5.addWidget(self.progressbar)
self.verticalLayout_5.setStretch(0, 2)
self.verticalLayout_5.setStretch(0, 1)
self.verticalLayout_5.setStretch(1, 2)
self.verticalLayout_5.setStretch(2, 1)
self.verticalLayout_5.setStretch(3, 2)
self.verticalLayout_5.setStretch(4, 1)
self.verticalLayout_5.setStretch(2, 2)
self.verticalLayout_5.setStretch(3, 1)
self.verticalLayout_5.setStretch(4, 2)
self.verticalLayout_5.setStretch(5, 1)
self.verticalLayout_5.setStretch(6, 1)
self.gridLayout_2.addWidget(self.groupBox_2, 0, 0, 1, 2)
self.groupBox = QGroupBox(self.groupBox_3)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout_6 = QVBoxLayout(self.groupBox)
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
self.textBrowser_info = QTextBrowser(self.groupBox)
self.textBrowser_info.setObjectName(u"textBrowser_info")
self.verticalLayout_6.addWidget(self.textBrowser_info)
self.gridLayout_2.addWidget(self.groupBox, 1, 0, 1, 1)
self.pushButton_execute = QPushButton(self.groupBox_3)
self.pushButton_execute.setObjectName(u"pushButton_execute")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred)
@ -180,34 +199,34 @@ class Ui_MainWindow_cut_PSG(object):
self.gridLayout_2.addWidget(self.pushButton_execute, 1, 1, 1, 1)
self.gridLayout_2.setRowStretch(0, 7)
self.gridLayout_2.setRowStretch(1, 3)
self.gridLayout_2.setColumnStretch(0, 4)
self.gridLayout_2.setColumnStretch(1, 1)
self.gridLayout.addWidget(self.groupBox_3, 0, 0, 1, 1)
self.gridLayout.setColumnStretch(0, 2)
MainWindow_cut_PSG.setCentralWidget(self.centralwidget)
self.statusbar = QStatusBar(MainWindow_cut_PSG)
MainWindow_cut_PAIR_FILE.setCentralWidget(self.centralwidget)
self.statusbar = QStatusBar(MainWindow_cut_PAIR_FILE)
self.statusbar.setObjectName(u"statusbar")
MainWindow_cut_PSG.setStatusBar(self.statusbar)
MainWindow_cut_PAIR_FILE.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow_cut_PSG)
self.retranslateUi(MainWindow_cut_PAIR_FILE)
QMetaObject.connectSlotsByName(MainWindow_cut_PSG)
QMetaObject.connectSlotsByName(MainWindow_cut_PAIR_FILE)
# setupUi
def retranslateUi(self, MainWindow_cut_PSG):
MainWindow_cut_PSG.setWindowTitle(QCoreApplication.translate("MainWindow_cut_PSG", u"\u5197\u4f59\u6570\u636e\u5207\u5272\u548c\u6807\u7b7e\u6620\u5c04", None))
self.action_selectPath.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u6570\u636e\u8def\u5f84\u9009\u62e9", None))
self.action.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u52a0\u8f7d\u5b58\u6863", None))
self.groupBox_3.setTitle(QCoreApplication.translate("MainWindow_cut_PSG", u"\u5197\u4f59\u6570\u636e\u5207\u5272\u548c\u6807\u7b7e\u6620\u5c04", None))
self.groupBox.setTitle(QCoreApplication.translate("MainWindow_cut_PSG", u"\u65e5\u5fd7", None))
self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow_cut_PSG", u"\u786e\u5b9a\u6570\u636e", None))
self.label_2.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u9700\u8981\u5207\u5272\u7684\u901a\u9053\u540d\uff1a", None))
self.label_6.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u9700\u8981\u6620\u5c04\u7684\u6807\u7b7e\uff1a", None))
self.label_7.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u6570\u636e\u7cbe\u540c\u6b65\u65f6ECG\u7684\u91c7\u6837\u7387\uff1a", None))
self.label_show.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u70b9\u51fb\u6267\u884c\u4ee5\u5f00\u59cb...", None))
self.pushButton_execute.setText(QCoreApplication.translate("MainWindow_cut_PSG", u"\u6267\u884c", None))
def retranslateUi(self, MainWindow_cut_PAIR_FILE):
MainWindow_cut_PAIR_FILE.setWindowTitle(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u5197\u4f59\u6570\u636e\u5207\u5272\u548c\u6807\u7b7e\u6620\u5c04", None))
self.action_selectPath.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u6570\u636e\u8def\u5f84\u9009\u62e9", None))
self.action.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u52a0\u8f7d\u5b58\u6863", None))
self.groupBox_3.setTitle(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u5197\u4f59\u6570\u636e\u5207\u5272\u548c\u6807\u7b7e\u6620\u5c04", None))
self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u786e\u5b9a\u6570\u636e", None))
self.checkBox_roughCut.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u7c97\u5bf9\u9f50\u7ed3\u679c\u5207\u5272", None))
self.pushButton_deteleRougnCut.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u5220\u9664\u7c97\u5bf9\u9f50\u5207\u5272\u6587\u4ef6", None))
self.label_2.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u9700\u8981\u5207\u5272\u7684\u901a\u9053\u540d\uff1a", None))
self.label_6.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u9700\u8981\u6620\u5c04\u7684\u6807\u7b7e\uff1a", None))
self.label_7.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u6570\u636e\u7cbe\u540c\u6b65\u65f6ECG\u7684\u91c7\u6837\u7387\uff1a", None))
self.label_show.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u70b9\u51fb\u6267\u884c\u4ee5\u5f00\u59cb...", None))
self.groupBox.setTitle(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u65e5\u5fd7", None))
self.pushButton_execute.setText(QCoreApplication.translate("MainWindow_cut_PAIR_FILE", u"\u6267\u884c", None))
# retranslateUi

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow_cut_PSG</class>
<widget class="QMainWindow" name="MainWindow_cut_PSG">
<class>MainWindow_cut_PAIR_FILE</class>
<widget class="QMainWindow" name="MainWindow_cut_PAIR_FILE">
<property name="enabled">
<bool>true</bool>
</property>
@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>540</width>
<height>720</height>
<width>548</width>
<height>739</height>
</rect>
</property>
<property name="sizePolicy">
@ -39,25 +39,41 @@
<property name="title">
<string>冗余数据切割和标签映射</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" rowstretch="7,3" columnstretch="4,1">
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>日志</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QTextBrowser" name="textBrowser_info"/>
</item>
</layout>
</widget>
</item>
<layout class="QGridLayout" name="gridLayout_2" rowstretch="7,0" columnstretch="4,0">
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>确定数据</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5" stretch="2,2,1,2,1,1">
<layout class="QVBoxLayout" name="verticalLayout_5" stretch="1,2,2,1,2,1,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="checkBox_roughCut">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>粗对齐结果切割</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_deteleRougnCut">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>删除粗对齐切割文件</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
@ -108,8 +124,8 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7" stretch="1,1">
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="font">
<font>
@ -121,7 +137,7 @@
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBox_ECGFreq">
<property name="font">
<font>
@ -136,6 +152,33 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>BCG原始采样率</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinBox">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -189,6 +232,18 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>日志</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QTextBrowser" name="textBrowser_info"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="pushButton_execute">
<property name="sizePolicy">