misleep 0.2.2b0__tar.gz → 0.2.4b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {misleep-0.2.2b0 → misleep-0.2.4b0}/PKG-INFO +7 -1
- {misleep-0.2.2b0 → misleep-0.2.4b0}/README.md +5 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/config.ini +2 -2
- misleep-0.2.4b0/misleep/gui/dialog.py +374 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/main_window.py +19 -3
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/main_window_ui.py +5 -4
- misleep-0.2.4b0/misleep/gui/uis/state_spectral_dialog_ui.py +82 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/transfer_result_dialog_ui.py +27 -39
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/utils.py +61 -1
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/io/annotation_io.py +75 -47
- misleep-0.2.4b0/misleep/preprocessing/signals.py +69 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep.egg-info/PKG-INFO +7 -1
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep.egg-info/SOURCES.txt +1 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep.egg-info/requires.txt +1 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/setup.py +3 -2
- misleep-0.2.2b0/misleep/gui/dialog.py +0 -217
- misleep-0.2.2b0/misleep/preprocessing/signals.py +0 -15
- {misleep-0.2.2b0 → misleep-0.2.4b0}/LICENSE +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/__main__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/about.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/resources/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/resources/entire_logo.png +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/resources/logo.png +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/resources/misleep.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/resources/misleep.qrc +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/show.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/spec_window.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/thread.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/about_ui.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/label_dialog_ui.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/save_data_dialog_ui.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/gui/uis/spec_window_ui.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/io/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/io/base.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/io/signal_io.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/preprocessing/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/preprocessing/channel.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/preprocessing/spectral.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/utils/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/utils/annotation.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/utils/signals.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/viz/__init__.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/viz/hypnogram.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/viz/signals.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep/viz/spectral.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep.egg-info/dependency_links.txt +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/misleep.egg-info/top_level.txt +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/setup.cfg +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_annotation_io.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_loadmat73.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_midata.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_show.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_signal_io.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_signals_viz.py +0 -0
- {misleep-0.2.2b0 → misleep-0.2.4b0}/test/test_spectral_viz.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: misleep
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4b0
|
|
4
4
|
Summary: MiSleep: Mice Sleep EEG/EMG visualization, scoring and analysis.
|
|
5
5
|
Home-page: https://github.com/BryanWang0702/MiSleep/
|
|
6
6
|
Download-URL: https://github.com/BryanWang0702/MiSleep/
|
|
@@ -29,6 +29,7 @@ Requires-Dist: pyqt5
|
|
|
29
29
|
Requires-Dist: mat73
|
|
30
30
|
Requires-Dist: pandas
|
|
31
31
|
Requires-Dist: openpyxl
|
|
32
|
+
Requires-Dist: yasa
|
|
32
33
|
|
|
33
34
|
# MiSleep
|
|
34
35
|
MiSleep is for EEG/EMG signal processing and visualization
|
|
@@ -40,6 +41,11 @@ MiSleep is for EEG/EMG signal processing and visualization
|
|
|
40
41
|
pip install misleep==0.2.2b0
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
Find the directory where you installed misleep, run
|
|
45
|
+
```shell
|
|
46
|
+
python -m misleep
|
|
47
|
+
```
|
|
48
|
+
|
|
43
49
|
### New features
|
|
44
50
|
1. New data structure
|
|
45
51
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[gui]
|
|
2
|
-
version = v0.2.
|
|
2
|
+
version = v0.2.4 Beta
|
|
3
3
|
updatetime = 2024/04/01
|
|
4
4
|
marker = ['pat', 'add water', 'third']
|
|
5
5
|
startend = ['SWA', 'SWA', 'start end label', 'start end label', 'start end label', 'start end label', 'start end label']
|
|
@@ -8,5 +8,5 @@ statecolor = {"1": "orange", "2": "skyblue", "3": "red", "4": "white"}
|
|
|
8
8
|
statecolorbgalpha = 0.1
|
|
9
9
|
markerlinecolor = "red"
|
|
10
10
|
startendlinecolor = "blue"
|
|
11
|
-
openpath = E:/workplace/EEGProcessing/00_DATA/
|
|
11
|
+
openpath = E:/workplace/EEGProcessing/00_DATA/20240114_17_0700_7pin/female3/female3_label.txt
|
|
12
12
|
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""art_detection
|
|
3
|
+
@Project: MiSleep_v2
|
|
4
|
+
@File: label_dialog.py
|
|
5
|
+
@Author: Xueqiang Wang
|
|
6
|
+
@Date: 2024/3/28
|
|
7
|
+
@Description: Dialog file, label dialog, transfer result dialog
|
|
8
|
+
"""
|
|
9
|
+
from PyQt5.QtCore import QCoreApplication, Qt, QStringListModel
|
|
10
|
+
from PyQt5.QtWidgets import QDialog, QMessageBox, QFileDialog
|
|
11
|
+
import datetime
|
|
12
|
+
|
|
13
|
+
from misleep.gui.uis.label_dialog_ui import Ui_Dialog
|
|
14
|
+
from misleep.gui.uis.transfer_result_dialog_ui import Ui_TransferResultDialog
|
|
15
|
+
from misleep.gui.uis.state_spectral_dialog_ui import Ui_StateSpectralDialog
|
|
16
|
+
from misleep.gui.thread import SaveThread
|
|
17
|
+
from misleep.io.annotation_io import transfer_result
|
|
18
|
+
from misleep.utils.signals import signal_filter
|
|
19
|
+
from misleep.utils.annotation import lst2group
|
|
20
|
+
from misleep.gui.utils import cal_draw_spectrum
|
|
21
|
+
from misleep.preprocessing.signals import reject_artifact
|
|
22
|
+
import pandas as pd
|
|
23
|
+
from copy import deepcopy
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class label_dialog(QDialog, Ui_Dialog):
|
|
27
|
+
def __init__(self, parent=None, config=None):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the Label dialog of MiSleep
|
|
30
|
+
"""
|
|
31
|
+
super().__init__(parent)
|
|
32
|
+
|
|
33
|
+
# Enable high dpi devices
|
|
34
|
+
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
35
|
+
self.setupUi(self)
|
|
36
|
+
|
|
37
|
+
# Load configuration
|
|
38
|
+
self.config = config
|
|
39
|
+
|
|
40
|
+
# Type representing marker(0) or start_end(1)
|
|
41
|
+
self._type = 0
|
|
42
|
+
|
|
43
|
+
# List view of selected labels
|
|
44
|
+
self.slm = QStringListModel()
|
|
45
|
+
self.LabelListView.setModel(self.slm)
|
|
46
|
+
self.marker_label = [each[1:-1] for each in
|
|
47
|
+
self.config['gui']['marker'][1:-1].split(', ')]
|
|
48
|
+
self.start_end_label = [each[1:-1] for each in
|
|
49
|
+
self.config['gui']['startend'][1:-1].split(', ')]
|
|
50
|
+
self.label_name = ""
|
|
51
|
+
self.closed = False
|
|
52
|
+
|
|
53
|
+
self.OKBt.clicked.connect(self.submit_label)
|
|
54
|
+
self.CancelBt.clicked.connect(self.cancel_event)
|
|
55
|
+
self.AddBt.clicked.connect(self.add_label)
|
|
56
|
+
self.DeleteBt.clicked.connect(self.delete_label)
|
|
57
|
+
|
|
58
|
+
self.slm.dataChanged.connect(self.update_label_list)
|
|
59
|
+
self.add_or_delete = False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def show_contents(self, idx=0):
|
|
63
|
+
"""Show label contents"""
|
|
64
|
+
self.closed = False
|
|
65
|
+
|
|
66
|
+
if self._type == 0:
|
|
67
|
+
self.slm.setStringList(self.marker_label)
|
|
68
|
+
self.LabelListView.setModel(self.slm)
|
|
69
|
+
|
|
70
|
+
if self._type == 1:
|
|
71
|
+
self.slm.setStringList(self.start_end_label)
|
|
72
|
+
self.LabelListView.setModel(self.slm)
|
|
73
|
+
|
|
74
|
+
if idx == -1:
|
|
75
|
+
idx = len(self.slm.stringList()) - 1
|
|
76
|
+
idx = self.slm.index(idx)
|
|
77
|
+
self.LabelListView.setCurrentIndex(idx)
|
|
78
|
+
|
|
79
|
+
def submit_label(self):
|
|
80
|
+
"""Triggered by Clicking Ok Button"""
|
|
81
|
+
|
|
82
|
+
if self._type == 0:
|
|
83
|
+
self.label_name = self.marker_label[
|
|
84
|
+
self.LabelListView.selectedIndexes()[0].row()
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
self.label_name = self.start_end_label[
|
|
89
|
+
self.LabelListView.selectedIndexes()[0].row()
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
self.hide()
|
|
93
|
+
|
|
94
|
+
def update_label_list(self):
|
|
95
|
+
"""Update label list when edited"""
|
|
96
|
+
if not self.add_or_delete:
|
|
97
|
+
string_list = self.slm.stringList()
|
|
98
|
+
|
|
99
|
+
if self._type == 0:
|
|
100
|
+
self.marker_label = string_list
|
|
101
|
+
if self._type == 1:
|
|
102
|
+
self.start_end_label = string_list
|
|
103
|
+
else:
|
|
104
|
+
self.show_contents(idx=-1)
|
|
105
|
+
self.add_or_delete = False
|
|
106
|
+
|
|
107
|
+
self.save_config()
|
|
108
|
+
|
|
109
|
+
def add_label(self):
|
|
110
|
+
if self._type == 0:
|
|
111
|
+
self.marker_label.append('label')
|
|
112
|
+
elif self._type == 1:
|
|
113
|
+
self.start_end_label.append('start end label')
|
|
114
|
+
|
|
115
|
+
self.add_or_delete = True
|
|
116
|
+
self.update_label_list()
|
|
117
|
+
|
|
118
|
+
def delete_label(self):
|
|
119
|
+
if not self.LabelListView.selectedIndexes():
|
|
120
|
+
return
|
|
121
|
+
if len(self.slm.stringList()) == 1:
|
|
122
|
+
QMessageBox.about(self, "Error", "You can't delete all labels!")
|
|
123
|
+
return
|
|
124
|
+
if self._type == 0:
|
|
125
|
+
self.marker_label.pop(
|
|
126
|
+
self.LabelListView.selectedIndexes()[0].row()
|
|
127
|
+
)
|
|
128
|
+
elif self._type == 1:
|
|
129
|
+
self.start_end_label.pop(
|
|
130
|
+
self.LabelListView.selectedIndexes()[0].row()
|
|
131
|
+
)
|
|
132
|
+
self.add_or_delete = True
|
|
133
|
+
self.update_label_list()
|
|
134
|
+
|
|
135
|
+
def save_config(self):
|
|
136
|
+
"""When label changed, save configuration to file, open a new thread"""
|
|
137
|
+
self.config.set('gui', 'MARKER', str(self.marker_label))
|
|
138
|
+
self.config.set('gui', 'STARTEND', str(self.start_end_label))
|
|
139
|
+
save_thread = SaveThread(file=self.config,
|
|
140
|
+
file_path='./misleep/config.ini')
|
|
141
|
+
save_thread.save_config()
|
|
142
|
+
save_thread.quit()
|
|
143
|
+
|
|
144
|
+
def cancel_event(self):
|
|
145
|
+
"""Triggered by the `cancel` button"""
|
|
146
|
+
self.closed = True
|
|
147
|
+
self.hide()
|
|
148
|
+
|
|
149
|
+
def closeEvent(self, event):
|
|
150
|
+
event.ignore()
|
|
151
|
+
self.closed = True
|
|
152
|
+
self.hide()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class transferResult_dialog(QDialog, Ui_TransferResultDialog):
|
|
156
|
+
def __init__(self, parent=None):
|
|
157
|
+
"""
|
|
158
|
+
Initialize the transfer dialog of MiSleep
|
|
159
|
+
"""
|
|
160
|
+
super().__init__(parent)
|
|
161
|
+
|
|
162
|
+
# Enable high dpi devices
|
|
163
|
+
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
164
|
+
self.setupUi(self)
|
|
165
|
+
|
|
166
|
+
self.ACTimeEditor.setDisabled(True)
|
|
167
|
+
self.TransferStartTimeEdit.setDisabled(True)
|
|
168
|
+
self.ResetTimeCheckBox.clicked.connect(self.ac_time_editor_changed)
|
|
169
|
+
self.ResetTransferStartTimeCheckBox.clicked.connect(self.start_time_editor_changed)
|
|
170
|
+
self.OKBt.clicked.connect(self.okEvent)
|
|
171
|
+
self.CancelBt.clicked.connect(self.cancelEvent)
|
|
172
|
+
self.closed = True
|
|
173
|
+
|
|
174
|
+
def ac_time_editor_changed(self):
|
|
175
|
+
if self.ResetTimeCheckBox.isChecked():
|
|
176
|
+
self.ACTimeEditor.setEnabled(True)
|
|
177
|
+
if not self.ResetTimeCheckBox.isChecked():
|
|
178
|
+
self.ACTimeEditor.setDisabled(True)
|
|
179
|
+
|
|
180
|
+
def start_time_editor_changed(self):
|
|
181
|
+
if self.ResetTransferStartTimeCheckBox.isChecked():
|
|
182
|
+
self.TransferStartTimeEdit.setEnabled(True)
|
|
183
|
+
if not self.ResetTransferStartTimeCheckBox.isChecked():
|
|
184
|
+
self.TransferStartTimeEdit.setDisabled(True)
|
|
185
|
+
|
|
186
|
+
def transfer(self, config, mianno, ac_time):
|
|
187
|
+
"""Transfer result to dataframe, triggered by okay button"""
|
|
188
|
+
mianno = deepcopy(mianno)
|
|
189
|
+
ac_time = deepcopy(ac_time)
|
|
190
|
+
|
|
191
|
+
if self.ResetTimeCheckBox.isChecked():
|
|
192
|
+
ac_time = self.ACTimeEditor.dateTime().toPyDateTime()
|
|
193
|
+
else:
|
|
194
|
+
ac_time = datetime.datetime.strptime(ac_time, "%Y%m%d-%H:%M:%S")
|
|
195
|
+
|
|
196
|
+
if self.ResetTransferStartTimeCheckBox.isChecked():
|
|
197
|
+
start_time = self.TransferStartTimeEdit.dateTime().toPyDateTime()
|
|
198
|
+
if start_time > ac_time:
|
|
199
|
+
delay_seconds = (start_time - ac_time).seconds
|
|
200
|
+
mianno._marker = mianno.marker[delay_seconds:]
|
|
201
|
+
mianno._start_end = mianno.start_end[delay_seconds:]
|
|
202
|
+
mianno._sleep_state = mianno.sleep_state[delay_seconds:]
|
|
203
|
+
ac_time = start_time
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
fd, _ = QFileDialog.getSaveFileName(self, "Save transfered result",
|
|
207
|
+
f"{config['gui']['openpath'].split('/')[0]}/transfer_result.xlsx",
|
|
208
|
+
"*.xlsx;;")
|
|
209
|
+
if fd == '':
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
df, analyse_df = transfer_result(mianno=mianno, ac_time=ac_time)
|
|
213
|
+
|
|
214
|
+
writer = pd.ExcelWriter(fd, datetime_format='yyyy-mm-dd hh:mm:ss')
|
|
215
|
+
pd.concat([df, analyse_df], axis=1).to_excel(
|
|
216
|
+
excel_writer=writer, sheet_name='All', index=False)
|
|
217
|
+
|
|
218
|
+
writer.close()
|
|
219
|
+
|
|
220
|
+
QMessageBox.about(self, "Info", "Transfered result saved")
|
|
221
|
+
|
|
222
|
+
def okEvent(self):
|
|
223
|
+
self.closed = False
|
|
224
|
+
self.hide()
|
|
225
|
+
|
|
226
|
+
def cancelEvent(self):
|
|
227
|
+
"""Triggered by the `cancel` button"""
|
|
228
|
+
self.closed = True
|
|
229
|
+
self.hide()
|
|
230
|
+
|
|
231
|
+
def closeEvent(self, event):
|
|
232
|
+
event.ignore()
|
|
233
|
+
self.closed = True
|
|
234
|
+
self.hide()
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class stateSpectral_dialog(QDialog, Ui_StateSpectralDialog):
|
|
238
|
+
def __init__(self, parent=None):
|
|
239
|
+
"""
|
|
240
|
+
Initialize the state spectral dialog of MiSleep
|
|
241
|
+
"""
|
|
242
|
+
super().__init__(parent)
|
|
243
|
+
|
|
244
|
+
# Enable high dpi devices
|
|
245
|
+
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
246
|
+
self.setupUi(self)
|
|
247
|
+
|
|
248
|
+
self.BPFilterCheckBox.clicked.connect(self.BP_filter_check_changed)
|
|
249
|
+
self.BPFilterCheckBox.setChecked(True)
|
|
250
|
+
self.RejectArtifactCheckBox.clicked.connect(self.reject_artifact_artifacts_changed)
|
|
251
|
+
self.RejectArtifactCheckBox.setChecked(False)
|
|
252
|
+
|
|
253
|
+
self.OKBt.clicked.connect(self.okEvent)
|
|
254
|
+
self.CancelBt.clicked.connect(self.cancelEvent)
|
|
255
|
+
self.closed = True
|
|
256
|
+
|
|
257
|
+
def BP_filter_check_changed(self):
|
|
258
|
+
if self.BPFilterCheckBox.isChecked():
|
|
259
|
+
self.BPLow.setEnabled(True)
|
|
260
|
+
self.BPHigh.setEnabled(True)
|
|
261
|
+
if not self.BPFilterCheckBox.isChecked():
|
|
262
|
+
self.BPLow.setDisabled(True)
|
|
263
|
+
self.BPHigh.setDisabled(True)
|
|
264
|
+
|
|
265
|
+
def reject_artifact_artifacts_changed(self):
|
|
266
|
+
if self.RejectArtifactCheckBox.isChecked():
|
|
267
|
+
self.ArtThresholdSpinBox.setEnabled(True)
|
|
268
|
+
if not self.RejectArtifactCheckBox.isChecked():
|
|
269
|
+
self.ArtThresholdSpinBox.setDisabled(True)
|
|
270
|
+
|
|
271
|
+
def dialog_show(self, channels):
|
|
272
|
+
"""Show state spectral dialog, fill params"""
|
|
273
|
+
self.ChannelSelector.addItems(channels)
|
|
274
|
+
self.ChannelSelector.setCurrentIndex(0)
|
|
275
|
+
|
|
276
|
+
def spectral_analysis(self, midata, mianno, config):
|
|
277
|
+
"""Do spectral analysis"""
|
|
278
|
+
|
|
279
|
+
channel_idx = self.ChannelSelector.currentIndex()
|
|
280
|
+
channel_data = midata.signals[channel_idx]
|
|
281
|
+
sleep_state = lst2group([[idx, each] for idx, each in enumerate(mianno.sleep_state)])
|
|
282
|
+
sf = midata.sf[channel_idx]
|
|
283
|
+
|
|
284
|
+
# Do filter if checked
|
|
285
|
+
if self.BPFilterCheckBox.isChecked():
|
|
286
|
+
low = self.BPLow.value()
|
|
287
|
+
high = self.BPHigh.value()
|
|
288
|
+
channel_data, _ = signal_filter(channel_data, sf=sf, btype='bandpass',
|
|
289
|
+
low=low, high=high)
|
|
290
|
+
|
|
291
|
+
# Merge 4 states' data
|
|
292
|
+
NREM_data = [channel_data[int(each[0]*sf): int(each[1]*sf)]
|
|
293
|
+
for each in sleep_state if each[2] == 1]
|
|
294
|
+
NREM_data = [element for sublist in NREM_data for element in sublist]
|
|
295
|
+
REM_data = [channel_data[int(each[0]*sf): int(each[1]*sf)]
|
|
296
|
+
for each in sleep_state if each[2] == 2]
|
|
297
|
+
REM_data = [element for sublist in REM_data for element in sublist]
|
|
298
|
+
Wake_data = [channel_data[int(each[0]*sf): int(each[1]*sf)]
|
|
299
|
+
for each in sleep_state if each[2] == 3]
|
|
300
|
+
Wake_data = [element for sublist in Wake_data for element in sublist]
|
|
301
|
+
Init_data = [channel_data[int(each[0]*sf): int(each[1]*sf)]
|
|
302
|
+
for each in sleep_state if each[2] == 4]
|
|
303
|
+
Init_data = [element for sublist in Init_data for element in sublist]
|
|
304
|
+
|
|
305
|
+
# Reject artifact if checked
|
|
306
|
+
if self.RejectArtifactCheckBox.isChecked():
|
|
307
|
+
threshold = self.ArtThresholdSpinBox.value()
|
|
308
|
+
else:
|
|
309
|
+
threshold = 1.5
|
|
310
|
+
if self.RejectArtifactCheckBox.isChecked():
|
|
311
|
+
NREM_data = reject_artifact(NREM_data, sf=sf, threshold=threshold)
|
|
312
|
+
REM_data = reject_artifact(REM_data, sf=sf, threshold=threshold)
|
|
313
|
+
Wake_data = reject_artifact(Wake_data, sf=sf, threshold=threshold)
|
|
314
|
+
Init_data = reject_artifact(Init_data, sf=sf, threshold=threshold)
|
|
315
|
+
|
|
316
|
+
nperseg = 10*sf
|
|
317
|
+
NREM_spec, NREM_figure = cal_draw_spectrum(data=NREM_data, sf=sf, nperseg=nperseg)
|
|
318
|
+
REM_spec, REM_figure = cal_draw_spectrum(data=REM_data, sf=sf, nperseg=nperseg)
|
|
319
|
+
Wake_spec, Wake_figure = cal_draw_spectrum(data=Wake_data, sf=sf, nperseg=nperseg)
|
|
320
|
+
|
|
321
|
+
name_map = {
|
|
322
|
+
1: 'NREM',
|
|
323
|
+
2: 'REM',
|
|
324
|
+
3: 'Wake',
|
|
325
|
+
4: 'Init'
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
fd = QFileDialog.getExistingDirectory(self,
|
|
329
|
+
"Select a folder to save 4 stages' data",
|
|
330
|
+
f"{config['gui']['openpath']}")
|
|
331
|
+
if fd == '':
|
|
332
|
+
return
|
|
333
|
+
|
|
334
|
+
# Save figure
|
|
335
|
+
NREM_figure.savefig(fd+'/NREM_spectrum.pdf')
|
|
336
|
+
REM_figure.savefig(fd+'/REM_spectrum.pdf')
|
|
337
|
+
Wake_figure.savefig(fd+'/Wake_spectrum.pdf')
|
|
338
|
+
writer = pd.ExcelWriter(fd+'/power_results.xlsx')
|
|
339
|
+
|
|
340
|
+
# Write to excel file
|
|
341
|
+
for idx, spec in enumerate([NREM_spec, REM_spec, Wake_spec]):
|
|
342
|
+
_df = pd.DataFrame(data=spec.T, columns=['frequency', 'power'])
|
|
343
|
+
_df.to_excel(excel_writer=writer, sheet_name=name_map[idx+1], index=False)
|
|
344
|
+
if len(Init_data) > sf*10:
|
|
345
|
+
Init_spec, Init_figure = cal_draw_spectrum(data=Init_data, sf=sf, nperseg=nperseg)
|
|
346
|
+
_df = pd.DataFrame(data=Init_spec.T, columns=['frequency', 'power'])
|
|
347
|
+
_df.to_excel(excel_writer=writer, sheet_name=name_map[4], index=False)
|
|
348
|
+
Init_figure.savefig(fd + '/Init_spectrum.pdf')
|
|
349
|
+
|
|
350
|
+
writer.close()
|
|
351
|
+
|
|
352
|
+
def okEvent(self):
|
|
353
|
+
self.closed = False
|
|
354
|
+
self.hide()
|
|
355
|
+
|
|
356
|
+
def cancelEvent(self):
|
|
357
|
+
"""Triggered by the `cancel` button"""
|
|
358
|
+
self.closed = True
|
|
359
|
+
self.hide()
|
|
360
|
+
|
|
361
|
+
def closeEvent(self, event):
|
|
362
|
+
event.ignore()
|
|
363
|
+
self.closed = True
|
|
364
|
+
self.hide()
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
@@ -26,7 +26,7 @@ from misleep.io.annotation_io import load_misleep_anno, load_bio_anno
|
|
|
26
26
|
from misleep.gui.utils import create_new_mianno
|
|
27
27
|
from misleep.utils.annotation import lst2group
|
|
28
28
|
from misleep.gui.about import about_dialog
|
|
29
|
-
from misleep.gui.dialog import label_dialog, transferResult_dialog
|
|
29
|
+
from misleep.gui.dialog import label_dialog, transferResult_dialog, stateSpectral_dialog
|
|
30
30
|
from misleep.gui.spec_window import SpecWindow
|
|
31
31
|
from misleep.gui.uis.main_window_ui import Ui_MiSleep
|
|
32
32
|
from misleep.preprocessing.spectral import spectrogram, spectrum, band_power
|
|
@@ -121,6 +121,9 @@ class main_window(QMainWindow, Ui_MiSleep):
|
|
|
121
121
|
# Initial transfer result dialog
|
|
122
122
|
self.transfer_result_dialog = transferResult_dialog()
|
|
123
123
|
|
|
124
|
+
# Initial state spectral dialog
|
|
125
|
+
self.state_spectral_dialog = stateSpectral_dialog()
|
|
126
|
+
|
|
124
127
|
# Check wheher operation done and saved or not
|
|
125
128
|
self.is_saved = True
|
|
126
129
|
|
|
@@ -1346,6 +1349,8 @@ class main_window(QMainWindow, Ui_MiSleep):
|
|
|
1346
1349
|
|
|
1347
1350
|
def tool_bar_dispatcher(self, signal):
|
|
1348
1351
|
"""Triggered by ToolBar action, transfer result"""
|
|
1352
|
+
if signal.text() == "State Spectral":
|
|
1353
|
+
self.state_spectral()
|
|
1349
1354
|
if signal.text() == "Transfer Result":
|
|
1350
1355
|
self.transfer_result()
|
|
1351
1356
|
|
|
@@ -1353,12 +1358,23 @@ class main_window(QMainWindow, Ui_MiSleep):
|
|
|
1353
1358
|
"""Transfer result into file"""
|
|
1354
1359
|
self.transfer_result_dialog.ACTimeEditor.setDateTime(self.ac_time)
|
|
1355
1360
|
self.transfer_result_dialog.TransferStartTimeEdit.setDateTime(self.ac_time)
|
|
1356
|
-
self.transfer_result_dialog.
|
|
1357
|
-
if self.
|
|
1361
|
+
self.transfer_result_dialog.exec()
|
|
1362
|
+
if self.transfer_result_dialog.closed:
|
|
1358
1363
|
return
|
|
1359
1364
|
self.transfer_result_dialog.transfer(config=self.config,
|
|
1360
1365
|
mianno=self.mianno,
|
|
1361
1366
|
ac_time=self.midata.time)
|
|
1367
|
+
|
|
1368
|
+
def state_spectral(self):
|
|
1369
|
+
"""Analyze state spectral"""
|
|
1370
|
+
self.state_spectral_dialog.dialog_show(channels=self.midata.channels)
|
|
1371
|
+
self.state_spectral_dialog.exec()
|
|
1372
|
+
if self.state_spectral_dialog.closed:
|
|
1373
|
+
return
|
|
1374
|
+
self.state_spectral_dialog.spectral_analysis(midata=self.midata,
|
|
1375
|
+
mianno=self.mianno,
|
|
1376
|
+
config=self.config)
|
|
1377
|
+
|
|
1362
1378
|
|
|
1363
1379
|
def save_anno(self):
|
|
1364
1380
|
"""Save annotation into file"""
|
|
@@ -327,8 +327,8 @@ class Ui_MiSleep(object):
|
|
|
327
327
|
self.actionSave_Annotation.setObjectName("actionSave_Annotation")
|
|
328
328
|
self.actionAbout = QtWidgets.QAction(MiSleep)
|
|
329
329
|
self.actionAbout.setObjectName("actionAbout")
|
|
330
|
-
self.
|
|
331
|
-
self.
|
|
330
|
+
self.actionState_Spectral = QtWidgets.QAction(MiSleep)
|
|
331
|
+
self.actionState_Spectral.setObjectName("actionState_Spectral")
|
|
332
332
|
self.actionTransfer_Result = QtWidgets.QAction(MiSleep)
|
|
333
333
|
self.actionTransfer_Result.setObjectName("actionTransfer_Result")
|
|
334
334
|
self.LoadBar.addAction(self.actionLoad_Data)
|
|
@@ -342,7 +342,7 @@ class Ui_MiSleep(object):
|
|
|
342
342
|
self.SaveBar.addAction(self.actionSave_Annotation)
|
|
343
343
|
self.SaveBar.addSeparator()
|
|
344
344
|
self.AboutBar.addAction(self.actionAbout)
|
|
345
|
-
self.ToolBar.addAction(self.
|
|
345
|
+
self.ToolBar.addAction(self.actionState_Spectral)
|
|
346
346
|
self.ToolBar.addSeparator()
|
|
347
347
|
self.ToolBar.addAction(self.actionTransfer_Result)
|
|
348
348
|
self.ToolBar.addSeparator()
|
|
@@ -404,7 +404,8 @@ class Ui_MiSleep(object):
|
|
|
404
404
|
self.actionSave_data.setText(_translate("MiSleep", "Save Data"))
|
|
405
405
|
self.actionSave_Annotation.setText(_translate("MiSleep", "Save Annotation"))
|
|
406
406
|
self.actionAbout.setText(_translate("MiSleep", "About"))
|
|
407
|
-
self.
|
|
407
|
+
self.actionState_Spectral.setText(_translate("MiSleep", "State Spectral"))
|
|
408
|
+
self.actionState_Spectral.setToolTip(_translate("MiSleep", "State Spectral"))
|
|
408
409
|
self.actionTransfer_Result.setText(_translate("MiSleep", "Transfer Result"))
|
|
409
410
|
self.actionTransfer_Result.setToolTip(_translate("MiSleep", "Transfer Result"))
|
|
410
411
|
from misleep.gui.resources import misleep
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Form implementation generated from reading ui file 'misleep/gui/uis/state_spectral_dialog.ui'
|
|
4
|
+
#
|
|
5
|
+
# Created by: PyQt5 UI code generator 5.15.10
|
|
6
|
+
#
|
|
7
|
+
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
|
8
|
+
# run again. Do not edit this file unless you know what you are doing.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Ui_StateSpectralDialog(object):
|
|
15
|
+
def setupUi(self, StateSpectralDialog):
|
|
16
|
+
StateSpectralDialog.setObjectName("StateSpectralDialog")
|
|
17
|
+
StateSpectralDialog.resize(261, 255)
|
|
18
|
+
self.gridLayout_2 = QtWidgets.QGridLayout(StateSpectralDialog)
|
|
19
|
+
self.gridLayout_2.setObjectName("gridLayout_2")
|
|
20
|
+
self.groupBox = QtWidgets.QGroupBox(StateSpectralDialog)
|
|
21
|
+
self.groupBox.setObjectName("groupBox")
|
|
22
|
+
self.gridLayout = QtWidgets.QGridLayout(self.groupBox)
|
|
23
|
+
self.gridLayout.setSpacing(10)
|
|
24
|
+
self.gridLayout.setObjectName("gridLayout")
|
|
25
|
+
self.label = QtWidgets.QLabel(self.groupBox)
|
|
26
|
+
self.label.setObjectName("label")
|
|
27
|
+
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
|
28
|
+
self.BPHigh = QtWidgets.QDoubleSpinBox(self.groupBox)
|
|
29
|
+
self.BPHigh.setDecimals(1)
|
|
30
|
+
self.BPHigh.setMaximum(10000.0)
|
|
31
|
+
self.BPHigh.setSingleStep(0.1)
|
|
32
|
+
self.BPHigh.setProperty("value", 30.0)
|
|
33
|
+
self.BPHigh.setObjectName("BPHigh")
|
|
34
|
+
self.gridLayout.addWidget(self.BPHigh, 5, 1, 1, 1)
|
|
35
|
+
self.BPFilterCheckBox = QtWidgets.QCheckBox(self.groupBox)
|
|
36
|
+
self.BPFilterCheckBox.setObjectName("BPFilterCheckBox")
|
|
37
|
+
self.gridLayout.addWidget(self.BPFilterCheckBox, 4, 0, 1, 1)
|
|
38
|
+
self.ChannelSelector = QtWidgets.QComboBox(self.groupBox)
|
|
39
|
+
self.ChannelSelector.setObjectName("ChannelSelector")
|
|
40
|
+
self.gridLayout.addWidget(self.ChannelSelector, 1, 0, 1, 2)
|
|
41
|
+
self.BPLow = QtWidgets.QDoubleSpinBox(self.groupBox)
|
|
42
|
+
self.BPLow.setDecimals(1)
|
|
43
|
+
self.BPLow.setMaximum(100000.0)
|
|
44
|
+
self.BPLow.setSingleStep(0.1)
|
|
45
|
+
self.BPLow.setProperty("value", 0.5)
|
|
46
|
+
self.BPLow.setObjectName("BPLow")
|
|
47
|
+
self.gridLayout.addWidget(self.BPLow, 5, 0, 1, 1)
|
|
48
|
+
self.RejectArtifactCheckBox = QtWidgets.QCheckBox(self.groupBox)
|
|
49
|
+
self.RejectArtifactCheckBox.setObjectName("RejectArtifactCheckBox")
|
|
50
|
+
self.gridLayout.addWidget(self.RejectArtifactCheckBox, 7, 0, 1, 1)
|
|
51
|
+
self.RelativeCheckBox = QtWidgets.QCheckBox(self.groupBox)
|
|
52
|
+
self.RelativeCheckBox.setObjectName("RelativeCheckBox")
|
|
53
|
+
self.gridLayout.addWidget(self.RelativeCheckBox, 6, 0, 1, 1)
|
|
54
|
+
self.MergeDataCheckBox = QtWidgets.QCheckBox(self.groupBox)
|
|
55
|
+
self.MergeDataCheckBox.setObjectName("MergeDataCheckBox")
|
|
56
|
+
self.gridLayout.addWidget(self.MergeDataCheckBox, 8, 0, 1, 1)
|
|
57
|
+
self.ArtThresholdSpinBox = QtWidgets.QDoubleSpinBox(self.groupBox)
|
|
58
|
+
self.ArtThresholdSpinBox.setDecimals(1)
|
|
59
|
+
self.ArtThresholdSpinBox.setObjectName("ArtThresholdSpinBox")
|
|
60
|
+
self.gridLayout.addWidget(self.ArtThresholdSpinBox, 7, 1, 1, 1)
|
|
61
|
+
self.gridLayout_2.addWidget(self.groupBox, 0, 0, 1, 2)
|
|
62
|
+
self.OKBt = QtWidgets.QPushButton(StateSpectralDialog)
|
|
63
|
+
self.OKBt.setObjectName("OKBt")
|
|
64
|
+
self.gridLayout_2.addWidget(self.OKBt, 1, 0, 1, 1)
|
|
65
|
+
self.CancelBt = QtWidgets.QPushButton(StateSpectralDialog)
|
|
66
|
+
self.CancelBt.setObjectName("CancelBt")
|
|
67
|
+
self.gridLayout_2.addWidget(self.CancelBt, 1, 1, 1, 1)
|
|
68
|
+
|
|
69
|
+
self.retranslateUi(StateSpectralDialog)
|
|
70
|
+
QtCore.QMetaObject.connectSlotsByName(StateSpectralDialog)
|
|
71
|
+
|
|
72
|
+
def retranslateUi(self, StateSpectralDialog):
|
|
73
|
+
_translate = QtCore.QCoreApplication.translate
|
|
74
|
+
StateSpectralDialog.setWindowTitle(_translate("StateSpectralDialog", "Dialog"))
|
|
75
|
+
self.groupBox.setTitle(_translate("StateSpectralDialog", "State spectral analysis options"))
|
|
76
|
+
self.label.setText(_translate("StateSpectralDialog", "Channel:"))
|
|
77
|
+
self.BPFilterCheckBox.setText(_translate("StateSpectralDialog", "Bandpass filter"))
|
|
78
|
+
self.RejectArtifactCheckBox.setText(_translate("StateSpectralDialog", "Reject artifact"))
|
|
79
|
+
self.RelativeCheckBox.setText(_translate("StateSpectralDialog", "Relative"))
|
|
80
|
+
self.MergeDataCheckBox.setText(_translate("StateSpectralDialog", "Merge data"))
|
|
81
|
+
self.OKBt.setText(_translate("StateSpectralDialog", "OK"))
|
|
82
|
+
self.CancelBt.setText(_translate("StateSpectralDialog", "Cancel"))
|