accusleepy 0.1.2__tar.gz → 0.4.0__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.
- {accusleepy-0.1.2 → accusleepy-0.4.0}/PKG-INFO +10 -5
- {accusleepy-0.1.2 → accusleepy-0.4.0}/README.md +8 -4
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/config.json +3 -2
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/constants.py +2 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/fileio.py +14 -6
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/main.py +54 -32
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/manual_scoring.py +3 -3
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/primary_window.py +138 -7
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/primary_window.ui +241 -119
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/text/config_guide.txt +3 -1
- accusleepy-0.1.2/accusleepy/gui/text/main_guide.txt → accusleepy-0.4.0/accusleepy/gui/text/main_guide_text.py +52 -21
- {accusleepy-0.1.2 → accusleepy-0.4.0}/pyproject.toml +2 -1
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/__init__.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/__main__.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/brain_state_set.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/classification.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/__init__.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/brightness_down.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/brightness_up.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/double_down_arrow.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/double_up_arrow.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/down_arrow.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/home.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/question.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/save.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/up_arrow.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/zoom_in.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/icons/zoom_out.png +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/mplwidget.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/resources.qrc +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/resources_rc.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/text/manual_scoring_guide.txt +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/viewer_window.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/gui/viewer_window.ui +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/models.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/multitaper.py +0 -0
- {accusleepy-0.1.2 → accusleepy-0.4.0}/accusleepy/signal_processing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: accusleepy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Python implementation of AccuSleep
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Author: Zeke Barger
|
|
@@ -20,6 +20,7 @@ Requires-Dist: pillow (>=11.1.0,<12.0.0)
|
|
|
20
20
|
Requires-Dist: pre-commit (>=4.2.0,<5.0.0)
|
|
21
21
|
Requires-Dist: pyside6 (>=6.7.1,<6.8.0)
|
|
22
22
|
Requires-Dist: scipy (>=1.15.2,<2.0.0)
|
|
23
|
+
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
23
24
|
Requires-Dist: torch (>=2.6.0,<3.0.0)
|
|
24
25
|
Requires-Dist: torchvision (>=0.21.0,<0.22.0)
|
|
25
26
|
Requires-Dist: tqdm (>=4.67.1,<5.0.0)
|
|
@@ -30,16 +31,19 @@ Description-Content-Type: text/markdown
|
|
|
30
31
|
## Description
|
|
31
32
|
|
|
32
33
|
AccuSleePy is a python implementation of AccuSleep--a set of graphical user interfaces for scoring rodent
|
|
33
|
-
sleep using EEG and EMG recordings.
|
|
34
|
+
sleep using EEG and EMG recordings. It offers several improvements over the original MATLAB version
|
|
35
|
+
and is the only version that will be actively maintained.
|
|
36
|
+
|
|
37
|
+
If you use AccuSleep in your research, please cite our
|
|
34
38
|
[publication](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0224642):
|
|
35
39
|
|
|
36
40
|
Barger, Z., Frye, C. G., Liu, D., Dan, Y., & Bouchard, K. E. (2019). Robust, automated sleep scoring by a compact neural network with distributional shift correction. *PLOS ONE, 14*(12), 1–18.
|
|
37
41
|
|
|
38
|
-
The data
|
|
42
|
+
The data and models associated with AccuSleep are available at https://osf.io/py5eb/
|
|
39
43
|
|
|
40
44
|
Please contact zekebarger (at) gmail (dot) com with any questions or comments about the software.
|
|
41
45
|
|
|
42
|
-
## Installation
|
|
46
|
+
## Installation
|
|
43
47
|
|
|
44
48
|
- (recommended) create a new virtual environment (using
|
|
45
49
|
[venv](https://docs.python.org/3/library/venv.html),
|
|
@@ -47,10 +51,11 @@ Please contact zekebarger (at) gmail (dot) com with any questions or comments ab
|
|
|
47
51
|
etc.) using python >=3.10,<3.13
|
|
48
52
|
- (optional) if you have a CUDA device and want to speed up model training, [install PyTorch](https://pytorch.org/)
|
|
49
53
|
- `pip install accusleepy`
|
|
54
|
+
- (optional) download a classification model from https://osf.io/py5eb/ under /python_format/models/
|
|
50
55
|
|
|
51
56
|
## Usage
|
|
52
57
|
|
|
53
|
-
`python -m accusleepy`
|
|
58
|
+
`python -m accusleepy` will open the primary interface.
|
|
54
59
|
|
|
55
60
|
## Acknowledgements
|
|
56
61
|
|
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
## Description
|
|
4
4
|
|
|
5
5
|
AccuSleePy is a python implementation of AccuSleep--a set of graphical user interfaces for scoring rodent
|
|
6
|
-
sleep using EEG and EMG recordings.
|
|
6
|
+
sleep using EEG and EMG recordings. It offers several improvements over the original MATLAB version
|
|
7
|
+
and is the only version that will be actively maintained.
|
|
8
|
+
|
|
9
|
+
If you use AccuSleep in your research, please cite our
|
|
7
10
|
[publication](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0224642):
|
|
8
11
|
|
|
9
12
|
Barger, Z., Frye, C. G., Liu, D., Dan, Y., & Bouchard, K. E. (2019). Robust, automated sleep scoring by a compact neural network with distributional shift correction. *PLOS ONE, 14*(12), 1–18.
|
|
10
13
|
|
|
11
|
-
The data
|
|
14
|
+
The data and models associated with AccuSleep are available at https://osf.io/py5eb/
|
|
12
15
|
|
|
13
16
|
Please contact zekebarger (at) gmail (dot) com with any questions or comments about the software.
|
|
14
17
|
|
|
15
|
-
## Installation
|
|
18
|
+
## Installation
|
|
16
19
|
|
|
17
20
|
- (recommended) create a new virtual environment (using
|
|
18
21
|
[venv](https://docs.python.org/3/library/venv.html),
|
|
@@ -20,10 +23,11 @@ Please contact zekebarger (at) gmail (dot) com with any questions or comments ab
|
|
|
20
23
|
etc.) using python >=3.10,<3.13
|
|
21
24
|
- (optional) if you have a CUDA device and want to speed up model training, [install PyTorch](https://pytorch.org/)
|
|
22
25
|
- `pip install accusleepy`
|
|
26
|
+
- (optional) download a classification model from https://osf.io/py5eb/ under /python_format/models/
|
|
23
27
|
|
|
24
28
|
## Usage
|
|
25
29
|
|
|
26
|
-
`python -m accusleepy`
|
|
30
|
+
`python -m accusleepy` will open the primary interface.
|
|
27
31
|
|
|
28
32
|
## Acknowledgements
|
|
29
33
|
|
|
@@ -11,6 +11,7 @@ from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainState, BrainStateS
|
|
|
11
11
|
from accusleepy.constants import (
|
|
12
12
|
BRAIN_STATE_COL,
|
|
13
13
|
CONFIG_FILE,
|
|
14
|
+
DEFAULT_EPOCH_LENGTH_KEY,
|
|
14
15
|
EEG_COL,
|
|
15
16
|
EMG_COL,
|
|
16
17
|
MIXTURE_MEAN_COL,
|
|
@@ -82,7 +83,9 @@ def load_model(filename: str) -> tuple[SSANN, int | float, int, str, dict]:
|
|
|
82
83
|
(default or real-time), set of brain state options
|
|
83
84
|
used when training the model
|
|
84
85
|
"""
|
|
85
|
-
state_dict = torch.load(
|
|
86
|
+
state_dict = torch.load(
|
|
87
|
+
filename, weights_only=True, map_location=torch.device("cpu")
|
|
88
|
+
)
|
|
86
89
|
epoch_length = state_dict.pop("epoch_length")
|
|
87
90
|
epochs_per_img = state_dict.pop("epochs_per_img")
|
|
88
91
|
model_type = state_dict.pop("model_type")
|
|
@@ -141,10 +144,10 @@ def save_labels(labels: np.array, filename: str) -> None:
|
|
|
141
144
|
pd.DataFrame({BRAIN_STATE_COL: labels}).to_csv(filename, index=False)
|
|
142
145
|
|
|
143
146
|
|
|
144
|
-
def load_config() -> BrainStateSet:
|
|
147
|
+
def load_config() -> tuple[BrainStateSet, int | float]:
|
|
145
148
|
"""Load configuration file with brain state options
|
|
146
149
|
|
|
147
|
-
:return: set of brain state options
|
|
150
|
+
:return: set of brain state options and default epoch length
|
|
148
151
|
"""
|
|
149
152
|
with open(
|
|
150
153
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), CONFIG_FILE), "r"
|
|
@@ -152,18 +155,23 @@ def load_config() -> BrainStateSet:
|
|
|
152
155
|
data = json.load(f)
|
|
153
156
|
return BrainStateSet(
|
|
154
157
|
[BrainState(**b) for b in data[BRAIN_STATES_KEY]], UNDEFINED_LABEL
|
|
155
|
-
)
|
|
158
|
+
), data[DEFAULT_EPOCH_LENGTH_KEY]
|
|
156
159
|
|
|
157
160
|
|
|
158
|
-
def save_config(
|
|
161
|
+
def save_config(
|
|
162
|
+
brain_state_set: BrainStateSet, default_epoch_length: int | float
|
|
163
|
+
) -> None:
|
|
159
164
|
"""Save configuration of brain state options to json file
|
|
160
165
|
|
|
161
166
|
:param brain_state_set: set of brain state options
|
|
167
|
+
:param default_epoch_length: epoch length to use when the GUI starts
|
|
162
168
|
"""
|
|
169
|
+
output_dict = brain_state_set.to_output_dict()
|
|
170
|
+
output_dict.update({DEFAULT_EPOCH_LENGTH_KEY: default_epoch_length})
|
|
163
171
|
with open(
|
|
164
172
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), CONFIG_FILE), "w"
|
|
165
173
|
) as f:
|
|
166
|
-
json.dump(
|
|
174
|
+
json.dump(output_dict, f, indent=4)
|
|
167
175
|
|
|
168
176
|
|
|
169
177
|
def load_recording_list(filename: str) -> list[Recording]:
|
|
@@ -5,6 +5,7 @@ import datetime
|
|
|
5
5
|
import os
|
|
6
6
|
import shutil
|
|
7
7
|
import sys
|
|
8
|
+
import toml
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from functools import partial
|
|
10
11
|
|
|
@@ -40,6 +41,7 @@ from accusleepy.fileio import (
|
|
|
40
41
|
save_model,
|
|
41
42
|
save_recording_list,
|
|
42
43
|
)
|
|
44
|
+
from accusleepy.gui.text.main_guide_text import MAIN_GUIDE_TEXT
|
|
43
45
|
from accusleepy.gui.manual_scoring import ManualScoringWindow
|
|
44
46
|
from accusleepy.gui.primary_window import Ui_PrimaryWindow
|
|
45
47
|
from accusleepy.signal_processing import (
|
|
@@ -52,9 +54,8 @@ from accusleepy.signal_processing import (
|
|
|
52
54
|
# max number of messages to display
|
|
53
55
|
MESSAGE_BOX_MAX_DEPTH = 50
|
|
54
56
|
LABEL_LENGTH_ERROR = "label file length does not match recording length"
|
|
55
|
-
# relative path to
|
|
56
|
-
|
|
57
|
-
CONFIG_GUIDE_FILE = "text/config_guide.txt"
|
|
57
|
+
# relative path to config guide txt file
|
|
58
|
+
CONFIG_GUIDE_FILE = os.path.normpath(r"text/config_guide.txt")
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
@dataclass
|
|
@@ -80,12 +81,12 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
80
81
|
self.setWindowTitle("AccuSleePy")
|
|
81
82
|
|
|
82
83
|
# fill in settings tab
|
|
83
|
-
self.brain_state_set = load_config()
|
|
84
|
+
self.brain_state_set, self.epoch_length = load_config()
|
|
84
85
|
self.settings_widgets = None
|
|
85
86
|
self.initialize_settings_tab()
|
|
86
87
|
|
|
87
88
|
# initialize info about the recordings, classification data / settings
|
|
88
|
-
self.epoch_length
|
|
89
|
+
self.ui.epoch_length_input.setValue(self.epoch_length)
|
|
89
90
|
self.model = None
|
|
90
91
|
self.only_overwrite_undefined = False
|
|
91
92
|
self.min_bout_length = 5
|
|
@@ -116,6 +117,20 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
116
117
|
# messages to display
|
|
117
118
|
self.messages = []
|
|
118
119
|
|
|
120
|
+
# display current version
|
|
121
|
+
version = ""
|
|
122
|
+
toml_file = os.path.join(
|
|
123
|
+
os.path.dirname(
|
|
124
|
+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
125
|
+
),
|
|
126
|
+
"pyproject.toml",
|
|
127
|
+
)
|
|
128
|
+
if os.path.isfile(toml_file):
|
|
129
|
+
toml_data = toml.load(toml_file)
|
|
130
|
+
if "project" in toml_data and "version" in toml_data["project"]:
|
|
131
|
+
version = toml_data["project"]["version"]
|
|
132
|
+
self.ui.version_label.setText(f"v{version}")
|
|
133
|
+
|
|
119
134
|
# user input: keyboard shortcuts
|
|
120
135
|
keypress_quit = QtGui.QShortcut(
|
|
121
136
|
QtGui.QKeySequence(
|
|
@@ -179,6 +194,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
179
194
|
)
|
|
180
195
|
if not filename:
|
|
181
196
|
return
|
|
197
|
+
filename = os.path.normpath(filename)
|
|
182
198
|
save_recording_list(filename=filename, recordings=self.recordings)
|
|
183
199
|
self.show_message(f"Saved list of recordings to {filename}")
|
|
184
200
|
|
|
@@ -193,6 +209,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
193
209
|
if file_dialog.exec():
|
|
194
210
|
selected_files = file_dialog.selectedFiles()
|
|
195
211
|
filename = selected_files[0]
|
|
212
|
+
filename = os.path.normpath(filename)
|
|
196
213
|
else:
|
|
197
214
|
return
|
|
198
215
|
|
|
@@ -229,7 +246,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
229
246
|
if event.type() == QtCore.QEvent.Drop:
|
|
230
247
|
urls = event.mimeData().urls()
|
|
231
248
|
if len(urls) == 1:
|
|
232
|
-
filename = urls[0].toLocalFile()
|
|
249
|
+
filename = os.path.normpath(urls[0].toLocalFile())
|
|
233
250
|
|
|
234
251
|
if filename is None:
|
|
235
252
|
return super().eventFilter(obj, event)
|
|
@@ -289,24 +306,31 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
289
306
|
)
|
|
290
307
|
if not model_filename:
|
|
291
308
|
self.show_message("Model training canceled, no filename given")
|
|
309
|
+
return
|
|
310
|
+
model_filename = os.path.normpath(model_filename)
|
|
311
|
+
|
|
312
|
+
# create (probably temporary) image folder
|
|
313
|
+
temp_image_dir = os.path.join(
|
|
314
|
+
self.training_image_dir,
|
|
315
|
+
"images_" + datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
|
316
|
+
)
|
|
292
317
|
|
|
293
|
-
#
|
|
294
|
-
if os.path.exists(self.training_image_dir):
|
|
318
|
+
if os.path.exists(temp_image_dir): # unlikely
|
|
295
319
|
self.show_message(
|
|
296
320
|
"Warning: training image folder exists, will be overwritten"
|
|
297
321
|
)
|
|
298
|
-
os.makedirs(
|
|
322
|
+
os.makedirs(temp_image_dir, exist_ok=True)
|
|
299
323
|
|
|
300
324
|
# create training images
|
|
301
325
|
self.show_message(
|
|
302
|
-
(f"Creating training images in {
|
|
326
|
+
(f"Creating training images in {temp_image_dir}, please wait...")
|
|
303
327
|
)
|
|
304
328
|
self.ui.message_area.repaint()
|
|
305
329
|
QtWidgets.QApplication.processEvents()
|
|
306
330
|
print("Creating training images")
|
|
307
331
|
failed_recordings = create_training_images(
|
|
308
332
|
recordings=self.recordings,
|
|
309
|
-
output_path=
|
|
333
|
+
output_path=temp_image_dir,
|
|
310
334
|
epoch_length=self.epoch_length,
|
|
311
335
|
epochs_per_img=self.training_epochs_per_img,
|
|
312
336
|
brain_state_set=self.brain_state_set,
|
|
@@ -330,10 +354,8 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
330
354
|
QtWidgets.QApplication.processEvents()
|
|
331
355
|
print("Training model")
|
|
332
356
|
model = train_model(
|
|
333
|
-
annotations_file=os.path.join(
|
|
334
|
-
|
|
335
|
-
),
|
|
336
|
-
img_dir=self.training_image_dir,
|
|
357
|
+
annotations_file=os.path.join(temp_image_dir, ANNOTATIONS_FILENAME),
|
|
358
|
+
img_dir=temp_image_dir,
|
|
337
359
|
mixture_weights=self.brain_state_set.mixture_weights,
|
|
338
360
|
n_classes=self.brain_state_set.n_classes,
|
|
339
361
|
)
|
|
@@ -350,20 +372,19 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
350
372
|
|
|
351
373
|
# optionally delete images
|
|
352
374
|
if self.delete_training_images:
|
|
353
|
-
shutil.rmtree(
|
|
375
|
+
shutil.rmtree(temp_image_dir)
|
|
354
376
|
|
|
355
377
|
self.show_message(f"Training complete, saved model to {model_filename}")
|
|
356
378
|
|
|
357
|
-
def set_training_folder(self):
|
|
379
|
+
def set_training_folder(self) -> None:
|
|
380
|
+
"""Select location in which to create a folder for training images"""
|
|
358
381
|
training_folder_parent = QtWidgets.QFileDialog.getExistingDirectory(
|
|
359
382
|
self, "Select directory for training images"
|
|
360
383
|
)
|
|
361
384
|
if training_folder_parent:
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
)
|
|
366
|
-
self.ui.image_folder_label.setText(self.training_image_dir)
|
|
385
|
+
training_folder_parent = os.path.normpath(training_folder_parent)
|
|
386
|
+
self.training_image_dir = training_folder_parent
|
|
387
|
+
self.ui.image_folder_label.setText(training_folder_parent)
|
|
367
388
|
|
|
368
389
|
def update_image_deletion(self) -> None:
|
|
369
390
|
"""Update choice of whether to delete images after training"""
|
|
@@ -559,6 +580,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
559
580
|
if file_dialog.exec():
|
|
560
581
|
selected_files = file_dialog.selectedFiles()
|
|
561
582
|
filename = selected_files[0]
|
|
583
|
+
filename = os.path.normpath(filename)
|
|
562
584
|
else:
|
|
563
585
|
return
|
|
564
586
|
|
|
@@ -706,6 +728,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
706
728
|
)
|
|
707
729
|
if not filename:
|
|
708
730
|
return
|
|
731
|
+
filename = os.path.normpath(filename)
|
|
709
732
|
|
|
710
733
|
create_calibration_file(
|
|
711
734
|
filename=filename,
|
|
@@ -870,6 +893,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
870
893
|
filter="*" + LABEL_FILE_TYPE,
|
|
871
894
|
)
|
|
872
895
|
if filename:
|
|
896
|
+
filename = os.path.normpath(filename)
|
|
873
897
|
self.recordings[self.recording_index].label_file = filename
|
|
874
898
|
self.ui.label_file_label.setText(filename)
|
|
875
899
|
|
|
@@ -884,6 +908,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
884
908
|
if file_dialog.exec():
|
|
885
909
|
selected_files = file_dialog.selectedFiles()
|
|
886
910
|
filename = selected_files[0]
|
|
911
|
+
filename = os.path.normpath(filename)
|
|
887
912
|
self.recordings[self.recording_index].label_file = filename
|
|
888
913
|
self.ui.label_file_label.setText(filename)
|
|
889
914
|
|
|
@@ -898,6 +923,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
898
923
|
if file_dialog.exec():
|
|
899
924
|
selected_files = file_dialog.selectedFiles()
|
|
900
925
|
filename = selected_files[0]
|
|
926
|
+
filename = os.path.normpath(filename)
|
|
901
927
|
self.recordings[self.recording_index].calibration_file = filename
|
|
902
928
|
self.ui.calibration_file_label.setText(filename)
|
|
903
929
|
|
|
@@ -912,6 +938,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
912
938
|
if file_dialog.exec():
|
|
913
939
|
selected_files = file_dialog.selectedFiles()
|
|
914
940
|
filename = selected_files[0]
|
|
941
|
+
filename = os.path.normpath(filename)
|
|
915
942
|
self.recordings[self.recording_index].recording_file = filename
|
|
916
943
|
self.ui.recording_file_label.setText(filename)
|
|
917
944
|
|
|
@@ -1004,15 +1031,8 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
1004
1031
|
|
|
1005
1032
|
def show_user_manual(self) -> None:
|
|
1006
1033
|
"""Show a popup window with the user manual"""
|
|
1007
|
-
user_manual_file = open(
|
|
1008
|
-
os.path.join(os.path.dirname(os.path.abspath(__file__)), USER_MANUAL_FILE),
|
|
1009
|
-
"r",
|
|
1010
|
-
)
|
|
1011
|
-
user_manual_text = user_manual_file.read()
|
|
1012
|
-
user_manual_file.close()
|
|
1013
|
-
|
|
1014
1034
|
label_widget = QtWidgets.QLabel()
|
|
1015
|
-
label_widget.setText(
|
|
1035
|
+
label_widget.setText(MAIN_GUIDE_TEXT)
|
|
1016
1036
|
scroll_area = QtWidgets.QScrollArea()
|
|
1017
1037
|
scroll_area.setStyleSheet("background-color: white;")
|
|
1018
1038
|
scroll_area.setWidget(label_widget)
|
|
@@ -1110,6 +1130,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
1110
1130
|
}
|
|
1111
1131
|
|
|
1112
1132
|
# update widget state to display current config
|
|
1133
|
+
self.ui.default_epoch_input.setValue(self.epoch_length)
|
|
1113
1134
|
states = {b.digit: b for b in self.brain_state_set.brain_states}
|
|
1114
1135
|
for digit in range(10):
|
|
1115
1136
|
if digit in states.keys():
|
|
@@ -1247,7 +1268,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
1247
1268
|
self.brain_state_set = BrainStateSet(brain_states, UNDEFINED_LABEL)
|
|
1248
1269
|
|
|
1249
1270
|
# save to file
|
|
1250
|
-
save_config(self.brain_state_set)
|
|
1271
|
+
save_config(self.brain_state_set, self.ui.default_epoch_input.value())
|
|
1251
1272
|
self.ui.save_config_status.setText("configuration saved")
|
|
1252
1273
|
|
|
1253
1274
|
|
|
@@ -1257,7 +1278,7 @@ def check_label_validity(
|
|
|
1257
1278
|
sampling_rate: int | float,
|
|
1258
1279
|
epoch_length: int | float,
|
|
1259
1280
|
brain_state_set: BrainStateSet,
|
|
1260
|
-
) -> str:
|
|
1281
|
+
) -> str | None:
|
|
1261
1282
|
"""Check whether a set of brain state labels is valid
|
|
1262
1283
|
|
|
1263
1284
|
This returns an error message if a problem is found with the
|
|
@@ -1357,6 +1378,7 @@ def check_config_consistency(
|
|
|
1357
1378
|
output.append(
|
|
1358
1379
|
(
|
|
1359
1380
|
"Warning: the epoch length used when training this model "
|
|
1381
|
+
f"({model_epoch_length} seconds) "
|
|
1360
1382
|
"does not match the current epoch length setting."
|
|
1361
1383
|
)
|
|
1362
1384
|
)
|
|
@@ -27,7 +27,7 @@ LABEL_CMAP = np.concatenate(
|
|
|
27
27
|
[np.array([[0, 0, 0, 0]]), plt.colormaps["tab10"](range(10))], axis=0
|
|
28
28
|
)
|
|
29
29
|
# relative path to user manual text file
|
|
30
|
-
USER_MANUAL_FILE = "text/manual_scoring_guide.txt"
|
|
30
|
+
USER_MANUAL_FILE = os.path.normpath(r"text/manual_scoring_guide.txt")
|
|
31
31
|
|
|
32
32
|
# constants used by callback functions
|
|
33
33
|
# label formats
|
|
@@ -111,7 +111,7 @@ class ManualScoringWindow(QtWidgets.QDialog):
|
|
|
111
111
|
self.setWindowTitle("AccuSleePy manual scoring window")
|
|
112
112
|
|
|
113
113
|
# load set of valid brain states
|
|
114
|
-
self.brain_state_set = load_config()
|
|
114
|
+
self.brain_state_set, _ = load_config()
|
|
115
115
|
|
|
116
116
|
# initial setting for number of epochs to show in the lower plot
|
|
117
117
|
self.epochs_to_show = 5
|
|
@@ -833,7 +833,7 @@ class ManualScoringWindow(QtWidgets.QDialog):
|
|
|
833
833
|
self.adjust_upper_figure_x_limits()
|
|
834
834
|
|
|
835
835
|
# update parts of lower plot
|
|
836
|
-
old_window_center = round(self.epochs_to_show / 2) + self.lower_left_epoch
|
|
836
|
+
old_window_center = round((self.epochs_to_show - 1) / 2) + self.lower_left_epoch
|
|
837
837
|
# change the window bounds if needed
|
|
838
838
|
if self.epoch < old_window_center and self.lower_left_epoch > 0:
|
|
839
839
|
self.lower_left_epoch -= 1
|