boris-behav-obs 8.16.5__py3-none-any.whl → 9.7.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.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +24 -36
- boris/add_modifier.py +88 -80
- boris/add_modifier_ui.py +235 -131
- boris/advanced_event_filtering.py +23 -29
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +16 -34
- boris/config.py +102 -50
- boris/config_file.py +55 -64
- boris/connections.py +105 -58
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2108 -1275
- boris/core_qrc.py +15892 -10829
- boris/core_ui.py +941 -806
- boris/db_functions.py +17 -42
- boris/dev.py +27 -7
- boris/dialog.py +461 -242
- boris/duration_widget.py +9 -14
- boris/edit_event.py +61 -31
- boris/edit_event_ui.py +208 -97
- boris/event_operations.py +405 -281
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +180 -203
- boris/export_observation.py +60 -73
- boris/external_processes.py +123 -98
- boris/geometric_measurement.py +427 -218
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +4 -4
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +304 -0
- boris/irr.py +20 -57
- boris/latency.py +31 -24
- boris/measurement_widget.py +14 -18
- boris/media_file.py +17 -19
- boris/menu_options.py +16 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv2.py +128 -35
- boris/observation.py +493 -210
- boris/observation_operations.py +1010 -391
- boris/observation_ui.py +573 -363
- boris/observations_list.py +51 -58
- boris/otx_parser.py +74 -68
- boris/param_panel.py +45 -59
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +91 -56
- boris/plot_data_module.py +18 -53
- boris/plot_events.py +56 -153
- boris/plot_events_rt.py +16 -30
- boris/plot_spectrogram_rt.py +80 -56
- boris/plot_waveform_rt.py +23 -48
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +298 -123
- boris/preferences_ui.py +664 -225
- boris/project.py +293 -270
- boris/project_functions.py +610 -537
- boris/project_import_export.py +204 -213
- boris/project_ui.py +673 -441
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +62 -90
- boris/select_observations.py +19 -197
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +51 -33
- boris/subjects_pad.py +6 -8
- boris/synthetic_time_budget.py +42 -26
- boris/time_budget_functions.py +169 -169
- boris/time_budget_widget.py +77 -89
- boris/transitions.py +41 -41
- boris/utilities.py +562 -222
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +78 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +240 -136
- boris_behav_obs-9.7.1.dist-info/METADATA +140 -0
- boris_behav_obs-9.7.1.dist-info/RECORD +109 -0
- {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.1.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.1.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/converters.ui +0 -289
- boris/core.qrc +0 -37
- boris/core.ui +0 -1571
- boris/edit_event.ui +0 -233
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -982
- boris/observation.ui +0 -814
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1074
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.16.5.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.16.5.dist-info/METADATA +0 -134
- boris_behav_obs-8.16.5.dist-info/RECORD +0 -107
- boris_behav_obs-8.16.5.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.1.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.1.dist-info}/top_level.txt +0 -0
boris/events_cursor.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
This program is free software; you can redistribute it and/or modify
|
|
@@ -21,14 +21,14 @@ Copyright 2012-2023 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
from
|
|
25
|
-
from
|
|
26
|
-
from
|
|
24
|
+
from PySide6.QtCore import QPoint, Qt
|
|
25
|
+
from PySide6.QtGui import QPolygon, QPen, QColor, QBrush, QPainter
|
|
26
|
+
from PySide6.QtWidgets import QStyledItemDelegate
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class StyledItemDelegateTriangle(QStyledItemDelegate):
|
|
30
30
|
"""
|
|
31
|
-
painter for
|
|
31
|
+
painter for tv_events with current time highlighting
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
def __init__(self, row, parent=None):
|
|
@@ -36,18 +36,26 @@ class StyledItemDelegateTriangle(QStyledItemDelegate):
|
|
|
36
36
|
self.row = row
|
|
37
37
|
|
|
38
38
|
def paint(self, painter, option, index):
|
|
39
|
+
"""
|
|
40
|
+
draw a red triangle on ceel corresponfing to current event
|
|
41
|
+
"""
|
|
39
42
|
|
|
40
43
|
super(StyledItemDelegateTriangle, self).paint(painter, option, index)
|
|
41
44
|
|
|
42
|
-
if self.row
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
if self.row == -1:
|
|
46
|
+
return
|
|
47
|
+
if index.row() == self.row:
|
|
48
|
+
triangle = QPolygon(
|
|
49
|
+
[
|
|
50
|
+
QPoint(option.rect.x() + 15, option.rect.y()),
|
|
51
|
+
QPoint(option.rect.x(), option.rect.y() - 5),
|
|
52
|
+
QPoint(option.rect.x(), option.rect.y() + 5),
|
|
53
|
+
]
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
painter.save()
|
|
57
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
58
|
+
painter.setBrush(QBrush(QColor(Qt.red)))
|
|
59
|
+
painter.setPen(QPen(QColor(Qt.red)))
|
|
60
|
+
painter.drawPolygon(triangle)
|
|
61
|
+
painter.restore()
|
boris/events_snapshots.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
This program is free software; you can redistribute it and/or modify
|
|
@@ -21,14 +21,13 @@ Copyright 2012-2023 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
|
|
25
24
|
import logging
|
|
26
25
|
import os
|
|
27
26
|
import pathlib as pl
|
|
28
27
|
import subprocess
|
|
29
28
|
from decimal import Decimal as dec
|
|
30
29
|
|
|
31
|
-
from
|
|
30
|
+
from PySide6.QtWidgets import QApplication, QFileDialog, QInputDialog, QMessageBox
|
|
32
31
|
|
|
33
32
|
from . import config as cfg
|
|
34
33
|
from . import db_functions, dialog, project_functions, select_observations, select_subj_behav
|
|
@@ -80,8 +79,8 @@ def events_snapshots(self):
|
|
|
80
79
|
selected_observations,
|
|
81
80
|
start_coding=dec("NaN"),
|
|
82
81
|
end_coding=dec("NaN"),
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
show_include_modifiers=False,
|
|
83
|
+
show_exclude_non_coded_behaviors=False,
|
|
85
84
|
n_observations=len(selected_observations),
|
|
86
85
|
)
|
|
87
86
|
|
|
@@ -110,11 +109,11 @@ def events_snapshots(self):
|
|
|
110
109
|
return
|
|
111
110
|
|
|
112
111
|
# directory for saving frames
|
|
113
|
-
export_dir = QFileDialog
|
|
112
|
+
export_dir = QFileDialog.getExistingDirectory(
|
|
114
113
|
self,
|
|
115
114
|
"Choose a directory to extract events",
|
|
116
115
|
os.path.expanduser("~"),
|
|
117
|
-
options=QFileDialog
|
|
116
|
+
options=QFileDialog.ShowDirsOnly,
|
|
118
117
|
)
|
|
119
118
|
if not export_dir:
|
|
120
119
|
return
|
|
@@ -128,9 +127,7 @@ def events_snapshots(self):
|
|
|
128
127
|
)
|
|
129
128
|
|
|
130
129
|
for obs_id in selected_observations:
|
|
131
|
-
|
|
132
130
|
for nplayer in self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE]:
|
|
133
|
-
|
|
134
131
|
if not self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer]:
|
|
135
132
|
continue
|
|
136
133
|
duration1 = [] # in seconds
|
|
@@ -138,9 +135,7 @@ def events_snapshots(self):
|
|
|
138
135
|
duration1.append(self.pj[cfg.OBSERVATIONS][obs_id][cfg.MEDIA_INFO][cfg.LENGTH][mediaFile])
|
|
139
136
|
|
|
140
137
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
141
|
-
|
|
142
138
|
for behavior in parameters[cfg.SELECTED_BEHAVIORS]:
|
|
143
|
-
|
|
144
139
|
cursor.execute(
|
|
145
140
|
"SELECT occurence FROM events WHERE observation = ? AND subject = ? AND code = ?",
|
|
146
141
|
(obs_id, subject, behavior),
|
|
@@ -150,10 +145,7 @@ def events_snapshots(self):
|
|
|
150
145
|
behavior_state = project_functions.event_type(behavior, self.pj[cfg.ETHOGRAM])
|
|
151
146
|
|
|
152
147
|
for idx, row in enumerate(rows):
|
|
153
|
-
|
|
154
|
-
mediaFileIdx = [
|
|
155
|
-
idx1 for idx1, x in enumerate(duration1) if row["occurence"] >= sum(duration1[0:idx1])
|
|
156
|
-
][-1]
|
|
148
|
+
mediaFileIdx = [idx1 for idx1, x in enumerate(duration1) if row["occurence"] >= sum(duration1[0:idx1])][-1]
|
|
157
149
|
|
|
158
150
|
# check if media has video
|
|
159
151
|
flag_no_video = False
|
|
@@ -165,9 +157,7 @@ def events_snapshots(self):
|
|
|
165
157
|
flag_no_video = True
|
|
166
158
|
|
|
167
159
|
if flag_no_video:
|
|
168
|
-
logging.debug(
|
|
169
|
-
f"Media {self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]} does not have video"
|
|
170
|
-
)
|
|
160
|
+
logging.debug(f"Media {self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]} does not have video")
|
|
171
161
|
flag_no_video = True
|
|
172
162
|
response = dialog.MessageDialog(
|
|
173
163
|
cfg.programName,
|
|
@@ -197,9 +187,7 @@ def events_snapshots(self):
|
|
|
197
187
|
mediafile_fps = 0
|
|
198
188
|
|
|
199
189
|
if not mediafile_fps:
|
|
200
|
-
logging.debug(
|
|
201
|
-
f"FPS not found for {self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]}"
|
|
202
|
-
)
|
|
190
|
+
logging.debug(f"FPS not found for {self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]}")
|
|
203
191
|
response = dialog.MessageDialog(
|
|
204
192
|
cfg.programName,
|
|
205
193
|
(
|
|
@@ -213,11 +201,7 @@ def events_snapshots(self):
|
|
|
213
201
|
if response == "Abort":
|
|
214
202
|
return
|
|
215
203
|
|
|
216
|
-
global_start = (
|
|
217
|
-
dec("0.000")
|
|
218
|
-
if row["occurence"] < time_interval
|
|
219
|
-
else round(row["occurence"] - time_interval, 3)
|
|
220
|
-
)
|
|
204
|
+
global_start = dec("0.000") if row["occurence"] < time_interval else round(row["occurence"] - time_interval, 3)
|
|
221
205
|
start = round(
|
|
222
206
|
row["occurence"]
|
|
223
207
|
- time_interval
|
|
@@ -228,8 +212,7 @@ def events_snapshots(self):
|
|
|
228
212
|
if start < time_interval:
|
|
229
213
|
start = dec("0.000")
|
|
230
214
|
|
|
231
|
-
if
|
|
232
|
-
|
|
215
|
+
if behavior_state in cfg.POINT_EVENT_TYPES:
|
|
233
216
|
media_path = project_functions.full_path(
|
|
234
217
|
self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx],
|
|
235
218
|
self.projectFileName,
|
|
@@ -239,17 +222,14 @@ def events_snapshots(self):
|
|
|
239
222
|
if vframes == 0:
|
|
240
223
|
vframes = 1
|
|
241
224
|
|
|
242
|
-
if
|
|
225
|
+
if behavior_state in cfg.STATE_EVENT_TYPES:
|
|
243
226
|
if idx % 2 == 0:
|
|
244
|
-
|
|
245
227
|
# check if stop is on same media file
|
|
246
228
|
if (
|
|
247
229
|
mediaFileIdx
|
|
248
|
-
!= [
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if rows[idx + 1]["occurence"] >= sum(duration1[0:idx1])
|
|
252
|
-
][-1]
|
|
230
|
+
!= [idx1 for idx1, x in enumerate(duration1) if rows[idx + 1]["occurence"] >= sum(duration1[0:idx1])][
|
|
231
|
+
-1
|
|
232
|
+
]
|
|
253
233
|
):
|
|
254
234
|
response = dialog.MessageDialog(
|
|
255
235
|
cfg.programName,
|
|
@@ -365,8 +345,8 @@ def extract_events(self):
|
|
|
365
345
|
selected_observations,
|
|
366
346
|
start_coding=dec("NaN"),
|
|
367
347
|
end_coding=dec("NaN"),
|
|
368
|
-
|
|
369
|
-
|
|
348
|
+
show_include_modifiers=False,
|
|
349
|
+
show_exclude_non_coded_behaviors=False,
|
|
370
350
|
)
|
|
371
351
|
if parameters == {}:
|
|
372
352
|
return
|
|
@@ -377,9 +357,7 @@ def extract_events(self):
|
|
|
377
357
|
|
|
378
358
|
# Ask for time interval around the event
|
|
379
359
|
while True:
|
|
380
|
-
text, ok = QInputDialog.getDouble(
|
|
381
|
-
self, "Time interval around the events", "Time (in seconds):", 0.0, 0.0, 86400, 1
|
|
382
|
-
)
|
|
360
|
+
text, ok = QInputDialog.getDouble(self, "Time interval around the events", "Time (in seconds):", 0.0, 0.0, 86400, 1)
|
|
383
361
|
if not ok:
|
|
384
362
|
return
|
|
385
363
|
try:
|
|
@@ -395,11 +373,11 @@ def extract_events(self):
|
|
|
395
373
|
if not ok:
|
|
396
374
|
return
|
|
397
375
|
|
|
398
|
-
export_dir = QFileDialog
|
|
376
|
+
export_dir = QFileDialog.getExistingDirectory(
|
|
399
377
|
self,
|
|
400
378
|
"Choose a directory to extract events",
|
|
401
379
|
os.path.expanduser("~"),
|
|
402
|
-
options=QFileDialog
|
|
380
|
+
options=QFileDialog.ShowDirsOnly,
|
|
403
381
|
)
|
|
404
382
|
if not export_dir:
|
|
405
383
|
return
|
|
@@ -412,19 +390,13 @@ def extract_events(self):
|
|
|
412
390
|
time_interval=cfg.TIME_FULL_OBS,
|
|
413
391
|
)
|
|
414
392
|
|
|
415
|
-
""
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
' "{dir_}{sep}{obsId}_{player}_{subject}_{behavior}_{globalStart}'
|
|
419
|
-
'-{globalStop}{extension}" '
|
|
420
|
-
)
|
|
421
|
-
"""
|
|
393
|
+
self.statusBar().showMessage("Extracting sequences from media files")
|
|
394
|
+
QApplication.processEvents()
|
|
395
|
+
|
|
422
396
|
ffmpeg_extract_command: str = '"{ffmpeg_bin}" -ss {start} -i "{input_}" -y -t {duration} {codecs} '
|
|
423
397
|
mem_command: str = ""
|
|
424
398
|
for obs_id in selected_observations:
|
|
425
|
-
|
|
426
399
|
for nplayer in self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE]:
|
|
427
|
-
|
|
428
400
|
if not self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer]:
|
|
429
401
|
continue
|
|
430
402
|
|
|
@@ -433,9 +405,7 @@ def extract_events(self):
|
|
|
433
405
|
duration1.append(self.pj[cfg.OBSERVATIONS][obs_id][cfg.MEDIA_INFO][cfg.LENGTH][mediaFile])
|
|
434
406
|
|
|
435
407
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
436
|
-
|
|
437
408
|
for behavior in parameters[cfg.SELECTED_BEHAVIORS]:
|
|
438
|
-
|
|
439
409
|
cursor.execute(
|
|
440
410
|
"SELECT occurence FROM events WHERE observation = ? AND subject = ? AND code = ?",
|
|
441
411
|
(obs_id, subject, behavior),
|
|
@@ -443,14 +413,11 @@ def extract_events(self):
|
|
|
443
413
|
rows = [{"occurence": util.float2decimal(r["occurence"])} for r in cursor.fetchall()]
|
|
444
414
|
|
|
445
415
|
behavior_state = project_functions.event_type(behavior, self.pj[cfg.ETHOGRAM])
|
|
446
|
-
if
|
|
416
|
+
if behavior_state in cfg.STATE_EVENT_TYPES and len(rows) % 2: # unpaired events
|
|
447
417
|
continue
|
|
448
418
|
|
|
449
419
|
for idx, row in enumerate(rows):
|
|
450
|
-
|
|
451
|
-
mediaFileIdx = [
|
|
452
|
-
idx1 for idx1, x in enumerate(duration1) if row["occurence"] >= sum(duration1[0:idx1])
|
|
453
|
-
][-1]
|
|
420
|
+
mediaFileIdx = [idx1 for idx1, x in enumerate(duration1) if row["occurence"] >= sum(duration1[0:idx1])][-1]
|
|
454
421
|
|
|
455
422
|
if "VIDEO" in items_to_extract.upper():
|
|
456
423
|
# check if media has video
|
|
@@ -459,7 +426,7 @@ def extract_events(self):
|
|
|
459
426
|
has_video = self.pj[cfg.OBSERVATIONS][obs_id][cfg.MEDIA_INFO][cfg.HAS_VIDEO][
|
|
460
427
|
self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]
|
|
461
428
|
]
|
|
462
|
-
except:
|
|
429
|
+
except Exception:
|
|
463
430
|
has_video = False
|
|
464
431
|
if not has_video:
|
|
465
432
|
if (
|
|
@@ -487,10 +454,9 @@ def extract_events(self):
|
|
|
487
454
|
has_audio = self.pj[cfg.OBSERVATIONS][obs_id][cfg.MEDIA_INFO][cfg.HAS_AUDIO][
|
|
488
455
|
self.pj[cfg.OBSERVATIONS][obs_id][cfg.FILE][nplayer][mediaFileIdx]
|
|
489
456
|
]
|
|
490
|
-
except:
|
|
457
|
+
except Exception:
|
|
491
458
|
has_audio = False
|
|
492
459
|
if not has_audio:
|
|
493
|
-
|
|
494
460
|
if (
|
|
495
461
|
dialog.MessageDialog(
|
|
496
462
|
cfg.programName,
|
|
@@ -506,13 +472,8 @@ def extract_events(self):
|
|
|
506
472
|
new_extension = ".wav"
|
|
507
473
|
codecs = "-vn"
|
|
508
474
|
|
|
509
|
-
if
|
|
510
|
-
|
|
511
|
-
globalStart = (
|
|
512
|
-
dec("0.000")
|
|
513
|
-
if row["occurence"] < timeOffset
|
|
514
|
-
else round(row["occurence"] - timeOffset, 3)
|
|
515
|
-
)
|
|
475
|
+
if behavior_state in cfg.POINT_EVENT_TYPES:
|
|
476
|
+
globalStart = dec("0.000") if row["occurence"] < timeOffset else round(row["occurence"] - timeOffset, 3)
|
|
516
477
|
start = round(
|
|
517
478
|
row["occurence"]
|
|
518
479
|
- (timeOffset if timeOffset else 1) # if time offset is not set default = 1 s
|
|
@@ -533,17 +494,14 @@ def extract_events(self):
|
|
|
533
494
|
3,
|
|
534
495
|
)
|
|
535
496
|
|
|
536
|
-
if
|
|
537
|
-
|
|
497
|
+
if behavior_state in cfg.STATE_EVENT_TYPES:
|
|
538
498
|
if idx % 2 == 0:
|
|
539
499
|
# check if stop is on same media file
|
|
540
500
|
if (
|
|
541
501
|
mediaFileIdx
|
|
542
|
-
!= [
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
if rows[idx + 1]["occurence"] >= sum(duration1[0:idx1])
|
|
546
|
-
][-1]
|
|
502
|
+
!= [idx1 for idx1, x in enumerate(duration1) if rows[idx + 1]["occurence"] >= sum(duration1[0:idx1])][
|
|
503
|
+
-1
|
|
504
|
+
]
|
|
547
505
|
):
|
|
548
506
|
response = dialog.MessageDialog(
|
|
549
507
|
cfg.programName,
|
|
@@ -558,11 +516,7 @@ def extract_events(self):
|
|
|
558
516
|
if response == "Abort":
|
|
559
517
|
return
|
|
560
518
|
|
|
561
|
-
globalStart = (
|
|
562
|
-
dec("0.000")
|
|
563
|
-
if row["occurence"] < timeOffset
|
|
564
|
-
else round(row["occurence"] - timeOffset, 3)
|
|
565
|
-
)
|
|
519
|
+
globalStart = dec("0.000") if row["occurence"] < timeOffset else round(row["occurence"] - timeOffset, 3)
|
|
566
520
|
start = round(
|
|
567
521
|
row["occurence"]
|
|
568
522
|
- timeOffset
|
|
@@ -629,10 +583,10 @@ def extract_events(self):
|
|
|
629
583
|
codecs=codecs,
|
|
630
584
|
)
|
|
631
585
|
|
|
632
|
-
logging.debug(f
|
|
586
|
+
logging.debug(f'ffmpeg command: {ffmpeg_command} "{new_file_name}"')
|
|
633
587
|
|
|
634
588
|
p = subprocess.Popen(
|
|
635
|
-
f
|
|
589
|
+
f'{ffmpeg_command} "{new_file_name}"',
|
|
636
590
|
stdout=subprocess.PIPE,
|
|
637
591
|
stderr=subprocess.PIPE,
|
|
638
592
|
shell=True,
|
boris/exclusion_matrix.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -21,7 +21,7 @@ This file is part of BORIS.
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
import logging
|
|
24
|
-
from
|
|
24
|
+
from PySide6.QtWidgets import (
|
|
25
25
|
QDialog,
|
|
26
26
|
QVBoxLayout,
|
|
27
27
|
QHBoxLayout,
|
|
@@ -42,10 +42,7 @@ class ExclusionMatrix(QDialog):
|
|
|
42
42
|
|
|
43
43
|
self.label = QLabel()
|
|
44
44
|
self.label.setText(
|
|
45
|
-
(
|
|
46
|
-
"Check if behaviors are mutually exclusive.\n"
|
|
47
|
-
"The Point events (displayed on blue background) cannot be excluded)"
|
|
48
|
-
)
|
|
45
|
+
("Check if behaviors are mutually exclusive.\nThe Point events (displayed on blue background) cannot be excluded)")
|
|
49
46
|
)
|
|
50
47
|
hbox.addWidget(self.label)
|
|
51
48
|
|
|
@@ -139,8 +136,6 @@ class ExclusionMatrix(QDialog):
|
|
|
139
136
|
if c_name != r_name:
|
|
140
137
|
try:
|
|
141
138
|
if f"{c_name}|{r_name}" in self.checkboxes:
|
|
142
|
-
self.checkboxes[f"{c_name}|{r_name}"].setChecked(
|
|
143
|
-
self.checkboxes[f"{r_name}|{c_name}"].isChecked()
|
|
144
|
-
)
|
|
139
|
+
self.checkboxes[f"{c_name}|{r_name}"].setChecked(self.checkboxes[f"{r_name}|{c_name}"].isChecked())
|
|
145
140
|
except Exception:
|
|
146
141
|
logging.warning(f"Error during checking/unchecking for {r_name}/{c_name} in exclusion matrix")
|