accusleepy 0.9.3__py3-none-any.whl → 0.10.1__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.
accusleepy/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ """AccuSleePy: Python implementation of AccuSleep for automated sleep scoring."""
accusleepy/__main__.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Entry point for running AccuSleePy as a module (python -m accusleepy)."""
2
+
1
3
  from accusleepy.gui.main import run_primary_window
2
4
 
3
5
  if __name__ == "__main__":
accusleepy/bouts.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Brain state bout length enforcement."""
2
+
1
3
  import re
2
4
  from dataclasses import dataclass
3
5
  from operator import attrgetter
@@ -1,3 +1,5 @@
1
+ """Define and organize brain states."""
2
+
1
3
  from dataclasses import dataclass
2
4
 
3
5
  import numpy as np
@@ -1,3 +1,5 @@
1
+ """Model training and brain state classification."""
2
+
1
3
  import os
2
4
 
3
5
  import numpy as np
accusleepy/config.json CHANGED
@@ -35,5 +35,6 @@
35
35
  "training_epochs": 6
36
36
  },
37
37
  "epochs_to_show": 5,
38
- "autoscroll_state": false
38
+ "autoscroll_state": false,
39
+ "delete_training_images": true
39
40
  }
accusleepy/constants.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Application-wide constants and default settings."""
2
+
1
3
  import numpy as np
2
4
 
3
5
  # probably don't change these unless you really need to
@@ -72,6 +74,7 @@ EMG_FILTER_KEY = "emg_filter"
72
74
  HYPERPARAMETERS_KEY = "hyperparameters"
73
75
  EPOCHS_TO_SHOW_KEY = "epochs_to_show"
74
76
  AUTOSCROLL_KEY = "autoscroll_state"
77
+ DELETE_TRAINING_IMAGES_KEY = "delete_training_images"
75
78
 
76
79
  # default values
77
80
  # default UI settings
@@ -87,6 +90,7 @@ DEFAULT_BATCH_SIZE = 64
87
90
  DEFAULT_LEARNING_RATE = 1e-3
88
91
  DEFAULT_MOMENTUM = 0.9
89
92
  DEFAULT_TRAINING_EPOCHS = 6
93
+ DEFAULT_DELETE_TRAINING_IMAGES_STATE = True
90
94
  # default manual scoring settings
91
95
  DEFAULT_EPOCHS_TO_SHOW = 5
92
96
  DEFAULT_AUTOSCROLL_STATE = False
accusleepy/fileio.py CHANGED
@@ -1,3 +1,5 @@
1
+ """File I/O for recordings, labels, calibration data, and configuration."""
2
+
1
3
  import json
2
4
  import os
3
5
  from dataclasses import dataclass
@@ -5,7 +7,6 @@ from importlib.metadata import version, PackageNotFoundError
5
7
 
6
8
  import numpy as np
7
9
  import pandas as pd
8
- from PySide6.QtWidgets import QListWidgetItem
9
10
 
10
11
  from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainState, BrainStateSet
11
12
  import accusleepy.constants as c
@@ -43,6 +44,7 @@ class AccuSleePyConfig:
43
44
  hyperparameters: Hyperparameters
44
45
  epochs_to_show: int
45
46
  autoscroll_state: bool
47
+ delete_training_images: bool
46
48
 
47
49
 
48
50
  @dataclass
@@ -54,7 +56,6 @@ class Recording:
54
56
  label_file: str = "" # path to label file
55
57
  calibration_file: str = "" # path to calibration file
56
58
  sampling_rate: int | float = 0.0 # sampling rate, in Hz
57
- widget: QListWidgetItem = None # list item widget shown in the GUI
58
59
 
59
60
 
60
61
  def load_calibration_file(filename: str) -> tuple[np.ndarray, np.ndarray]:
@@ -139,7 +140,8 @@ def load_config() -> AccuSleePyConfig:
139
140
  EMG filter parameters,
140
141
  model training hyperparameters,
141
142
  default epochs to show for manual scoring,
142
- default autoscroll state for manual scoring
143
+ default autoscroll state for manual scoring,
144
+ setting to delete training images automatically
143
145
  """
144
146
  with open(
145
147
  os.path.join(os.path.dirname(os.path.abspath(__file__)), c.CONFIG_FILE), "r"
@@ -183,6 +185,9 @@ def load_config() -> AccuSleePyConfig:
183
185
  ),
184
186
  epochs_to_show=data.get(c.EPOCHS_TO_SHOW_KEY, c.DEFAULT_EPOCHS_TO_SHOW),
185
187
  autoscroll_state=data.get(c.AUTOSCROLL_KEY, c.DEFAULT_AUTOSCROLL_STATE),
188
+ delete_training_images=data.get(
189
+ c.DELETE_TRAINING_IMAGES_KEY, c.DEFAULT_DELETE_TRAINING_IMAGES_STATE
190
+ ),
186
191
  )
187
192
 
188
193
 
@@ -196,6 +201,7 @@ def save_config(
196
201
  hyperparameters: Hyperparameters,
197
202
  epochs_to_show: int,
198
203
  autoscroll_state: bool,
204
+ delete_training_images: bool,
199
205
  ) -> None:
200
206
  """Save configuration of brain state options to json file
201
207
 
@@ -210,6 +216,8 @@ def save_config(
210
216
  :param hyperparameters: model training hyperparameters
211
217
  :param epochs_to_show: default epochs to show for manual scoring,
212
218
  :param autoscroll_state: default autoscroll state for manual scoring
219
+ :param delete_training_images: whether to automatically delete images
220
+ created for model training once training is complete
213
221
  """
214
222
  output_dict = brain_state_set.to_output_dict()
215
223
  output_dict.update({c.DEFAULT_EPOCH_LENGTH_KEY: default_epoch_length})
@@ -220,6 +228,7 @@ def save_config(
220
228
  output_dict.update({c.HYPERPARAMETERS_KEY: hyperparameters.__dict__})
221
229
  output_dict.update({c.EPOCHS_TO_SHOW_KEY: epochs_to_show})
222
230
  output_dict.update({c.AUTOSCROLL_KEY: autoscroll_state})
231
+ output_dict.update({c.DELETE_TRAINING_IMAGES_KEY: delete_training_images})
223
232
  with open(
224
233
  os.path.join(os.path.dirname(os.path.abspath(__file__)), c.CONFIG_FILE), "w"
225
234
  ) as f:
@@ -0,0 +1 @@
1
+ """Graphical user interface components for AccuSleePy."""
Binary file
accusleepy/gui/main.py CHANGED
@@ -1,5 +1,7 @@
1
- # AccuSleePy main window
2
- # Icon source: Arkinasi, https://www.flaticon.com/authors/arkinasi
1
+ """AccuSleePy main window.
2
+
3
+ Icon source: Arkinasi, https://www.flaticon.com/authors/arkinasi
4
+ """
3
5
 
4
6
  import logging
5
7
  import os
@@ -76,7 +78,6 @@ class TrainingSettings:
76
78
  """Settings for training a new model"""
77
79
 
78
80
  epochs_per_img: int = 9
79
- delete_images: bool = True
80
81
  model_type: str = DEFAULT_MODEL_TYPE
81
82
  calibrate: bool = True
82
83
 
@@ -176,9 +177,6 @@ class AccuSleepWindow(QMainWindow):
176
177
  self.ui.image_number_input.valueChanged.connect(
177
178
  lambda v: setattr(self.training, "epochs_per_img", v)
178
179
  )
179
- self.ui.delete_image_box.stateChanged.connect(
180
- lambda v: setattr(self.training, "delete_images", bool(v))
181
- )
182
180
  self.ui.calibrate_checkbox.stateChanged.connect(
183
181
  self.update_training_calibration
184
182
  )
@@ -304,7 +302,7 @@ class AccuSleepWindow(QMainWindow):
304
302
  emg_filter=self.config.emg_filter,
305
303
  hyperparameters=self.config.hyperparameters,
306
304
  model_filename=model_filename,
307
- delete_images=self.training.delete_images,
305
+ delete_images=self.config.delete_training_images,
308
306
  )
309
307
 
310
308
  # Display results
@@ -1,11 +1,13 @@
1
- # AccuSleePy manual scoring GUI
2
- # Icon sources:
3
- # Arkinasi, https://www.flaticon.com/authors/arkinasi
4
- # kendis lasman, https://www.flaticon.com/packs/ui-79
1
+ """AccuSleePy manual scoring GUI.
5
2
 
3
+ Icon sources:
4
+ Arkinasi, https://www.flaticon.com/authors/arkinasi
5
+ kendis lasman, https://www.flaticon.com/packs/ui-79
6
+ """
6
7
 
7
8
  import copy
8
9
  import os
10
+ import time
9
11
  from dataclasses import dataclass
10
12
  from functools import partial
11
13
  from types import SimpleNamespace
@@ -82,9 +84,10 @@ UNDO_LIMIT = 1000
82
84
  # brightness scaling factors for the spectrogram
83
85
  BRIGHTER_SCALE_FACTOR = 0.96
84
86
  DIMMER_SCALE_FACTOR = 1.07
85
- # zoom factor for upper plots
86
- ZOOM_IN_FACTOR = 0.45
87
- ZOOM_OUT_FACTOR = 1.017
87
+ # zoom factor for upper plots - larger values = bigger changes
88
+ ZOOM_FACTOR = 0.1
89
+ # rate limit for zoom events triggered by scrolling
90
+ MAX_SCROLL_EVENTS_PER_SEC = 24
88
91
 
89
92
 
90
93
  @dataclass
@@ -352,8 +355,10 @@ class ManualScoringWindow(QDialog):
352
355
  )
353
356
  keypress_redo.activated.connect(self.redo)
354
357
 
355
- # user input: clicks
358
+ # user input: mouse events
356
359
  self.ui.upperfigure.canvas.mpl_connect("button_press_event", self.click_to_jump)
360
+ self.ui.upperfigure.canvas.mpl_connect("scroll_event", self.scroll_zoom)
361
+ self.now_zooming = False # impose timeout on zoom events
357
362
 
358
363
  # user input: buttons
359
364
  self.ui.savebutton.clicked.connect(self.save)
@@ -483,7 +488,7 @@ class ManualScoringWindow(QDialog):
483
488
 
484
489
  def closeEvent(self, event: QCloseEvent) -> None:
485
490
  """Check if there are unsaved changes before closing"""
486
- if not all(self.labels == self.last_saved_labels):
491
+ if not np.array_equal(self.labels, self.last_saved_labels):
487
492
  result = QMessageBox.question(
488
493
  self,
489
494
  "Unsaved changes",
@@ -668,6 +673,8 @@ class ManualScoringWindow(QDialog):
668
673
  self.label_display_options,
669
674
  )
670
675
  self.update_figures()
676
+ # upper plot x limits might need to change
677
+ self.zoom_x(direction=None)
671
678
 
672
679
  def update_signal_offset(self, signal: str, direction: str) -> None:
673
680
  """Shift EEG or EMG up or down
@@ -713,39 +720,19 @@ class ManualScoringWindow(QDialog):
713
720
  (self.upper_left_epoch, self.upper_right_epoch + 1)
714
721
  )
715
722
 
716
- def zoom_x(self, direction: str) -> None:
723
+ def zoom_x(self, direction: str | None) -> None:
717
724
  """Change upper figure x-axis zoom level
718
725
 
719
- :param direction: in, out, or reset
726
+ :param direction: in, out, reset, or None
720
727
  """
721
- epochs_shown = self.upper_right_epoch - self.upper_left_epoch + 1
722
- if direction == ZOOM_IN:
723
- self.upper_left_epoch = max(
724
- [
725
- self.upper_left_epoch,
726
- round(self.epoch - ZOOM_IN_FACTOR * epochs_shown),
727
- ]
728
- )
729
-
730
- self.upper_right_epoch = min(
731
- [
732
- self.upper_right_epoch,
733
- round(self.epoch + ZOOM_IN_FACTOR * epochs_shown),
734
- ]
735
- )
736
-
737
- elif direction == ZOOM_OUT:
738
- self.upper_left_epoch = max(
739
- [0, round(self.epoch - ZOOM_OUT_FACTOR * epochs_shown)]
740
- )
741
-
742
- self.upper_right_epoch = min(
743
- [self.n_epochs - 1, round(self.epoch + ZOOM_OUT_FACTOR * epochs_shown)]
744
- )
745
-
746
- else: # reset
747
- self.upper_left_epoch = 0
748
- self.upper_right_epoch = self.n_epochs - 1
728
+ self.upper_left_epoch, self.upper_right_epoch = find_new_x_limits(
729
+ direction=direction,
730
+ left_epoch=self.upper_left_epoch,
731
+ right_epoch=self.upper_right_epoch,
732
+ min_n_shown=self.epochs_to_show,
733
+ total_epochs=self.n_epochs,
734
+ selected_epoch=self.epoch,
735
+ )
749
736
  self.adjust_upper_figure_x_limits()
750
737
  self.ui.upperfigure.canvas.draw()
751
738
 
@@ -806,8 +793,11 @@ class ManualScoringWindow(QDialog):
806
793
  # update upper plot if needed
807
794
  upper_epochs_shown = self.upper_right_epoch - self.upper_left_epoch + 1
808
795
  if (
809
- self.epoch
810
- > self.upper_left_epoch + (1 - SCROLL_BOUNDARY) * upper_epochs_shown
796
+ (
797
+ self.epoch
798
+ > self.upper_left_epoch + (1 - SCROLL_BOUNDARY) * upper_epochs_shown
799
+ or self.epoch + (self.epochs_to_show - 1) / 2 > self.upper_right_epoch
800
+ )
811
801
  and self.upper_right_epoch < (self.n_epochs - 1)
812
802
  and direction == DIRECTION_RIGHT
813
803
  ):
@@ -815,7 +805,11 @@ class ManualScoringWindow(QDialog):
815
805
  self.upper_right_epoch += 1
816
806
  self.adjust_upper_figure_x_limits()
817
807
  elif (
818
- self.epoch < self.upper_left_epoch + SCROLL_BOUNDARY * upper_epochs_shown
808
+ (
809
+ self.epoch
810
+ < self.upper_left_epoch + SCROLL_BOUNDARY * upper_epochs_shown
811
+ or self.epoch - (self.epochs_to_show - 1) / 2 < self.upper_left_epoch
812
+ )
819
813
  and self.upper_left_epoch > 0
820
814
  and direction == DIRECTION_LEFT
821
815
  ):
@@ -984,6 +978,23 @@ class ManualScoringWindow(QDialog):
984
978
 
985
979
  self.update_figures()
986
980
 
981
+ def scroll_zoom(self, event) -> None:
982
+ """Zoom on mouse scroll events"""
983
+ if self.now_zooming:
984
+ return
985
+
986
+ self.now_zooming = True
987
+ start_time = time.time()
988
+ if event.button == "up":
989
+ self.zoom_x(direction=ZOOM_IN)
990
+ else:
991
+ self.zoom_x(direction=ZOOM_OUT)
992
+ end_time = time.time()
993
+ time_elapsed = end_time - start_time
994
+ if time_elapsed < 1 / MAX_SCROLL_EVENTS_PER_SEC:
995
+ time.sleep(1 / MAX_SCROLL_EVENTS_PER_SEC - time_elapsed)
996
+ self.now_zooming = False
997
+
987
998
 
988
999
  def convert_labels(labels: np.array, style: str) -> np.array:
989
1000
  """Convert labels between "display" and "digit" formats
@@ -1095,3 +1106,55 @@ def transform_eeg_emg(eeg: np.array, emg: np.array) -> (np.array, np.array):
1095
1106
  eeg = eeg / np.percentile(eeg, 95) / 2.2
1096
1107
  emg = emg / np.percentile(emg, 95) / 2.2
1097
1108
  return eeg, emg
1109
+
1110
+
1111
+ def find_new_x_limits(
1112
+ direction: str | None,
1113
+ left_epoch: int,
1114
+ right_epoch: int,
1115
+ total_epochs: int,
1116
+ min_n_shown: int,
1117
+ selected_epoch: int,
1118
+ ) -> (int, int):
1119
+ """Calculate new plot x limits to allow zooming
1120
+
1121
+ :param direction: in, out, reset, or None
1122
+ :param left_epoch: index of current leftmost epoch
1123
+ :param right_epoch: index of current rightmost epoch
1124
+ :param total_epochs: total number of epochs in the recording
1125
+ :param min_n_shown: minimum number of epochs to display
1126
+ :param selected_epoch: currently selected epoch
1127
+ """
1128
+ # number of epochs currently displayed in the upper plots
1129
+ current_n_shown = right_epoch - left_epoch + 1
1130
+ if direction == ZOOM_IN:
1131
+ # can't display fewer than the number of epochs in the lower plot
1132
+ new_n_shown = max([min_n_shown, round(current_n_shown * (1 - ZOOM_FACTOR))])
1133
+ elif direction == ZOOM_OUT:
1134
+ # can't display more than the total number of epochs
1135
+ new_n_shown = min([total_epochs, round(current_n_shown / (1 - ZOOM_FACTOR))])
1136
+ elif direction == ZOOM_RESET:
1137
+ left_epoch = 0
1138
+ right_epoch = total_epochs - 1
1139
+ return left_epoch, right_epoch
1140
+ else: # just recalculating if min_n_shown has changed
1141
+ new_n_shown = int(
1142
+ np.clip(current_n_shown, a_min=min_n_shown, a_max=total_epochs)
1143
+ )
1144
+
1145
+ # count epochs to show on either side of the selected epoch
1146
+ epochs_on_left_side = int(np.ceil((new_n_shown - 1) / 2))
1147
+ epochs_on_right_side = new_n_shown - epochs_on_left_side - 1
1148
+ if selected_epoch - epochs_on_left_side < 0:
1149
+ # can't go further left than 0
1150
+ left_epoch = 0
1151
+ right_epoch = new_n_shown - 1
1152
+ elif selected_epoch + epochs_on_right_side >= total_epochs:
1153
+ # can't go further right than the total number of epochs
1154
+ left_epoch = total_epochs - new_n_shown
1155
+ right_epoch = total_epochs - 1
1156
+ else:
1157
+ left_epoch = selected_epoch - epochs_on_left_side
1158
+ right_epoch = selected_epoch + epochs_on_right_side
1159
+
1160
+ return left_epoch, right_epoch
@@ -1,4 +1,5 @@
1
- # Widget with a matplotlib FigureCanvas for manual scoring
1
+ """Matplotlib FigureCanvas widget for manual scoring."""
2
+
2
3
  from collections.abc import Callable
3
4
 
4
5
  import matplotlib.ticker as mticker
@@ -757,7 +757,7 @@ class Ui_PrimaryWindow(object):
757
757
  self.top_training_layout.setSpacing(10)
758
758
  self.top_training_layout.setObjectName("top_training_layout")
759
759
  self.horizontalLayout_5 = QHBoxLayout()
760
- self.horizontalLayout_5.setSpacing(5)
760
+ self.horizontalLayout_5.setSpacing(3)
761
761
  self.horizontalLayout_5.setObjectName("horizontalLayout_5")
762
762
  self.label = QLabel(self.model_training_tab)
763
763
  self.label.setObjectName("label")
@@ -786,21 +786,48 @@ class Ui_PrimaryWindow(object):
786
786
 
787
787
  self.top_training_layout.addItem(self.horizontalSpacer)
788
788
 
789
- self.delete_image_box = QCheckBox(self.model_training_tab)
790
- self.delete_image_box.setObjectName("delete_image_box")
789
+ self.calibrate_checkbox = QCheckBox(self.model_training_tab)
790
+ self.calibrate_checkbox.setObjectName("calibrate_checkbox")
791
791
  sizePolicy1.setHeightForWidth(
792
- self.delete_image_box.sizePolicy().hasHeightForWidth()
792
+ self.calibrate_checkbox.sizePolicy().hasHeightForWidth()
793
793
  )
794
- self.delete_image_box.setSizePolicy(sizePolicy1)
795
- self.delete_image_box.setChecked(True)
794
+ self.calibrate_checkbox.setSizePolicy(sizePolicy1)
795
+ self.calibrate_checkbox.setChecked(True)
796
+
797
+ self.top_training_layout.addWidget(self.calibrate_checkbox)
798
+
799
+ self.horizontalLayout_84 = QHBoxLayout()
800
+ self.horizontalLayout_84.setSpacing(3)
801
+ self.horizontalLayout_84.setObjectName("horizontalLayout_84")
802
+ self.horizontalLayout_84.setContentsMargins(10, -1, -1, -1)
803
+ self.calibrate_label = QLabel(self.model_training_tab)
804
+ self.calibrate_label.setObjectName("calibrate_label")
805
+ sizePolicy1.setHeightForWidth(
806
+ self.calibrate_label.sizePolicy().hasHeightForWidth()
807
+ )
808
+ self.calibrate_label.setSizePolicy(sizePolicy1)
809
+
810
+ self.horizontalLayout_84.addWidget(self.calibrate_label)
811
+
812
+ self.calibration_spinbox = QSpinBox(self.model_training_tab)
813
+ self.calibration_spinbox.setObjectName("calibration_spinbox")
814
+ sizePolicy1.setHeightForWidth(
815
+ self.calibration_spinbox.sizePolicy().hasHeightForWidth()
816
+ )
817
+ self.calibration_spinbox.setSizePolicy(sizePolicy1)
818
+ self.calibration_spinbox.setMinimum(10)
819
+ self.calibration_spinbox.setMaximum(50)
820
+ self.calibration_spinbox.setValue(15)
821
+
822
+ self.horizontalLayout_84.addWidget(self.calibration_spinbox)
796
823
 
797
- self.top_training_layout.addWidget(self.delete_image_box)
824
+ self.top_training_layout.addLayout(self.horizontalLayout_84)
798
825
 
799
- self.horizontalSpacer_6 = QSpacerItem(
826
+ self.horizontalSpacer_3 = QSpacerItem(
800
827
  10, 5, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum
801
828
  )
802
829
 
803
- self.top_training_layout.addItem(self.horizontalSpacer_6)
830
+ self.top_training_layout.addItem(self.horizontalSpacer_3)
804
831
 
805
832
  self.horizontalLayout_6 = QHBoxLayout()
806
833
  self.horizontalLayout_6.setObjectName("horizontalLayout_6")
@@ -836,18 +863,12 @@ class Ui_PrimaryWindow(object):
836
863
 
837
864
  self.top_training_layout.addLayout(self.horizontalLayout_6)
838
865
 
839
- self.horizontalSpacer_3 = QSpacerItem(
840
- 10, 5, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum
841
- )
842
-
843
- self.top_training_layout.addItem(self.horizontalSpacer_3)
844
-
845
- self.top_training_layout.setStretch(0, 2)
866
+ self.top_training_layout.setStretch(0, 5)
846
867
  self.top_training_layout.setStretch(1, 1)
847
- self.top_training_layout.setStretch(2, 2)
848
- self.top_training_layout.setStretch(3, 1)
849
- self.top_training_layout.setStretch(4, 3)
850
- self.top_training_layout.setStretch(5, 1)
868
+ self.top_training_layout.setStretch(2, 3)
869
+ self.top_training_layout.setStretch(3, 4)
870
+ self.top_training_layout.setStretch(4, 1)
871
+ self.top_training_layout.setStretch(5, 5)
851
872
 
852
873
  self.model_training_layout.addLayout(self.top_training_layout, 0, 0, 1, 1)
853
874
 
@@ -855,10 +876,10 @@ class Ui_PrimaryWindow(object):
855
876
  self.bottom_training_layout.setObjectName("bottom_training_layout")
856
877
  self.train_model_button = QPushButton(self.model_training_tab)
857
878
  self.train_model_button.setObjectName("train_model_button")
858
- sizePolicy3.setHeightForWidth(
879
+ sizePolicy1.setHeightForWidth(
859
880
  self.train_model_button.sizePolicy().hasHeightForWidth()
860
881
  )
861
- self.train_model_button.setSizePolicy(sizePolicy3)
882
+ self.train_model_button.setSizePolicy(sizePolicy1)
862
883
 
863
884
  self.bottom_training_layout.addWidget(self.train_model_button)
864
885
 
@@ -868,56 +889,8 @@ class Ui_PrimaryWindow(object):
868
889
 
869
890
  self.bottom_training_layout.addItem(self.horizontalSpacer_8)
870
891
 
871
- self.calibrate_checkbox = QCheckBox(self.model_training_tab)
872
- self.calibrate_checkbox.setObjectName("calibrate_checkbox")
873
- sizePolicy1.setHeightForWidth(
874
- self.calibrate_checkbox.sizePolicy().hasHeightForWidth()
875
- )
876
- self.calibrate_checkbox.setSizePolicy(sizePolicy1)
877
- self.calibrate_checkbox.setChecked(True)
878
-
879
- self.bottom_training_layout.addWidget(self.calibrate_checkbox)
880
-
881
- self.horizontalSpacer_7 = QSpacerItem(
882
- 10, 10, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum
883
- )
884
-
885
- self.bottom_training_layout.addItem(self.horizontalSpacer_7)
886
-
887
- self.calibrate_label = QLabel(self.model_training_tab)
888
- self.calibrate_label.setObjectName("calibrate_label")
889
- sizePolicy1.setHeightForWidth(
890
- self.calibrate_label.sizePolicy().hasHeightForWidth()
891
- )
892
- self.calibrate_label.setSizePolicy(sizePolicy1)
893
-
894
- self.bottom_training_layout.addWidget(self.calibrate_label)
895
-
896
- self.calibration_spinbox = QSpinBox(self.model_training_tab)
897
- self.calibration_spinbox.setObjectName("calibration_spinbox")
898
- sizePolicy1.setHeightForWidth(
899
- self.calibration_spinbox.sizePolicy().hasHeightForWidth()
900
- )
901
- self.calibration_spinbox.setSizePolicy(sizePolicy1)
902
- self.calibration_spinbox.setMinimum(10)
903
- self.calibration_spinbox.setMaximum(50)
904
- self.calibration_spinbox.setValue(15)
905
-
906
- self.bottom_training_layout.addWidget(self.calibration_spinbox)
907
-
908
- self.horizontalSpacer_78 = QSpacerItem(
909
- 10, 10, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum
910
- )
911
-
912
- self.bottom_training_layout.addItem(self.horizontalSpacer_78)
913
-
914
- self.bottom_training_layout.setStretch(0, 6)
892
+ self.bottom_training_layout.setStretch(0, 5)
915
893
  self.bottom_training_layout.setStretch(1, 5)
916
- self.bottom_training_layout.setStretch(2, 3)
917
- self.bottom_training_layout.setStretch(3, 1)
918
- self.bottom_training_layout.setStretch(4, 3)
919
- self.bottom_training_layout.setStretch(5, 1)
920
- self.bottom_training_layout.setStretch(6, 1)
921
894
 
922
895
  self.model_training_layout.addLayout(self.bottom_training_layout, 1, 0, 1, 1)
923
896
 
@@ -2749,6 +2722,30 @@ class Ui_PrimaryWindow(object):
2749
2722
 
2750
2723
  self.verticalLayout_14.addLayout(self.horizontalLayout_71)
2751
2724
 
2725
+ self.horizontalLayout_79 = QHBoxLayout()
2726
+ self.horizontalLayout_79.setObjectName("horizontalLayout_79")
2727
+ self.horizontalLayout_85 = QHBoxLayout()
2728
+ self.horizontalLayout_85.setObjectName("horizontalLayout_85")
2729
+ self.delete_image_box = QCheckBox(self.hyperparameter_page)
2730
+ self.delete_image_box.setObjectName("delete_image_box")
2731
+ sizePolicy1.setHeightForWidth(
2732
+ self.delete_image_box.sizePolicy().hasHeightForWidth()
2733
+ )
2734
+ self.delete_image_box.setSizePolicy(sizePolicy1)
2735
+ self.delete_image_box.setChecked(True)
2736
+
2737
+ self.horizontalLayout_85.addWidget(self.delete_image_box)
2738
+
2739
+ self.horizontalLayout_79.addLayout(self.horizontalLayout_85)
2740
+
2741
+ self.horizontalSpacer_93 = QSpacerItem(
2742
+ 40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum
2743
+ )
2744
+
2745
+ self.horizontalLayout_79.addItem(self.horizontalSpacer_93)
2746
+
2747
+ self.verticalLayout_14.addLayout(self.horizontalLayout_79)
2748
+
2752
2749
  self.horizontalLayout_74 = QHBoxLayout()
2753
2750
  self.horizontalLayout_74.setObjectName("horizontalLayout_74")
2754
2751
  self.horizontalSpacer_86 = QSpacerItem(
@@ -3033,11 +3030,16 @@ class Ui_PrimaryWindow(object):
3033
3030
  self.label.setText(
3034
3031
  QCoreApplication.translate("PrimaryWindow", "Epochs per image:", None)
3035
3032
  )
3036
- self.delete_image_box.setText(
3037
- QCoreApplication.translate(
3038
- "PrimaryWindow", "Delete images after training", None
3039
- )
3033
+ self.calibrate_checkbox.setText(
3034
+ QCoreApplication.translate("PrimaryWindow", "Calibrate model", None)
3040
3035
  )
3036
+ self.calibrate_label.setText(
3037
+ QCoreApplication.translate("PrimaryWindow", "Calibration set size:", None)
3038
+ )
3039
+ self.calibration_spinbox.setSuffix(
3040
+ QCoreApplication.translate("PrimaryWindow", "%", None)
3041
+ )
3042
+ self.calibration_spinbox.setPrefix("")
3041
3043
  self.label_2.setText(
3042
3044
  QCoreApplication.translate("PrimaryWindow", "Model type:", None)
3043
3045
  )
@@ -3059,15 +3061,6 @@ class Ui_PrimaryWindow(object):
3059
3061
  "PrimaryWindow", "Train classification model", None
3060
3062
  )
3061
3063
  )
3062
- self.calibrate_checkbox.setText(
3063
- QCoreApplication.translate("PrimaryWindow", "Calibrate model", None)
3064
- )
3065
- self.calibrate_label.setText(
3066
- QCoreApplication.translate("PrimaryWindow", "Calibration set size:", None)
3067
- )
3068
- self.calibration_spinbox.setSuffix(
3069
- QCoreApplication.translate("PrimaryWindow", "%", None)
3070
- )
3071
3064
  self.lower_tab_widget.setTabText(
3072
3065
  self.lower_tab_widget.indexOf(self.model_training_tab),
3073
3066
  QCoreApplication.translate("PrimaryWindow", "Model training", None),
@@ -3296,6 +3289,11 @@ class Ui_PrimaryWindow(object):
3296
3289
  self.training_epochs_label.setText(
3297
3290
  QCoreApplication.translate("PrimaryWindow", "Epochs:", None)
3298
3291
  )
3292
+ self.delete_image_box.setText(
3293
+ QCoreApplication.translate(
3294
+ "PrimaryWindow", "Delete images after training", None
3295
+ )
3296
+ )
3299
3297
  self.reset_hyperparams_button.setText(
3300
3298
  QCoreApplication.translate("PrimaryWindow", "reset to defaults", None)
3301
3299
  )
@@ -3306,7 +3304,9 @@ class Ui_PrimaryWindow(object):
3306
3304
  "- Batch size: number of examples in each training iteration\n"
3307
3305
  "- Learning rate: step size for adjusting model weights\n"
3308
3306
  "- Momentum: amount of past gradients to use in the current update. Typically between 0.6 and 0.99\n"
3309
- "- Epochs: number of passes over the training data",
3307
+ "- Epochs: number of passes over the training data\n"
3308
+ "\n"
3309
+ "You can also choose whether to delete images created by the training process once it's finished. This is recommended unless you are debugging the training code.",
3310
3310
  None,
3311
3311
  )
3312
3312
  )