boris-behav-obs 8.27.7__py2.py3-none-any.whl → 8.27.9__py2.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/advanced_event_filtering.py +2 -2
- boris/behavior_binary_table.py +2 -2
- boris/config.py +1 -0
- boris/cooccurence.py +2 -2
- boris/core.py +6 -6
- boris/db_functions.py +2 -2
- boris/event_operations.py +5 -5
- boris/events_snapshots.py +4 -5
- boris/export_events.py +10 -10
- boris/export_observation.py +1 -1
- boris/geometric_measurement.py +0 -4
- boris/irr.py +4 -4
- boris/latency.py +2 -2
- boris/mpv2.py +2 -0
- boris/observation.py +7 -2
- boris/param_panel.py +10 -1
- boris/param_panel_ui.py +6 -1
- boris/player_dock_widget.py +1 -1
- boris/project_functions.py +13 -1
- boris/select_modifiers.py +1 -1
- boris/select_observations.py +1 -174
- boris/select_subj_behav.py +13 -5
- boris/synthetic_time_budget.py +4 -2
- boris/time_budget_functions.py +105 -91
- boris/time_budget_widget.py +2 -16
- boris/transitions.py +2 -2
- boris/utilities.py +7 -1
- boris/version.py +2 -2
- {boris_behav_obs-8.27.7.dist-info → boris_behav_obs-8.27.9.dist-info}/METADATA +1 -111
- {boris_behav_obs-8.27.7.dist-info → boris_behav_obs-8.27.9.dist-info}/RECORD +34 -46
- {boris_behav_obs-8.27.7.dist-info → boris_behav_obs-8.27.9.dist-info}/WHEEL +1 -1
- boris_behav_obs-8.27.9.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -330
- boris/converters.ui +0 -289
- boris/core.qrc +0 -67
- boris/core.ui +0 -1585
- boris/edit_event.ui +0 -240
- boris/icons/logo_eye.ico +0 -0
- boris/observation.ui +0 -848
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -588
- boris/project.ui +0 -1074
- boris_behav_obs-8.27.7.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.27.7.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-8.27.9.dist-info}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.27.7.dist-info → boris_behav_obs-8.27.9.dist-info}/top_level.txt +0 -0
|
@@ -358,8 +358,8 @@ def event_filtering(self):
|
|
|
358
358
|
start_coding=start_coding,
|
|
359
359
|
end_coding=end_coding,
|
|
360
360
|
maxTime=max_media_duration_all_obs,
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
show_include_modifiers=False,
|
|
362
|
+
show_exclude_non_coded_behaviors=False,
|
|
363
363
|
by_category=False,
|
|
364
364
|
n_observations=len(selected_observations),
|
|
365
365
|
)
|
boris/behavior_binary_table.py
CHANGED
|
@@ -207,8 +207,8 @@ def behavior_binary_table(self):
|
|
|
207
207
|
start_coding=start_coding,
|
|
208
208
|
end_coding=end_coding,
|
|
209
209
|
maxTime=max_media_duration_all_obs,
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
show_include_modifiers=True,
|
|
211
|
+
show_exclude_non_coded_behaviors=True,
|
|
212
212
|
by_category=False,
|
|
213
213
|
n_observations=len(selected_observations),
|
|
214
214
|
)
|
boris/config.py
CHANGED
|
@@ -365,6 +365,7 @@ SELECTED_BEHAVIORS = "selected behaviors"
|
|
|
365
365
|
SELECTED_SUBJECTS = "selected subjects"
|
|
366
366
|
INCLUDE_MODIFIERS = "include modifiers"
|
|
367
367
|
EXCLUDE_BEHAVIORS = "exclude behaviors"
|
|
368
|
+
EXCLUDE_NON_CODED_MODIFIERS = "exclude_non_coded_modifiers"
|
|
368
369
|
EXCLUDED_BEHAVIORS = "excluded behaviors"
|
|
369
370
|
TIME_INTERVAL = "time"
|
|
370
371
|
START_TIME = "start time"
|
boris/cooccurence.py
CHANGED
|
@@ -101,8 +101,8 @@ def get_cooccurence(self):
|
|
|
101
101
|
selected_observations,
|
|
102
102
|
window_title="Select the behaviors",
|
|
103
103
|
n_observations=len(selected_observations),
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
show_include_modifiers=False,
|
|
105
|
+
show_exclude_non_coded_behaviors=True,
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
if parameters == {}: # cancel button pressed
|
boris/core.py
CHANGED
|
@@ -774,7 +774,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
774
774
|
"""
|
|
775
775
|
|
|
776
776
|
if not self.pj[cfg.ETHOGRAM]:
|
|
777
|
-
True, []
|
|
777
|
+
return True, []
|
|
778
778
|
|
|
779
779
|
behavior_type = [x.upper() for x in behavior_type]
|
|
780
780
|
|
|
@@ -2750,7 +2750,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
2750
2750
|
start_coding=start_coding,
|
|
2751
2751
|
end_coding=end_coding,
|
|
2752
2752
|
maxTime=max_obs_length,
|
|
2753
|
-
|
|
2753
|
+
show_exclude_non_coded_behaviors=True,
|
|
2754
2754
|
by_category=False,
|
|
2755
2755
|
n_observations=len(selected_observations),
|
|
2756
2756
|
)
|
|
@@ -2849,8 +2849,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
2849
2849
|
start_coding=start_coding,
|
|
2850
2850
|
end_coding=end_coding,
|
|
2851
2851
|
maxTime=max_obs_length if len(selected_observations) > 1 else selectedObsTotalMediaLength,
|
|
2852
|
-
|
|
2853
|
-
|
|
2852
|
+
show_include_modifiers=False,
|
|
2853
|
+
show_exclude_non_coded_behaviors=True,
|
|
2854
2854
|
n_observations=len(selected_observations),
|
|
2855
2855
|
)
|
|
2856
2856
|
|
|
@@ -3730,8 +3730,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3730
3730
|
start_coding=start_coding,
|
|
3731
3731
|
end_coding=end_coding,
|
|
3732
3732
|
maxTime=max_media_duration_all_obs,
|
|
3733
|
-
|
|
3734
|
-
|
|
3733
|
+
show_include_modifiers=False,
|
|
3734
|
+
show_exclude_non_coded_behaviors=False,
|
|
3735
3735
|
n_observations=len(selected_observations),
|
|
3736
3736
|
)
|
|
3737
3737
|
if parameters == {}:
|
boris/db_functions.py
CHANGED
|
@@ -70,6 +70,7 @@ def load_events_in_db(
|
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
72
|
db = sqlite3.connect(":memory:", isolation_level=None)
|
|
73
|
+
|
|
73
74
|
"""
|
|
74
75
|
import os
|
|
75
76
|
os.system("rm /tmp/ramdisk/events.sqlite")
|
|
@@ -211,11 +212,10 @@ def load_aggregated_events_in_db(
|
|
|
211
212
|
|
|
212
213
|
db = sqlite3.connect(":memory:")
|
|
213
214
|
|
|
214
|
-
"""
|
|
215
215
|
import os
|
|
216
|
+
|
|
216
217
|
os.system("rm /tmp/ramdisk/aggreg.sqlite")
|
|
217
218
|
db = sqlite3.connect("/tmp/ramdisk/aggreg.sqlite", isolation_level=None)
|
|
218
|
-
"""
|
|
219
219
|
|
|
220
220
|
db.row_factory = sqlite3.Row
|
|
221
221
|
cursor2 = db.cursor()
|
boris/event_operations.py
CHANGED
|
@@ -222,8 +222,8 @@ def filter_events(self):
|
|
|
222
222
|
start_coding=dec("NaN"),
|
|
223
223
|
end_coding=dec("NaN"),
|
|
224
224
|
maxTime=None,
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
show_include_modifiers=False,
|
|
226
|
+
show_exclude_non_coded_behaviors=False,
|
|
227
227
|
by_category=False,
|
|
228
228
|
)
|
|
229
229
|
if parameters == {}:
|
|
@@ -983,9 +983,9 @@ def add_frame_indexes(self):
|
|
|
983
983
|
continue
|
|
984
984
|
if not self.seek_mediaplayer(event[0]):
|
|
985
985
|
time.sleep(0.1)
|
|
986
|
-
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][idx][
|
|
987
|
-
|
|
988
|
-
|
|
986
|
+
self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][idx][cfg.PJ_OBS_FIELDS[cfg.MEDIA][cfg.FRAME_INDEX]] = (
|
|
987
|
+
self.get_frame_index()
|
|
988
|
+
)
|
|
989
989
|
|
|
990
990
|
self.seek_mediaplayer(mem_time)
|
|
991
991
|
|
boris/events_snapshots.py
CHANGED
|
@@ -21,7 +21,6 @@ Copyright 2012-2024 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
|
|
25
24
|
import logging
|
|
26
25
|
import os
|
|
27
26
|
import pathlib as pl
|
|
@@ -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
|
|
|
@@ -346,8 +345,8 @@ def extract_events(self):
|
|
|
346
345
|
selected_observations,
|
|
347
346
|
start_coding=dec("NaN"),
|
|
348
347
|
end_coding=dec("NaN"),
|
|
349
|
-
|
|
350
|
-
|
|
348
|
+
show_include_modifiers=False,
|
|
349
|
+
show_exclude_non_coded_behaviors=False,
|
|
351
350
|
)
|
|
352
351
|
if parameters == {}:
|
|
353
352
|
return
|
boris/export_events.py
CHANGED
|
@@ -82,8 +82,8 @@ def export_events_as_behavioral_sequences(self, separated_subjects=False, timed=
|
|
|
82
82
|
start_coding=start_coding,
|
|
83
83
|
end_coding=end_coding,
|
|
84
84
|
maxTime=max_media_duration_all_obs,
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
show_include_modifiers=True,
|
|
86
|
+
show_exclude_non_coded_behaviors=False,
|
|
87
87
|
n_observations=len(selected_observations),
|
|
88
88
|
)
|
|
89
89
|
|
|
@@ -171,8 +171,8 @@ def export_tabular_events(self, mode: str = "tabular") -> None:
|
|
|
171
171
|
start_coding=start_coding,
|
|
172
172
|
end_coding=end_coding,
|
|
173
173
|
maxTime=max_media_duration_all_obs,
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
show_include_modifiers=False,
|
|
175
|
+
show_exclude_non_coded_behaviors=False,
|
|
176
176
|
n_observations=len(selected_observations),
|
|
177
177
|
)
|
|
178
178
|
if parameters == {}:
|
|
@@ -283,7 +283,7 @@ def export_aggregated_events(self):
|
|
|
283
283
|
- select subjects and behaviors
|
|
284
284
|
- export events in aggregated format
|
|
285
285
|
|
|
286
|
-
Formats can be SQL (sql), SDIS (sds)
|
|
286
|
+
Formats can be SQL (sql), SDIS (sds), Tabular format (tsv, csv, ods, xlsx, xls, html) or Pandas dataframe
|
|
287
287
|
"""
|
|
288
288
|
|
|
289
289
|
def fields_type(max_modif_number: int) -> dict:
|
|
@@ -366,14 +366,14 @@ def export_aggregated_events(self):
|
|
|
366
366
|
start_coding=start_coding,
|
|
367
367
|
end_coding=end_coding,
|
|
368
368
|
maxTime=max_media_duration_all_obs,
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
show_include_modifiers=False,
|
|
370
|
+
show_exclude_non_coded_behaviors=False,
|
|
371
371
|
n_observations=len(selected_observations),
|
|
372
372
|
)
|
|
373
373
|
if parameters == {}:
|
|
374
374
|
return
|
|
375
375
|
if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
|
|
376
|
-
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to
|
|
376
|
+
QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to export")
|
|
377
377
|
return
|
|
378
378
|
|
|
379
379
|
# check for grouping results
|
|
@@ -660,8 +660,8 @@ def export_events_as_textgrid(self) -> None:
|
|
|
660
660
|
selected_observations,
|
|
661
661
|
start_coding=start_coding,
|
|
662
662
|
end_coding=end_coding,
|
|
663
|
-
|
|
664
|
-
|
|
663
|
+
show_include_modifiers=False,
|
|
664
|
+
show_exclude_non_coded_behaviors=False,
|
|
665
665
|
maxTime=max_obs_length,
|
|
666
666
|
n_observations=len(selected_observations),
|
|
667
667
|
)
|
boris/export_observation.py
CHANGED
|
@@ -583,7 +583,7 @@ def dataset_write(dataset: tablib.Dataset, file_name: str, output_format: str, d
|
|
|
583
583
|
try:
|
|
584
584
|
if output_format in (cfg.PANDAS_DF_EXT, cfg.RDS_EXT):
|
|
585
585
|
# build pandas dataframe from the tsv export of tablib dataset
|
|
586
|
-
date_type = []
|
|
586
|
+
date_type: list = []
|
|
587
587
|
for field_name in dtype:
|
|
588
588
|
if dtype[field_name] == dt.datetime:
|
|
589
589
|
date_type.append(field_name)
|
boris/geometric_measurement.py
CHANGED
|
@@ -547,16 +547,12 @@ def image_clicked(self, n_player: int, event) -> None:
|
|
|
547
547
|
if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA:
|
|
548
548
|
fn = self.dw_player[n_player - 1].player.playlist[self.dw_player[n_player - 1].player.playlist_pos]["filename"]
|
|
549
549
|
|
|
550
|
-
print(f"{fn=}")
|
|
551
|
-
|
|
552
550
|
# check if media file path contained in media list
|
|
553
551
|
if [True for x in self.pj[cfg.OBSERVATIONS][self.observationId]["file"].values() if fn in x]:
|
|
554
552
|
media_file_name = self.dw_player[n_player - 1].player.playlist[self.dw_player[n_player - 1].player.playlist_pos]["filename"]
|
|
555
553
|
else:
|
|
556
554
|
media_file_name = str(pl.Path(fn).relative_to(pl.Path(self.projectFileName).parent))
|
|
557
555
|
|
|
558
|
-
print(f"{media_file_name=}")
|
|
559
|
-
|
|
560
556
|
if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
|
|
561
557
|
# check if pictures dir path is relative
|
|
562
558
|
if str(pl.Path(self.images_list[current_frame]).parent) not in self.pj[cfg.OBSERVATIONS][self.observationId].get(
|
boris/irr.py
CHANGED
|
@@ -288,8 +288,8 @@ def irr_cohen_kappa(self):
|
|
|
288
288
|
selected_observations,
|
|
289
289
|
start_coding=dec("NaN"),
|
|
290
290
|
end_coding=dec("NaN"),
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
show_include_modifiers=True,
|
|
292
|
+
show_exclude_non_coded_behaviors=False,
|
|
293
293
|
n_observations=len(selected_observations),
|
|
294
294
|
)
|
|
295
295
|
if parameters == {}:
|
|
@@ -581,8 +581,8 @@ def needleman_wunch(self):
|
|
|
581
581
|
selected_observations,
|
|
582
582
|
start_coding=dec("NaN"),
|
|
583
583
|
end_coding=dec("NaN"),
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
show_include_modifiers=True,
|
|
585
|
+
show_exclude_non_coded_behaviors=False,
|
|
586
586
|
n_observations=len(selected_observations),
|
|
587
587
|
)
|
|
588
588
|
|
boris/latency.py
CHANGED
|
@@ -83,7 +83,7 @@ def get_latency(self):
|
|
|
83
83
|
parameters: dict = select_subj_behav.choose_obs_subj_behav_category(
|
|
84
84
|
self,
|
|
85
85
|
selected_observations,
|
|
86
|
-
|
|
86
|
+
show_exclude_non_coded_behaviors=False,
|
|
87
87
|
window_title="Select the marker behaviors (stimulus)",
|
|
88
88
|
n_observations=len(selected_observations),
|
|
89
89
|
)
|
|
@@ -102,7 +102,7 @@ def get_latency(self):
|
|
|
102
102
|
print(f"{marker_behaviors=} {marker_subjects=} {include_marker_modifiers=}")
|
|
103
103
|
|
|
104
104
|
parameters: dict = select_subj_behav.choose_obs_subj_behav_category(
|
|
105
|
-
self, selected_observations,
|
|
105
|
+
self, selected_observations, show_exclude_non_coded_behaviors=False, window_title="Select the latency behaviors"
|
|
106
106
|
)
|
|
107
107
|
if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
|
|
108
108
|
return
|
boris/mpv2.py
CHANGED
boris/observation.py
CHANGED
|
@@ -1151,11 +1151,13 @@ class Observation(QDialog, Ui_Form):
|
|
|
1151
1151
|
|
|
1152
1152
|
media_info = util.accurate_media_analysis(self.ffmpeg_bin, file_path)
|
|
1153
1153
|
|
|
1154
|
+
print(f"{media_info=}")
|
|
1155
|
+
|
|
1154
1156
|
if "error" in media_info:
|
|
1155
|
-
return (
|
|
1157
|
+
return (True, media_info["error"])
|
|
1156
1158
|
|
|
1157
1159
|
if media_info["format_long_name"] == "Tele-typewriter":
|
|
1158
|
-
return (
|
|
1160
|
+
return (True, "Text file")
|
|
1159
1161
|
|
|
1160
1162
|
if media_info["duration"] > 0:
|
|
1161
1163
|
if " rel " in mode:
|
|
@@ -1249,6 +1251,9 @@ class Observation(QDialog, Ui_Form):
|
|
|
1249
1251
|
|
|
1250
1252
|
for file_path in file_paths:
|
|
1251
1253
|
(error, msg) = self.check_media(file_path, mode)
|
|
1254
|
+
|
|
1255
|
+
print(f"{(error, msg)=}")
|
|
1256
|
+
|
|
1252
1257
|
if error:
|
|
1253
1258
|
QMessageBox.critical(self, cfg.programName, f"<b>{file_path}</b>. {msg}")
|
|
1254
1259
|
|
boris/param_panel.py
CHANGED
|
@@ -20,7 +20,6 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
from PyQt5.QtCore import Qt
|
|
25
24
|
from PyQt5.QtWidgets import QDialog
|
|
26
25
|
|
|
@@ -61,6 +60,16 @@ class Param_panel(QDialog, Ui_Dialog):
|
|
|
61
60
|
self.rb_observed_events.clicked.connect(lambda: self.rb_time_interval_selection(cfg.TIME_EVENTS))
|
|
62
61
|
self.rb_user_defined.clicked.connect(lambda: self.rb_time_interval_selection(cfg.TIME_ARBITRARY_INTERVAL))
|
|
63
62
|
|
|
63
|
+
self.cbIncludeModifiers.stateChanged.connect(self.cb_exclude_non_coded_modifiers_visibility)
|
|
64
|
+
|
|
65
|
+
self.cb_exclude_non_coded_modifiers.setVisible(False)
|
|
66
|
+
|
|
67
|
+
def cb_exclude_non_coded_modifiers_visibility(self):
|
|
68
|
+
"""
|
|
69
|
+
set visibility of cb_exclude_non_coded_modifiers
|
|
70
|
+
"""
|
|
71
|
+
self.cb_exclude_non_coded_modifiers.setEnabled(self.cbIncludeModifiers.isChecked())
|
|
72
|
+
|
|
64
73
|
def rb_time_interval_selection(self, button):
|
|
65
74
|
"""
|
|
66
75
|
select the time interval for operation
|
boris/param_panel_ui.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Form implementation generated from reading ui file 'boris/param_panel.ui'
|
|
4
4
|
#
|
|
5
|
-
# Created by: PyQt5 UI code generator 5.15.
|
|
5
|
+
# Created by: PyQt5 UI code generator 5.15.6
|
|
6
6
|
#
|
|
7
7
|
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
|
8
8
|
# run again. Do not edit this file unless you know what you are doing.
|
|
@@ -64,6 +64,10 @@ class Ui_Dialog(object):
|
|
|
64
64
|
self.cbIncludeModifiers = QtWidgets.QCheckBox(Dialog)
|
|
65
65
|
self.cbIncludeModifiers.setObjectName("cbIncludeModifiers")
|
|
66
66
|
self.horizontalLayout_9.addWidget(self.cbIncludeModifiers)
|
|
67
|
+
self.cb_exclude_non_coded_modifiers = QtWidgets.QCheckBox(Dialog)
|
|
68
|
+
self.cb_exclude_non_coded_modifiers.setChecked(True)
|
|
69
|
+
self.cb_exclude_non_coded_modifiers.setObjectName("cb_exclude_non_coded_modifiers")
|
|
70
|
+
self.horizontalLayout_9.addWidget(self.cb_exclude_non_coded_modifiers)
|
|
67
71
|
self.cbExcludeBehaviors = QtWidgets.QCheckBox(Dialog)
|
|
68
72
|
self.cbExcludeBehaviors.setObjectName("cbExcludeBehaviors")
|
|
69
73
|
self.horizontalLayout_9.addWidget(self.cbExcludeBehaviors)
|
|
@@ -177,6 +181,7 @@ class Ui_Dialog(object):
|
|
|
177
181
|
self.pbUnselectAllBehaviors.setText(_translate("Dialog", "Unselect all"))
|
|
178
182
|
self.pbReverseBehaviorsSelection.setText(_translate("Dialog", "Reverse selection"))
|
|
179
183
|
self.cbIncludeModifiers.setText(_translate("Dialog", "Include modifiers"))
|
|
184
|
+
self.cb_exclude_non_coded_modifiers.setText(_translate("Dialog", "Exclude non coded modifiers"))
|
|
180
185
|
self.cbExcludeBehaviors.setText(_translate("Dialog", "Exclude behaviors without events"))
|
|
181
186
|
self.lb_time_bin_size.setText(_translate("Dialog", "Time bin size (s)"))
|
|
182
187
|
self.lb_time_interval.setText(_translate("Dialog", "Time interval"))
|
boris/player_dock_widget.py
CHANGED
|
@@ -117,7 +117,7 @@ class DW_player(QDockWidget):
|
|
|
117
117
|
|
|
118
118
|
self.player = mpv.MPV(
|
|
119
119
|
wid=str(int(self.videoframe.winId())),
|
|
120
|
-
|
|
120
|
+
vo="x11" if sys.platform.startswith("linux") else "",
|
|
121
121
|
log_handler=functools.partial(mpv_logger, self.id_),
|
|
122
122
|
loglevel="debug",
|
|
123
123
|
)
|
boris/project_functions.py
CHANGED
|
@@ -244,6 +244,18 @@ def check_coded_behaviors_in_obs_list(pj: dict, observations_list: list) -> bool
|
|
|
244
244
|
return False
|
|
245
245
|
|
|
246
246
|
|
|
247
|
+
def get_modifiers_of_behavior(ethogram, behavior: str) -> list:
|
|
248
|
+
"""
|
|
249
|
+
get all modifiers for a behavior (if any)
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
return [
|
|
253
|
+
[ethogram[x][cfg.MODIFIERS][y]["values"] for y in ethogram[x][cfg.MODIFIERS]]
|
|
254
|
+
for x in ethogram
|
|
255
|
+
if ethogram[x][cfg.BEHAVIOR_CODE] == behavior
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
|
|
247
259
|
def check_coded_behaviors(pj: dict) -> set:
|
|
248
260
|
"""
|
|
249
261
|
check if behaviors coded in events are defined in ethogram for all observations
|
|
@@ -252,7 +264,7 @@ def check_coded_behaviors(pj: dict) -> set:
|
|
|
252
264
|
pj (dict): project dictionary
|
|
253
265
|
|
|
254
266
|
Returns:
|
|
255
|
-
set: behaviors present in observations that are not
|
|
267
|
+
set: behaviors present in observations that are not defined in ethogram
|
|
256
268
|
"""
|
|
257
269
|
|
|
258
270
|
# set of behaviors defined in ethogram
|
boris/select_modifiers.py
CHANGED
|
@@ -80,7 +80,7 @@ class ModifiersList(QDialog):
|
|
|
80
80
|
V2layout.addWidget(lb1)
|
|
81
81
|
|
|
82
82
|
if self.modifiers_dict[idx].get("description", ""):
|
|
83
|
-
lb2 = QLabel(f"<small>{self.modifiers_dict[idx].get('description', '')}</small>")
|
|
83
|
+
lb2 = QLabel(f"<small>{self.modifiers_dict[idx].get('description', '')[:50]}</small>")
|
|
84
84
|
V2layout.addWidget(lb2)
|
|
85
85
|
|
|
86
86
|
if self.modifiers_dict[idx]["type"] in (
|
boris/select_observations.py
CHANGED
|
@@ -20,6 +20,7 @@ Copyright 2012-2024 Olivier Friard
|
|
|
20
20
|
MA 02110-1301, USA.
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
|
+
|
|
23
24
|
from typing import Tuple
|
|
24
25
|
from PyQt5.QtCore import Qt
|
|
25
26
|
from PyQt5.QtWidgets import QAbstractItemView
|
|
@@ -203,177 +204,3 @@ def select_observations2(self, mode: str, windows_title: str = "") -> Tuple[str,
|
|
|
203
204
|
resultStr = cfg.VIEW
|
|
204
205
|
|
|
205
206
|
return resultStr, selected_observations
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
'''
|
|
209
|
-
def select_observations(pj: dict, mode: str, windows_title: str = "") -> Tuple[str, list]:
|
|
210
|
-
"""
|
|
211
|
-
allow user to select observations
|
|
212
|
-
mode: accepted values: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
pj (dict): BORIS project dictionary
|
|
216
|
-
mode (str): mode for selection: OPEN, EDIT, SINGLE, MULTIPLE, SELECT1
|
|
217
|
-
windows_title (str): title for windows
|
|
218
|
-
|
|
219
|
-
Returns:
|
|
220
|
-
str: selected mode: OPEN, EDIT, VIEW
|
|
221
|
-
list: list of selected observations
|
|
222
|
-
"""
|
|
223
|
-
|
|
224
|
-
fields_list = ["id", "date", "description", "subjects", "observation duration", "exhaustivity %", "media"]
|
|
225
|
-
indep_var_header, column_type = [], [cfg.TEXT, cfg.TEXT, cfg.TEXT, cfg.TEXT, cfg.NUMERIC, cfg.NUMERIC, cfg.TEXT]
|
|
226
|
-
|
|
227
|
-
if cfg.INDEPENDENT_VARIABLES in pj:
|
|
228
|
-
for idx in util.sorted_keys(pj[cfg.INDEPENDENT_VARIABLES]):
|
|
229
|
-
indep_var_header.append(pj[cfg.INDEPENDENT_VARIABLES][idx]["label"])
|
|
230
|
-
column_type.append(pj[cfg.INDEPENDENT_VARIABLES][idx]["type"])
|
|
231
|
-
|
|
232
|
-
state_events_list = [
|
|
233
|
-
pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
|
|
234
|
-
for x in pj[cfg.ETHOGRAM]
|
|
235
|
-
if cfg.STATE in pj[cfg.ETHOGRAM][x][cfg.TYPE].upper()
|
|
236
|
-
]
|
|
237
|
-
|
|
238
|
-
data: list = []
|
|
239
|
-
not_paired: list = []
|
|
240
|
-
|
|
241
|
-
for obs in sorted(list(pj[cfg.OBSERVATIONS].keys())):
|
|
242
|
-
date = pj[cfg.OBSERVATIONS][obs]["date"].replace("T", " ")
|
|
243
|
-
descr = util.eol2space(pj[cfg.OBSERVATIONS][obs][cfg.DESCRIPTION])
|
|
244
|
-
|
|
245
|
-
# subjects
|
|
246
|
-
observed_subjects = [
|
|
247
|
-
cfg.NO_FOCAL_SUBJECT if x == "" else x for x in project_functions.extract_observed_subjects(pj, [obs])
|
|
248
|
-
]
|
|
249
|
-
|
|
250
|
-
subjectsList = ", ".join(observed_subjects)
|
|
251
|
-
|
|
252
|
-
# observed time interval
|
|
253
|
-
interval = project_functions.observed_interval(pj[cfg.OBSERVATIONS][obs])
|
|
254
|
-
observed_interval_str = str(interval[1] - interval[0])
|
|
255
|
-
|
|
256
|
-
# media
|
|
257
|
-
media = ""
|
|
258
|
-
if pj[cfg.OBSERVATIONS][obs][cfg.TYPE] == cfg.MEDIA:
|
|
259
|
-
media_list = []
|
|
260
|
-
if pj[cfg.OBSERVATIONS][obs][cfg.FILE]:
|
|
261
|
-
for player in sorted(pj[cfg.OBSERVATIONS][obs][cfg.FILE].keys()):
|
|
262
|
-
for media in pj[cfg.OBSERVATIONS][obs][cfg.FILE][player]:
|
|
263
|
-
media_list.append(f"#{player}: {media}")
|
|
264
|
-
|
|
265
|
-
if len(media_list) > 8:
|
|
266
|
-
media = " ".join(media_list)
|
|
267
|
-
else:
|
|
268
|
-
media = "\n".join(media_list)
|
|
269
|
-
|
|
270
|
-
if pj[cfg.OBSERVATIONS][obs][cfg.TYPE] == cfg.LIVE:
|
|
271
|
-
media = cfg.LIVE
|
|
272
|
-
|
|
273
|
-
if pj[cfg.OBSERVATIONS][obs][cfg.TYPE] == cfg.IMAGES:
|
|
274
|
-
dir_list = []
|
|
275
|
-
for dir_path in pj[cfg.OBSERVATIONS][obs].get(cfg.DIRECTORIES_LIST, []):
|
|
276
|
-
dir_list.append(dir_path)
|
|
277
|
-
media = "; ".join(dir_list)
|
|
278
|
-
|
|
279
|
-
# independent variables
|
|
280
|
-
indepvar = []
|
|
281
|
-
if cfg.INDEPENDENT_VARIABLES in pj[cfg.OBSERVATIONS][obs]:
|
|
282
|
-
for var_label in indep_var_header:
|
|
283
|
-
if var_label in pj[cfg.OBSERVATIONS][obs][cfg.INDEPENDENT_VARIABLES]:
|
|
284
|
-
indepvar.append(pj[cfg.OBSERVATIONS][obs][cfg.INDEPENDENT_VARIABLES][var_label])
|
|
285
|
-
else:
|
|
286
|
-
indepvar.append("")
|
|
287
|
-
|
|
288
|
-
# check unpaired events
|
|
289
|
-
ok, _ = project_functions.check_state_events_obs(obs, pj[cfg.ETHOGRAM], pj[cfg.OBSERVATIONS][obs], cfg.HHMMSS)
|
|
290
|
-
if not ok:
|
|
291
|
-
not_paired.append(obs)
|
|
292
|
-
|
|
293
|
-
# exhaustivity
|
|
294
|
-
if pj[cfg.OBSERVATIONS][obs][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
|
|
295
|
-
# check exhaustivity of observation
|
|
296
|
-
exhaustivity = project_functions.check_observation_exhaustivity(
|
|
297
|
-
pj[cfg.OBSERVATIONS][obs][cfg.EVENTS], state_events_list
|
|
298
|
-
)
|
|
299
|
-
elif pj[cfg.OBSERVATIONS][obs][cfg.TYPE] == cfg.IMAGES:
|
|
300
|
-
# TODO: add exhaustivity for images observation (number of coded images?)
|
|
301
|
-
exhaustivity = project_functions.check_observation_exhaustivity_pictures(pj[cfg.OBSERVATIONS][obs])
|
|
302
|
-
|
|
303
|
-
data.append([obs, date, descr, subjectsList, observed_interval_str, str(exhaustivity), media] + indepvar)
|
|
304
|
-
|
|
305
|
-
obsList = observations_list.observationsList_widget(
|
|
306
|
-
data, header=fields_list + indep_var_header, column_type=column_type, not_paired=not_paired
|
|
307
|
-
)
|
|
308
|
-
if windows_title:
|
|
309
|
-
obsList.setWindowTitle(windows_title)
|
|
310
|
-
|
|
311
|
-
obsList.pbOpen.setVisible(False)
|
|
312
|
-
obsList.pbView.setVisible(False)
|
|
313
|
-
obsList.pbEdit.setVisible(False)
|
|
314
|
-
obsList.pbOk.setVisible(False)
|
|
315
|
-
obsList.pbSelectAll.setVisible(False)
|
|
316
|
-
obsList.pbUnSelectAll.setVisible(False)
|
|
317
|
-
obsList.mode = mode
|
|
318
|
-
|
|
319
|
-
if mode == cfg.OPEN:
|
|
320
|
-
obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
321
|
-
obsList.pbOpen.setVisible(True)
|
|
322
|
-
|
|
323
|
-
if mode == cfg.VIEW:
|
|
324
|
-
obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
325
|
-
obsList.pbView.setVisible(True)
|
|
326
|
-
|
|
327
|
-
if mode == cfg.EDIT:
|
|
328
|
-
obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
329
|
-
obsList.pbEdit.setVisible(True)
|
|
330
|
-
|
|
331
|
-
if mode == cfg.SINGLE:
|
|
332
|
-
obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
333
|
-
obsList.pbOpen.setVisible(True)
|
|
334
|
-
obsList.pbView.setVisible(True)
|
|
335
|
-
obsList.pbEdit.setVisible(True)
|
|
336
|
-
|
|
337
|
-
if mode == cfg.MULTIPLE:
|
|
338
|
-
obsList.view.setSelectionMode(QAbstractItemView.MultiSelection)
|
|
339
|
-
obsList.pbOk.setVisible(True)
|
|
340
|
-
obsList.pbSelectAll.setVisible(True)
|
|
341
|
-
obsList.pbUnSelectAll.setVisible(True)
|
|
342
|
-
|
|
343
|
-
if mode == cfg.SELECT1:
|
|
344
|
-
obsList.view.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
345
|
-
obsList.pbOk.setVisible(True)
|
|
346
|
-
|
|
347
|
-
# restore window geometry
|
|
348
|
-
gui_utilities.restore_geometry(obsList, "observations list", (900, 600))
|
|
349
|
-
|
|
350
|
-
obsList.view.sortItems(0, Qt.AscendingOrder)
|
|
351
|
-
for row in range(obsList.view.rowCount()):
|
|
352
|
-
obsList.view.resizeRowToContents(row)
|
|
353
|
-
|
|
354
|
-
selected_observations = []
|
|
355
|
-
|
|
356
|
-
result = obsList.exec_()
|
|
357
|
-
|
|
358
|
-
# saving window geometry in ini file
|
|
359
|
-
gui_utilities.save_geometry(obsList, "observations list")
|
|
360
|
-
|
|
361
|
-
if result:
|
|
362
|
-
if obsList.view.selectedIndexes():
|
|
363
|
-
for idx in obsList.view.selectedIndexes():
|
|
364
|
-
if idx.column() == 0: # first column
|
|
365
|
-
selected_observations.append(idx.data())
|
|
366
|
-
|
|
367
|
-
if result == 0: # cancel
|
|
368
|
-
resultStr = ""
|
|
369
|
-
if result == 1: # select
|
|
370
|
-
resultStr = "ok"
|
|
371
|
-
if result == 2: # open
|
|
372
|
-
resultStr = cfg.OPEN
|
|
373
|
-
if result == 3: # edit
|
|
374
|
-
resultStr = cfg.EDIT
|
|
375
|
-
if result == 4: # view
|
|
376
|
-
resultStr = cfg.VIEW
|
|
377
|
-
|
|
378
|
-
return resultStr, selected_observations
|
|
379
|
-
'''
|