accusleepy 0.3.1__py3-none-any.whl → 0.4.3__py3-none-any.whl

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.
Binary file
Binary file
accusleepy/gui/main.py CHANGED
@@ -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,7 +41,6 @@ from accusleepy.fileio import (
40
41
  save_model,
41
42
  save_recording_list,
42
43
  )
43
- from accusleepy.gui.text.main_guide_text import MAIN_GUIDE_TEXT
44
44
  from accusleepy.gui.manual_scoring import ManualScoringWindow
45
45
  from accusleepy.gui.primary_window import Ui_PrimaryWindow
46
46
  from accusleepy.signal_processing import (
@@ -54,7 +54,8 @@ from accusleepy.signal_processing import (
54
54
  MESSAGE_BOX_MAX_DEPTH = 50
55
55
  LABEL_LENGTH_ERROR = "label file length does not match recording length"
56
56
  # relative path to config guide txt file
57
- CONFIG_GUIDE_FILE = "text/config_guide.txt"
57
+ CONFIG_GUIDE_FILE = os.path.normpath(r"text/config_guide.txt")
58
+ MAIN_GUIDE_FILE = os.path.normpath(r"text/main_guide.md")
58
59
 
59
60
 
60
61
  @dataclass
@@ -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,6 +306,8 @@ 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)
292
311
 
293
312
  # create (probably temporary) image folder
294
313
  temp_image_dir = os.path.join(
@@ -363,6 +382,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
363
382
  self, "Select directory for training images"
364
383
  )
365
384
  if training_folder_parent:
385
+ training_folder_parent = os.path.normpath(training_folder_parent)
366
386
  self.training_image_dir = training_folder_parent
367
387
  self.ui.image_folder_label.setText(training_folder_parent)
368
388
 
@@ -560,6 +580,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
560
580
  if file_dialog.exec():
561
581
  selected_files = file_dialog.selectedFiles()
562
582
  filename = selected_files[0]
583
+ filename = os.path.normpath(filename)
563
584
  else:
564
585
  return
565
586
 
@@ -707,6 +728,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
707
728
  )
708
729
  if not filename:
709
730
  return
731
+ filename = os.path.normpath(filename)
710
732
 
711
733
  create_calibration_file(
712
734
  filename=filename,
@@ -871,6 +893,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
871
893
  filter="*" + LABEL_FILE_TYPE,
872
894
  )
873
895
  if filename:
896
+ filename = os.path.normpath(filename)
874
897
  self.recordings[self.recording_index].label_file = filename
875
898
  self.ui.label_file_label.setText(filename)
876
899
 
@@ -885,6 +908,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
885
908
  if file_dialog.exec():
886
909
  selected_files = file_dialog.selectedFiles()
887
910
  filename = selected_files[0]
911
+ filename = os.path.normpath(filename)
888
912
  self.recordings[self.recording_index].label_file = filename
889
913
  self.ui.label_file_label.setText(filename)
890
914
 
@@ -899,6 +923,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
899
923
  if file_dialog.exec():
900
924
  selected_files = file_dialog.selectedFiles()
901
925
  filename = selected_files[0]
926
+ filename = os.path.normpath(filename)
902
927
  self.recordings[self.recording_index].calibration_file = filename
903
928
  self.ui.calibration_file_label.setText(filename)
904
929
 
@@ -913,6 +938,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
913
938
  if file_dialog.exec():
914
939
  selected_files = file_dialog.selectedFiles()
915
940
  filename = selected_files[0]
941
+ filename = os.path.normpath(filename)
916
942
  self.recordings[self.recording_index].recording_file = filename
917
943
  self.ui.recording_file_label.setText(filename)
918
944
 
@@ -1005,15 +1031,15 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
1005
1031
 
1006
1032
  def show_user_manual(self) -> None:
1007
1033
  """Show a popup window with the user manual"""
1008
- label_widget = QtWidgets.QLabel()
1009
- label_widget.setText(MAIN_GUIDE_TEXT)
1010
- scroll_area = QtWidgets.QScrollArea()
1011
- scroll_area.setStyleSheet("background-color: white;")
1012
- scroll_area.setWidget(label_widget)
1013
- grid = QtWidgets.QGridLayout()
1014
- grid.addWidget(scroll_area)
1015
1034
  self.popup = QtWidgets.QWidget()
1016
- self.popup.setLayout(grid)
1035
+ self.popup_vlayout = QtWidgets.QVBoxLayout(self.popup)
1036
+ self.guide_textbox = QtWidgets.QTextBrowser(self.popup)
1037
+ self.popup_vlayout.addWidget(self.guide_textbox)
1038
+
1039
+ url = QtCore.QUrl.fromLocalFile(MAIN_GUIDE_FILE)
1040
+ self.guide_textbox.setSource(url)
1041
+ self.guide_textbox.setOpenLinks(False)
1042
+
1017
1043
  self.popup.setGeometry(QtCore.QRect(100, 100, 600, 600))
1018
1044
  self.popup.show()
1019
1045
 
@@ -1252,7 +1278,7 @@ def check_label_validity(
1252
1278
  sampling_rate: int | float,
1253
1279
  epoch_length: int | float,
1254
1280
  brain_state_set: BrainStateSet,
1255
- ) -> str:
1281
+ ) -> str | None:
1256
1282
  """Check whether a set of brain state labels is valid
1257
1283
 
1258
1284
  This returns an error message if a problem is found with the
@@ -1352,6 +1378,7 @@ def check_config_consistency(
1352
1378
  output.append(
1353
1379
  (
1354
1380
  "Warning: the epoch length used when training this model "
1381
+ f"({model_epoch_length} seconds) "
1355
1382
  "does not match the current epoch length setting."
1356
1383
  )
1357
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.md")
31
31
 
32
32
  # constants used by callback functions
33
33
  # label formats
@@ -61,7 +61,7 @@ UNDEFINED_STATE = "undefined"
61
61
  # before starting to scroll again - must be in (0, 0.5)
62
62
  SCROLL_BOUNDARY = 0.35
63
63
  # max number of sequential undo actions allowed
64
- UNDO_LIMIT = 100
64
+ UNDO_LIMIT = 1000
65
65
 
66
66
 
67
67
  @dataclass
@@ -501,21 +501,18 @@ class ManualScoringWindow(QtWidgets.QDialog):
501
501
 
502
502
  def show_user_manual(self) -> None:
503
503
  """Show a popup window with the user manual"""
504
- user_manual_file = open(
505
- os.path.join(os.path.dirname(os.path.abspath(__file__)), USER_MANUAL_FILE),
506
- "r",
504
+ self.popup = QtWidgets.QWidget()
505
+ self.popup_vlayout = QtWidgets.QVBoxLayout(self.popup)
506
+ self.guide_textbox = QtWidgets.QTextBrowser(self.popup)
507
+ self.popup_vlayout.addWidget(self.guide_textbox)
508
+
509
+ url = QtCore.QUrl.fromLocalFile(
510
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), USER_MANUAL_FILE)
507
511
  )
508
- user_manual_text = user_manual_file.read()
509
- user_manual_file.close()
510
- label_widget = QtWidgets.QLabel()
511
- label_widget.setText(user_manual_text)
512
- label_widget.setStyleSheet("background-color: white;")
512
+ self.guide_textbox.setSource(url)
513
+ self.guide_textbox.setOpenLinks(False)
513
514
 
514
- self.popup = QtWidgets.QWidget()
515
- grid = QtWidgets.QGridLayout()
516
- grid.addWidget(label_widget)
517
- self.popup.setLayout(grid)
518
- self.popup.setGeometry(QtCore.QRect(50, 100, 350, 400))
515
+ self.popup.setGeometry(QtCore.QRect(100, 100, 830, 600))
519
516
  self.popup.show()
520
517
 
521
518
  def jump_to_next_state(self, direction: str, target: str) -> None:
@@ -8,20 +8,8 @@
8
8
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
9
9
  ################################################################################
10
10
 
11
- from PySide6.QtCore import (
12
- QCoreApplication,
13
- QMetaObject,
14
- QRect,
15
- QSize,
16
- Qt,
17
- )
18
- from PySide6.QtGui import (
19
- QBrush,
20
- QColor,
21
- QFont,
22
- QIcon,
23
- QPalette,
24
- )
11
+ from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt
12
+ from PySide6.QtGui import QBrush, QColor, QFont, QIcon, QPalette
25
13
  from PySide6.QtWidgets import (
26
14
  QCheckBox,
27
15
  QDoubleSpinBox,
@@ -202,6 +190,8 @@ class Ui_PrimaryWindow(object):
202
190
 
203
191
  self.left_scoring_vlayout.addWidget(self.recordinglistgroupbox)
204
192
 
193
+ self.logo_and_version_layout = QVBoxLayout()
194
+ self.logo_and_version_layout.setObjectName("logo_and_version_layout")
205
195
  self.logo_layout = QVBoxLayout()
206
196
  self.logo_layout.setObjectName("logo_layout")
207
197
  self.frame = QFrame(self.scoring_tab)
@@ -252,7 +242,24 @@ class Ui_PrimaryWindow(object):
252
242
 
253
243
  self.logo_layout.addWidget(self.frame)
254
244
 
255
- self.left_scoring_vlayout.addLayout(self.logo_layout)
245
+ self.logo_and_version_layout.addLayout(self.logo_layout)
246
+
247
+ self.version_label = QLabel(self.scoring_tab)
248
+ self.version_label.setObjectName("version_label")
249
+ sizePolicy3 = QSizePolicy(
250
+ QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed
251
+ )
252
+ sizePolicy3.setHorizontalStretch(0)
253
+ sizePolicy3.setVerticalStretch(0)
254
+ sizePolicy3.setHeightForWidth(
255
+ self.version_label.sizePolicy().hasHeightForWidth()
256
+ )
257
+ self.version_label.setSizePolicy(sizePolicy3)
258
+ self.version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
259
+
260
+ self.logo_and_version_layout.addWidget(self.version_label)
261
+
262
+ self.left_scoring_vlayout.addLayout(self.logo_and_version_layout)
256
263
 
257
264
  self.user_manual_layout = QHBoxLayout()
258
265
  self.user_manual_layout.setObjectName("user_manual_layout")
@@ -340,11 +347,6 @@ class Ui_PrimaryWindow(object):
340
347
  self.horizontalLayout.setObjectName("horizontalLayout")
341
348
  self.recording_file_button = QPushButton(self.selected_recording_groupbox)
342
349
  self.recording_file_button.setObjectName("recording_file_button")
343
- sizePolicy3 = QSizePolicy(
344
- QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed
345
- )
346
- sizePolicy3.setHorizontalStretch(0)
347
- sizePolicy3.setVerticalStretch(0)
348
350
  sizePolicy3.setHeightForWidth(
349
351
  self.recording_file_button.sizePolicy().hasHeightForWidth()
350
352
  )
@@ -2217,9 +2219,23 @@ class Ui_PrimaryWindow(object):
2217
2219
  self.remove_button.setText(
2218
2220
  QCoreApplication.translate("PrimaryWindow", "remove", None)
2219
2221
  )
2222
+ # if QT_CONFIG(tooltip)
2223
+ self.export_button.setToolTip(
2224
+ QCoreApplication.translate(
2225
+ "PrimaryWindow", "Export recording list to file", None
2226
+ )
2227
+ )
2228
+ # endif // QT_CONFIG(tooltip)
2220
2229
  self.export_button.setText(
2221
2230
  QCoreApplication.translate("PrimaryWindow", "export", None)
2222
2231
  )
2232
+ # if QT_CONFIG(tooltip)
2233
+ self.import_button.setToolTip(
2234
+ QCoreApplication.translate(
2235
+ "PrimaryWindow", "Import recording list from file", None
2236
+ )
2237
+ )
2238
+ # endif // QT_CONFIG(tooltip)
2223
2239
  self.import_button.setText(
2224
2240
  QCoreApplication.translate("PrimaryWindow", "import", None)
2225
2241
  )
@@ -2232,6 +2248,9 @@ class Ui_PrimaryWindow(object):
2232
2248
  self.accusleepy1.setText(
2233
2249
  QCoreApplication.translate("PrimaryWindow", "AccuSleePy", None)
2234
2250
  )
2251
+ self.version_label.setText(
2252
+ QCoreApplication.translate("PrimaryWindow", "TextLabel", None)
2253
+ )
2235
2254
  # if QT_CONFIG(tooltip)
2236
2255
  self.user_manual_button.setToolTip(
2237
2256
  QCoreApplication.translate("PrimaryWindow", "User manual", None)
@@ -2246,6 +2265,13 @@ class Ui_PrimaryWindow(object):
2246
2265
  self.samplingratelabel.setText(
2247
2266
  QCoreApplication.translate("PrimaryWindow", "Sampling rate (Hz):", None)
2248
2267
  )
2268
+ # if QT_CONFIG(tooltip)
2269
+ self.recording_file_button.setToolTip(
2270
+ QCoreApplication.translate(
2271
+ "PrimaryWindow", "Select EEG+EMG recording", None
2272
+ )
2273
+ )
2274
+ # endif // QT_CONFIG(tooltip)
2249
2275
  self.recording_file_button.setText(
2250
2276
  QCoreApplication.translate("PrimaryWindow", "Select recording file", None)
2251
2277
  )
@@ -2288,10 +2314,24 @@ class Ui_PrimaryWindow(object):
2288
2314
  QCoreApplication.translate("PrimaryWindow", "Score manually", None)
2289
2315
  )
2290
2316
  self.manual_scoring_status.setText("")
2317
+ # if QT_CONFIG(tooltip)
2318
+ self.create_calibration_button.setToolTip(
2319
+ QCoreApplication.translate(
2320
+ "PrimaryWindow", "Create calibration file for this subject", None
2321
+ )
2322
+ )
2323
+ # endif // QT_CONFIG(tooltip)
2291
2324
  self.create_calibration_button.setText(
2292
2325
  QCoreApplication.translate("PrimaryWindow", "Create calibration file", None)
2293
2326
  )
2294
2327
  self.calibration_status.setText("")
2328
+ # if QT_CONFIG(tooltip)
2329
+ self.select_calibration_button.setToolTip(
2330
+ QCoreApplication.translate(
2331
+ "PrimaryWindow", "Load calibration file for this recording", None
2332
+ )
2333
+ )
2334
+ # endif // QT_CONFIG(tooltip)
2295
2335
  self.select_calibration_button.setText(
2296
2336
  QCoreApplication.translate("PrimaryWindow", "Select calibration file", None)
2297
2337
  )
@@ -2319,6 +2359,13 @@ class Ui_PrimaryWindow(object):
2319
2359
  "PrimaryWindow", "Minimum bout length (sec):", None
2320
2360
  )
2321
2361
  )
2362
+ # if QT_CONFIG(tooltip)
2363
+ self.load_model_button.setToolTip(
2364
+ QCoreApplication.translate(
2365
+ "PrimaryWindow", "Load a trained sleep scoring classifier", None
2366
+ )
2367
+ )
2368
+ # endif // QT_CONFIG(tooltip)
2322
2369
  self.load_model_button.setText(
2323
2370
  QCoreApplication.translate(
2324
2371
  "PrimaryWindow", "Load classification model", None
@@ -2346,6 +2393,13 @@ class Ui_PrimaryWindow(object):
2346
2393
  self.real_time_button.setText(
2347
2394
  QCoreApplication.translate("PrimaryWindow", "Real-time", None)
2348
2395
  )
2396
+ # if QT_CONFIG(tooltip)
2397
+ self.train_model_button.setToolTip(
2398
+ QCoreApplication.translate(
2399
+ "PrimaryWindow", "Begin training the classification model", None
2400
+ )
2401
+ )
2402
+ # endif // QT_CONFIG(tooltip)
2349
2403
  self.train_model_button.setText(
2350
2404
  QCoreApplication.translate(
2351
2405
  "PrimaryWindow", "Train classification model", None
@@ -2422,6 +2476,13 @@ class Ui_PrimaryWindow(object):
2422
2476
  self.label_17.setText(
2423
2477
  QCoreApplication.translate("PrimaryWindow", "Default epoch length:", None)
2424
2478
  )
2479
+ # if QT_CONFIG(tooltip)
2480
+ self.save_config_button.setToolTip(
2481
+ QCoreApplication.translate(
2482
+ "PrimaryWindow", "Save current configuration", None
2483
+ )
2484
+ )
2485
+ # endif // QT_CONFIG(tooltip)
2425
2486
  self.save_config_button.setText(
2426
2487
  QCoreApplication.translate("PrimaryWindow", "Save", None)
2427
2488
  )
@@ -263,6 +263,9 @@
263
263
  <verstretch>0</verstretch>
264
264
  </sizepolicy>
265
265
  </property>
266
+ <property name="toolTip">
267
+ <string>Export recording list to file</string>
268
+ </property>
266
269
  <property name="text">
267
270
  <string>export</string>
268
271
  </property>
@@ -276,6 +279,9 @@
276
279
  <verstretch>0</verstretch>
277
280
  </sizepolicy>
278
281
  </property>
282
+ <property name="toolTip">
283
+ <string>Import recording list from file</string>
284
+ </property>
279
285
  <property name="text">
280
286
  <string>import</string>
281
287
  </property>
@@ -287,132 +293,152 @@
287
293
  </widget>
288
294
  </item>
289
295
  <item>
290
- <layout class="QVBoxLayout" name="logo_layout">
296
+ <layout class="QVBoxLayout" name="logo_and_version_layout">
297
+ <item>
298
+ <layout class="QVBoxLayout" name="logo_layout">
299
+ <item>
300
+ <widget class="QFrame" name="frame">
301
+ <property name="sizePolicy">
302
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
303
+ <horstretch>0</horstretch>
304
+ <verstretch>0</verstretch>
305
+ </sizepolicy>
306
+ </property>
307
+ <property name="minimumSize">
308
+ <size>
309
+ <width>180</width>
310
+ <height>80</height>
311
+ </size>
312
+ </property>
313
+ <property name="styleSheet">
314
+ <string notr="true">background-color: transparent;</string>
315
+ </property>
316
+ <property name="frameShape">
317
+ <enum>QFrame::Shape::NoFrame</enum>
318
+ </property>
319
+ <property name="frameShadow">
320
+ <enum>QFrame::Shadow::Raised</enum>
321
+ </property>
322
+ <widget class="QLabel" name="accusleepy2">
323
+ <property name="geometry">
324
+ <rect>
325
+ <x>11</x>
326
+ <y>15</y>
327
+ <width>160</width>
328
+ <height>50</height>
329
+ </rect>
330
+ </property>
331
+ <property name="sizePolicy">
332
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
333
+ <horstretch>0</horstretch>
334
+ <verstretch>0</verstretch>
335
+ </sizepolicy>
336
+ </property>
337
+ <property name="font">
338
+ <font>
339
+ <pointsize>21</pointsize>
340
+ <italic>true</italic>
341
+ <bold>true</bold>
342
+ </font>
343
+ </property>
344
+ <property name="styleSheet">
345
+ <string notr="true">background-color: transparent;
346
+ color: rgb(130, 169, 68);</string>
347
+ </property>
348
+ <property name="text">
349
+ <string>AccuSleePy</string>
350
+ </property>
351
+ <property name="alignment">
352
+ <set>Qt::AlignmentFlag::AlignCenter</set>
353
+ </property>
354
+ </widget>
355
+ <widget class="QLabel" name="accusleepy3">
356
+ <property name="geometry">
357
+ <rect>
358
+ <x>13</x>
359
+ <y>17</y>
360
+ <width>160</width>
361
+ <height>50</height>
362
+ </rect>
363
+ </property>
364
+ <property name="sizePolicy">
365
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
366
+ <horstretch>0</horstretch>
367
+ <verstretch>0</verstretch>
368
+ </sizepolicy>
369
+ </property>
370
+ <property name="font">
371
+ <font>
372
+ <pointsize>21</pointsize>
373
+ <italic>true</italic>
374
+ <bold>true</bold>
375
+ </font>
376
+ </property>
377
+ <property name="styleSheet">
378
+ <string notr="true">background-color: transparent;
379
+ color: rgb(46, 63, 150);</string>
380
+ </property>
381
+ <property name="text">
382
+ <string>AccuSleePy</string>
383
+ </property>
384
+ <property name="alignment">
385
+ <set>Qt::AlignmentFlag::AlignCenter</set>
386
+ </property>
387
+ </widget>
388
+ <widget class="QLabel" name="accusleepy1">
389
+ <property name="geometry">
390
+ <rect>
391
+ <x>9</x>
392
+ <y>13</y>
393
+ <width>160</width>
394
+ <height>50</height>
395
+ </rect>
396
+ </property>
397
+ <property name="sizePolicy">
398
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
399
+ <horstretch>0</horstretch>
400
+ <verstretch>0</verstretch>
401
+ </sizepolicy>
402
+ </property>
403
+ <property name="font">
404
+ <font>
405
+ <pointsize>21</pointsize>
406
+ <italic>true</italic>
407
+ <bold>true</bold>
408
+ </font>
409
+ </property>
410
+ <property name="styleSheet">
411
+ <string notr="true">background-color: transparent;
412
+ color: rgb(244, 195, 68);</string>
413
+ </property>
414
+ <property name="text">
415
+ <string>AccuSleePy</string>
416
+ </property>
417
+ <property name="alignment">
418
+ <set>Qt::AlignmentFlag::AlignCenter</set>
419
+ </property>
420
+ </widget>
421
+ <zorder>accusleepy1</zorder>
422
+ <zorder>accusleepy2</zorder>
423
+ <zorder>accusleepy3</zorder>
424
+ </widget>
425
+ </item>
426
+ </layout>
427
+ </item>
291
428
  <item>
292
- <widget class="QFrame" name="frame">
429
+ <widget class="QLabel" name="version_label">
293
430
  <property name="sizePolicy">
294
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
431
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
295
432
  <horstretch>0</horstretch>
296
433
  <verstretch>0</verstretch>
297
434
  </sizepolicy>
298
435
  </property>
299
- <property name="minimumSize">
300
- <size>
301
- <width>180</width>
302
- <height>80</height>
303
- </size>
304
- </property>
305
- <property name="styleSheet">
306
- <string notr="true">background-color: transparent;</string>
307
- </property>
308
- <property name="frameShape">
309
- <enum>QFrame::Shape::NoFrame</enum>
436
+ <property name="text">
437
+ <string>TextLabel</string>
310
438
  </property>
311
- <property name="frameShadow">
312
- <enum>QFrame::Shadow::Raised</enum>
439
+ <property name="alignment">
440
+ <set>Qt::AlignmentFlag::AlignCenter</set>
313
441
  </property>
314
- <widget class="QLabel" name="accusleepy2">
315
- <property name="geometry">
316
- <rect>
317
- <x>11</x>
318
- <y>15</y>
319
- <width>160</width>
320
- <height>50</height>
321
- </rect>
322
- </property>
323
- <property name="sizePolicy">
324
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
325
- <horstretch>0</horstretch>
326
- <verstretch>0</verstretch>
327
- </sizepolicy>
328
- </property>
329
- <property name="font">
330
- <font>
331
- <pointsize>21</pointsize>
332
- <italic>true</italic>
333
- <bold>true</bold>
334
- </font>
335
- </property>
336
- <property name="styleSheet">
337
- <string notr="true">background-color: transparent;
338
- color: rgb(130, 169, 68);</string>
339
- </property>
340
- <property name="text">
341
- <string>AccuSleePy</string>
342
- </property>
343
- <property name="alignment">
344
- <set>Qt::AlignmentFlag::AlignCenter</set>
345
- </property>
346
- </widget>
347
- <widget class="QLabel" name="accusleepy3">
348
- <property name="geometry">
349
- <rect>
350
- <x>13</x>
351
- <y>17</y>
352
- <width>160</width>
353
- <height>50</height>
354
- </rect>
355
- </property>
356
- <property name="sizePolicy">
357
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
358
- <horstretch>0</horstretch>
359
- <verstretch>0</verstretch>
360
- </sizepolicy>
361
- </property>
362
- <property name="font">
363
- <font>
364
- <pointsize>21</pointsize>
365
- <italic>true</italic>
366
- <bold>true</bold>
367
- </font>
368
- </property>
369
- <property name="styleSheet">
370
- <string notr="true">background-color: transparent;
371
- color: rgb(46, 63, 150);</string>
372
- </property>
373
- <property name="text">
374
- <string>AccuSleePy</string>
375
- </property>
376
- <property name="alignment">
377
- <set>Qt::AlignmentFlag::AlignCenter</set>
378
- </property>
379
- </widget>
380
- <widget class="QLabel" name="accusleepy1">
381
- <property name="geometry">
382
- <rect>
383
- <x>9</x>
384
- <y>13</y>
385
- <width>160</width>
386
- <height>50</height>
387
- </rect>
388
- </property>
389
- <property name="sizePolicy">
390
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
391
- <horstretch>0</horstretch>
392
- <verstretch>0</verstretch>
393
- </sizepolicy>
394
- </property>
395
- <property name="font">
396
- <font>
397
- <pointsize>21</pointsize>
398
- <italic>true</italic>
399
- <bold>true</bold>
400
- </font>
401
- </property>
402
- <property name="styleSheet">
403
- <string notr="true">background-color: transparent;
404
- color: rgb(244, 195, 68);</string>
405
- </property>
406
- <property name="text">
407
- <string>AccuSleePy</string>
408
- </property>
409
- <property name="alignment">
410
- <set>Qt::AlignmentFlag::AlignCenter</set>
411
- </property>
412
- </widget>
413
- <zorder>accusleepy1</zorder>
414
- <zorder>accusleepy2</zorder>
415
- <zorder>accusleepy3</zorder>
416
442
  </widget>
417
443
  </item>
418
444
  </layout>
@@ -562,6 +588,9 @@ color: rgb(244, 195, 68);</string>
562
588
  <verstretch>0</verstretch>
563
589
  </sizepolicy>
564
590
  </property>
591
+ <property name="toolTip">
592
+ <string>Select EEG+EMG recording</string>
593
+ </property>
565
594
  <property name="text">
566
595
  <string>Select recording file</string>
567
596
  </property>
@@ -753,6 +782,9 @@ color: rgb(244, 195, 68);</string>
753
782
  <verstretch>0</verstretch>
754
783
  </sizepolicy>
755
784
  </property>
785
+ <property name="toolTip">
786
+ <string>Create calibration file for this subject</string>
787
+ </property>
756
788
  <property name="text">
757
789
  <string>Create calibration file</string>
758
790
  </property>
@@ -813,6 +845,9 @@ color: rgb(244, 195, 68);</string>
813
845
  <verstretch>0</verstretch>
814
846
  </sizepolicy>
815
847
  </property>
848
+ <property name="toolTip">
849
+ <string>Load calibration file for this recording</string>
850
+ </property>
816
851
  <property name="text">
817
852
  <string>Select calibration file</string>
818
853
  </property>
@@ -1042,6 +1077,9 @@ color: rgb(244, 195, 68);</string>
1042
1077
  <verstretch>0</verstretch>
1043
1078
  </sizepolicy>
1044
1079
  </property>
1080
+ <property name="toolTip">
1081
+ <string>Load a trained sleep scoring classifier</string>
1082
+ </property>
1045
1083
  <property name="text">
1046
1084
  <string>Load classification model</string>
1047
1085
  </property>
@@ -1296,6 +1334,9 @@ color: rgb(244, 195, 68);</string>
1296
1334
  <verstretch>0</verstretch>
1297
1335
  </sizepolicy>
1298
1336
  </property>
1337
+ <property name="toolTip">
1338
+ <string>Begin training the classification model</string>
1339
+ </property>
1299
1340
  <property name="text">
1300
1341
  <string>Train classification model</string>
1301
1342
  </property>
@@ -3553,6 +3594,9 @@ color: rgb(244, 195, 68);</string>
3553
3594
  <verstretch>0</verstretch>
3554
3595
  </sizepolicy>
3555
3596
  </property>
3597
+ <property name="toolTip">
3598
+ <string>Save current configuration</string>
3599
+ </property>
3556
3600
  <property name="text">
3557
3601
  <string>Save</string>
3558
3602
  </property>
@@ -1,5 +1,4 @@
1
1
  This is the current brain state configuration.
2
- You can also choose the default epoch length.
3
2
  If you make changes, click 'Save' to store them.
4
3
 
5
4
  Each brain state has several attributes:
@@ -26,3 +25,5 @@ Important notes:
26
25
  - Changing these settings can invalidate existing label files,
27
26
  calibration files, and trained models!
28
27
  - Reinstalling AccuSleePy will overwrite this configuration.
28
+ - You can also choose the default epoch length that is shown
29
+ when the primary interface starts up.
@@ -1,28 +1,31 @@
1
- from accusleepy.constants import BRAIN_STATE_COL, EEG_COL, EMG_COL, UNDEFINED_LABEL
2
-
3
- MAIN_GUIDE_TEXT = f"""
4
- Section 1: Overview of the GUI
5
- Section 2: AccuSleePy file types
6
- Section 3: Manually assigning brain state labels
7
- Section 4: Automatically assigning brain state labels
8
-
9
- -----------------------------------------------------------------------
10
- Section 1: Overview of the primary interface
11
- -----------------------------------------------------------------------
12
- This interface allows a user to assign brain state labels to 1-channel
13
- electroencephalogram (EEG) and electromyogram (EMG) data.
14
- The overall workflow looks like this:
15
- 1. Enter the epoch length for all recordings. This determines the time
16
- resolution of the brain state labels.
17
- 2. For each of your recordings, add to the recording list, enter its
18
- sampling rate, load the EEG/EMG data, and determine where to save
19
- the brain state labels (or load the labels if they already exist)
1
+ # Definitions
2
+ - Recording: a table containing one channel of electroencephalogram (EEG)
3
+ data and one channel of electromyogram (EMG) data collected at a
4
+ constant sampling rate.
5
+ - Epoch: the temporal resolution of brain state scoring. If, for example,
6
+ the epoch length is 5 seconds, then a brain state label will be
7
+ assigned to each 5-second segment of a recording.
8
+ - Bout: a contiguous set of epochs with the same brain state.
9
+
10
+
11
+ # 1. Overview of the primary interface
12
+
13
+ The workflow for sleep scoring is as follows:
14
+ 1. Set the epoch length
15
+ 2. For each of your recordings:
16
+ 1. create a new entry in the list of recordings
17
+ 2. enter the sampling rate
18
+ 3. select the recording file containing the EEG and EMG data
19
+ 4. choose a filename for saving the brain state labels,
20
+ or select an existing label file
21
+
20
22
  At this point, you can score the recordings manually.
23
+
21
24
  3. For each recording, create a calibration file using a small amount
22
25
  of labeled data, or choose a calibration file created using
23
26
  another recording from the same subject and under the same recording
24
- conditions (i.e., equipment)
25
- 4. Choose a trained classification model file with a matching epoch size
27
+ conditions
28
+ 4. Select a trained classification model file with the correct epoch length
26
29
  5. Score all recordings automatically using the classifier
27
30
 
28
31
  By default, there are three brain state options: REM, wake, and NREM.
@@ -33,49 +36,46 @@ classification model.
33
36
 
34
37
  Use the "import" and "export" buttons to load or save a list of
35
38
  recordings. This can be useful if you need to re-score a set of
36
- recordings with a different model, or if you want to keep a record of
37
- the recordings that were used when training your model.
39
+ recordings with a new model, or if you want to keep a record of
40
+ the recordings that were used when training a model.
38
41
 
39
- -----------------------------------------------------------------------
40
- Section 2: AccuSleePy file types
41
- -----------------------------------------------------------------------
42
+ # 2. AccuSleePy file types
42
43
  There are four types of files associated with AccuSleePy.
43
44
  To select a file in the primary interface, you can either use the
44
45
  associated button, or drag/drop the file into the empty box adjacent
45
46
  to the button.
46
- Recording file: a .csv or .parquet file containing one column of EEG
47
- data and one column of EMG data. The column names must be
48
- {EEG_COL} and {EMG_COL}.
49
- Label file: a .csv file with one column titled {BRAIN_STATE_COL}
50
- with entries that are either the undefined brain state, {UNDEFINED_LABEL},
47
+ - Recording file: a .parquet or .csv file containing one
48
+ column of EEG data and one column of EMG data.
49
+ The column names must be eeg and emg.
50
+ - Label file: a .csv file with one column titled brain_state
51
+ with entries that are either the undefined brain state (by default, this is -1)
51
52
  or one of the digits in your brain state configuration.
52
53
  By default, these are 1-3 where REM = 1, wake = 2, NREM = 3.
53
- Calibration data file: required for automatic labeling. See Section 4
54
+ - Calibration data file: required for automated scoring. See Section 4
54
55
  for details. These have .csv format.
55
- Trained classification model: required for automatic labeling. See
56
+ - Trained classification model: required for automated scoring. See
56
57
  Section 4 for details. These have .pth format.
57
58
 
58
- -----------------------------------------------------------------------
59
- Section 3: Manually assigning brain state labels
60
- -----------------------------------------------------------------------
59
+ # 3. Manually assigning brain state labels
61
60
  1. Select the recording you wish to modify from the recording list, or
62
61
  add a new one.
63
- 2. Click the 'Select recording file' button to set the location of the
62
+ 2. Click the "Select recording file" button to set the location of the
64
63
  EEG/EMG data, or drag/drop the recording file into the box next
65
64
  to the button.
66
- 3. Click the 'Select' label file button (or drag/drop) to choose an
67
- existing label file, or click the 'create' label file button to
65
+ 3. Click the "Select" label file button (or drag/drop) to choose an
66
+ existing label file, or click the "create" label file button to
68
67
  enter the filename for a new label file.
69
- 4. Click 'Score manually' to launch an interactive window for manual
70
- brain state labeling. Close the window when you are finished.
71
- This interface has many useful keyboard shortcuts, so it's recommended
72
- to consult its user manual.
73
-
74
- -----------------------------------------------------------------------
75
- Section 4: Automatically scoring recordings with a classification model
76
- -----------------------------------------------------------------------
68
+ 4. Click "Score manually" to launch an interactive window for manual
69
+ brain state scoring. When you are finished, save your changes and
70
+ close the window.
71
+
72
+ The manual scoring interface has many useful keyboard shortcuts,
73
+ so please consult its user manual by clicking the question mark icon.
74
+
75
+ # 4. Automatically scoring recordings with a classification model
77
76
  Automatic brain state scoring requires the inputs described in
78
- Section 3, as well as calibration data files and a trained classifier.
77
+ Section 3, as well as calibration data files and a trained classification
78
+ model.
79
79
  If you already have all of these files, proceed to Section 4C.
80
80
  Models trained on the AccuSleep dataset are provided at
81
81
  https://osf.io/py5eb under /python_format/models/ for epoch lengths of
@@ -84,14 +84,18 @@ they use several epochs of data before and after any given epoch when
84
84
  scoring that epoch. (The other model type, called "real-time", only
85
85
  uses data from the current epoch and several preceding epochs.)
86
86
 
87
- --- Section 4A: Creating calibration data files ---
88
- Each recording must have a calibration file assigned to it.
87
+ ## 4A. Creating calibration data files
88
+
89
+ In order to perform automated sleep scoring,
90
+ each recording must have a calibration file assigned to it.
89
91
  This file lets AccuSleep transform features of the EEG and EMG data so
90
92
  that they are in the same range as the classifier's training data.
91
93
  You can use the same calibration file for multiple recordings, as long
92
94
  as they come from the same subject and were collected under the same
93
95
  recording conditions (i.e., the same recording equipment was used).
96
+
94
97
  To create a calibration data file:
98
+
95
99
  1. Ensure you have a file containing brain state labels. You can create
96
100
  this file by following the steps in Section 3, or select an
97
101
  existing label file.
@@ -99,16 +103,23 @@ To create a calibration data file:
99
103
  stage (e.g., REM, wakefulness, and NREM). It is recommended to
100
104
  label at least several minutes of each stage, and more labels can
101
105
  improve classification accuracy.
102
- 3. Click 'Create calibration file'.
106
+ 3. Click "Create calibration file".
103
107
  4. Enter a filename for the calibration data file.
104
108
  5. The calibration file will automatically be assigned to the currently
105
109
  selected recording.
106
110
 
107
- --- Section 4B: Training your own classification model ---
111
+ Note that epoch length can affect the calibration process. If you make
112
+ a calibration file for a subject using one epoch length, but want to
113
+ score another recording from the same subject with a different epoch
114
+ length, it's best to create a new calibration file.
115
+
116
+ ## 4B. Training your own classification model
117
+
108
118
  To train a new model on your own data:
119
+
109
120
  1. Add your scored recordings to the recording list. Make sure the
110
121
  sampling rate, recording file, and label file are set for each
111
- recording.
122
+ recording. Calibration files are not required.
112
123
  2. Click the "Model training" tab
113
124
  3. Choose the number of epochs to consider when scoring each epoch.
114
125
  This will be the "width" of the training images. For "default"
@@ -118,32 +129,34 @@ To train a new model on your own data:
118
129
  deleted once training is complete. (It's generally best to
119
130
  leave this box checked.)
120
131
  5. Choose whether to create a "default" or "real-time"-type model.
121
- Note that scoring recordings in the GUI requires a default-type
122
- model.
123
- 6. Select a directory where the training images will be saved. A
132
+ Note that scoring recordings in the primary interface requires
133
+ a default-type model.
134
+ 6. Click "Set training image directory" to select the location
135
+ where the images used to train the model will be saved. A
124
136
  new directory with an automatically generated name will be
125
137
  created inside the directory you choose.
126
- 7. Click the "Train classification model" button and enter a
138
+ 7. Click "Train classification model" and enter a
127
139
  filename for the trained model. Training can take some time.
140
+ The console will display progress updates.
141
+
142
+ ## 4C. Automatic scoring
143
+
144
+ Instructions for automatic scoring using this interface are below.
128
145
 
129
- --- Section 4C: Automatic labeling ---
130
- Instructions for automatic labeling using this GUI are below.
131
146
  1. Set the epoch length for all recordings.
132
147
  2. Select the recording file, label file, and calibration file to use
133
148
  for each recording. See section 4A for instructions on creating
134
149
  calibration files.
135
- 3. Click 'Load classification model' to load the trained classification
150
+ 3. Click "Load classification model" to load the trained classification
136
151
  model. It's important that the epoch length used when training this
137
152
  model is the same as the current epoch length.
138
153
  4. If you wish to preserve any existing labels in the label file, and
139
154
  only overwrite undefined epochs, check the box labeled
140
- 'Only overwrite undefined epochs'.
155
+ "Only overwrite undefined epochs".
141
156
  5. Set the minimum bout length, in seconds. A typical value could be 5.
142
157
  Following automatic labeling, any brain state bout shorter than this
143
- duration will be reassigned to the surrounding stage (if the stages
144
- on either side of the bout match).
145
- 6. Click 'Score all automatically' to score all recordings in the
146
- recording list. Labels will be saved to the file specified by
147
- the 'Select or create label file' field of each recording. You can
148
- click 'Score manually' to visualize the results.
149
- """
158
+ duration will be reassigned to the surrounding state (if the states
159
+ on either side of the bout are the same).
160
+ 6. Click "Score all automatically" to score all recordings in the
161
+ recording list. To inspect the results, select a recording
162
+ in the list and click "Score manually".
@@ -0,0 +1,23 @@
1
+ # Manual scoring interface
2
+
3
+ Keyboard shortcuts:
4
+ - Mouse click on the upper 3 plots: jump to epoch
5
+ - Ctrl + S: save labels to file
6
+ - Right / left arrow: move one epoch forward / backward in time
7
+ - Numbers 0-9: set current epoch to this brain state
8
+ - Backspace: set current epoch to the undefined state
9
+ - Ctrl + Z: undo last change to brain state labels
10
+ - Ctrl + Y: redo last change to brain state labels
11
+ - Shift + (number 0-9, or backspace):
12
+ After pressing this combination, click and drag on the upper plot of brain
13
+ state labels to set the selected epochs
14
+ to this brain state. Press Escape to cancel.
15
+ - Plus (+): zoom in (upper panel x-axis)
16
+ - Minus (-): zoom out (upper panel x-axis)
17
+ - Shift + right / left arrow: jump to the next / previous epoch with a different brain state
18
+ than the current epoch
19
+ - Spacebar: equivalent to shift + right arrow
20
+ - Ctrl + right / left arrow: jump to the next / preceding undefined epoch
21
+ - Ctrl + W: quit
22
+
23
+ ![ ](../images/viewer_window_annotated.png)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: accusleepy
3
- Version: 0.3.1
3
+ Version: 0.4.3
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)
@@ -56,6 +57,17 @@ etc.) using python >=3.10,<3.13
56
57
 
57
58
  `python -m accusleepy` will open the primary interface.
58
59
 
60
+ [Guide to the primary interface](accusleepy/gui/text/main_guide.md)
61
+
62
+ [Guide to the manual scoring interface](accusleepy/gui/text/manual_scoring_guide.md)
63
+
64
+ ## Screenshots
65
+ Primary interface
66
+ ![AccuSleePy primary interface](accusleepy/gui/images/primary_window.png)
67
+
68
+ Manual scoring interface
69
+ ![AccuSleePy manual scoring interface](accusleepy/gui/images/viewer_window.png)
70
+
59
71
  ## Acknowledgements
60
72
 
61
73
  We would like to thank [Franz Weber](https://www.med.upenn.edu/weberlab/) for creating an
@@ -17,21 +17,24 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
17
17
  accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
18
18
  accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
19
19
  accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
20
- accusleepy/gui/main.py,sha256=IGhSD9vSTO09H72Ou1icbujQfzCy1twTCdeR2Dv8I7s,53055
21
- accusleepy/gui/manual_scoring.py,sha256=Jfu7uxXk6W4mmq81h4LnLSArP735l0XvbuZq9yYG650,40686
20
+ accusleepy/gui/images/primary_window.png,sha256=bT-A7ZPndVnJv3cX5DuzDwcZ44alz3GGIy7MxU0rtiA,593548
21
+ accusleepy/gui/images/viewer_window.png,sha256=gKwIXkgsl1rTMfmMeMwNyjEAUL5I6FXk9-hpMR92qTI,970630
22
+ accusleepy/gui/images/viewer_window_annotated.png,sha256=M5NmoWDHRLS334Rp8SsfOPUUXzPltH1p7aB0BrISgQU,261481
23
+ accusleepy/gui/main.py,sha256=GTHsUL9UPosDwzy86ohLj0Tok1OkqYBSl0Ba8Saq2Xs,54210
24
+ accusleepy/gui/manual_scoring.py,sha256=l1m7MIY2SgNAYJFe7_hrvQUcZ2w7r5U82iX1dab86LI,40623
22
25
  accusleepy/gui/mplwidget.py,sha256=f9O3u_96whQGUwpi3o_QGc7yjiETX5vE0oj3ePXTJWE,12279
23
- accusleepy/gui/primary_window.py,sha256=eK9yrU6PK5hX1RORjc7DvqCDuDt55iA1LylXp3oh0j8,101850
24
- accusleepy/gui/primary_window.ui,sha256=NqCSnvlC1mKrWNn034u8avpMF7WudlR8G5jLjFNdTmk,144812
26
+ accusleepy/gui/primary_window.py,sha256=RXpDvcb7zy8Ea4Da1VhMG1T6GC54KW3vyGjqqQJN45k,104582
27
+ accusleepy/gui/primary_window.ui,sha256=09k4xFcjgOL9mlhFlg6mXCc_tgj4_FY9CZ3H03e3z3A,147074
25
28
  accusleepy/gui/resources.qrc,sha256=ByNEmJqr0YbKBqoZGvONZtjyNYr4ST4enO6TEdYSqWg,802
26
29
  accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
27
- accusleepy/gui/text/config_guide.txt,sha256=wz2QtRgd1eXUZMmtfAIVgqvDOtjkOnTOK-IGkLALg4U,1073
28
- accusleepy/gui/text/main_guide_text.py,sha256=YP-2sexvU5VIeKc1FXHsG0cUCgGfcU4qLD59JRlOMMo,8038
29
- accusleepy/gui/text/manual_scoring_guide.txt,sha256=onBnUZJyX18oN1CgjD2HSnlEQHsUscHpOYf11kTKZ4U,1460
30
+ accusleepy/gui/text/config_guide.txt,sha256=7xyo5ifQjWgXmCdgHuei0DCsXZcU4vo5VRlpOb2KVs4,1130
31
+ accusleepy/gui/text/main_guide.md,sha256=VS6A5_CzQOBwIotNgEA_X0KHKfMT4lEK43Ki_Dkv8qE,7851
32
+ accusleepy/gui/text/manual_scoring_guide.md,sha256=ow_RMSjFy05NupEDSCuJtu-V65-BPnIkrZqtssFoZCQ,999
30
33
  accusleepy/gui/viewer_window.py,sha256=5PkbuYMXUegH1CExCoqSGDZ9GeJqCCUz0-3WWkM8Vfc,24049
31
34
  accusleepy/gui/viewer_window.ui,sha256=D1LwUFR-kZ_GWGZFFtXvGJdFWghLrOWZTblQeLQt9kI,30525
32
35
  accusleepy/models.py,sha256=Muapsw088AUHqRIbW97Rkbv0oiwCtQvO9tEoBCC-MYg,1476
33
36
  accusleepy/multitaper.py,sha256=V6MJDk0OSWhg2MFhrnt9dvYrHiNsk2T7IxAA7paZVyE,25549
34
37
  accusleepy/signal_processing.py,sha256=-aXnywfp1LBsk3DcbIMmZlgv3f8j6sZ6js0bizZId0o,21718
35
- accusleepy-0.3.1.dist-info/METADATA,sha256=xgwUR8pWzqQp3fi_g6UXCVbvxrL3CdVahoq476bIEaQ,2729
36
- accusleepy-0.3.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
37
- accusleepy-0.3.1.dist-info/RECORD,,
38
+ accusleepy-0.4.3.dist-info/METADATA,sha256=ooPrsNpkjVtzsWovyX30OW5qlcQgOW1bs1P7sHA3UDc,3137
39
+ accusleepy-0.4.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
40
+ accusleepy-0.4.3.dist-info/RECORD,,
@@ -1,28 +0,0 @@
1
- The lower panel shows a zoomed-in subset of the epochs shown in the upper panel.
2
- The red diamond in the upper panel and the red lines in the lower panel
3
- mark the currently selected epoch.
4
- The red horizontal line in the upper panel indicates the epochs shown in the lower panel.
5
- Click a location on the upper panel to jump to that epoch.
6
- The buttons next to the spectrogram control the zoom level and intensity.
7
- The buttons next to the EEG and EMG plots control the zoom level and y-axis offset.
8
- If "auto-scroll" is enabled, modifying the brain state of the current epoch
9
- will automatically select the next epoch.
10
-
11
- Keyboard shortcuts:
12
- Ctrl + S: save labels to file
13
- Right arrow: move one epoch forward in time
14
- Left arrow: move one epoch backward in time
15
- Numbers 0-9: set current epoch to this brain state
16
- Backspace: set current epoch to undefined
17
- Ctrl + Z: undo last change to brain state labels
18
- Ctrl + Y: redo last change to brain state labels
19
- Shift + (number 0-9, or backspace):
20
- Click and drag on the upper plot of brain state labels to set the selected epochs
21
- to the desired brain state. Press Esc to cancel.
22
- +: zoom in (upper panel x-axis)
23
- -: zoom out (upper panel x-axis)
24
- Shift + right arrow, or space: jump to the next epoch of a different state
25
- than the current epoch
26
- Shift + left arrow: jump to the preceding epoch of a different state than the current epoch
27
- Ctrl + right/left arrow: jump to the next/preceding undefined epoch
28
- Ctrl + W: quit