boris-behav-obs 9.7.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (109) hide show
  1. boris/__init__.py +26 -0
  2. boris/__main__.py +25 -0
  3. boris/about.py +143 -0
  4. boris/add_modifier.py +635 -0
  5. boris/add_modifier_ui.py +303 -0
  6. boris/advanced_event_filtering.py +455 -0
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +1110 -0
  18. boris/behavior_binary_table.py +305 -0
  19. boris/behaviors_coding_map.py +239 -0
  20. boris/boris_cli.py +340 -0
  21. boris/cmd_arguments.py +49 -0
  22. boris/coding_pad.py +280 -0
  23. boris/config.py +785 -0
  24. boris/config_file.py +356 -0
  25. boris/connections.py +409 -0
  26. boris/converters.py +333 -0
  27. boris/converters_ui.py +225 -0
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +5901 -0
  30. boris/core_qrc.py +15958 -0
  31. boris/core_ui.py +1107 -0
  32. boris/db_functions.py +324 -0
  33. boris/dev.py +134 -0
  34. boris/dialog.py +1108 -0
  35. boris/duration_widget.py +238 -0
  36. boris/edit_event.py +245 -0
  37. boris/edit_event_ui.py +233 -0
  38. boris/event_operations.py +1040 -0
  39. boris/events_cursor.py +61 -0
  40. boris/events_snapshots.py +596 -0
  41. boris/exclusion_matrix.py +141 -0
  42. boris/export_events.py +1006 -0
  43. boris/export_observation.py +1203 -0
  44. boris/external_processes.py +332 -0
  45. boris/geometric_measurement.py +941 -0
  46. boris/gui_utilities.py +135 -0
  47. boris/image_overlay.py +72 -0
  48. boris/import_observations.py +242 -0
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +634 -0
  51. boris/latency.py +244 -0
  52. boris/measurement_widget.py +161 -0
  53. boris/media_file.py +115 -0
  54. boris/menu_options.py +213 -0
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +157 -0
  57. boris/mpv.py +2016 -0
  58. boris/mpv2.py +2193 -0
  59. boris/observation.py +1453 -0
  60. boris/observation_operations.py +2538 -0
  61. boris/observation_ui.py +679 -0
  62. boris/observations_list.py +337 -0
  63. boris/otx_parser.py +442 -0
  64. boris/param_panel.py +201 -0
  65. boris/param_panel_ui.py +305 -0
  66. boris/player_dock_widget.py +198 -0
  67. boris/plot_data_module.py +536 -0
  68. boris/plot_events.py +634 -0
  69. boris/plot_events_rt.py +237 -0
  70. boris/plot_spectrogram_rt.py +316 -0
  71. boris/plot_waveform_rt.py +230 -0
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +31 -0
  74. boris/portion/const.py +95 -0
  75. boris/portion/dict.py +365 -0
  76. boris/portion/func.py +52 -0
  77. boris/portion/interval.py +581 -0
  78. boris/portion/io.py +181 -0
  79. boris/preferences.py +510 -0
  80. boris/preferences_ui.py +770 -0
  81. boris/project.py +2007 -0
  82. boris/project_functions.py +2041 -0
  83. boris/project_import_export.py +1096 -0
  84. boris/project_ui.py +794 -0
  85. boris/qrc_boris.py +10389 -0
  86. boris/qrc_boris5.py +2579 -0
  87. boris/select_modifiers.py +312 -0
  88. boris/select_observations.py +210 -0
  89. boris/select_subj_behav.py +286 -0
  90. boris/state_events.py +197 -0
  91. boris/subjects_pad.py +106 -0
  92. boris/synthetic_time_budget.py +290 -0
  93. boris/time_budget_functions.py +1136 -0
  94. boris/time_budget_widget.py +1039 -0
  95. boris/transitions.py +365 -0
  96. boris/utilities.py +1810 -0
  97. boris/version.py +24 -0
  98. boris/video_equalizer.py +159 -0
  99. boris/video_equalizer_ui.py +248 -0
  100. boris/video_operations.py +310 -0
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.7.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
  106. boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
  107. boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
  108. boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
  109. boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,290 @@
1
+ """
2
+ BORIS
3
+ Behavioral Observation Research Interactive Software
4
+ Copyright 2012-2025 Olivier Friard
5
+
6
+ This file is part of BORIS.
7
+
8
+ BORIS is free software; you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation; either version 3 of the License, or
11
+ any later version.
12
+
13
+ BORIS is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with this program; if not see <http://www.gnu.org/licenses/>.
20
+
21
+ """
22
+
23
+ import logging
24
+ import pathlib as pl
25
+
26
+ from PySide6.QtWidgets import QFileDialog, QMessageBox
27
+ from PySide6.QtGui import QFont, QTextOption, QTextCursor
28
+
29
+ from . import config as cfg
30
+ from . import (
31
+ dialog,
32
+ project_functions,
33
+ select_observations,
34
+ select_subj_behav,
35
+ time_budget_functions,
36
+ observation_operations,
37
+ )
38
+
39
+
40
+ def synthetic_time_budget(self) -> None:
41
+ """
42
+ Synthetic time budget
43
+ """
44
+
45
+ logging.debug("synthetic time budget function")
46
+
47
+ _, selected_observations = select_observations.select_observations2(
48
+ self, mode=cfg.MULTIPLE, windows_title="Select observations for synthetic time budget"
49
+ )
50
+
51
+ if not selected_observations:
52
+ return
53
+
54
+ # check if coded behaviors are defined in ethogram
55
+ if project_functions.check_coded_behaviors_in_obs_list(self.pj, selected_observations):
56
+ return
57
+
58
+ # check if state events are paired
59
+ not_ok, selected_observations = project_functions.check_state_events(self.pj, selected_observations)
60
+ if not_ok or not selected_observations:
61
+ return
62
+
63
+ max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
64
+
65
+ start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
66
+
67
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
68
+
69
+ synth_tb_param = select_subj_behav.choose_obs_subj_behav_category(
70
+ self,
71
+ selected_observations,
72
+ start_coding=start_coding,
73
+ end_coding=end_coding,
74
+ # start_interval=start_interval,
75
+ # end_interval=end_interval,
76
+ start_interval=None,
77
+ end_interval=None,
78
+ maxTime=max_media_duration_all_obs,
79
+ show_exclude_non_coded_behaviors=False,
80
+ by_category=False,
81
+ n_observations=len(selected_observations),
82
+ show_exclude_non_coded_modifiers=True,
83
+ )
84
+
85
+ if synth_tb_param == {}:
86
+ return
87
+
88
+ if not synth_tb_param[cfg.SELECTED_SUBJECTS] or not synth_tb_param[cfg.SELECTED_BEHAVIORS]:
89
+ QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
90
+ return
91
+
92
+ # ask for excluding behaviors durations from total time
93
+ if not start_coding.is_nan():
94
+ cancel_pressed, synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = self.filter_behaviors(
95
+ title="Select behaviors to exclude from the total time",
96
+ text="The duration of the selected behaviors will be subtracted from the total time",
97
+ table="",
98
+ behavior_type=cfg.STATE_EVENT_TYPES,
99
+ )
100
+ if cancel_pressed:
101
+ return
102
+ else:
103
+ synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = []
104
+
105
+ ok, msg, data_report = time_budget_functions.synthetic_time_budget(self.pj, selected_observations, synth_tb_param)
106
+
107
+ results = dialog.Results_dialog()
108
+ results.setWindowTitle("Synthetic time budget")
109
+ if not ok:
110
+ results.ptText.clear()
111
+ results.ptText.setReadOnly(True)
112
+ results.ptText.appendHtml(msg.replace("\n", "<br>"))
113
+ results.exec_()
114
+ return
115
+
116
+ results.dataset = True
117
+ font = QFont("Courier", 12)
118
+ results.ptText.setFont(font)
119
+ results.ptText.setWordWrapMode(QTextOption.NoWrap)
120
+ results.ptText.setReadOnly(True)
121
+ results.ptText.appendPlainText(data_report.export("cli", tablefmt="grid")) # other available format: github
122
+ results.ptText.moveCursor(QTextCursor.Start)
123
+ results.resize(960, 640)
124
+
125
+ if results.exec_() == cfg.SAVE_DATASET:
126
+ file_formats = [
127
+ cfg.TSV,
128
+ cfg.CSV,
129
+ cfg.ODS,
130
+ cfg.XLSX,
131
+ cfg.XLS,
132
+ cfg.HTML,
133
+ cfg.TEXT_FILE, # tablib format: cli
134
+ ]
135
+
136
+ file_name, filter_ = QFileDialog().getSaveFileName(self, "Synthetic time budget", "", ";;".join(file_formats))
137
+ if not file_name:
138
+ return
139
+
140
+ output_format = cfg.FILE_NAME_SUFFIX[filter_]
141
+
142
+ if pl.Path(file_name).suffix != "." + output_format:
143
+ if filter_ != cfg.TEXT_FILE:
144
+ file_name = str(pl.Path(file_name)) + "." + output_format
145
+ else:
146
+ file_name = str(pl.Path(file_name))
147
+ if pl.Path(file_name).is_file():
148
+ if (
149
+ dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE])
150
+ == cfg.CANCEL
151
+ ):
152
+ return
153
+
154
+ with open(file_name, "wb") as f:
155
+ if filter_ in (cfg.TSV, cfg.CSV, cfg.HTML, cfg.TEXT_FILE):
156
+ f.write(str.encode(data_report.export(output_format)))
157
+ if filter_ in (cfg.ODS, cfg.XLSX, cfg.XLS):
158
+ f.write(data_report.export(output_format))
159
+
160
+
161
+ def synthetic_binned_time_budget(self) -> None:
162
+ """
163
+ Synthetic time budget with time bin
164
+ """
165
+
166
+ _, selected_observations = select_observations.select_observations2(
167
+ self, mode=cfg.MULTIPLE, windows_title="Select observations for synthetic binned time budget"
168
+ )
169
+
170
+ if not selected_observations:
171
+ return
172
+
173
+ # check if coded behaviors are defined in ethogram
174
+ if project_functions.check_coded_behaviors_in_obs_list(self.pj, selected_observations):
175
+ return
176
+
177
+ # check if state events are paired
178
+ not_ok, selected_observations = project_functions.check_state_events(self.pj, selected_observations)
179
+ if not_ok or not selected_observations:
180
+ return
181
+
182
+ max_media_duration_all_obs, total_media_duration_all_obs = observation_operations.media_duration(
183
+ self.pj[cfg.OBSERVATIONS], selected_observations
184
+ )
185
+
186
+ start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
187
+
188
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
189
+
190
+ # exit with message if events do not have timestamp
191
+ if start_coding.is_nan():
192
+ QMessageBox.critical(
193
+ None,
194
+ cfg.programName,
195
+ ("This function is not available for observations with events that do not have timestamp"),
196
+ QMessageBox.Ok | QMessageBox.Default,
197
+ QMessageBox.NoButton,
198
+ )
199
+ return
200
+
201
+ synth_tb_param = select_subj_behav.choose_obs_subj_behav_category(
202
+ self,
203
+ selected_observations,
204
+ start_coding=start_coding,
205
+ end_coding=end_coding,
206
+ # start_interval=start_interval,
207
+ # end_interval=end_interval,
208
+ start_interval=None,
209
+ end_interval=None,
210
+ maxTime=max_media_duration_all_obs,
211
+ show_exclude_non_coded_behaviors=False,
212
+ by_category=False,
213
+ n_observations=len(selected_observations),
214
+ show_time_bin_size=True,
215
+ show_exclude_non_coded_modifiers=True,
216
+ )
217
+
218
+ if synth_tb_param == {}:
219
+ return
220
+
221
+ if not synth_tb_param[cfg.SELECTED_SUBJECTS] or not synth_tb_param[cfg.SELECTED_BEHAVIORS]:
222
+ QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
223
+ return
224
+
225
+ # ask for excluding behaviors durations from total time
226
+ cancel_pressed, synth_tb_param[cfg.EXCLUDED_BEHAVIORS] = self.filter_behaviors(
227
+ title="Select behaviors to exclude",
228
+ text=("The duration of the selected behaviors will be subtracted from the total time"),
229
+ table="",
230
+ behavior_type=cfg.STATE_EVENT_TYPES,
231
+ )
232
+ if cancel_pressed:
233
+ return
234
+
235
+ ok, data_report = time_budget_functions.synthetic_time_budget_bin(self.pj, selected_observations, synth_tb_param)
236
+
237
+ if not ok:
238
+ results = dialog.Results_dialog()
239
+ results.setWindowTitle("Synthetic time budget with time bin")
240
+ results.ptText.appendHtml("Error during the creation of the synthetic time budget with time bin")
241
+ results.exec_()
242
+ return
243
+
244
+ results = dialog.Results_dialog()
245
+ results.dataset = True
246
+ results.setWindowTitle("Synthetic time budget by time bin")
247
+ font = QFont("Courier", 12)
248
+ results.ptText.setFont(font)
249
+ results.ptText.setWordWrapMode(QTextOption.NoWrap)
250
+ results.ptText.setReadOnly(True)
251
+ results.ptText.appendPlainText(data_report.export("cli", tablefmt="grid")) # other available format: github
252
+ results.ptText.moveCursor(QTextCursor.Start)
253
+ results.resize(960, 640)
254
+
255
+ if results.exec_() == cfg.SAVE_DATASET:
256
+ file_formats = [
257
+ cfg.TSV,
258
+ cfg.CSV,
259
+ cfg.ODS,
260
+ cfg.XLSX,
261
+ cfg.XLS,
262
+ cfg.HTML,
263
+ cfg.TEXT_FILE,
264
+ ]
265
+
266
+ file_name, filter_ = QFileDialog().getSaveFileName(
267
+ self, "Save the Synthetic time budget with time bin", "", ";;".join(file_formats)
268
+ )
269
+ if not file_name:
270
+ return
271
+
272
+ output_format = cfg.FILE_NAME_SUFFIX[filter_]
273
+
274
+ if pl.Path(file_name).suffix != "." + output_format:
275
+ if filter_ != cfg.TEXT_FILE:
276
+ file_name = str(pl.Path(file_name)) + "." + output_format
277
+ else:
278
+ file_name = str(pl.Path(file_name))
279
+ if pl.Path(file_name).is_file():
280
+ if (
281
+ dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", (cfg.CANCEL, cfg.OVERWRITE))
282
+ == cfg.CANCEL
283
+ ):
284
+ return
285
+
286
+ with open(file_name, "wb") as f:
287
+ if filter_ in (cfg.TSV, cfg.CSV, cfg.HTML, cfg.TEXT_FILE):
288
+ f.write(str.encode(data_report.export(output_format)))
289
+ if filter_ in (cfg.ODS, cfg.XLSX, cfg.XLS):
290
+ f.write(data_report.export(output_format))