boris-behav-obs 8.9.16__py3-none-any.whl → 9.7.6__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.

Potentially problematic release.


This version of boris-behav-obs might be problematic. Click here for more details.

Files changed (129) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +36 -39
  4. boris/add_modifier.py +122 -109
  5. boris/add_modifier_ui.py +239 -135
  6. boris/advanced_event_filtering.py +81 -45
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +42 -49
  23. boris/config.py +161 -77
  24. boris/config_file.py +63 -83
  25. boris/connections.py +112 -57
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2511 -1824
  30. boris/core_qrc.py +15895 -10185
  31. boris/core_ui.py +946 -792
  32. boris/db_functions.py +21 -41
  33. boris/dev.py +134 -0
  34. boris/dialog.py +505 -244
  35. boris/duration_widget.py +15 -20
  36. boris/edit_event.py +84 -28
  37. boris/edit_event_ui.py +214 -78
  38. boris/event_operations.py +517 -415
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +213 -583
  43. boris/export_observation.py +98 -611
  44. boris/external_processes.py +156 -97
  45. boris/geometric_measurement.py +652 -287
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +9 -9
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +26 -63
  51. boris/latency.py +34 -25
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +52 -84
  54. boris/menu_options.py +17 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv.py +1 -0
  58. boris/mpv2.py +732 -705
  59. boris/observation.py +655 -310
  60. boris/observation_operations.py +1036 -404
  61. boris/observation_ui.py +584 -356
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -80
  64. boris/param_panel.py +31 -16
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +90 -60
  67. boris/plot_data_module.py +43 -46
  68. boris/plot_events.py +127 -90
  69. boris/plot_events_rt.py +17 -31
  70. boris/plot_spectrogram_rt.py +95 -30
  71. boris/plot_waveform_rt.py +32 -21
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +18 -8
  74. boris/portion/const.py +35 -18
  75. boris/portion/dict.py +5 -5
  76. boris/portion/func.py +2 -2
  77. boris/portion/interval.py +21 -41
  78. boris/portion/io.py +41 -32
  79. boris/preferences.py +306 -83
  80. boris/preferences_ui.py +685 -228
  81. boris/project.py +448 -293
  82. boris/project_functions.py +689 -254
  83. boris/project_import_export.py +213 -222
  84. boris/project_ui.py +674 -438
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +74 -48
  88. boris/select_observations.py +20 -199
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +53 -37
  91. boris/subjects_pad.py +6 -9
  92. boris/synthetic_time_budget.py +45 -28
  93. boris/time_budget_functions.py +171 -171
  94. boris/time_budget_widget.py +84 -114
  95. boris/transitions.py +41 -47
  96. boris/utilities.py +766 -266
  97. boris/version.py +3 -3
  98. boris/video_equalizer.py +16 -14
  99. boris/video_equalizer_ui.py +199 -130
  100. boris/video_operations.py +125 -28
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
  106. {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/boris_ui.py +0 -886
  111. boris/converters.ui +0 -289
  112. boris/core.qrc +0 -35
  113. boris/core.ui +0 -1543
  114. boris/edit_event.ui +0 -175
  115. boris/icons/logo_eye.ico +0 -0
  116. boris/map_creator.py +0 -850
  117. boris/observation.ui +0 -773
  118. boris/param_panel.ui +0 -379
  119. boris/preferences.ui +0 -537
  120. boris/project.ui +0 -1069
  121. boris/project_server.py +0 -236
  122. boris/vlc.py +0 -10343
  123. boris/vlc_local.py +0 -90
  124. boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
  125. boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
  126. boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
  127. boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
  128. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  129. {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/preferences.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
@@ -22,27 +22,30 @@ This file is part of BORIS.
22
22
 
23
23
  import logging
24
24
  import os
25
- import pathlib
25
+ from pathlib import Path
26
26
  import sys
27
-
28
27
  from . import dialog
29
28
  from . import gui_utilities
30
29
  from . import menu_options
31
- from . import dialog
32
30
  from . import config as cfg
33
31
  from . import config_file
32
+ from . import plugins
34
33
 
35
34
  from .preferences_ui import Ui_prefDialog
36
35
 
37
- from PyQt5.QtWidgets import QDialog, QFileDialog
36
+ from PySide6.QtWidgets import QDialog, QFileDialog, QListWidgetItem, QMessageBox
37
+ from PySide6.QtCore import Qt
38
+ from PySide6.QtGui import QFont
38
39
 
39
40
 
40
41
  class Preferences(QDialog, Ui_prefDialog):
41
42
  def __init__(self, parent=None):
42
-
43
43
  super().__init__()
44
44
  self.setupUi(self)
45
45
 
46
+ # plugins
47
+ self.pb_browse_plugins_dir.clicked.connect(self.browse_plugins_dir)
48
+
46
49
  self.pbBrowseFFmpegCacheDir.clicked.connect(self.browseFFmpegCacheDir)
47
50
 
48
51
  self.pb_reset_behav_colors.clicked.connect(self.reset_behav_colors)
@@ -53,7 +56,40 @@ class Preferences(QDialog, Ui_prefDialog):
53
56
  self.pbCancel.clicked.connect(self.reject)
54
57
 
55
58
  self.flag_refresh = False
56
- self.flag_reset_frames_memory = False
59
+
60
+ # Create a monospace QFont
61
+ monospace_font = QFont("Courier New") # or "Monospace", "Consolas", "Liberation Mono", etc.
62
+ monospace_font.setStyleHint(QFont.Monospace)
63
+ monospace_font.setPointSize(12)
64
+ self.pte_plugin_code.setFont(monospace_font)
65
+
66
+ def browse_plugins_dir(self):
67
+ """
68
+ get the personal plugins directory
69
+ """
70
+ directory = QFileDialog.getExistingDirectory(None, "Select the plugins directory", self.le_personal_plugins_dir.text())
71
+ if not directory:
72
+ return
73
+
74
+ self.le_personal_plugins_dir.setText(directory)
75
+ self.lw_personal_plugins.clear()
76
+ for file_ in Path(directory).glob("*.py"):
77
+ if file_.name.startswith("_"):
78
+ continue
79
+ plugin_name = plugins.get_plugin_name(file_)
80
+ if plugin_name is None:
81
+ continue
82
+ # check if personal plugin name is in BORIS plugins (case sensitive)
83
+ if plugin_name in [self.lv_all_plugins.item(i).text() for i in range(self.lv_all_plugins.count())]:
84
+ continue
85
+ item = QListWidgetItem(plugin_name)
86
+ item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
87
+ item.setCheckState(Qt.Checked)
88
+ item.setData(100, str(file_))
89
+ self.lw_personal_plugins.addItem(item)
90
+
91
+ if self.lw_personal_plugins.count() == 0:
92
+ QMessageBox.warning(self, cfg.programName, f"No plugin found in {directory}")
57
93
 
58
94
  def refresh_preferences(self):
59
95
  """
@@ -62,7 +98,7 @@ class Preferences(QDialog, Ui_prefDialog):
62
98
  if (
63
99
  dialog.MessageDialog(
64
100
  "BORIS",
65
- ("Refresh will re-initialize " "all your preferences and close BORIS"),
101
+ ("Refresh will re-initialize all your preferences and close BORIS"),
66
102
  [cfg.CANCEL, "Refresh preferences"],
67
103
  )
68
104
  == "Refresh preferences"
@@ -74,8 +110,11 @@ class Preferences(QDialog, Ui_prefDialog):
74
110
  """
75
111
  allow user select a cache dir for ffmpeg images
76
112
  """
77
- FFmpegCacheDir = QFileDialog().getExistingDirectory(
78
- self, "Select a directory", os.path.expanduser("~"), options=QFileDialog().ShowDirsOnly
113
+ FFmpegCacheDir = QFileDialog.getExistingDirectory(
114
+ self,
115
+ "Select a directory",
116
+ os.path.expanduser("~"),
117
+ options=QFileDialog.ShowDirsOnly,
79
118
  )
80
119
  if FFmpegCacheDir:
81
120
  self.leFFmpegCacheDir.setText(FFmpegCacheDir)
@@ -102,6 +141,63 @@ def preferences(self):
102
141
  show preferences window
103
142
  """
104
143
 
144
+ def show_plugin_info(item):
145
+ """
146
+ display information about the clicked plugin
147
+ """
148
+
149
+ if item.text() not in self.config_param[cfg.ANALYSIS_PLUGINS]:
150
+ return
151
+
152
+ plugin_path = item.data(100)
153
+
154
+ # Python plugins
155
+ if Path(plugin_path).suffix == ".py":
156
+ import importlib
157
+
158
+ module_name = Path(plugin_path).stem
159
+ spec = importlib.util.spec_from_file_location(module_name, plugin_path)
160
+ plugin_module = importlib.util.module_from_spec(spec)
161
+ spec.loader.exec_module(plugin_module)
162
+ attributes_list = dir(plugin_module)
163
+
164
+ out: list = []
165
+ out.append((plugin_module.__plugin_name__ + "\n") if "__plugin_name__" in attributes_list else "No plugin name provided")
166
+ out.append(plugin_module.__author__ if "__author__" in attributes_list else "No author provided")
167
+ version_str: str = ""
168
+ if "__version__" in attributes_list:
169
+ version_str += str(plugin_module.__version__)
170
+ if "__version_date__" in attributes_list:
171
+ version_str += " " if version_str else ""
172
+ version_str += f"({plugin_module.__version_date__})"
173
+
174
+ out.append(f"Version: {version_str}\n" if version_str else "No version provided")
175
+
176
+ # out.append(plugin_module.run.__doc__.strip())
177
+ # description
178
+ if "__description__" in attributes_list:
179
+ out.append("Description:\n")
180
+ out.append(plugin_module.__description__ if "__description__" in attributes_list else "No description provided")
181
+
182
+ preferencesWindow.pte_plugin_description.setPlainText("\n".join(out))
183
+
184
+ # R plugins
185
+ if Path(plugin_path).suffix == ".R":
186
+ plugin_description = plugins.get_r_plugin_description(plugin_path)
187
+ if plugin_description is not None:
188
+ preferencesWindow.pte_plugin_description.setPlainText("\n".join(plugin_description.split("\\n")))
189
+ else:
190
+ preferencesWindow.pte_plugin_description.setPlainText("No description provided")
191
+
192
+ # display plugin code
193
+ try:
194
+ with open(plugin_path, "r") as f_in:
195
+ plugin_code = f_in.read()
196
+ except Exception:
197
+ plugin_code = "Not available"
198
+
199
+ preferencesWindow.pte_plugin_code.setPlainText(plugin_code)
200
+
105
201
  preferencesWindow = Preferences()
106
202
  preferencesWindow.tabWidget.setCurrentIndex(0)
107
203
 
@@ -125,6 +221,9 @@ def preferences(self):
125
221
  preferencesWindow.cbConfirmSound.setChecked(self.confirmSound)
126
222
  # beep every
127
223
  preferencesWindow.sbBeepEvery.setValue(self.beep_every)
224
+ # frame step size
225
+ # preferencesWindow.sb_frame_step_size.setValue(self.config_param.get(cfg.FRAME_STEP_SIZE, cfg.FRAME_STEP_SIZE_DEFAULT_VALUE))
226
+
128
227
  # alert no focal subject
129
228
  preferencesWindow.cbAlertNoFocalSubject.setChecked(self.alertNoFocalSubject)
130
229
  # tracking cursor above event
@@ -132,7 +231,7 @@ def preferences(self):
132
231
  # check for new version
133
232
  preferencesWindow.cbCheckForNewVersion.setChecked(self.checkForNewVersion)
134
233
  # display subtitles
135
- preferencesWindow.cb_display_subtitles.setChecked(self.config_param[cfg.DISPLAY_SUBTITLES])
234
+ preferencesWindow.cb_display_subtitles.setChecked(self.config_param.get(cfg.DISPLAY_SUBTITLES, False))
136
235
  # pause before add event
137
236
  preferencesWindow.cb_pause_before_addevent.setChecked(self.pause_before_addevent)
138
237
  # MPV hwdec
@@ -143,7 +242,70 @@ def preferences(self):
143
242
  cfg.MPV_HWDEC_OPTIONS.index(self.config_param.get(cfg.MPV_HWDEC, cfg.MPV_HWDEC_DEFAULT_VALUE))
144
243
  )
145
244
  except Exception:
146
- preferencesWindow.cb_hwdec.setCurrentIndex(cfg.MPV_HWDEC_DEFAULT_VALUE)
245
+ preferencesWindow.cb_hwdec.setCurrentIndex(cfg.MPV_HWDEC_OPTIONS.index(cfg.MPV_HWDEC_DEFAULT_VALUE))
246
+ # check integrity
247
+ preferencesWindow.cb_check_integrity_at_opening.setChecked(self.config_param.get(cfg.CHECK_PROJECT_INTEGRITY, True))
248
+
249
+ # BORIS plugins
250
+ preferencesWindow.lv_all_plugins.itemClicked.connect(show_plugin_info)
251
+
252
+ preferencesWindow.lv_all_plugins.clear()
253
+
254
+ for file_ in (Path(__file__).parent / "analysis_plugins").glob("*.py"):
255
+ if file_.name.startswith("_"):
256
+ continue
257
+ plugin_name = plugins.get_plugin_name(file_)
258
+ if plugin_name is not None:
259
+ item = QListWidgetItem(plugin_name)
260
+ item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
261
+ if plugin_name in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
262
+ item.setCheckState(Qt.Unchecked)
263
+ else:
264
+ item.setCheckState(Qt.Checked)
265
+ item.setData(100, str(file_))
266
+ preferencesWindow.lv_all_plugins.addItem(item)
267
+
268
+ # personal plugins
269
+ preferencesWindow.le_personal_plugins_dir.setText(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""))
270
+ preferencesWindow.lw_personal_plugins.itemClicked.connect(show_plugin_info)
271
+
272
+ preferencesWindow.lw_personal_plugins.clear()
273
+ if self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""):
274
+ # Python plugins
275
+ for file_ in Path(self.config_param[cfg.PERSONAL_PLUGINS_DIR]).glob("*.py"):
276
+ if file_.name.startswith("_"):
277
+ continue
278
+ plugin_name = plugins.get_plugin_name(file_)
279
+ if plugin_name is None:
280
+ continue
281
+ # check if personal plugin name is in BORIS plugins (case sensitive)
282
+ if plugin_name in [preferencesWindow.lv_all_plugins.item(i).text() for i in range(preferencesWindow.lv_all_plugins.count())]:
283
+ continue
284
+ item = QListWidgetItem(plugin_name)
285
+ item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
286
+ if plugin_name in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
287
+ item.setCheckState(Qt.Unchecked)
288
+ else:
289
+ item.setCheckState(Qt.Checked)
290
+ item.setData(100, str(file_))
291
+ preferencesWindow.lw_personal_plugins.addItem(item)
292
+
293
+ # R plugins
294
+ for file_ in Path(self.config_param[cfg.PERSONAL_PLUGINS_DIR]).glob("*.R"):
295
+ plugin_name = plugins.get_r_plugin_name(file_)
296
+ if plugin_name is None:
297
+ continue
298
+ # check if personal plugin name is in BORIS plugins (case sensitive)
299
+ if plugin_name in [preferencesWindow.lv_all_plugins.item(i).text() for i in range(preferencesWindow.lv_all_plugins.count())]:
300
+ continue
301
+ item = QListWidgetItem(plugin_name)
302
+ item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
303
+ if plugin_name in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
304
+ item.setCheckState(Qt.Unchecked)
305
+ else:
306
+ item.setCheckState(Qt.Checked)
307
+ item.setData(100, str(file_))
308
+ preferencesWindow.lw_personal_plugins.addItem(item)
147
309
 
148
310
  # PROJET FILE INDENTATION
149
311
  preferencesWindow.combo_project_file_indentation.clear()
@@ -151,7 +313,10 @@ def preferences(self):
151
313
  try:
152
314
  preferencesWindow.combo_project_file_indentation.setCurrentIndex(
153
315
  cfg.PROJECT_FILE_INDENTATION_OPTIONS.index(
154
- self.config_param.get(cfg.PROJECT_FILE_INDENTATION, cfg.PROJECT_FILE_INDENTATION_DEFAULT_VALUE)
316
+ self.config_param.get(
317
+ cfg.PROJECT_FILE_INDENTATION,
318
+ cfg.PROJECT_FILE_INDENTATION_DEFAULT_VALUE,
319
+ )
155
320
  )
156
321
  )
157
322
  except Exception:
@@ -169,27 +334,24 @@ def preferences(self):
169
334
  preferencesWindow.cbSpectrogramColorMap.clear()
170
335
  preferencesWindow.cbSpectrogramColorMap.addItems(cfg.SPECTROGRAM_COLOR_MAPS)
171
336
  try:
172
- preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(
173
- cfg.SPECTROGRAM_COLOR_MAPS.index(self.spectrogram_color_map)
174
- )
337
+ preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(self.spectrogram_color_map))
175
338
  except Exception:
176
- preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(
177
- cfg.SPECTROGRAM_COLOR_MAPS.index(cfg.SPECTROGRAM_DEFAULT_COLOR_MAP)
178
- )
179
-
180
- try:
181
- preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(
182
- cfg.SPECTROGRAM_COLOR_MAPS.index(self.spectrogram_color_map)
183
- )
184
- except Exception:
185
- preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(
186
- cfg.SPECTROGRAM_COLOR_MAPS.index(cfg.SPECTROGRAM_DEFAULT_COLOR_MAP)
187
- )
188
-
339
+ preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(cfg.SPECTROGRAM_DEFAULT_COLOR_MAP))
340
+ # time interval
189
341
  try:
190
342
  preferencesWindow.sb_time_interval.setValue(self.spectrogram_time_interval)
191
343
  except Exception:
192
344
  preferencesWindow.sb_time_interval.setValue(cfg.SPECTROGRAM_DEFAULT_TIME_INTERVAL)
345
+ # window type
346
+ preferencesWindow.cb_window_type.setCurrentText(self.config_param.get(cfg.SPECTROGRAM_WINDOW_TYPE, cfg.SPECTROGRAM_DEFAULT_WINDOW_TYPE))
347
+ # NFFT
348
+ preferencesWindow.cb_NFFT.setCurrentText(self.config_param.get(cfg.SPECTROGRAM_NFFT, cfg.SPECTROGRAM_DEFAULT_NFFT))
349
+ # noverlap
350
+ preferencesWindow.sb_noverlap.setValue(self.config_param.get(cfg.SPECTROGRAM_NOVERLAP, cfg.SPECTROGRAM_DEFAULT_NOVERLAP))
351
+ # vmin
352
+ preferencesWindow.sb_vmin.setValue(self.config_param.get(cfg.SPECTROGRAM_VMIN, cfg.SPECTROGRAM_DEFAULT_VMIN))
353
+ # vmax
354
+ preferencesWindow.sb_vmax.setValue(self.config_param.get(cfg.SPECTROGRAM_VMAX, cfg.SPECTROGRAM_DEFAULT_VMAX))
193
355
 
194
356
  # behavior colors
195
357
  if not self.plot_colors:
@@ -201,87 +363,148 @@ def preferences(self):
201
363
  self.behav_category_colors = cfg.CATEGORY_COLORS_LIST
202
364
  preferencesWindow.te_category_colors.setPlainText("\n".join(self.behav_category_colors))
203
365
 
366
+ # interface
367
+ preferencesWindow.sb_toolbar_icon_size.setValue(self.config_param.get(cfg.TOOLBAR_ICON_SIZE, cfg.DEFAULT_TOOLBAR_ICON_SIZE_VALUE))
368
+
204
369
  gui_utilities.restore_geometry(preferencesWindow, "preferences", (700, 500))
205
370
 
206
- if preferencesWindow.exec_():
371
+ while True:
372
+ if preferencesWindow.exec():
373
+ if preferencesWindow.sb_vmin.value() >= preferencesWindow.sb_vmax.value():
374
+ QMessageBox.warning(self, cfg.programName, "Spectrogram parameters: the vmin value must be lower than the vmax value.")
375
+ continue
207
376
 
208
- gui_utilities.save_geometry(preferencesWindow, "preferences")
377
+ if preferencesWindow.sb_noverlap.value() >= int(preferencesWindow.cb_NFFT.currentText()):
378
+ QMessageBox.warning(self, cfg.programName, "Spectrogram parameters: the noverlap value must be lower than the NFFT value.")
379
+ continue
209
380
 
210
- if preferencesWindow.flag_refresh:
211
- # refresh preferences remove the config file
381
+ gui_utilities.save_geometry(preferencesWindow, "preferences")
212
382
 
213
- logging.debug("flag refresh ")
383
+ if preferencesWindow.flag_refresh:
384
+ # refresh preferences remove the config file
214
385
 
215
- self.config_param["refresh_preferences"] = True
216
- self.close()
217
- # check if refresh canceled for not saved project
218
- if "refresh_preferences" in self.config_param:
219
- if (pathlib.Path.home() / ".boris").exists():
220
- os.remove(pathlib.Path.home() / ".boris")
221
- sys.exit()
386
+ logging.debug("flag refresh ")
222
387
 
223
- if preferencesWindow.cbTimeFormat.currentIndex() == 0:
224
- self.timeFormat = cfg.S
388
+ self.config_param["refresh_preferences"] = True
389
+ self.close()
390
+ # check if refresh canceled for not saved project
391
+ if "refresh_preferences" in self.config_param:
392
+ if (Path.home() / ".boris").exists():
393
+ os.remove(Path.home() / ".boris")
394
+ sys.exit()
225
395
 
226
- if preferencesWindow.cbTimeFormat.currentIndex() == 1:
227
- self.timeFormat = cfg.HHMMSS
396
+ if preferencesWindow.cbTimeFormat.currentIndex() == 0:
397
+ self.timeFormat = cfg.S
228
398
 
229
- self.fast = preferencesWindow.sbffSpeed.value()
399
+ if preferencesWindow.cbTimeFormat.currentIndex() == 1:
400
+ self.timeFormat = cfg.HHMMSS
230
401
 
231
- self.config_param[cfg.ADAPT_FAST_JUMP] = preferencesWindow.cb_adapt_fast_jump.isChecked()
402
+ self.fast = preferencesWindow.sbffSpeed.value()
232
403
 
233
- self.repositioningTimeOffset = preferencesWindow.sbRepositionTimeOffset.value()
404
+ self.config_param[cfg.ADAPT_FAST_JUMP] = preferencesWindow.cb_adapt_fast_jump.isChecked()
234
405
 
235
- self.play_rate_step = preferencesWindow.sbSpeedStep.value()
406
+ self.repositioningTimeOffset = preferencesWindow.sbRepositionTimeOffset.value()
236
407
 
237
- self.automaticBackup = preferencesWindow.sbAutomaticBackup.value()
238
- if self.automaticBackup:
239
- self.automaticBackupTimer.start(self.automaticBackup * 60000)
240
- else:
241
- self.automaticBackupTimer.stop()
408
+ self.play_rate_step = preferencesWindow.sbSpeedStep.value()
242
409
 
243
- self.behav_seq_separator = preferencesWindow.leSeparator.text()
410
+ self.automaticBackup = preferencesWindow.sbAutomaticBackup.value()
411
+ if self.automaticBackup:
412
+ self.automaticBackupTimer.start(self.automaticBackup * 60000)
413
+ else:
414
+ self.automaticBackupTimer.stop()
244
415
 
245
- self.close_the_same_current_event = preferencesWindow.cbCloseSameEvent.isChecked()
416
+ self.behav_seq_separator = preferencesWindow.leSeparator.text()
246
417
 
247
- self.confirmSound = preferencesWindow.cbConfirmSound.isChecked()
418
+ self.close_the_same_current_event = preferencesWindow.cbCloseSameEvent.isChecked()
248
419
 
249
- self.beep_every = preferencesWindow.sbBeepEvery.value()
420
+ self.confirmSound = preferencesWindow.cbConfirmSound.isChecked()
250
421
 
251
- self.alertNoFocalSubject = preferencesWindow.cbAlertNoFocalSubject.isChecked()
422
+ self.beep_every = preferencesWindow.sbBeepEvery.value()
252
423
 
253
- self.trackingCursorAboveEvent = preferencesWindow.cbTrackingCursorAboveEvent.isChecked()
424
+ # frame step size
425
+ # self.config_param[cfg.FRAME_STEP_SIZE] = preferencesWindow.sb_frame_step_size.value()
254
426
 
255
- self.checkForNewVersion = preferencesWindow.cbCheckForNewVersion.isChecked()
427
+ self.alertNoFocalSubject = preferencesWindow.cbAlertNoFocalSubject.isChecked()
256
428
 
257
- self.config_param[cfg.DISPLAY_SUBTITLES] = preferencesWindow.cb_display_subtitles.isChecked()
429
+ self.trackingCursorAboveEvent = preferencesWindow.cbTrackingCursorAboveEvent.isChecked()
258
430
 
259
- self.pause_before_addevent = preferencesWindow.cb_pause_before_addevent.isChecked()
431
+ self.checkForNewVersion = preferencesWindow.cbCheckForNewVersion.isChecked()
260
432
 
261
- # MPV hwdec
262
- self.config_param[cfg.MPV_HWDEC] = cfg.MPV_HWDEC_OPTIONS[preferencesWindow.cb_hwdec.currentIndex()]
433
+ self.config_param[cfg.DISPLAY_SUBTITLES] = preferencesWindow.cb_display_subtitles.isChecked()
263
434
 
264
- # project file indentation
265
- self.config_param[cfg.PROJECT_FILE_INDENTATION] = cfg.PROJECT_FILE_INDENTATION_OPTIONS[
266
- preferencesWindow.combo_project_file_indentation.currentIndex()
267
- ]
435
+ self.pause_before_addevent = preferencesWindow.cb_pause_before_addevent.isChecked()
268
436
 
269
- if self.observationId:
270
- self.load_tw_events(self.observationId)
271
- self.display_statusbar_info(self.observationId)
437
+ # MPV hwdec
438
+ self.config_param[cfg.MPV_HWDEC] = cfg.MPV_HWDEC_OPTIONS[preferencesWindow.cb_hwdec.currentIndex()]
272
439
 
273
- self.ffmpeg_cache_dir = preferencesWindow.leFFmpegCacheDir.text()
440
+ # check project integrity
441
+ self.config_param[cfg.CHECK_PROJECT_INTEGRITY] = preferencesWindow.cb_check_integrity_at_opening.isChecked()
274
442
 
275
- # spectrogram
276
- self.spectrogram_color_map = preferencesWindow.cbSpectrogramColorMap.currentText()
277
- # self.spectrogramHeight = preferencesWindow.sbSpectrogramHeight.value()
278
- self.spectrogram_time_interval = preferencesWindow.sb_time_interval.value()
443
+ # update BORIS analysis plugins
444
+ self.config_param[cfg.ANALYSIS_PLUGINS] = {}
445
+ self.config_param[cfg.EXCLUDED_PLUGINS] = set()
446
+ for i in range(preferencesWindow.lv_all_plugins.count()):
447
+ if preferencesWindow.lv_all_plugins.item(i).checkState() == Qt.Checked:
448
+ self.config_param[cfg.ANALYSIS_PLUGINS][preferencesWindow.lv_all_plugins.item(i).text()] = (
449
+ preferencesWindow.lv_all_plugins.item(i).data(100)
450
+ )
451
+ else:
452
+ self.config_param[cfg.EXCLUDED_PLUGINS].add(preferencesWindow.lv_all_plugins.item(i).text())
453
+
454
+ # update personal plugins
455
+ self.config_param[cfg.PERSONAL_PLUGINS_DIR] = preferencesWindow.le_personal_plugins_dir.text()
456
+ for i in range(preferencesWindow.lw_personal_plugins.count()):
457
+ if preferencesWindow.lw_personal_plugins.item(i).checkState() == Qt.Checked:
458
+ self.config_param[cfg.ANALYSIS_PLUGINS][preferencesWindow.lw_personal_plugins.item(i).text()] = (
459
+ preferencesWindow.lw_personal_plugins.item(i).data(100)
460
+ )
461
+ else:
462
+ self.config_param[cfg.EXCLUDED_PLUGINS].add(preferencesWindow.lw_personal_plugins.item(i).text())
463
+
464
+ plugins.load_plugins(self)
465
+ plugins.add_plugins_to_menu(self)
466
+
467
+ # project file indentation
468
+ self.config_param[cfg.PROJECT_FILE_INDENTATION] = cfg.PROJECT_FILE_INDENTATION_OPTIONS[
469
+ preferencesWindow.combo_project_file_indentation.currentIndex()
470
+ ]
279
471
 
280
- # behav colors
281
- self.plot_colors = preferencesWindow.te_behav_colors.toPlainText().split()
282
- # category colors
283
- self.behav_category_colors = preferencesWindow.te_category_colors.toPlainText().split()
472
+ if self.observationId:
473
+ self.load_tw_events(self.observationId)
474
+ self.display_statusbar_info(self.observationId)
284
475
 
285
- menu_options.update_menu(self)
476
+ self.ffmpeg_cache_dir = preferencesWindow.leFFmpegCacheDir.text()
477
+
478
+ # spectrogram
479
+ self.spectrogram_color_map = preferencesWindow.cbSpectrogramColorMap.currentText()
480
+ self.spectrogram_time_interval = preferencesWindow.sb_time_interval.value()
481
+ # window type
482
+ self.config_param[cfg.SPECTROGRAM_WINDOW_TYPE] = preferencesWindow.cb_window_type.currentText()
483
+ # NFFT
484
+ self.config_param[cfg.SPECTROGRAM_NFFT] = preferencesWindow.cb_NFFT.currentText()
485
+ # noverlap
486
+ self.config_param[cfg.SPECTROGRAM_NOVERLAP] = preferencesWindow.sb_noverlap.value()
487
+ # vmin
488
+ self.config_param[cfg.SPECTROGRAM_VMIN] = preferencesWindow.sb_vmin.value()
489
+ # vmax
490
+ self.config_param[cfg.SPECTROGRAM_VMAX] = preferencesWindow.sb_vmax.value()
491
+
492
+ # behav colors
493
+ self.plot_colors = preferencesWindow.te_behav_colors.toPlainText().split()
494
+ # category colors
495
+ self.behav_category_colors = preferencesWindow.te_category_colors.toPlainText().split()
496
+
497
+ # interface
498
+ self.config_param[cfg.TOOLBAR_ICON_SIZE] = preferencesWindow.sb_toolbar_icon_size.value()
499
+
500
+ menu_options.update_menu(self)
501
+
502
+ config_file.save(self)
503
+
504
+ break
505
+
506
+ else:
507
+ break
286
508
 
287
- config_file.save(self)
509
+ # activate main window
510
+ self.activateWindow()