620 lines
28 KiB
Python
620 lines
28 KiB
Python
#!/usr/bin/python
|
||
# -*- coding: UTF-8 -*-
|
||
"""
|
||
@author:andrew
|
||
@file:RespCoarseAlign.py
|
||
@email:admin@marques22.com
|
||
@email:2021022362@m.scnu.edu.cn
|
||
@time:2023/09/20
|
||
"""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
import pyedflib
|
||
import numpy as np
|
||
import pandas as pd
|
||
from PySide6.QtGui import QPixmap, QImage
|
||
|
||
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox, QWidget, QPushButton
|
||
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
|
||
from matplotlib.figure import Figure
|
||
from scipy import signal
|
||
|
||
from ui.Mian import Ui_mainWindow as Ui_respCoarseAlign
|
||
from ui.setings import Ui_MainWindow as Ui_Setting
|
||
import yaml
|
||
|
||
Conf = {
|
||
"PSGConfig": {
|
||
"Path": "./Data/PSG/",
|
||
"Frequency": 100,
|
||
"THOChannel": {
|
||
"auto": True,
|
||
"Channel": 3,
|
||
},
|
||
"ABDChannel": {
|
||
"auto": True,
|
||
"Channel": 4,
|
||
},
|
||
},
|
||
"XXConfig": {
|
||
"Path": "./Data/XX/",
|
||
"Frequency": 100,
|
||
},
|
||
"RespFilterConfig": {
|
||
"LowPass": 0.01,
|
||
"HighPass": 0.7,
|
||
"order": 4
|
||
},
|
||
"ApplyFrequency": 5
|
||
}
|
||
|
||
ButtonState = {
|
||
"Default": {
|
||
"pushButton_Refresh": True,
|
||
"pushButton_OpenFile": True,
|
||
"pushButton_Standardize": False,
|
||
"pushButton_CutOff": False,
|
||
"pushButton_GetPos": False,
|
||
"pushButton_Align": False,
|
||
"pushButton_JUMP": False,
|
||
"pushButton_EM1": False,
|
||
"pushButton_EM10": False,
|
||
"pushButton_EM100": False,
|
||
"pushButton_EP1": False,
|
||
"pushButton_EP10": False,
|
||
"pushButton_EP100": False,
|
||
"pushButton_SaveInfo": False,
|
||
"pushButton_Exit": True},
|
||
"Current": {
|
||
"pushButton_Refresh": True,
|
||
"pushButton_OpenFile": True,
|
||
"pushButton_Standardize": False,
|
||
"pushButton_CutOff": False,
|
||
"pushButton_GetPos": False,
|
||
"pushButton_Align": False,
|
||
"pushButton_JUMP": False,
|
||
"pushButton_EM1": False,
|
||
"pushButton_EM10": False,
|
||
"pushButton_EM100": False,
|
||
"pushButton_EP1": False,
|
||
"pushButton_EP10": False,
|
||
"pushButton_EP100": False,
|
||
"pushButton_SaveInfo": False,
|
||
"pushButton_Exit": True}
|
||
}
|
||
|
||
|
||
class SettingWindow(QMainWindow):
|
||
def __init__(self):
|
||
super(SettingWindow, self).__init__()
|
||
self.ui = Ui_Setting()
|
||
self.ui.setupUi(self)
|
||
self.__read_settings__()
|
||
|
||
def __read_settings__(self):
|
||
with open("./config.yaml", "r") as f:
|
||
fileConfig = yaml.load(f.read(), Loader=yaml.FullLoader)
|
||
Conf.update(fileConfig)
|
||
# print(Conf)
|
||
self.ui.lineEdit_PSGFilePath.setText(Conf["PSGConfig"]["Path"])
|
||
self.ui.lineEdit_XXFilePath.setText(Conf["XXConfig"]["Path"])
|
||
self.ui.spinBox_PSGDefaultFreq.setValue(Conf["PSGConfig"]["Frequency"])
|
||
self.ui.spinBox_XXDefaultFreq.setValue(Conf["XXConfig"]["Frequency"])
|
||
self.ui.QSpinBox_ApplyFre.setValue(Conf["ApplyFrequency"])
|
||
autoTHO = Conf["PSGConfig"]["THOChannel"]["auto"]
|
||
self.ui.checkBox_THOautoChannel.setChecked(2 if autoTHO else 0)
|
||
autoABD = Conf["PSGConfig"]["ABDChannel"]["auto"]
|
||
self.ui.checkBox_ABDautoChannel.setChecked(2 if autoABD else 0)
|
||
self.ui.spinBox_THOcustomChannel.setValue(Conf["PSGConfig"]["THOChannel"]["Channel"])
|
||
self.ui.spinBox_ABDcustomChannel.setValue(Conf["PSGConfig"]["ABDChannel"]["Channel"])
|
||
self.ui.doubleSpinBox_ButterLowPassFreq.setValue(Conf["RespFilterConfig"]["LowCut"])
|
||
self.ui.doubleSpinBox_ButterHighPassFreq.setValue(Conf["RespFilterConfig"]["HighCut"])
|
||
self.ui.spinBox_ButterOrder.setValue(Conf["RespFilterConfig"]["Order"])
|
||
self.ui.spinBox_THOcustomChannel.setEnabled(not autoTHO)
|
||
self.ui.spinBox_ABDcustomChannel.setEnabled(not autoABD)
|
||
|
||
# 绑定事件
|
||
self.ui.toolButton_PSGFilePath.clicked.connect(self.__select_file__)
|
||
self.ui.toolButton_XXFilePath.clicked.connect(self.__select_file__)
|
||
|
||
self.ui.pushButton_SaveConfig.clicked.connect(self.__write_settings__)
|
||
self.ui.pushButton_Cancel.clicked.connect(self.close)
|
||
|
||
# ABD auto checkbox和SpinBox 互斥
|
||
self.ui.checkBox_ABDautoChannel.stateChanged.connect(self.__ABDAutoChannel__)
|
||
self.ui.checkBox_THOautoChannel.stateChanged.connect(self.__THOAutoChannel__)
|
||
|
||
def __ABDAutoChannel__(self, state):
|
||
if state == 2:
|
||
self.ui.spinBox_ABDcustomChannel.setEnabled(False)
|
||
else:
|
||
self.ui.spinBox_ABDcustomChannel.setEnabled(True)
|
||
|
||
def __THOAutoChannel__(self, state):
|
||
if state == 2:
|
||
self.ui.spinBox_THOcustomChannel.setEnabled(False)
|
||
else:
|
||
self.ui.spinBox_THOcustomChannel.setEnabled(True)
|
||
|
||
def __select_file__(self, event):
|
||
sender = self.sender()
|
||
if sender.objectName() == "toolButton_PSGFilePath":
|
||
path = QFileDialog.getExistingDirectory(self, "选择PSG数据文件夹", "./Data/PSG/")
|
||
self.ui.lineEdit_PSGFilePath.setText(path)
|
||
elif sender.objectName() == "toolButton_XXFilePath":
|
||
path = QFileDialog.getExistingDirectory(self, "选择XX数据文件夹", "./Data/XX/")
|
||
self.ui.lineEdit_XXFilePath.setText(path)
|
||
|
||
def __write_settings__(self):
|
||
# 从界面读取配置
|
||
Conf["PSGConfig"]["Path"] = self.ui.lineEdit_PSGFilePath.text()
|
||
Conf["XXConfig"]["Path"] = self.ui.lineEdit_XXFilePath.text()
|
||
Conf["PSGConfig"]["Frequency"] = self.ui.spinBox_PSGDefaultFreq.value()
|
||
Conf["XXConfig"]["Frequency"] = self.ui.spinBox_XXDefaultFreq.value()
|
||
Conf["ApplyFrequency"] = self.ui.QSpinBox_ApplyFre.value()
|
||
Conf["PSGConfig"]["THOChannel"]["auto"] = self.ui.checkBox_THOautoChannel.isChecked()
|
||
Conf["PSGConfig"]["ABDChannel"]["auto"] = self.ui.checkBox_ABDautoChannel.isChecked()
|
||
Conf["PSGConfig"]["THOChannel"]["Channel"] = self.ui.spinBox_THOcustomChannel.value()
|
||
Conf["PSGConfig"]["ABDChannel"]["Channel"] = self.ui.spinBox_ABDcustomChannel.value()
|
||
Conf["RespFilterConfig"]["LowCut"] = self.ui.doubleSpinBox_ButterLowPassFreq.value()
|
||
Conf["RespFilterConfig"]["HighCut"] = self.ui.doubleSpinBox_ButterHighPassFreq.value()
|
||
Conf["RespFilterConfig"]["Order"] = self.ui.spinBox_ButterOrder.value()
|
||
|
||
with open("./config.yaml", "w") as f:
|
||
yaml.dump(Conf, f)
|
||
|
||
self.close()
|
||
|
||
|
||
class Data:
|
||
def __init__(self, PSGDataPath, XXDataPath, sampNo, Config):
|
||
self.PSGDataPath = PSGDataPath / f"{sampNo}.edf"
|
||
if (XXDataPath / f"{sampNo}.npy").exists():
|
||
self.XXDataPath = XXDataPath / f"{sampNo}.npy"
|
||
elif (XXDataPath / f"{sampNo}.txt").exists():
|
||
self.XXDataPath = XXDataPath / f"{sampNo}.txt"
|
||
else:
|
||
self.XXDataPath = None
|
||
|
||
self.Config = Config
|
||
|
||
self.raw_THO = None
|
||
self.raw_ABD = None
|
||
self.raw_XX = None
|
||
|
||
self.processed_THO = None
|
||
self.processed_ABD = None
|
||
self.processed_XX = None
|
||
|
||
self.PSG_minutes = None
|
||
self.XX_minutes = None
|
||
|
||
def OpenFile(self):
|
||
# 判断是edf还是npy或txt
|
||
if self.PSGDataPath.suffix == ".edf":
|
||
PSG = pyedflib.EdfReader(self.PSGDataPath.__str__())
|
||
if self.Config["PSGConfig"]["THOChannel"]["auto"]:
|
||
self.raw_THO = PSG.readSignal(PSG.getSignalLabels().index('Effort THO'))
|
||
else:
|
||
self.raw_THO = PSG.readSignal(self.Config["PSGConfig"]["THOChannel"]["Channel"])
|
||
if self.Config["PSGConfig"]["ABDChannel"]["auto"]:
|
||
self.raw_ABD = PSG.readSignal(PSG.getSignalLabels().index('Effort ABD'))
|
||
else:
|
||
self.raw_ABD = PSG.readSignal(self.Config["PSGConfig"]["ABDChannel"]["Channel"])
|
||
PSG.close()
|
||
else:
|
||
return False, "PSG文件格式错误"
|
||
|
||
if self.XXDataPath.suffix == ".npy":
|
||
self.raw_XX = np.load(self.XXDataPath)
|
||
elif self.XXDataPath.suffix == ".txt":
|
||
self.raw_XX = pd.read_csv(self.XXDataPath, sep="\t", header=None).values
|
||
else:
|
||
return False, "XX文件格式错误"
|
||
|
||
# 获取时长
|
||
# print(self.raw_THO.shape, self.raw_XX.shape)
|
||
# print(self.Config["PSGConfig"]["Frequency"], self.Config["XXConfig"]["Frequency"])
|
||
self.PSG_minutes = round(self.raw_THO.shape[0] / self.Config["PSGConfig"]["Frequency"] / 60)
|
||
self.XX_minutes = round(self.raw_XX.shape[0] / self.Config["XXConfig"]["Frequency"] / 60)
|
||
|
||
return True, "读取成功"
|
||
|
||
def __Filter__(self):
|
||
def butter_bandpass_filter(data, lowCut, highCut, fs, order):
|
||
low = lowCut / (fs * 0.5)
|
||
high = highCut / (fs * 0.5)
|
||
sos = signal.butter(order, [low, high], btype="bandpass", output='sos')
|
||
return signal.sosfilt(sos, data)
|
||
|
||
# 滤波
|
||
self.processed_THO = butter_bandpass_filter(self.raw_THO, self.Config["RespFilterConfig"]["LowCut"],
|
||
self.Config["RespFilterConfig"]["HighCut"],
|
||
self.Config["PSGConfig"]["Frequency"],
|
||
self.Config["RespFilterConfig"]["Order"])
|
||
self.processed_ABD = butter_bandpass_filter(self.raw_ABD, self.Config["RespFilterConfig"]["LowCut"],
|
||
self.Config["RespFilterConfig"]["HighCut"],
|
||
self.Config["PSGConfig"]["Frequency"],
|
||
self.Config["RespFilterConfig"]["Order"])
|
||
self.processed_XX = butter_bandpass_filter(self.raw_XX, self.Config["RespFilterConfig"]["LowCut"],
|
||
self.Config["RespFilterConfig"]["HighCut"],
|
||
self.Config["XXConfig"]["Frequency"],
|
||
self.Config["RespFilterConfig"]["Order"])
|
||
|
||
return
|
||
|
||
def Standardize_0(self):
|
||
# 重采样
|
||
self.processed_THO = signal.resample(self.raw_THO, int(self.PSG_minutes * 60 * self.Config["ApplyFrequency"]))
|
||
self.processed_ABD = signal.resample(self.raw_ABD, int(self.PSG_minutes * 60 * self.Config["ApplyFrequency"]))
|
||
self.processed_XX = signal.resample(self.raw_XX, int(self.XX_minutes * 60 * self.Config["ApplyFrequency"]))
|
||
return True, "原始信号仅重采样"
|
||
|
||
def Standardize_1(self):
|
||
self.__Filter__()
|
||
return True, "呼吸提取完成 "
|
||
|
||
def Standardize_2(self):
|
||
# 如果XX采样率大于PSG采样率,那么XX重采样到PSG采样率
|
||
if self.Config["XXConfig"]["Frequency"] > self.Config["PSGConfig"]["Frequency"]:
|
||
# 用[::]完成
|
||
self.processed_XX = self.processed_XX[
|
||
::int(self.Config["XXConfig"]["Frequency"] / self.Config["PSGConfig"]["Frequency"])]
|
||
# 如果XX采样率小于PSG采样率,那么XX重采样到PSG采样率
|
||
elif self.Config["XXConfig"]["Frequency"] < self.Config["PSGConfig"]["Frequency"] < 100:
|
||
# 用repeat完成
|
||
self.processed_XX = np.repeat(self.processed_XX,
|
||
int(self.Config["PSGConfig"]["Frequency"] / self.Config["XXConfig"][
|
||
"Frequency"]),
|
||
axis=0)
|
||
# 修改Config
|
||
self.Config.update({"temp_frequency": self.Config["PSGConfig"]["Frequency"]})
|
||
return True, "预重采样完成"
|
||
|
||
def Standardize_3(self):
|
||
temp_frequency = self.Config["temp_frequency"]
|
||
# 判断是否去基线
|
||
if self.Config["PSGConfig"]["PSGDelBase"]:
|
||
# 减去四秒钟平均滤波
|
||
self.processed_THO = self.processed_THO - np.convolve(self.processed_THO,
|
||
np.ones(int(4 * temp_frequency)) / int(
|
||
4 * temp_frequency),
|
||
mode='same')
|
||
self.processed_ABD = self.processed_ABD - np.convolve(self.processed_ABD,
|
||
np.ones(int(4 * temp_frequency)) / int(
|
||
4 * temp_frequency),
|
||
mode='same')
|
||
if self.Config["XXConfig"]["XXDelBase"]:
|
||
self.processed_XX = self.processed_XX - np.convolve(self.processed_XX,
|
||
np.ones(int(4 * temp_frequency)) / int(
|
||
4 * temp_frequency),
|
||
mode='same')
|
||
return True, "去基线完成"
|
||
|
||
def Standardize_4(self):
|
||
# 判断是否标准化
|
||
if self.Config["PSGConfig"]["PSGZScore"]:
|
||
self.processed_THO = (self.processed_THO - np.mean(self.processed_THO)) / np.std(self.processed_THO)
|
||
self.processed_ABD = (self.processed_ABD - np.mean(self.processed_ABD)) / np.std(self.processed_ABD)
|
||
if self.Config["XXConfig"]["XXZScore"]:
|
||
self.processed_XX = (self.processed_XX - np.mean(self.processed_XX)) / np.std(self.processed_XX)
|
||
|
||
return True, "标准化完成"
|
||
|
||
def Standardize_5(self):
|
||
# 重采样
|
||
self.processed_THO = signal.resample(self.processed_THO,
|
||
int(self.PSG_minutes * 60 * self.Config["ApplyFrequency"]))
|
||
self.processed_ABD = signal.resample(self.processed_ABD,
|
||
int(self.PSG_minutes * 60 * self.Config["ApplyFrequency"]))
|
||
self.processed_XX = signal.resample(self.processed_XX,
|
||
int(self.XX_minutes * 60 * self.Config["ApplyFrequency"]))
|
||
|
||
return True, "最终重采样完成"
|
||
|
||
def DrawPicRawOverview(self):
|
||
fig = Figure(figsize=(8, 7), dpi=100)
|
||
canvas = FigureCanvas(fig)
|
||
max_x = max(self.processed_THO.shape[0], self.processed_ABD.shape[0], self.processed_XX.shape[0])
|
||
ax1 = fig.add_subplot(311)
|
||
ax1.plot(self.processed_THO, color='blue')
|
||
ax1.set_xlim(0, max_x)
|
||
ax1.set_title("THO")
|
||
|
||
ax2 = fig.add_subplot(312)
|
||
ax2.plot(self.processed_XX, color='blue')
|
||
ax2.set_xlim(0, max_x)
|
||
ax2.set_title("xinxiao")
|
||
|
||
ax3 = fig.add_subplot(313)
|
||
ax3.plot(self.processed_ABD, color='blue')
|
||
ax3.set_xlim(0, max_x)
|
||
ax3.set_title("ABD")
|
||
|
||
width, height = fig.figbbox.width, fig.figbbox.height
|
||
fig.canvas.draw()
|
||
# 返回图片以便存到QPixIamge
|
||
return canvas.buffer_rgba(), width, height
|
||
|
||
def DrawPicOverviewWithCutOff(self):
|
||
fig = Figure(figsize=(8, 7), dpi=100)
|
||
canvas = FigureCanvas(fig)
|
||
max_x = max(self.processed_THO.shape[0] + self.Config["PSGConfig"]["PreA"],
|
||
self.processed_XX.shape[0] + self.Config["XXConfig"]["PreA"])
|
||
min_x = min(self.Config["PSGConfig"]["PreA"], self.Config["XXConfig"]["PreA"], 0)
|
||
ax1 = fig.add_subplot(311)
|
||
ax1.plot(
|
||
np.linspace(self.Config["PSGConfig"]["PreA"], len(self.processed_THO) + self.Config["PSGConfig"]["PreA"],
|
||
len(self.processed_THO)), self.processed_THO, color='blue')
|
||
# 绘制x = PreCut的线 和 x = PostCut的虚线
|
||
ax1.axvline(x=self.Config["PSGConfig"]["PreCut"] + self.Config["PSGConfig"]["PreA"], color='red',
|
||
linestyle='--')
|
||
ax1.axvline(x=len(self.processed_THO) - self.Config["PSGConfig"]["PostCut"] + self.Config["PSGConfig"]["PreA"],
|
||
color='red', linestyle='--')
|
||
|
||
ax1.set_xlim(min_x, max_x)
|
||
ax1.set_title("THO")
|
||
|
||
ax2 = fig.add_subplot(312)
|
||
ax2.plot(np.linspace(self.Config["XXConfig"]["PreA"], len(self.processed_XX) + self.Config["XXConfig"]["PreA"],
|
||
len(self.processed_XX)), self.processed_XX, color='blue')
|
||
ax2.axvline(x=self.Config["XXConfig"]["PreCut"] + self.Config["XXConfig"]["PreA"], color='red', linestyle='--')
|
||
ax2.axvline(x=len(self.processed_XX) - self.Config["XXConfig"]["PostCut"] + self.Config["XXConfig"]["PreA"],
|
||
color='red', linestyle='--')
|
||
ax2.set_xlim(min_x, max_x)
|
||
ax2.set_title("xinxiao")
|
||
|
||
ax3 = fig.add_subplot(313)
|
||
ax3.plot(
|
||
np.linspace(self.Config["PSGConfig"]["PreA"], len(self.processed_ABD) + self.Config["PSGConfig"]["PreA"],
|
||
len(self.processed_ABD)), self.processed_ABD, color='blue')
|
||
ax3.axvline(x=self.Config["PSGConfig"]["PreCut"] + self.Config["PSGConfig"]["PreA"], color='red',
|
||
linestyle='--')
|
||
ax3.axvline(x=len(self.processed_THO) - self.Config["PSGConfig"]["PostCut"] + self.Config["PSGConfig"]["PreA"],
|
||
color='red', linestyle='--')
|
||
ax3.set_xlim(min_x, max_x)
|
||
ax3.set_title("ABD")
|
||
|
||
width, height = fig.figbbox.width, fig.figbbox.height
|
||
fig.canvas.draw()
|
||
# 返回图片以便存到QPixIamge
|
||
return canvas.buffer_rgba(), width, height
|
||
|
||
|
||
class MainWindow(QMainWindow):
|
||
def __init__(self):
|
||
super(MainWindow, self).__init__()
|
||
self.ui = Ui_respCoarseAlign()
|
||
self.setting = SettingWindow()
|
||
self.ui.setupUi(self)
|
||
|
||
self.ui.actionDefault_Configuration.triggered.connect(self.setting.show)
|
||
|
||
# checkbox custom 和SpinBox 互斥
|
||
self.ui.checkBox_custom.stateChanged.connect(self.__customChannel__)
|
||
self.__disableAllButton__()
|
||
|
||
# 绑定事件
|
||
# 刷新键分别获取PSG和XX文件夹里面的数据,获取所有编号显示在下拉框,比对编号同时存在的可选,仅存在一个文件夹的编号不可选
|
||
self.ui.pushButton_Refresh.clicked.connect(self.__refresh__)
|
||
self.ui.pushButton_OpenFile.clicked.connect(self.__openFile__)
|
||
self.ui.pushButton_Standardize.clicked.connect(self.__standardize__)
|
||
self.ui.pushButton_CutOff.clicked.connect(self.__cutOff__)
|
||
self.ui.pushButton_GetPos.clicked.connect(self.__getPosition__)
|
||
# self.ui.pushButton_Align.clicked.connect(self.__align__)
|
||
# self.ui.pushButton_JUMP.clicked.connect(self.__jump__)
|
||
# self.ui.pushButton_EM1.clicked.connect(self.__EM1__)
|
||
# self.ui.pushButton_EM10.clicked.connect(self.__EM10__)
|
||
# self.ui.pushButton_EM100.clicked.connect(self.__EM100__)
|
||
# self.ui.pushButton_EP1.clicked.connect(self.__EP1__)
|
||
# self.ui.pushButton_EP10.clicked.connect(self.__EP10__)
|
||
# self.ui.pushButton_EP100.clicked.connect(self.__EP100__)
|
||
# self.ui.pushButton_SaveInfo.clicked.connect(self.__saveInfo__)
|
||
self.ui.pushButton_Exit.clicked.connect(self.__exit__)
|
||
|
||
def __resset__(self):
|
||
ButtonState["Current"].update(ButtonState["Default"].copy())
|
||
ButtonState["Current"]["pushButton_Standardize"] = True
|
||
self.ui.spinBox_PSGPreA.setValue(0)
|
||
self.ui.spinBox_PSGPreCut.setValue(0)
|
||
self.ui.spinBox_PSGPostCut.setValue(0)
|
||
self.ui.spinBox_XXPreA.setValue(0)
|
||
self.ui.spinBox_XXPreCut.setValue(0)
|
||
self.ui.spinBox_XXPostCut.setValue(0)
|
||
self.ui.checkBox_NABD.setChecked(False)
|
||
self.ui.checkBox_NTHO.setChecked(False)
|
||
self.ui.checkBox_PABD.setChecked(False)
|
||
self.ui.checkBox_PTHO.setChecked(False)
|
||
self.ui.checkBox_custom.setChecked(False)
|
||
self.ui.spinBox_SelectEpoch.setValue(0)
|
||
self.ui.spinBox_custom.setValue(0)
|
||
|
||
def __cutOff__(self):
|
||
Conf2 = self.data.Config.copy()
|
||
Conf2["PSGConfig"].update({"PreA": self.ui.spinBox_PSGPreA.value(),
|
||
"PreCut": self.ui.spinBox_PSGPreCut.value(),
|
||
"PostCut": self.ui.spinBox_PSGPostCut.value()})
|
||
Conf2["XXConfig"].update({"PreA": self.ui.spinBox_XXPreA.value(),
|
||
"PreCut": self.ui.spinBox_XXPreCut.value(),
|
||
"PostCut": self.ui.spinBox_XXPostCut.value()})
|
||
|
||
self.data.Config = Conf2
|
||
self.__plot__()
|
||
|
||
# matplotlib 绘制图像
|
||
def __plot__(self):
|
||
# 判读是哪个按钮点击调用的本程序
|
||
if self.sender() == self.ui.pushButton_Standardize:
|
||
buffer, width, height = self.data.DrawPicRawOverview()
|
||
elif self.sender() == self.ui.pushButton_CutOff:
|
||
buffer, width, height = self.data.DrawPicOverviewWithCutOff()
|
||
else:
|
||
return
|
||
# 显示到labelPic上
|
||
img = QImage(buffer, width, height, QImage.Format_RGBA8888)
|
||
self.ui.label_Pic.setPixmap(QPixmap(img))
|
||
|
||
# noinspection PyUnresolvedReferences
|
||
def __exit__(self):
|
||
# 选择是否确认退出
|
||
reply = QMessageBox.question(self, '确认', '确认退出吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||
if reply == QMessageBox.Yes:
|
||
self.close()
|
||
|
||
def __customChannel__(self, state):
|
||
if state == 2:
|
||
self.ui.spinBox_custom.setEnabled(True)
|
||
else:
|
||
self.ui.spinBox_custom.setEnabled(False)
|
||
|
||
# 刷新键分别获取PSG和XX文件夹里面的数据,获取所有编号显示在下拉框,比对编号同时存在的可选,仅存在一个文件夹的编号不可选
|
||
def __refresh__(self):
|
||
# 检查两文件夹是否存在
|
||
PSGPath = Path(self.setting.ui.lineEdit_PSGFilePath.text())
|
||
XXPath = Path(self.setting.ui.lineEdit_XXFilePath.text())
|
||
if not PSGPath.exists():
|
||
QMessageBox.warning(self, "警告", "PSG文件夹不存在")
|
||
return
|
||
if not XXPath.exists():
|
||
QMessageBox.warning(self, "警告", "XX文件夹不存在")
|
||
return
|
||
|
||
# 获取两文件夹下所有的txt和npy文件编号
|
||
PSGFiles = [file.stem for file in PSGPath.glob("*.edf")]
|
||
XXFiles = [file.stem for file in XXPath.glob("*.txt")] + [file.stem for file in XXPath.glob("*.npy")]
|
||
sorted(PSGFiles)
|
||
sorted(XXFiles)
|
||
# 获取两文件夹下同时存在的编号
|
||
print(PSGFiles, XXFiles)
|
||
Files = list(set(PSGFiles).intersection(set(XXFiles)))
|
||
# 获取两文件夹下仅存在一个的编号
|
||
FilesOnlyInPSG = list(set(PSGFiles).difference(set(XXFiles)))
|
||
FilesOnlyInXX = list(set(XXFiles).difference(set(PSGFiles)))
|
||
print(Files, FilesOnlyInPSG, FilesOnlyInXX)
|
||
# 均显示到下拉框
|
||
self.ui.comboBox_SelectFile.clear()
|
||
self.ui.comboBox_SelectFile.addItems([file for file in Files])
|
||
self.ui.comboBox_SelectFile.addItems([file + " (仅PSG)" for file in FilesOnlyInPSG])
|
||
self.ui.comboBox_SelectFile.addItems([file + " (仅XX)" for file in FilesOnlyInXX])
|
||
self.ui.comboBox_SelectFile.setCurrentIndex(0)
|
||
|
||
# # 仅存在一个文件夹的编号不可选
|
||
for file in FilesOnlyInPSG:
|
||
self.ui.comboBox_SelectFile.model().item(FilesOnlyInPSG.index(file) + len(Files)).setEnabled(False)
|
||
for file in FilesOnlyInXX:
|
||
self.ui.comboBox_SelectFile.model().item(
|
||
FilesOnlyInXX.index(file) + len(Files) + len(FilesOnlyInPSG)).setEnabled(False)
|
||
|
||
def __disableAllButton__(self):
|
||
# 禁用所有按钮
|
||
all_widgets = self.centralWidget().findChildren(QWidget)
|
||
|
||
# 迭代所有部件,查找按钮并禁用它们
|
||
for widget in all_widgets:
|
||
if isinstance(widget, QPushButton):
|
||
if widget.objectName() in ButtonState["Current"].keys():
|
||
widget.setEnabled(ButtonState["Current"][widget.objectName()])
|
||
|
||
def __enableAllButton__(self):
|
||
# 启用按钮
|
||
all_widgets = self.centralWidget().findChildren(QWidget)
|
||
|
||
# 迭代所有部件,查找按钮并启用它们
|
||
for widget in all_widgets:
|
||
if isinstance(widget, QPushButton):
|
||
if widget.objectName() in ButtonState["Current"].keys():
|
||
widget.setEnabled(ButtonState["Current"][widget.objectName()])
|
||
|
||
def __openFile__(self):
|
||
self.ui.label_Info.setText("正在打开文件...")
|
||
self.__disableAllButton__()
|
||
# 长时间操作,刷新界面防止卡顿
|
||
QApplication.processEvents()
|
||
# 获取checkbox状态
|
||
self.data = Data(Path(self.setting.ui.lineEdit_PSGFilePath.text()),
|
||
Path(self.setting.ui.lineEdit_XXFilePath.text()),
|
||
self.ui.comboBox_SelectFile.currentText().split(" ")[0],
|
||
Conf)
|
||
opened, info = self.data.OpenFile()
|
||
if not opened:
|
||
QMessageBox.warning(self, "警告", info)
|
||
self.ui.label_Info.setText(info)
|
||
else:
|
||
self.ui.label_PSGmins.setText(str(self.data.PSG_minutes))
|
||
self.ui.label_XXmins.setText(str(self.data.XX_minutes))
|
||
self.ui.progressBar.setValue(100)
|
||
self.ui.label_Info.setText(info)
|
||
self.__resset__()
|
||
|
||
|
||
self.__enableAllButton__()
|
||
|
||
def __standardize__(self):
|
||
self.ui.progressBar.setValue(0)
|
||
self.ui.label_Info.setText("正在标准化...")
|
||
self.__disableAllButton__()
|
||
QApplication.processEvents()
|
||
Conf2 = self.data.Config.copy()
|
||
Conf2["RawSignal"] = self.ui.checkBox_RawSignal.isChecked()
|
||
Conf2["PSGConfig"].update({
|
||
"PSGDelBase": self.ui.checkBox_PSGDelBase.isChecked(),
|
||
"PSGZScore": self.ui.checkBox_PSGZScore.isChecked(),
|
||
"Frequency": self.ui.spinBox_PSGFreq.value(),
|
||
})
|
||
Conf2["XXConfig"].update({
|
||
"XXDelBase": self.ui.checkBox_XXDelBase.isChecked(),
|
||
"XXZScore": self.ui.checkBox_XXZScore.isChecked(),
|
||
"Frequency": self.ui.spinBox_XXFreq.value(),
|
||
})
|
||
self.data.Config = Conf2
|
||
if self.data.Config["RawSignal"]:
|
||
opened, info = self.data.Standardize_0()
|
||
if not opened:
|
||
QMessageBox.warning(self, "警告", info)
|
||
self.ui.label_Info.setText(info)
|
||
else:
|
||
self.ui.progressBar.setValue(100)
|
||
self.ui.label_Info.setText(info)
|
||
else:
|
||
self.ui.label_Info.setText('正在进行呼吸提取...')
|
||
QApplication.processEvents()
|
||
|
||
opened, info = self.data.Standardize_1()
|
||
self.ui.label_Info.setText(info)
|
||
self.ui.progressBar.setValue(10)
|
||
QApplication.processEvents()
|
||
|
||
opened, info = self.data.Standardize_2()
|
||
self.ui.progressBar.setValue(30)
|
||
self.ui.label_Info.setText(info)
|
||
QApplication.processEvents()
|
||
|
||
opened, info = self.data.Standardize_3()
|
||
self.ui.label_Info.setText(info)
|
||
self.ui.progressBar.setValue(50)
|
||
QApplication.processEvents()
|
||
|
||
opened, info = self.data.Standardize_4()
|
||
self.ui.label_Info.setText(info)
|
||
self.ui.progressBar.setValue(70)
|
||
QApplication.processEvents()
|
||
|
||
opened, info = self.data.Standardize_5()
|
||
self.ui.label_Info.setText(info)
|
||
self.ui.progressBar.setValue(90)
|
||
QApplication.processEvents()
|
||
|
||
self.__plot__()
|
||
self.ui.progressBar.setValue(100)
|
||
ButtonState["Current"]["pushButton_CutOff"] = True
|
||
ButtonState["Current"]["pushButton_GetPos"] = True
|
||
self.__enableAllButton__()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
app = QApplication(sys.argv)
|
||
window = MainWindow()
|
||
window.show()
|
||
sys.exit(app.exec())
|