boris-behav-obs 9.7.12__py3-none-any.whl → 9.8.2__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.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +4 -3
- boris/add_modifier.py +1 -1
- boris/advanced_event_filtering.py +1 -1
- boris/analysis_plugins/export_to_feral.py +336 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +2 -2
- boris/behav_coding_map_creator.py +1 -1
- boris/behavior_binary_table.py +1 -1
- boris/behaviors_coding_map.py +1 -1
- boris/boris_cli.py +1 -1
- boris/cmd_arguments.py +1 -1
- boris/coding_pad.py +1 -1
- boris/config.py +15 -3
- boris/config_file.py +18 -19
- boris/connections.py +12 -13
- boris/converters.py +1 -1
- boris/converters_ui.py +2 -3
- boris/cooccurence.py +1 -1
- boris/core.py +168 -166
- boris/core_qrc.py +1830 -1967
- boris/core_ui.py +1 -1
- boris/db_functions.py +5 -14
- boris/dialog.py +24 -24
- boris/edit_event.py +1 -1
- boris/event_operations.py +1 -1
- boris/events_cursor.py +1 -1
- boris/events_snapshots.py +133 -78
- boris/exclusion_matrix.py +1 -1
- boris/export_events.py +49 -43
- boris/export_observation.py +1 -1
- boris/external_processes.py +1 -1
- boris/geometric_measurement.py +1 -1
- boris/gui_utilities.py +1 -1
- boris/image_overlay.py +1 -1
- boris/import_observations.py +1 -1
- boris/ipc_mpv.py +1 -1
- boris/irr.py +1 -1
- boris/latency.py +1 -1
- boris/measurement_widget.py +1 -1
- boris/media_file.py +1 -1
- boris/menu_options.py +14 -12
- boris/modifier_coding_map_creator.py +1 -1
- boris/modifiers_coding_map.py +1 -1
- boris/observation.py +13 -14
- boris/observation_operations.py +1 -1
- boris/observations_list.py +1 -1
- boris/otx_parser.py +1 -1
- boris/param_panel.py +1 -1
- boris/player_dock_widget.py +1 -1
- boris/plot_data_module.py +1 -1
- boris/plot_events.py +1 -1
- boris/plot_events_rt.py +1 -1
- boris/plot_spectrogram_rt.py +42 -73
- boris/plot_waveform_rt.py +1 -1
- boris/plugins.py +1 -1
- boris/preferences.py +35 -4
- boris/preferences_ui.py +48 -18
- boris/project.py +1 -1
- boris/project_functions.py +19 -22
- boris/project_import_export.py +1 -1
- boris/select_modifiers.py +1 -1
- boris/select_observations.py +22 -23
- boris/select_subj_behav.py +4 -4
- boris/state_events.py +1 -1
- boris/subjects_pad.py +1 -1
- boris/synthetic_time_budget.py +1 -1
- boris/time_budget_functions.py +1 -1
- boris/time_budget_widget.py +1 -1
- boris/transitions.py +1 -1
- boris/utilities.py +1 -1
- boris/version.py +3 -3
- boris/video_equalizer.py +1 -1
- boris/video_operations.py +1 -1
- boris/view_df.py +28 -4
- boris/write_event.py +1 -1
- {boris_behav_obs-9.7.12.dist-info → boris_behav_obs-9.8.2.dist-info}/METADATA +2 -2
- boris_behav_obs-9.8.2.dist-info/RECORD +110 -0
- {boris_behav_obs-9.7.12.dist-info → boris_behav_obs-9.8.2.dist-info}/WHEEL +1 -1
- boris/analysis_plugins/_export_to_feral.py +0 -225
- boris_behav_obs-9.7.12.dist-info/RECORD +0 -110
- {boris_behav_obs-9.7.12.dist-info → boris_behav_obs-9.8.2.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.7.12.dist-info → boris_behav_obs-9.8.2.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.7.12.dist-info → boris_behav_obs-9.8.2.dist-info}/top_level.txt +0 -0
boris/plot_spectrogram_rt.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2026 Olivier Friard
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
This program is free software; you can redistribute it and/or modify
|
|
@@ -183,7 +183,7 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
183
183
|
|
|
184
184
|
return {"media_length": self.media_length, "frame_rate": self.frame_rate}
|
|
185
185
|
|
|
186
|
-
def plot_spectro(self, current_time: float, force_plot: bool = False) -> tuple[float, bool]:
|
|
186
|
+
def plot_spectro(self, current_time: float | None, force_plot: bool = False) -> tuple[float, bool] | None:
|
|
187
187
|
"""
|
|
188
188
|
plot sound spectrogram centered on the current time
|
|
189
189
|
|
|
@@ -192,6 +192,31 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
192
192
|
force_plot (bool): force plot even if media paused
|
|
193
193
|
"""
|
|
194
194
|
|
|
195
|
+
def spectrogram(self, x, window_type, nfft, noverlap, vmin, vmax) -> None:
|
|
196
|
+
window = matplotlib.mlab.window_hanning
|
|
197
|
+
|
|
198
|
+
if window_type == "hanning":
|
|
199
|
+
window = matplotlib.mlab.window_hanning
|
|
200
|
+
|
|
201
|
+
if window_type == "hamming":
|
|
202
|
+
window = signal.get_window(window_type, nfft)
|
|
203
|
+
|
|
204
|
+
if window_type == "blackmanharris":
|
|
205
|
+
window = signal.get_window(window_type, nfft)
|
|
206
|
+
|
|
207
|
+
# print(f"{self.frame_rate=} {vmin=} {vmax=} {window_type=} {nfft=} {noverlap=}")
|
|
208
|
+
self.ax.specgram(
|
|
209
|
+
x,
|
|
210
|
+
mode="psd",
|
|
211
|
+
NFFT=nfft,
|
|
212
|
+
Fs=self.frame_rate,
|
|
213
|
+
noverlap=noverlap,
|
|
214
|
+
window=window,
|
|
215
|
+
cmap=self.spectro_color_map,
|
|
216
|
+
vmin=vmin,
|
|
217
|
+
vmax=vmax,
|
|
218
|
+
)
|
|
219
|
+
|
|
195
220
|
if not force_plot and current_time == self.time_mem:
|
|
196
221
|
return
|
|
197
222
|
|
|
@@ -199,40 +224,21 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
199
224
|
|
|
200
225
|
self.ax.clear()
|
|
201
226
|
|
|
202
|
-
window_type =
|
|
227
|
+
window_type = self.config_param.get(cfg.SPECTROGRAM_WINDOW_TYPE, cfg.SPECTROGRAM_DEFAULT_WINDOW_TYPE)
|
|
203
228
|
nfft = int(self.config_param.get(cfg.SPECTROGRAM_NFFT, cfg.SPECTROGRAM_DEFAULT_NFFT))
|
|
204
229
|
noverlap = self.config_param.get(cfg.SPECTROGRAM_NOVERLAP, cfg.SPECTROGRAM_DEFAULT_NOVERLAP)
|
|
205
|
-
|
|
206
|
-
|
|
230
|
+
if self.config_param.get(cfg.SPECTROGRAM_USE_VMIN_VMAX, cfg.SPECTROGRAM_USE_VMIN_VMAX_DEFAULT):
|
|
231
|
+
vmin = self.config_param.get(cfg.SPECTROGRAM_VMIN, cfg.SPECTROGRAM_DEFAULT_VMIN)
|
|
232
|
+
vmax = self.config_param.get(cfg.SPECTROGRAM_VMAX, cfg.SPECTROGRAM_DEFAULT_VMAX)
|
|
233
|
+
else:
|
|
234
|
+
vmin, vmax = None, None
|
|
207
235
|
|
|
208
236
|
if current_time is None:
|
|
209
237
|
return
|
|
210
238
|
|
|
211
239
|
# start
|
|
212
240
|
if current_time <= self.interval / 2:
|
|
213
|
-
self.
|
|
214
|
-
self.sound_info[: int(self.interval * self.frame_rate)],
|
|
215
|
-
mode="psd",
|
|
216
|
-
NFFT=nfft,
|
|
217
|
-
Fs=self.frame_rate,
|
|
218
|
-
noverlap=noverlap,
|
|
219
|
-
window=signal.get_window(window_type, nfft),
|
|
220
|
-
# matplotlib.mlab.window_hanning
|
|
221
|
-
# if window_type == "hanning"
|
|
222
|
-
# else matplotlib.mlab.window_hamming
|
|
223
|
-
# if window_type == "hamming"
|
|
224
|
-
# else matplotlib.mlab.window_blackmanharris
|
|
225
|
-
# if window_type == "blackmanharris"
|
|
226
|
-
# else matplotlib.mlab.window_hanning,
|
|
227
|
-
cmap=self.spectro_color_map,
|
|
228
|
-
vmin=vmin,
|
|
229
|
-
vmax=vmax,
|
|
230
|
-
# mode="psd",
|
|
231
|
-
## NFFT=1024,
|
|
232
|
-
# Fs=self.frame_rate,
|
|
233
|
-
## noverlap=900,
|
|
234
|
-
# cmap=self.spectro_color_map,
|
|
235
|
-
)
|
|
241
|
+
spectrogram(self, self.sound_info[: int(self.interval * self.frame_rate)], window_type, nfft, noverlap, vmin, vmax)
|
|
236
242
|
|
|
237
243
|
self.ax.set_xlim(current_time - self.interval / 2, current_time + self.interval / 2)
|
|
238
244
|
|
|
@@ -242,29 +248,7 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
242
248
|
elif current_time >= self.media_length - self.interval / 2:
|
|
243
249
|
i = int(round(len(self.sound_info) - (self.interval * self.frame_rate), 0))
|
|
244
250
|
|
|
245
|
-
self.
|
|
246
|
-
self.sound_info[i:],
|
|
247
|
-
mode="psd",
|
|
248
|
-
NFFT=nfft,
|
|
249
|
-
Fs=self.frame_rate,
|
|
250
|
-
noverlap=noverlap,
|
|
251
|
-
window=signal.get_window(window_type, nfft),
|
|
252
|
-
# matplotlib.mlab.window_hanning
|
|
253
|
-
# if window_type == "hanning"
|
|
254
|
-
# else matplotlib.mlab.window_hamming
|
|
255
|
-
# if window_type == "hamming"
|
|
256
|
-
# else matplotlib.mlab.window_blackmanharris
|
|
257
|
-
# if window_type == "blackmanharris"
|
|
258
|
-
# else matplotlib.mlab.window_hanning,
|
|
259
|
-
cmap=self.spectro_color_map,
|
|
260
|
-
vmin=vmin,
|
|
261
|
-
vmax=vmax,
|
|
262
|
-
# mode="psd",
|
|
263
|
-
## NFFT=1024,
|
|
264
|
-
# Fs=self.frame_rate,
|
|
265
|
-
## noverlap=900,
|
|
266
|
-
# cmap=self.spectro_color_map,
|
|
267
|
-
)
|
|
251
|
+
spectrogram(self, self.sound_info[i:], window_type, nfft, noverlap, vmin, vmax)
|
|
268
252
|
|
|
269
253
|
lim1 = current_time - (self.media_length - self.interval / 2)
|
|
270
254
|
lim2 = lim1 + self.interval
|
|
@@ -279,32 +263,18 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
279
263
|
|
|
280
264
|
# middle
|
|
281
265
|
else:
|
|
282
|
-
|
|
266
|
+
spectrogram(
|
|
267
|
+
self,
|
|
283
268
|
self.sound_info[
|
|
284
269
|
int(round((current_time - self.interval / 2) * self.frame_rate, 0)) : int(
|
|
285
270
|
round((current_time + self.interval / 2) * self.frame_rate, 0)
|
|
286
271
|
)
|
|
287
272
|
],
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
# matplotlib.mlab.window_hanning
|
|
294
|
-
# if window_type == "hanning"
|
|
295
|
-
# else matplotlib.mlab.window_hamming
|
|
296
|
-
# if window_type == "hamming"
|
|
297
|
-
# else matplotlib.mlab.window_blackmanharris
|
|
298
|
-
# if window_type == "blackmanharris"
|
|
299
|
-
# else matplotlib.mlab.window_hanning,
|
|
300
|
-
cmap=self.spectro_color_map,
|
|
301
|
-
vmin=vmin,
|
|
302
|
-
vmax=vmax,
|
|
303
|
-
# mode="psd",
|
|
304
|
-
## NFFT=1024,
|
|
305
|
-
# Fs=self.frame_rate,
|
|
306
|
-
## noverlap=900,
|
|
307
|
-
# cmap=self.spectro_color_map,
|
|
273
|
+
window_type,
|
|
274
|
+
nfft,
|
|
275
|
+
noverlap,
|
|
276
|
+
vmin,
|
|
277
|
+
vmax,
|
|
308
278
|
)
|
|
309
279
|
|
|
310
280
|
self.ax.xaxis.set_major_locator(mticker.FixedLocator(self.ax.get_xticks().tolist()))
|
|
@@ -314,6 +284,5 @@ class Plot_spectrogram_RT(QWidget):
|
|
|
314
284
|
self.ax.axvline(x=self.interval / 2, color=self.cursor_color, linestyle="-")
|
|
315
285
|
|
|
316
286
|
self.ax.set_ylim(self.sb_freq_min.value(), self.sb_freq_max.value())
|
|
317
|
-
"""self.figure.subplots_adjust(wspace=0, hspace=0)"""
|
|
318
287
|
|
|
319
288
|
self.canvas.draw()
|
boris/plot_waveform_rt.py
CHANGED
boris/plugins.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2026 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This program is free software; you can redistribute it and/or modify
|
|
7
7
|
it under the terms of the GNU General Public License as published by
|
boris/preferences.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2026 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -55,14 +55,38 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
55
55
|
self.pbOK.clicked.connect(self.accept)
|
|
56
56
|
self.pbCancel.clicked.connect(self.reject)
|
|
57
57
|
|
|
58
|
+
self.pb_reset_spectro_values.clicked.connect(self.reset_spectro_values)
|
|
59
|
+
self.cb_use_vmin_vmax.toggled.connect(self.cb_vmin_vmax_changed)
|
|
60
|
+
|
|
58
61
|
self.flag_refresh = False
|
|
59
62
|
|
|
60
63
|
# Create a monospace QFont
|
|
61
|
-
monospace_font = QFont("Courier New")
|
|
64
|
+
monospace_font = QFont("Courier New")
|
|
62
65
|
monospace_font.setStyleHint(QFont.Monospace)
|
|
63
66
|
monospace_font.setPointSize(12)
|
|
64
67
|
self.pte_plugin_code.setFont(monospace_font)
|
|
65
68
|
|
|
69
|
+
def reset_spectro_values(self):
|
|
70
|
+
"""
|
|
71
|
+
reset spectrogram values to default
|
|
72
|
+
"""
|
|
73
|
+
self.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(cfg.SPECTROGRAM_DEFAULT_COLOR_MAP))
|
|
74
|
+
self.sb_time_interval.setValue(cfg.SPECTROGRAM_DEFAULT_TIME_INTERVAL)
|
|
75
|
+
self.cb_window_type.setCurrentText(cfg.SPECTROGRAM_DEFAULT_WINDOW_TYPE)
|
|
76
|
+
self.cb_NFFT.setCurrentText(cfg.SPECTROGRAM_DEFAULT_NFFT)
|
|
77
|
+
self.sb_noverlap.setValue(cfg.SPECTROGRAM_DEFAULT_NOVERLAP)
|
|
78
|
+
self.cb_use_vmin_vmax.setChecked(cfg.SPECTROGRAM_USE_VMIN_VMAX_DEFAULT)
|
|
79
|
+
self.cb_vmin_vmax_changed()
|
|
80
|
+
self.sb_vmin.setValue(cfg.SPECTROGRAM_DEFAULT_VMIN)
|
|
81
|
+
self.sb_vmax.setValue(cfg.SPECTROGRAM_DEFAULT_VMAX)
|
|
82
|
+
|
|
83
|
+
def cb_vmin_vmax_changed(self):
|
|
84
|
+
"""
|
|
85
|
+
activate or de-activate vmin and vmax
|
|
86
|
+
"""
|
|
87
|
+
for w in (self.sb_vmin, self.sb_vmax, self.label_vmin, self.label_vmin_2, self.label_vmax, self.label_vmax_2):
|
|
88
|
+
w.setEnabled(self.cb_use_vmin_vmax.isChecked())
|
|
89
|
+
|
|
66
90
|
def browse_plugins_dir(self):
|
|
67
91
|
"""
|
|
68
92
|
get the personal plugins directory
|
|
@@ -99,7 +123,7 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
99
123
|
dialog.MessageDialog(
|
|
100
124
|
"BORIS",
|
|
101
125
|
("Refresh will re-initialize all your preferences and close BORIS"),
|
|
102
|
-
|
|
126
|
+
(cfg.CANCEL, "Refresh preferences"),
|
|
103
127
|
)
|
|
104
128
|
== "Refresh preferences"
|
|
105
129
|
):
|
|
@@ -348,6 +372,11 @@ def preferences(self):
|
|
|
348
372
|
preferencesWindow.cb_NFFT.setCurrentText(self.config_param.get(cfg.SPECTROGRAM_NFFT, cfg.SPECTROGRAM_DEFAULT_NFFT))
|
|
349
373
|
# noverlap
|
|
350
374
|
preferencesWindow.sb_noverlap.setValue(self.config_param.get(cfg.SPECTROGRAM_NOVERLAP, cfg.SPECTROGRAM_DEFAULT_NOVERLAP))
|
|
375
|
+
# use vmin/xmax
|
|
376
|
+
preferencesWindow.cb_use_vmin_vmax.setChecked(
|
|
377
|
+
self.config_param.get(cfg.SPECTROGRAM_USE_VMIN_VMAX, cfg.SPECTROGRAM_USE_VMIN_VMAX_DEFAULT)
|
|
378
|
+
)
|
|
379
|
+
preferencesWindow.cb_vmin_vmax_changed()
|
|
351
380
|
# vmin
|
|
352
381
|
preferencesWindow.sb_vmin.setValue(self.config_param.get(cfg.SPECTROGRAM_VMIN, cfg.SPECTROGRAM_DEFAULT_VMIN))
|
|
353
382
|
# vmax
|
|
@@ -370,7 +399,7 @@ def preferences(self):
|
|
|
370
399
|
|
|
371
400
|
while True:
|
|
372
401
|
if preferencesWindow.exec():
|
|
373
|
-
if preferencesWindow.sb_vmin.value() >= preferencesWindow.sb_vmax.value():
|
|
402
|
+
if preferencesWindow.cb_use_vmin_vmax.isChecked() and preferencesWindow.sb_vmin.value() >= preferencesWindow.sb_vmax.value():
|
|
374
403
|
QMessageBox.warning(self, cfg.programName, "Spectrogram parameters: the vmin value must be lower than the vmax value.")
|
|
375
404
|
continue
|
|
376
405
|
|
|
@@ -484,6 +513,8 @@ def preferences(self):
|
|
|
484
513
|
self.config_param[cfg.SPECTROGRAM_NFFT] = preferencesWindow.cb_NFFT.currentText()
|
|
485
514
|
# noverlap
|
|
486
515
|
self.config_param[cfg.SPECTROGRAM_NOVERLAP] = preferencesWindow.sb_noverlap.value()
|
|
516
|
+
# use vmin/vmax
|
|
517
|
+
self.config_param[cfg.SPECTROGRAM_USE_VMIN_VMAX] = preferencesWindow.cb_use_vmin_vmax.isChecked()
|
|
487
518
|
# vmin
|
|
488
519
|
self.config_param[cfg.SPECTROGRAM_VMIN] = preferencesWindow.sb_vmin.value()
|
|
489
520
|
# vmax
|
boris/preferences_ui.py
CHANGED
|
@@ -27,7 +27,7 @@ class Ui_prefDialog(object):
|
|
|
27
27
|
if not prefDialog.objectName():
|
|
28
28
|
prefDialog.setObjectName(u"prefDialog")
|
|
29
29
|
prefDialog.setWindowModality(Qt.WindowModality.WindowModal)
|
|
30
|
-
prefDialog.resize(
|
|
30
|
+
prefDialog.resize(899, 757)
|
|
31
31
|
self.horizontalLayout_17 = QHBoxLayout(prefDialog)
|
|
32
32
|
self.horizontalLayout_17.setObjectName(u"horizontalLayout_17")
|
|
33
33
|
self.verticalLayout_2 = QVBoxLayout()
|
|
@@ -413,6 +413,20 @@ class Ui_prefDialog(object):
|
|
|
413
413
|
self.groupBox.setObjectName(u"groupBox")
|
|
414
414
|
self.verticalLayout_8 = QVBoxLayout(self.groupBox)
|
|
415
415
|
self.verticalLayout_8.setObjectName(u"verticalLayout_8")
|
|
416
|
+
self.horizontalLayout_24 = QHBoxLayout()
|
|
417
|
+
self.horizontalLayout_24.setObjectName(u"horizontalLayout_24")
|
|
418
|
+
self.pb_reset_spectro_values = QPushButton(self.groupBox)
|
|
419
|
+
self.pb_reset_spectro_values.setObjectName(u"pb_reset_spectro_values")
|
|
420
|
+
|
|
421
|
+
self.horizontalLayout_24.addWidget(self.pb_reset_spectro_values)
|
|
422
|
+
|
|
423
|
+
self.horizontalSpacer_9 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
424
|
+
|
|
425
|
+
self.horizontalLayout_24.addItem(self.horizontalSpacer_9)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
self.verticalLayout_8.addLayout(self.horizontalLayout_24)
|
|
429
|
+
|
|
416
430
|
self.horizontalLayout_7 = QHBoxLayout()
|
|
417
431
|
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
|
|
418
432
|
self.label_7 = QLabel(self.groupBox)
|
|
@@ -521,12 +535,26 @@ class Ui_prefDialog(object):
|
|
|
521
535
|
|
|
522
536
|
self.verticalLayout_8.addLayout(self.horizontalLayout_20)
|
|
523
537
|
|
|
538
|
+
self.horizontalLayout_25 = QHBoxLayout()
|
|
539
|
+
self.horizontalLayout_25.setObjectName(u"horizontalLayout_25")
|
|
540
|
+
self.cb_use_vmin_vmax = QCheckBox(self.groupBox)
|
|
541
|
+
self.cb_use_vmin_vmax.setObjectName(u"cb_use_vmin_vmax")
|
|
542
|
+
|
|
543
|
+
self.horizontalLayout_25.addWidget(self.cb_use_vmin_vmax)
|
|
544
|
+
|
|
545
|
+
self.horizontalSpacer_10 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
546
|
+
|
|
547
|
+
self.horizontalLayout_25.addItem(self.horizontalSpacer_10)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
self.verticalLayout_8.addLayout(self.horizontalLayout_25)
|
|
551
|
+
|
|
524
552
|
self.horizontalLayout_21 = QHBoxLayout()
|
|
525
553
|
self.horizontalLayout_21.setObjectName(u"horizontalLayout_21")
|
|
526
|
-
self.
|
|
527
|
-
self.
|
|
554
|
+
self.label_vmin = QLabel(self.groupBox)
|
|
555
|
+
self.label_vmin.setObjectName(u"label_vmin")
|
|
528
556
|
|
|
529
|
-
self.horizontalLayout_21.addWidget(self.
|
|
557
|
+
self.horizontalLayout_21.addWidget(self.label_vmin)
|
|
530
558
|
|
|
531
559
|
self.sb_vmin = QSpinBox(self.groupBox)
|
|
532
560
|
self.sb_vmin.setObjectName(u"sb_vmin")
|
|
@@ -536,10 +564,10 @@ class Ui_prefDialog(object):
|
|
|
536
564
|
|
|
537
565
|
self.horizontalLayout_21.addWidget(self.sb_vmin)
|
|
538
566
|
|
|
539
|
-
self.
|
|
540
|
-
self.
|
|
567
|
+
self.label_vmin_2 = QLabel(self.groupBox)
|
|
568
|
+
self.label_vmin_2.setObjectName(u"label_vmin_2")
|
|
541
569
|
|
|
542
|
-
self.horizontalLayout_21.addWidget(self.
|
|
570
|
+
self.horizontalLayout_21.addWidget(self.label_vmin_2)
|
|
543
571
|
|
|
544
572
|
self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
545
573
|
|
|
@@ -550,10 +578,10 @@ class Ui_prefDialog(object):
|
|
|
550
578
|
|
|
551
579
|
self.horizontalLayout_22 = QHBoxLayout()
|
|
552
580
|
self.horizontalLayout_22.setObjectName(u"horizontalLayout_22")
|
|
553
|
-
self.
|
|
554
|
-
self.
|
|
581
|
+
self.label_vmax = QLabel(self.groupBox)
|
|
582
|
+
self.label_vmax.setObjectName(u"label_vmax")
|
|
555
583
|
|
|
556
|
-
self.horizontalLayout_22.addWidget(self.
|
|
584
|
+
self.horizontalLayout_22.addWidget(self.label_vmax)
|
|
557
585
|
|
|
558
586
|
self.sb_vmax = QSpinBox(self.groupBox)
|
|
559
587
|
self.sb_vmax.setObjectName(u"sb_vmax")
|
|
@@ -563,10 +591,10 @@ class Ui_prefDialog(object):
|
|
|
563
591
|
|
|
564
592
|
self.horizontalLayout_22.addWidget(self.sb_vmax)
|
|
565
593
|
|
|
566
|
-
self.
|
|
567
|
-
self.
|
|
594
|
+
self.label_vmax_2 = QLabel(self.groupBox)
|
|
595
|
+
self.label_vmax_2.setObjectName(u"label_vmax_2")
|
|
568
596
|
|
|
569
|
-
self.horizontalLayout_22.addWidget(self.
|
|
597
|
+
self.horizontalLayout_22.addWidget(self.label_vmax_2)
|
|
570
598
|
|
|
571
599
|
self.horizontalSpacer_8 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
572
600
|
|
|
@@ -695,7 +723,7 @@ class Ui_prefDialog(object):
|
|
|
695
723
|
|
|
696
724
|
self.retranslateUi(prefDialog)
|
|
697
725
|
|
|
698
|
-
self.tabWidget.setCurrentIndex(
|
|
726
|
+
self.tabWidget.setCurrentIndex(4)
|
|
699
727
|
|
|
700
728
|
|
|
701
729
|
QMetaObject.connectSlotsByName(prefDialog)
|
|
@@ -739,6 +767,7 @@ class Ui_prefDialog(object):
|
|
|
739
767
|
self.pbBrowseFFmpegCacheDir.setText(QCoreApplication.translate("prefDialog", u"...", None))
|
|
740
768
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_ffmpeg), QCoreApplication.translate("prefDialog", u"FFmpeg framework", None))
|
|
741
769
|
self.groupBox.setTitle(QCoreApplication.translate("prefDialog", u"Spectrogram", None))
|
|
770
|
+
self.pb_reset_spectro_values.setText(QCoreApplication.translate("prefDialog", u"Reset to default values", None))
|
|
742
771
|
self.label_7.setText(QCoreApplication.translate("prefDialog", u"Color map", None))
|
|
743
772
|
self.label_12.setText(QCoreApplication.translate("prefDialog", u"Default time interval (s)", None))
|
|
744
773
|
self.label_16.setText(QCoreApplication.translate("prefDialog", u"Window type", None))
|
|
@@ -753,10 +782,11 @@ class Ui_prefDialog(object):
|
|
|
753
782
|
self.cb_NFFT.setItemText(3, QCoreApplication.translate("prefDialog", u"2048", None))
|
|
754
783
|
|
|
755
784
|
self.label_18.setText(QCoreApplication.translate("prefDialog", u"noverlap", None))
|
|
756
|
-
self.
|
|
757
|
-
self.
|
|
758
|
-
self.
|
|
759
|
-
self.
|
|
785
|
+
self.cb_use_vmin_vmax.setText(QCoreApplication.translate("prefDialog", u"Use vmin/vmax", None))
|
|
786
|
+
self.label_vmin.setText(QCoreApplication.translate("prefDialog", u"vmin", None))
|
|
787
|
+
self.label_vmin_2.setText(QCoreApplication.translate("prefDialog", u"dBFS", None))
|
|
788
|
+
self.label_vmax.setText(QCoreApplication.translate("prefDialog", u"vmax", None))
|
|
789
|
+
self.label_vmax_2.setText(QCoreApplication.translate("prefDialog", u"dBFS", None))
|
|
760
790
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_spectro), QCoreApplication.translate("prefDialog", u"Spectrogram/Wave form", None))
|
|
761
791
|
self.label_10.setText(QCoreApplication.translate("prefDialog", u"<html><head/><body><p>List of colors for behaviors. See <a href=\"https://matplotlib.org/api/colors_api.html\"><span style=\" text-decoration: underline; color:#0000ff;\">matplotlib colors</span></a></p></body></html>", None))
|
|
762
792
|
self.pb_reset_behav_colors.setText(QCoreApplication.translate("prefDialog", u"Reset colors to default", None))
|
boris/project.py
CHANGED
boris/project_functions.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2026 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This program is free software; you can redistribute it and/or modify
|
|
7
7
|
it under the terms of the GNU General Public License as published by
|
|
@@ -22,25 +22,22 @@ Copyright 2012-2025 Olivier Friard
|
|
|
22
22
|
import gzip
|
|
23
23
|
import json
|
|
24
24
|
import logging
|
|
25
|
-
import pandas as pd
|
|
26
|
-
import numpy as np
|
|
27
|
-
from pathlib import Path
|
|
28
25
|
import sys
|
|
29
26
|
from decimal import Decimal as dec
|
|
27
|
+
from pathlib import Path
|
|
30
28
|
from shutil import copyfile
|
|
31
|
-
from typing import List, Tuple
|
|
29
|
+
from typing import Dict, List, Tuple
|
|
32
30
|
|
|
31
|
+
import numpy as np
|
|
32
|
+
import pandas as pd
|
|
33
33
|
import tablib
|
|
34
|
-
from PySide6.QtWidgets import QMessageBox, QTableWidgetItem, QAbstractItemView
|
|
35
34
|
from PySide6.QtCore import Qt
|
|
35
|
+
from PySide6.QtWidgets import QAbstractItemView, QMessageBox, QTableWidgetItem
|
|
36
36
|
|
|
37
37
|
from . import config as cfg
|
|
38
|
-
from . import db_functions
|
|
39
|
-
from . import dialog
|
|
40
|
-
from . import observation_operations
|
|
38
|
+
from . import db_functions, dialog, observation_operations, version
|
|
41
39
|
from . import portion as I
|
|
42
40
|
from . import utilities as util
|
|
43
|
-
from . import version
|
|
44
41
|
|
|
45
42
|
|
|
46
43
|
def check_observation_exhaustivity(
|
|
@@ -350,7 +347,7 @@ def check_state_events_obs(obsId: str, ethogram: dict, observation: dict, time_f
|
|
|
350
347
|
return (False, out) if out else (True, "No problem detected")
|
|
351
348
|
|
|
352
349
|
|
|
353
|
-
def check_state_events(pj: dict, observations_list: list) ->
|
|
350
|
+
def check_state_events(pj: dict, observations_list: list) -> tuple[bool, list]:
|
|
354
351
|
"""
|
|
355
352
|
check if state events are paired in a list of observations
|
|
356
353
|
use check_state_events_obs function
|
|
@@ -425,9 +422,9 @@ def check_project_integrity(
|
|
|
425
422
|
r = check_coded_behaviors(pj)
|
|
426
423
|
if r:
|
|
427
424
|
out += f"The following behaviors are not defined in the ethogram: <b>{', '.join(r)}</b><br>"
|
|
428
|
-
|
|
429
|
-
else:
|
|
430
|
-
|
|
425
|
+
# flag_all_behaviors_defined = False
|
|
426
|
+
# else:
|
|
427
|
+
# flag_all_behaviors_defined = True
|
|
431
428
|
|
|
432
429
|
# check for unpaired state events
|
|
433
430
|
for obs_id in pj[cfg.OBSERVATIONS]:
|
|
@@ -733,13 +730,13 @@ def create_subtitles(pj: dict, selected_observations: list, parameters: dict, ex
|
|
|
733
730
|
mem_command = dialog.MessageDialog(
|
|
734
731
|
cfg.programName,
|
|
735
732
|
f"The file {file_name} already exists.",
|
|
736
|
-
|
|
733
|
+
(
|
|
737
734
|
cfg.OVERWRITE,
|
|
738
735
|
cfg.OVERWRITE_ALL,
|
|
739
736
|
cfg.SKIP,
|
|
740
737
|
cfg.SKIP_ALL,
|
|
741
738
|
cfg.CANCEL,
|
|
742
|
-
|
|
739
|
+
),
|
|
743
740
|
)
|
|
744
741
|
if mem_command == cfg.CANCEL:
|
|
745
742
|
return False, ""
|
|
@@ -840,13 +837,13 @@ def create_subtitles(pj: dict, selected_observations: list, parameters: dict, ex
|
|
|
840
837
|
mem_command = dialog.MessageDialog(
|
|
841
838
|
cfg.programName,
|
|
842
839
|
f"The file {file_name} already exists.",
|
|
843
|
-
|
|
840
|
+
(
|
|
844
841
|
cfg.OVERWRITE,
|
|
845
842
|
cfg.OVERWRITE_ALL,
|
|
846
843
|
cfg.SKIP,
|
|
847
844
|
cfg.SKIP_ALL,
|
|
848
845
|
cfg.CANCEL,
|
|
849
|
-
|
|
846
|
+
),
|
|
850
847
|
)
|
|
851
848
|
if mem_command == cfg.CANCEL:
|
|
852
849
|
return False, ""
|
|
@@ -1148,7 +1145,7 @@ def remove_media_files_path(pj: dict, project_file_name: str) -> bool:
|
|
|
1148
1145
|
f"{',<br>'.join(file_not_found)}"
|
|
1149
1146
|
"<br><br>Are you sure to continue?"
|
|
1150
1147
|
),
|
|
1151
|
-
|
|
1148
|
+
(cfg.YES, cfg.NO),
|
|
1152
1149
|
)
|
|
1153
1150
|
== cfg.NO
|
|
1154
1151
|
):
|
|
@@ -1218,7 +1215,7 @@ def full_path(path: str, project_file_name: str) -> str:
|
|
|
1218
1215
|
return ""
|
|
1219
1216
|
|
|
1220
1217
|
|
|
1221
|
-
def observed_interval(observation: dict) ->
|
|
1218
|
+
def observed_interval(observation: dict) -> tuple[dec, dec] | None:
|
|
1222
1219
|
"""
|
|
1223
1220
|
Observed interval for observation
|
|
1224
1221
|
|
|
@@ -1583,7 +1580,7 @@ def open_project_json(project_file_name: str) -> tuple:
|
|
|
1583
1580
|
copyfile(project_file_name, old_project_file_name)
|
|
1584
1581
|
msg += f"\n\nThe old file project was saved as {old_project_file_name}"
|
|
1585
1582
|
except Exception:
|
|
1586
|
-
QMessageBox.critical(cfg.programName, f"Error saving old project to {old_project_file_name}")
|
|
1583
|
+
QMessageBox.critical(None, cfg.programName, f"Error saving old project to {old_project_file_name}")
|
|
1587
1584
|
|
|
1588
1585
|
pj[cfg.PROJECT_VERSION] = cfg.project_format_version
|
|
1589
1586
|
|
|
@@ -1805,7 +1802,7 @@ def explore_project(self) -> None:
|
|
|
1805
1802
|
if results:
|
|
1806
1803
|
self.results_dialog = dialog.View_explore_project_results()
|
|
1807
1804
|
self.results_dialog.setWindowTitle("Explore project results")
|
|
1808
|
-
self.results_dialog.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
1805
|
+
self.results_dialog.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
|
|
1809
1806
|
self.results_dialog.double_click_signal.connect(double_click_explore_project)
|
|
1810
1807
|
txt = f"<b>{len(results)}</b> events"
|
|
1811
1808
|
txt2 = ""
|
boris/project_import_export.py
CHANGED