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/behavior_binary_table.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 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
|
|
@@ -21,12 +21,10 @@ Copyright 2012-2023 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
import os
|
|
23
23
|
import pathlib
|
|
24
|
-
import re
|
|
25
24
|
from decimal import Decimal as dec
|
|
26
25
|
|
|
27
26
|
import tablib
|
|
28
|
-
from
|
|
29
|
-
from PyQt5.QtWidgets import QFileDialog, QInputDialog, QMessageBox
|
|
27
|
+
from PySide6.QtWidgets import QFileDialog, QInputDialog, QMessageBox
|
|
30
28
|
|
|
31
29
|
from . import observation_operations
|
|
32
30
|
|
|
@@ -38,9 +36,7 @@ from . import config as cfg
|
|
|
38
36
|
from . import select_subj_behav
|
|
39
37
|
|
|
40
38
|
|
|
41
|
-
def create_behavior_binary_table(
|
|
42
|
-
pj: dict, selected_observations: list, parameters_obs: dict, time_interval: float
|
|
43
|
-
) -> dict:
|
|
39
|
+
def create_behavior_binary_table(pj: dict, selected_observations: list, parameters_obs: dict, time_interval: float) -> dict:
|
|
44
40
|
"""
|
|
45
41
|
create behavior binary table
|
|
46
42
|
|
|
@@ -57,17 +53,12 @@ def create_behavior_binary_table(
|
|
|
57
53
|
|
|
58
54
|
results_df = {}
|
|
59
55
|
|
|
60
|
-
state_behavior_codes = [
|
|
61
|
-
|
|
62
|
-
]
|
|
63
|
-
point_behavior_codes = [
|
|
64
|
-
x for x in util.point_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]
|
|
65
|
-
]
|
|
56
|
+
state_behavior_codes = [x for x in util.state_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]]
|
|
57
|
+
point_behavior_codes = [x for x in util.point_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]]
|
|
66
58
|
if not state_behavior_codes and not point_behavior_codes:
|
|
67
59
|
return {"error": True, "msg": "No events selected"}
|
|
68
60
|
|
|
69
61
|
for obs_id in selected_observations:
|
|
70
|
-
|
|
71
62
|
start_time = parameters_obs[cfg.START_TIME]
|
|
72
63
|
end_time = parameters_obs[cfg.END_TIME]
|
|
73
64
|
|
|
@@ -78,7 +69,6 @@ def create_behavior_binary_table(
|
|
|
78
69
|
end_time = dec(max_obs_length)
|
|
79
70
|
|
|
80
71
|
if parameters_obs["time"] == cfg.TIME_EVENTS:
|
|
81
|
-
|
|
82
72
|
try:
|
|
83
73
|
start_time = dec(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0])
|
|
84
74
|
except Exception:
|
|
@@ -89,23 +79,29 @@ def create_behavior_binary_table(
|
|
|
89
79
|
max_obs_length, _ = observation_operations.observation_length(pj, [obs_id])
|
|
90
80
|
end_time = dec(max_obs_length)
|
|
91
81
|
|
|
82
|
+
if parameters_obs["time"] == cfg.TIME_OBS_INTERVAL:
|
|
83
|
+
obs_interval = pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
|
|
84
|
+
offset = pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET]
|
|
85
|
+
start_time = dec(obs_interval[0]) + offset
|
|
86
|
+
# Use max observation length for end time if no interval is defined (=0)
|
|
87
|
+
max_obs_length, _ = observation_operations.observation_length(pj, [obs_id])
|
|
88
|
+
end_time = dec(obs_interval[1]) + offset if obs_interval[1] not in (0, None) else dec(max_obs_length)
|
|
89
|
+
|
|
92
90
|
if obs_id not in results_df:
|
|
93
91
|
results_df[obs_id] = {}
|
|
94
92
|
|
|
95
93
|
for subject in parameters_obs[cfg.SELECTED_SUBJECTS]:
|
|
96
|
-
|
|
97
94
|
# extract tuple (behavior, modifier)
|
|
98
95
|
behav_modif_list = [
|
|
99
96
|
(idx[2], idx[3])
|
|
100
97
|
for idx in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
|
|
101
|
-
if idx[1] == (subject if subject != cfg.NO_FOCAL_SUBJECT else "")
|
|
102
|
-
and idx[2] in parameters_obs[cfg.SELECTED_BEHAVIORS]
|
|
98
|
+
if idx[1] == (subject if subject != cfg.NO_FOCAL_SUBJECT else "") and idx[2] in parameters_obs[cfg.SELECTED_BEHAVIORS]
|
|
103
99
|
]
|
|
104
100
|
|
|
105
101
|
# extract observed subjects NOT USED at the moment
|
|
106
|
-
observed_subjects = [
|
|
102
|
+
"""observed_subjects = [
|
|
107
103
|
event[cfg.EVENT_SUBJECT_FIELD_IDX] for event in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
|
|
108
|
-
]
|
|
104
|
+
]"""
|
|
109
105
|
|
|
110
106
|
# add selected behavior if not found in (behavior, modifier)
|
|
111
107
|
if not parameters_obs[cfg.EXCLUDE_BEHAVIORS]:
|
|
@@ -127,17 +123,12 @@ def create_behavior_binary_table(
|
|
|
127
123
|
sel_subject_dict = {"": {cfg.SUBJECT_NAME: ""}}
|
|
128
124
|
else:
|
|
129
125
|
sel_subject_dict = dict(
|
|
130
|
-
[
|
|
131
|
-
(idx, pj[cfg.SUBJECTS][idx])
|
|
132
|
-
for idx in pj[cfg.SUBJECTS]
|
|
133
|
-
if pj[cfg.SUBJECTS][idx][cfg.SUBJECT_NAME] == subject
|
|
134
|
-
]
|
|
126
|
+
[(idx, pj[cfg.SUBJECTS][idx]) for idx in pj[cfg.SUBJECTS] if pj[cfg.SUBJECTS][idx][cfg.SUBJECT_NAME] == subject]
|
|
135
127
|
)
|
|
136
128
|
|
|
137
129
|
row_idx = 0
|
|
138
130
|
t = start_time
|
|
139
131
|
while t <= end_time:
|
|
140
|
-
|
|
141
132
|
# state events
|
|
142
133
|
current_states = util.get_current_states_modifiers_by_subject_2(
|
|
143
134
|
state_behavior_codes, pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS], sel_subject_dict, t
|
|
@@ -175,8 +166,8 @@ def behavior_binary_table(self):
|
|
|
175
166
|
None,
|
|
176
167
|
cfg.programName,
|
|
177
168
|
(
|
|
178
|
-
"Depending
|
|
179
|
-
"the execution of this function may
|
|
169
|
+
"Depending on the length of yours observations "
|
|
170
|
+
"the execution of this function may take a long time.<br>"
|
|
180
171
|
"The program interface may freeze, be patient. <br>"
|
|
181
172
|
),
|
|
182
173
|
)
|
|
@@ -214,24 +205,26 @@ def behavior_binary_table(self):
|
|
|
214
205
|
return
|
|
215
206
|
"""
|
|
216
207
|
|
|
217
|
-
max_media_duration_all_obs, _ = observation_operations.media_duration(
|
|
218
|
-
self.pj[cfg.OBSERVATIONS], selected_observations
|
|
219
|
-
)
|
|
208
|
+
max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
220
209
|
|
|
221
210
|
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
222
211
|
|
|
212
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
213
|
+
|
|
223
214
|
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
224
215
|
self,
|
|
225
216
|
selected_observations,
|
|
226
217
|
start_coding=start_coding,
|
|
227
218
|
end_coding=end_coding,
|
|
219
|
+
start_interval=start_interval,
|
|
220
|
+
end_interval=end_interval,
|
|
228
221
|
maxTime=max_media_duration_all_obs,
|
|
229
|
-
|
|
230
|
-
|
|
222
|
+
show_include_modifiers=True,
|
|
223
|
+
show_exclude_non_coded_behaviors=True,
|
|
231
224
|
by_category=False,
|
|
232
225
|
n_observations=len(selected_observations),
|
|
233
226
|
)
|
|
234
|
-
if parameters
|
|
227
|
+
if not parameters:
|
|
235
228
|
return
|
|
236
229
|
if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
|
|
237
230
|
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
|
|
@@ -253,7 +246,6 @@ def behavior_binary_table(self):
|
|
|
253
246
|
file_formats = [cfg.TSV, cfg.CSV, cfg.ODS, cfg.XLSX, cfg.XLS, cfg.HTML]
|
|
254
247
|
|
|
255
248
|
if len(selected_observations) == 1:
|
|
256
|
-
|
|
257
249
|
file_name, filter_ = QFileDialog().getSaveFileName(None, "Save results", "", ";;".join(file_formats))
|
|
258
250
|
if not file_name:
|
|
259
251
|
return
|
|
@@ -265,18 +257,15 @@ def behavior_binary_table(self):
|
|
|
265
257
|
# check if file with new extension already exists
|
|
266
258
|
if pathlib.Path(file_name).is_file():
|
|
267
259
|
if (
|
|
268
|
-
dialog.MessageDialog(
|
|
269
|
-
cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE]
|
|
270
|
-
)
|
|
260
|
+
dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE])
|
|
271
261
|
== cfg.CANCEL
|
|
272
262
|
):
|
|
273
263
|
return
|
|
274
264
|
else:
|
|
275
|
-
|
|
276
265
|
item, ok = QInputDialog.getItem(None, "Save results", "Available formats", file_formats, 0, False)
|
|
277
266
|
if not ok:
|
|
278
267
|
return
|
|
279
|
-
|
|
268
|
+
|
|
280
269
|
output_format = cfg.FILE_NAME_SUFFIX[item]
|
|
281
270
|
|
|
282
271
|
export_dir = QFileDialog().getExistingDirectory(
|
|
@@ -287,17 +276,11 @@ def behavior_binary_table(self):
|
|
|
287
276
|
|
|
288
277
|
mem_command = ""
|
|
289
278
|
for obs_id in results_df:
|
|
290
|
-
|
|
291
279
|
for subject in results_df[obs_id]:
|
|
292
|
-
|
|
293
280
|
if len(selected_observations) > 1:
|
|
294
|
-
file_name_with_subject = (
|
|
295
|
-
str(pathlib.Path(export_dir) / util.safeFileName(obs_id + "_" + subject)) + "." + output_format
|
|
296
|
-
)
|
|
281
|
+
file_name_with_subject = str(pathlib.Path(export_dir) / util.safeFileName(obs_id + "_" + subject)) + "." + output_format
|
|
297
282
|
else:
|
|
298
|
-
file_name_with_subject = (
|
|
299
|
-
str(os.path.splitext(file_name)[0] + util.safeFileName("_" + subject)) + "." + output_format
|
|
300
|
-
)
|
|
283
|
+
file_name_with_subject = str(os.path.splitext(file_name)[0] + util.safeFileName("_" + subject)) + "." + output_format
|
|
301
284
|
|
|
302
285
|
# check if file with new extension already exists
|
|
303
286
|
if mem_command != cfg.OVERWRITE_ALL and pathlib.Path(file_name_with_subject).is_file():
|
|
@@ -313,10 +296,10 @@ def behavior_binary_table(self):
|
|
|
313
296
|
if mem_command in ["Skip", "Skip all"]:
|
|
314
297
|
continue
|
|
315
298
|
|
|
316
|
-
if output_format in [
|
|
299
|
+
if output_format in [cfg.CSV_EXT, cfg.TSV_EXT, cfg.HTML]:
|
|
317
300
|
with open(file_name_with_subject, "wb") as f:
|
|
318
301
|
f.write(str.encode(results_df[obs_id][subject].export(output_format)))
|
|
319
302
|
|
|
320
|
-
if output_format in [
|
|
303
|
+
if output_format in [cfg.ODS_EXT, cfg.XLSX_EXT, cfg.XLS_EXT]:
|
|
321
304
|
with open(file_name_with_subject, "wb") as f:
|
|
322
305
|
f.write(results_df[obs_id][subject].export(output_format))
|
boris/behaviors_coding_map.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
|
|
|
@@ -20,9 +20,12 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
import json
|
|
24
|
+
import binascii
|
|
25
|
+
|
|
26
|
+
from PySide6.QtGui import QMouseEvent, QPixmap, QPolygonF, QColor, QBrush, QPen
|
|
27
|
+
from PySide6.QtCore import Qt, Signal, QEvent, QPoint
|
|
28
|
+
from PySide6.QtWidgets import (
|
|
26
29
|
QLabel,
|
|
27
30
|
QHBoxLayout,
|
|
28
31
|
QGraphicsView,
|
|
@@ -38,20 +41,17 @@ from PyQt5.QtWidgets import (
|
|
|
38
41
|
QApplication,
|
|
39
42
|
)
|
|
40
43
|
|
|
41
|
-
import json
|
|
42
|
-
import binascii
|
|
43
44
|
from . import config as cfg
|
|
44
45
|
|
|
45
|
-
codeSeparator = ","
|
|
46
|
-
penWidth = 0
|
|
46
|
+
codeSeparator: str = ","
|
|
47
|
+
penWidth: int = 0
|
|
47
48
|
penStyle = Qt.NoPen
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
class BehaviorsCodingMapWindowClass(QWidget):
|
|
51
52
|
class View(QGraphicsView):
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
mouseMove = pyqtSignal(QMouseEvent)
|
|
53
|
+
mousePress = Signal(QMouseEvent)
|
|
54
|
+
mouseMove = Signal(QMouseEvent)
|
|
55
55
|
|
|
56
56
|
def eventFilter(self, source, event):
|
|
57
57
|
if event.type() == QEvent.MouseMove:
|
|
@@ -72,9 +72,9 @@ class BehaviorsCodingMapWindowClass(QWidget):
|
|
|
72
72
|
self.viewport().installEventFilter(self)
|
|
73
73
|
self.setMouseTracking(True)
|
|
74
74
|
|
|
75
|
-
clickSignal =
|
|
76
|
-
keypressSignal =
|
|
77
|
-
close_signal =
|
|
75
|
+
clickSignal = Signal(str, list) # click signal to be sent to mainwindow
|
|
76
|
+
keypressSignal = Signal(QEvent)
|
|
77
|
+
close_signal = Signal(str)
|
|
78
78
|
|
|
79
79
|
def __init__(self, behaviors_coding_map, idx=0):
|
|
80
80
|
super(BehaviorsCodingMapWindowClass, self).__init__()
|
|
@@ -103,7 +103,7 @@ class BehaviorsCodingMapWindowClass(QWidget):
|
|
|
103
103
|
self.leareaCode = QLineEdit(self)
|
|
104
104
|
hBoxLayout1.addWidget(self.leareaCode)
|
|
105
105
|
|
|
106
|
-
self.btClose = QPushButton(
|
|
106
|
+
self.btClose = QPushButton(cfg.CLOSE)
|
|
107
107
|
self.btClose.clicked.connect(self.close)
|
|
108
108
|
hBoxLayout1.addWidget(self.btClose)
|
|
109
109
|
|
|
@@ -140,13 +140,13 @@ class BehaviorsCodingMapWindowClass(QWidget):
|
|
|
140
140
|
codes.append(areaCode)
|
|
141
141
|
self.leareaCode.setText(", ".join(codes))
|
|
142
142
|
|
|
143
|
-
def viewMousePressEvent(self, event):
|
|
143
|
+
def viewMousePressEvent(self, event) -> None:
|
|
144
144
|
"""
|
|
145
145
|
insert clicked areas codes
|
|
146
146
|
"""
|
|
147
147
|
|
|
148
148
|
test = self.view.mapToScene(event.pos()).toPoint()
|
|
149
|
-
to_be_sent = []
|
|
149
|
+
to_be_sent: list = []
|
|
150
150
|
|
|
151
151
|
for areaCode, pg in self.polygonsList2:
|
|
152
152
|
if pg.contains(test):
|
|
@@ -227,7 +227,6 @@ def show_behaviors_coding_map(self):
|
|
|
227
227
|
|
|
228
228
|
|
|
229
229
|
if __name__ == "__main__":
|
|
230
|
-
|
|
231
230
|
import sys
|
|
232
231
|
|
|
233
232
|
app = QApplication(sys.argv)
|
boris/boris_cli.py
CHANGED
|
@@ -3,7 +3,7 @@ BORIS CLI
|
|
|
3
3
|
|
|
4
4
|
Behavioral Observation Research Interactive Software Command Line Interface
|
|
5
5
|
|
|
6
|
-
Copyright 2012-
|
|
6
|
+
Copyright 2012-2025 Olivier Friard
|
|
7
7
|
|
|
8
8
|
This program is free software; you can redistribute it and/or modify
|
|
9
9
|
it under the terms of the GNU General Public License as published by
|
|
@@ -91,9 +91,7 @@ commands_usage = {
|
|
|
91
91
|
parser = argparse.ArgumentParser(description="BORIS CLI")
|
|
92
92
|
parser.add_argument("-v", "--version", action="store_true", dest="version", help="BORIS version")
|
|
93
93
|
parser.add_argument("-p", "--project", action="store", dest="project_file", help="Project file path")
|
|
94
|
-
parser.add_argument(
|
|
95
|
-
"-o", "--observation", nargs="*", action="store", default=[], dest="observation_id", help="Observation id"
|
|
96
|
-
)
|
|
94
|
+
parser.add_argument("-o", "--observation", nargs="*", action="store", default=[], dest="observation_id", help="Observation id")
|
|
97
95
|
parser.add_argument("-i", "--info", action="store_true", dest="project_info", help="Project information")
|
|
98
96
|
parser.add_argument("-c", "--command", nargs="*", action="store", dest="command", help="Command to execute")
|
|
99
97
|
|
|
@@ -117,7 +115,6 @@ if args.command:
|
|
|
117
115
|
sys.exit()
|
|
118
116
|
|
|
119
117
|
if args.project_file:
|
|
120
|
-
|
|
121
118
|
if not args.command:
|
|
122
119
|
print("Project path: {}".format(args.project_file))
|
|
123
120
|
|
|
@@ -164,9 +161,7 @@ if args.project_info:
|
|
|
164
161
|
print("Subjects\n========")
|
|
165
162
|
print("Number of subjects: {}".format(len(pj[SUBJECTS])))
|
|
166
163
|
for idx in utilities.sorted_keys(pj[SUBJECTS]):
|
|
167
|
-
print(
|
|
168
|
-
"Name: {}\tDescription: {}".format(pj[SUBJECTS][idx]["name"], pj[SUBJECTS][idx]["description"])
|
|
169
|
-
)
|
|
164
|
+
print("Name: {}\tDescription: {}".format(pj[SUBJECTS][idx]["name"], pj[SUBJECTS][idx]["description"]))
|
|
170
165
|
print()
|
|
171
166
|
|
|
172
167
|
print("Observations\n============")
|
|
@@ -179,7 +174,6 @@ if args.project_info:
|
|
|
179
174
|
for observation_id in observations_id_list:
|
|
180
175
|
print("Observation id: {}".format(observation_id))
|
|
181
176
|
if pj[OBSERVATIONS][observation_id][EVENTS]:
|
|
182
|
-
|
|
183
177
|
for event in pj[OBSERVATIONS][observation_id][EVENTS]:
|
|
184
178
|
print("\t".join([str(x) for x in event]))
|
|
185
179
|
else:
|
|
@@ -190,7 +184,6 @@ if args.project_info:
|
|
|
190
184
|
sys.exit()
|
|
191
185
|
|
|
192
186
|
if args.command:
|
|
193
|
-
|
|
194
187
|
print("Command: {}\n".format(" ".join(args.command)))
|
|
195
188
|
|
|
196
189
|
if not pj:
|
|
@@ -198,20 +191,16 @@ if args.command:
|
|
|
198
191
|
sys.exit()
|
|
199
192
|
|
|
200
193
|
if "check_state_events" in args.command:
|
|
201
|
-
|
|
202
194
|
if not observations_id_list:
|
|
203
195
|
print("No observation selected. Command applied on all observations found in project\n")
|
|
204
196
|
observations_id_list = all_observations(pj)
|
|
205
197
|
|
|
206
198
|
for observation_id in observations_id_list:
|
|
207
|
-
ret, msg = project_functions.check_state_events_obs(
|
|
208
|
-
observation_id, pj[ETHOGRAM], pj[OBSERVATIONS][observation_id], HHMMSS
|
|
209
|
-
)
|
|
199
|
+
ret, msg = project_functions.check_state_events_obs(observation_id, pj[ETHOGRAM], pj[OBSERVATIONS][observation_id], HHMMSS)
|
|
210
200
|
print("{}: {}".format(observation_id, cleanhtml(msg)))
|
|
211
201
|
sys.exit()
|
|
212
202
|
|
|
213
203
|
if "export_events" in args.command:
|
|
214
|
-
|
|
215
204
|
if not observations_id_list:
|
|
216
205
|
print("No observation selected. Command applied on all observations found in project\n")
|
|
217
206
|
observations_id_list = [idx for idx in pj[OBSERVATIONS]]
|
|
@@ -261,21 +250,14 @@ if args.command:
|
|
|
261
250
|
if len(args.command) > 2:
|
|
262
251
|
include_modifiers = "TRUE" in args.command[2].upper()
|
|
263
252
|
|
|
264
|
-
K, out = irr.cohen_kappa(
|
|
265
|
-
cursor, observations_id_list[0], observations_id_list[1], interval, subjects, include_modifiers
|
|
266
|
-
)
|
|
253
|
+
K, out = irr.cohen_kappa(cursor, observations_id_list[0], observations_id_list[1], interval, subjects, include_modifiers)
|
|
267
254
|
|
|
268
|
-
print(
|
|
269
|
-
("Cohen's Kappa - Index of Inter-Rater Reliability\n\n" "Interval time: {interval:.3f} s\n").format(
|
|
270
|
-
interval=interval
|
|
271
|
-
)
|
|
272
|
-
)
|
|
255
|
+
print(("Cohen's Kappa - Index of Inter-Rater Reliability\n\nInterval time: {interval:.3f} s\n").format(interval=interval))
|
|
273
256
|
|
|
274
257
|
print(out)
|
|
275
258
|
sys.exit()
|
|
276
259
|
|
|
277
260
|
if "subtitles" in args.command:
|
|
278
|
-
|
|
279
261
|
if not observations_id_list:
|
|
280
262
|
print("No observation selected. Command applied on all observations found in project\n")
|
|
281
263
|
observations_id_list = all_observations(pj)
|
|
@@ -309,7 +291,6 @@ if args.command:
|
|
|
309
291
|
sys.exit()
|
|
310
292
|
|
|
311
293
|
if "plot_events" in args.command[0]:
|
|
312
|
-
|
|
313
294
|
if not observations_id_list:
|
|
314
295
|
print("No observation selected. Command applied on all observations found in project\n")
|
|
315
296
|
observations_id_list = all_observations(pj)
|
boris/cmd_arguments.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
|
|
@@ -30,9 +30,20 @@ def parse_arguments():
|
|
|
30
30
|
parser = OptionParser(usage=usage)
|
|
31
31
|
|
|
32
32
|
parser.add_option("-d", "--debug", action="store_true", default=False, dest="debug", help="Use debugging mode")
|
|
33
|
+
parser.add_option("-q", "--quit", action="store_true", default=False, dest="quit", help="Quit after launch")
|
|
33
34
|
parser.add_option("-v", "--version", action="store_true", default=False, dest="version", help="Print version")
|
|
34
35
|
parser.add_option("-n", "--nosplashscreen", action="store_true", default=False, help="No splash screen")
|
|
35
36
|
parser.add_option("-p", "--project", action="store", default="", dest="project", help="Project file")
|
|
36
37
|
parser.add_option("-o", "--observation", action="store", default="", dest="observation", help="Observation id")
|
|
38
|
+
parser.add_option("-i", "--ipc", action="store_true", default="", dest="ipc", help="MPV IPC mode")
|
|
39
|
+
|
|
40
|
+
parser.add_option(
|
|
41
|
+
"-f",
|
|
42
|
+
"--no-first-launch-dialog",
|
|
43
|
+
action="store_true",
|
|
44
|
+
default=False,
|
|
45
|
+
dest="no_first_launch_dialog",
|
|
46
|
+
help="No first launch dialog (for new version automatic check)",
|
|
47
|
+
)
|
|
37
48
|
|
|
38
49
|
return parser.parse_args()
|
boris/coding_pad.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 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
|
|
@@ -19,9 +19,9 @@ Copyright 2012-2023 Olivier Friard
|
|
|
19
19
|
MA 02110-1301, USA.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
24
|
-
from
|
|
22
|
+
from PySide6.QtCore import Qt, Signal, QEvent, QRect
|
|
23
|
+
from PySide6.QtGui import QFont
|
|
24
|
+
from PySide6.QtWidgets import QWidget, QPushButton, QHBoxLayout, QGridLayout, QComboBox, QMessageBox
|
|
25
25
|
|
|
26
26
|
from . import config as cfg
|
|
27
27
|
from . import utilities as util
|
|
@@ -38,10 +38,9 @@ class Button(QWidget):
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class CodingPad(QWidget):
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
close_signal = pyqtSignal(QRect, dict)
|
|
41
|
+
clickSignal = Signal(str)
|
|
42
|
+
sendEventSignal = Signal(QEvent)
|
|
43
|
+
close_signal = Signal(QRect, dict)
|
|
45
44
|
|
|
46
45
|
def __init__(self, pj: dict, filtered_behaviors, parent=None):
|
|
47
46
|
super().__init__(parent)
|
|
@@ -56,9 +55,7 @@ class CodingPad(QWidget):
|
|
|
56
55
|
|
|
57
56
|
self.preferences: dict = {"button font size": 20, "button color": cfg.BEHAVIOR_CATEGORY}
|
|
58
57
|
|
|
59
|
-
self.button_css: str =
|
|
60
|
-
"min-width: 50px; min-height:50px; font-weight: bold; max-height:5000px; max-width: 5000px;"
|
|
61
|
-
)
|
|
58
|
+
self.button_css: str = "min-width: 50px; min-height:50px; font-weight: bold; max-height:5000px; max-width: 5000px;"
|
|
62
59
|
|
|
63
60
|
self.setWindowTitle("Coding pad")
|
|
64
61
|
|
|
@@ -95,6 +92,7 @@ class CodingPad(QWidget):
|
|
|
95
92
|
# combobox for coding pad configuration
|
|
96
93
|
vlayout = QHBoxLayout()
|
|
97
94
|
self.cb_config = QComboBox()
|
|
95
|
+
self.cb_config.setFocusPolicy(Qt.NoFocus)
|
|
98
96
|
self.cb_config.addItems(
|
|
99
97
|
[
|
|
100
98
|
"Choose an option to configure",
|
|
@@ -109,9 +107,7 @@ class CodingPad(QWidget):
|
|
|
109
107
|
vlayout.addWidget(self.cb_config)
|
|
110
108
|
self.grid.addLayout(vlayout, 0, 1, 1, 1)
|
|
111
109
|
|
|
112
|
-
self.all_behaviors = [
|
|
113
|
-
self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
|
|
114
|
-
]
|
|
110
|
+
self.all_behaviors = [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]
|
|
115
111
|
|
|
116
112
|
# behavioral category colors
|
|
117
113
|
self.unique_behavioral_categories = sorted(
|
|
@@ -126,8 +122,7 @@ class CodingPad(QWidget):
|
|
|
126
122
|
behaviorsList = [
|
|
127
123
|
[self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CATEGORY], self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]]
|
|
128
124
|
for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
|
|
129
|
-
if cfg.BEHAVIOR_CATEGORY in self.pj[cfg.ETHOGRAM][x]
|
|
130
|
-
and self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in self.filtered_behaviors
|
|
125
|
+
if cfg.BEHAVIOR_CATEGORY in self.pj[cfg.ETHOGRAM][x] and self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in self.filtered_behaviors
|
|
131
126
|
]
|
|
132
127
|
|
|
133
128
|
# square grid dimension
|
|
@@ -144,7 +139,6 @@ class CodingPad(QWidget):
|
|
|
144
139
|
self.button_configuration()
|
|
145
140
|
|
|
146
141
|
def addWidget(self, behavior_code: str, i: int, j: int) -> None:
|
|
147
|
-
|
|
148
142
|
self.grid.addWidget(Button(), i, j)
|
|
149
143
|
index = self.grid.count() - 1
|
|
150
144
|
widget = self.grid.itemAt(index).widget()
|
|
@@ -166,7 +160,6 @@ class CodingPad(QWidget):
|
|
|
166
160
|
behavior_code = self.grid.itemAt(index).widget().pushButton.text()
|
|
167
161
|
|
|
168
162
|
if self.preferences["button color"] == cfg.BEHAVIOR_CATEGORY:
|
|
169
|
-
|
|
170
163
|
behav_cat = [
|
|
171
164
|
self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CATEGORY]
|
|
172
165
|
for x in self.pj[cfg.ETHOGRAM]
|
|
@@ -184,11 +177,7 @@ class CodingPad(QWidget):
|
|
|
184
177
|
if self.preferences["button color"] == "behavior":
|
|
185
178
|
# behavioral categories are not defined
|
|
186
179
|
behavior_position = int(
|
|
187
|
-
[
|
|
188
|
-
x
|
|
189
|
-
for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
|
|
190
|
-
if self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] == behavior_code
|
|
191
|
-
][0]
|
|
180
|
+
[x for x in util.sorted_keys(self.pj[cfg.ETHOGRAM]) if self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] == behavior_code][0]
|
|
192
181
|
)
|
|
193
182
|
|
|
194
183
|
# behavior button color
|
|
@@ -197,18 +186,14 @@ class CodingPad(QWidget):
|
|
|
197
186
|
if behav_color is not None:
|
|
198
187
|
color = behav_color
|
|
199
188
|
else:
|
|
200
|
-
color = self.behavior_colors_list[behavior_position % len(self.behavior_colors_list)].replace(
|
|
201
|
-
"tab:", ""
|
|
202
|
-
)
|
|
189
|
+
color = self.behavior_colors_list[behavior_position % len(self.behavior_colors_list)].replace("tab:", "")
|
|
203
190
|
|
|
204
191
|
if self.preferences["button color"] == "no color":
|
|
205
192
|
color = ""
|
|
206
193
|
|
|
207
194
|
# set checkable if state behavior
|
|
208
195
|
self.grid.itemAt(index).widget().pushButton.setCheckable(behavior_code in state_behaviors_list)
|
|
209
|
-
self.grid.itemAt(index).widget().pushButton.setStyleSheet(
|
|
210
|
-
self.button_css + (f"background-color: {color};" if color else "")
|
|
211
|
-
)
|
|
196
|
+
self.grid.itemAt(index).widget().pushButton.setStyleSheet(self.button_css + (f"background-color: {color};" if color else ""))
|
|
212
197
|
font = QFont("Arial", self.preferences["button font size"])
|
|
213
198
|
self.grid.itemAt(index).widget().pushButton.setFont(font)
|
|
214
199
|
|
|
@@ -251,10 +236,7 @@ def show_coding_pad(self):
|
|
|
251
236
|
return
|
|
252
237
|
|
|
253
238
|
if hasattr(self, "codingpad"):
|
|
254
|
-
|
|
255
|
-
self.codingpad.filtered_behaviors = [
|
|
256
|
-
self.twEthogram.item(i, 1).text() for i in range(self.twEthogram.rowCount())
|
|
257
|
-
]
|
|
239
|
+
self.codingpad.filtered_behaviors = [self.twEthogram.item(i, 1).text() for i in range(self.twEthogram.rowCount())]
|
|
258
240
|
if not self.codingpad.filtered_behaviors:
|
|
259
241
|
QMessageBox.warning(self, cfg.programName, "No behaviors to show!")
|
|
260
242
|
return
|
|
@@ -265,7 +247,6 @@ def show_coding_pad(self):
|
|
|
265
247
|
self.codingpad.behavioral_category_colors_list = self.behav_category_colors
|
|
266
248
|
|
|
267
249
|
else: # coding pad does not exist
|
|
268
|
-
|
|
269
250
|
filtered_behaviors = [self.twEthogram.item(i, 1).text() for i in range(self.twEthogram.rowCount())]
|
|
270
251
|
if not filtered_behaviors:
|
|
271
252
|
QMessageBox.warning(self, cfg.programName, "No behaviors to show!")
|
|
@@ -279,6 +260,7 @@ def show_coding_pad(self):
|
|
|
279
260
|
|
|
280
261
|
self.codingpad.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
281
262
|
self.codingpad.sendEventSignal.connect(self.signal_from_widget)
|
|
263
|
+
|
|
282
264
|
self.codingpad.clickSignal.connect(self.click_signal_from_coding_pad)
|
|
283
265
|
self.codingpad.close_signal.connect(self.close_signal_from_coding_pad)
|
|
284
266
|
self.codingpad.show()
|