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
boris/config_file.py ADDED
@@ -0,0 +1,356 @@
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
+ Read and write the BORIS config file
22
+ """
23
+
24
+ import pathlib as pl
25
+ import logging
26
+ import time
27
+
28
+ from . import config as cfg
29
+ from . import dialog
30
+
31
+ from PySide6.QtCore import QByteArray, QSettings
32
+
33
+
34
+ def read(self):
35
+ """
36
+ read config file
37
+ """
38
+
39
+ ini_fil_p_pe_p_path = pl.Path.home() / pl.Path(".boris")
40
+
41
+ logging.debug(f"read config file: {ini_fil_p_pe_p_path}")
42
+
43
+ if ini_fil_p_pe_p_path.is_file():
44
+ settings = QSettings(str(ini_fil_p_pe_p_path), QSettings.IniFormat)
45
+
46
+ try:
47
+ self.config_param = settings.value("config")
48
+ except Exception:
49
+ self.config_param = {}
50
+
51
+ if self.config_param == {}:
52
+ self.config_param = cfg.INIT_PARAM
53
+
54
+ # for back compatibility
55
+ # display subtitles
56
+ try:
57
+ self.config_param[cfg.DISPLAY_SUBTITLES] = settings.value(cfg.DISPLAY_SUBTITLES) == "true"
58
+ except Exception:
59
+ self.config_param[cfg.DISPLAY_SUBTITLES] = False
60
+
61
+ logging.debug(f"{cfg.DISPLAY_SUBTITLES}: {self.config_param[cfg.DISPLAY_SUBTITLES]}")
62
+
63
+ try:
64
+ logging.debug("restore geometry")
65
+
66
+ self.restoreGeometry(settings.value("geometry"))
67
+ except Exception:
68
+ logging.warning("Error restoring geometry")
69
+ pass
70
+
71
+ self.saved_state = settings.value("dockwidget_positions")
72
+ if not isinstance(self.saved_state, QByteArray):
73
+ self.saved_state = None
74
+
75
+ # logging.debug(f"saved state: {self.saved_state}")
76
+
77
+ self.timeFormat = cfg.HHMMSS
78
+ try:
79
+ self.timeFormat = settings.value("Time/Format")
80
+ except Exception:
81
+ self.timeFormat = cfg.HHMMSS
82
+
83
+ logging.debug(f"time format: {self.timeFormat}")
84
+
85
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
86
+ try:
87
+ self.fast = float(settings.value("Time/fast_forward_speed"))
88
+ except Exception:
89
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
90
+
91
+ logging.debug(f"Time/fast_forward_speed: {self.fast}")
92
+
93
+ self.repositioningTimeOffset = 0
94
+ try:
95
+ self.repositioningTimeOffset = int(settings.value("Time/Repositioning_time_offset"))
96
+ except Exception:
97
+ self.repositioningTimeOffset = 0
98
+
99
+ logging.debug(f"Time/Repositioning_time_offset: {self.repositioningTimeOffset}")
100
+
101
+ self.play_rate_step = 0.1
102
+ try:
103
+ self.play_rate_step = float(settings.value("Time/play_rate_step"))
104
+ except Exception:
105
+ self.play_rate_step = 0.1
106
+
107
+ logging.debug(f"Time/play_rate_step: {self.play_rate_step}")
108
+
109
+ self.automaticBackup = 0
110
+ try:
111
+ self.automaticBackup = int(settings.value("Automatic_backup"))
112
+ except Exception:
113
+ self.automaticBackup = 0
114
+
115
+ # activate or desactivate autosave timer
116
+ if self.automaticBackup:
117
+ self.automaticBackupTimer.start(self.automaticBackup * 60000)
118
+ else:
119
+ self.automaticBackupTimer.stop()
120
+
121
+ logging.debug(f"Autosave: {self.automaticBackup}")
122
+
123
+ self.behav_seq_separator = "|"
124
+ try:
125
+ self.behav_seq_separator = settings.value("behavioural_strings_separator")
126
+ if not self.behav_seq_separator:
127
+ self.behav_seq_separator = "|"
128
+ except Exception:
129
+ self.behav_seq_separator = "|"
130
+
131
+ logging.debug(f"behavioural_strings_separator: {self.behav_seq_separator}")
132
+
133
+ self.close_the_same_current_event = False
134
+ try:
135
+ self.close_the_same_current_event = settings.value("close_the_same_current_event") == "true"
136
+ except Exception:
137
+ self.close_the_same_current_event = False
138
+
139
+ logging.debug(f"close_the_same_current_event: {self.close_the_same_current_event}")
140
+
141
+ self.confirmSound = False
142
+ try:
143
+ self.confirmSound = settings.value("confirm_sound") == "true"
144
+ except Exception:
145
+ self.confirmSound = False
146
+
147
+ logging.debug(f"confirm_sound: {self.confirmSound}")
148
+
149
+ self.alertNoFocalSubject = False
150
+ try:
151
+ self.alertNoFocalSubject = settings.value("alert_nosubject") == "true"
152
+ except Exception:
153
+ self.alertNoFocalSubject = False
154
+ logging.debug(f"alert_nosubject: {self.alertNoFocalSubject}")
155
+
156
+ try:
157
+ self.beep_every = int(settings.value("beep_every"))
158
+ except Exception:
159
+ self.beep_every = 0
160
+ logging.debug(f"beep_every: {self.beep_every}")
161
+
162
+ self.trackingCursorAboveEvent = False
163
+ try:
164
+ self.trackingCursorAboveEvent = settings.value("tracking_cursor_above_event") == "true"
165
+ except Exception:
166
+ self.trackingCursorAboveEvent = False
167
+ logging.debug(f"tracking_cursor_above_event: {self.trackingCursorAboveEvent}")
168
+
169
+ # check for new version
170
+ self.checkForNewVersion = False
171
+
172
+ if not self.no_first_launch_dialog:
173
+ try:
174
+ if settings.value("check_for_new_version") is None:
175
+ self.checkForNewVersion = (
176
+ dialog.MessageDialog(
177
+ cfg.programName,
178
+ (
179
+ "Allow BORIS to automatically check for new version and news?\n"
180
+ "(An internet connection is required)\n"
181
+ "You can change this option in the Preferences (File > Preferences)"
182
+ ),
183
+ [cfg.YES, cfg.NO],
184
+ )
185
+ == cfg.YES
186
+ )
187
+ else:
188
+ self.checkForNewVersion = settings.value("check_for_new_version") == "true"
189
+ except Exception:
190
+ self.checkForNewVersion = False
191
+ logging.debug(f"Automatic check for new version: {self.checkForNewVersion}")
192
+
193
+ # pause before add event
194
+ self.pause_before_addevent = False
195
+ try:
196
+ self.pause_before_addevent = settings.value("pause_before_addevent") == "true"
197
+ except Exception:
198
+ self.pause_before_addevent = False
199
+
200
+ logging.debug(f"pause_before_addevent: {self.pause_before_addevent}")
201
+
202
+ if self.checkForNewVersion:
203
+ if settings.value("last_check_for_new_version") and (
204
+ int(time.mktime(time.localtime())) - int(settings.value("last_check_for_new_version")) > cfg.CHECK_NEW_VERSION_DELAY
205
+ ):
206
+ self.actionCheckUpdate_activated(flagMsgOnlyIfNew=True)
207
+
208
+ logging.debug(f"last check for new version: {settings.value('last_check_for_new_version')}")
209
+
210
+ self.ffmpeg_cache_dir = ""
211
+ try:
212
+ self.ffmpeg_cache_dir = settings.value("ffmpeg_cache_dir")
213
+ if not self.ffmpeg_cache_dir:
214
+ self.ffmpeg_cache_dir = ""
215
+ except Exception:
216
+ self.ffmpeg_cache_dir = ""
217
+ logging.debug(f"ffmpeg_cache_dir: {self.ffmpeg_cache_dir}")
218
+
219
+ # spectrogram
220
+ self.spectrogramHeight = 80
221
+
222
+ try:
223
+ self.spectrogram_color_map = settings.value("spectrogram_color_map")
224
+ if self.spectrogram_color_map is None:
225
+ self.spectrogram_color_map = cfg.SPECTROGRAM_DEFAULT_COLOR_MAP
226
+ except Exception:
227
+ self.spectrogram_color_map = cfg.SPECTROGRAM_DEFAULT_COLOR_MAP
228
+
229
+ try:
230
+ self.spectrogram_time_interval = int(settings.value("spectrogram_time_interval"))
231
+ if not self.spectrogram_time_interval:
232
+ self.spectrogram_time_interval = cfg.SPECTROGRAM_DEFAULT_TIME_INTERVAL
233
+ except Exception:
234
+ self.spectrogram_time_interval = cfg.SPECTROGRAM_DEFAULT_TIME_INTERVAL
235
+
236
+ # plot colors
237
+ try:
238
+ self.plot_colors = settings.value("plot_colors").split("|")
239
+ except Exception:
240
+ self.plot_colors = cfg.BEHAVIORS_PLOT_COLORS
241
+
242
+ if "white" in self.plot_colors or "azure" in self.plot_colors or "snow" in self.plot_colors:
243
+ if (
244
+ dialog.MessageDialog(
245
+ cfg.programName,
246
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
247
+ [cfg.NO, cfg.YES],
248
+ )
249
+ == cfg.YES
250
+ ):
251
+ self.plot_colors = cfg.BEHAVIORS_PLOT_COLORS
252
+
253
+ # behavioral categories colors
254
+ try:
255
+ self.behav_category_colors = settings.value("behav_category_colors").split("|")
256
+ except Exception:
257
+ self.behav_category_colors = cfg.CATEGORY_COLORS_LIST
258
+
259
+ if "white" in self.behav_category_colors or "azure" in self.behav_category_colors or "snow" in self.behav_category_colors:
260
+ if (
261
+ dialog.MessageDialog(
262
+ cfg.programName,
263
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
264
+ [cfg.NO, cfg.YES],
265
+ )
266
+ == cfg.YES
267
+ ):
268
+ self.behav_category_colors = cfg.CATEGORY_COLORS_LIST
269
+
270
+ else: # no .boris file found
271
+ logging.info("No config file found")
272
+ # ask user for checking for new version
273
+ if not self.no_first_launch_dialog:
274
+ self.checkForNewVersion = (
275
+ dialog.MessageDialog(
276
+ cfg.programName,
277
+ (
278
+ "Allow BORIS to automatically check for new version?\n"
279
+ "(An internet connection is required)\n"
280
+ "You can change this option in the"
281
+ " Preferences (File > Preferences)"
282
+ ),
283
+ [cfg.NO, cfg.YES],
284
+ )
285
+ == cfg.YES
286
+ )
287
+ else:
288
+ self.checkForNewVersion = False
289
+
290
+ # recent projects
291
+ logging.debug("read recent projects")
292
+ recent_projects_file_path = pl.Path.home() / ".boris_recent_projects"
293
+ if recent_projects_file_path.is_file():
294
+ settings = QSettings(str(recent_projects_file_path), QSettings.IniFormat)
295
+ try:
296
+ self.recent_projects = settings.value("recent_projects").split("|||")
297
+ while "" in self.recent_projects:
298
+ self.recent_projects.remove("")
299
+ self.set_recent_projects_menu()
300
+ except Exception:
301
+ self.recent_projects = []
302
+ else:
303
+ self.recent_projects = []
304
+
305
+
306
+ def save(self, lastCheckForNewVersion=0):
307
+ """
308
+ save config file in $HOME/.boris
309
+ """
310
+
311
+ file_path = pl.Path.home() / pl.Path(".boris")
312
+
313
+ logging.debug(f"save config file: {file_path}")
314
+
315
+ settings = QSettings(str(file_path), QSettings.IniFormat)
316
+
317
+ settings.setValue("config", self.config_param)
318
+
319
+ settings.setValue("geometry", self.saveGeometry())
320
+
321
+ if self.saved_state:
322
+ settings.setValue("dockwidget_positions", self.saved_state)
323
+
324
+ settings.setValue("Time/Format", self.timeFormat)
325
+ settings.setValue("Time/Repositioning_time_offset", self.repositioningTimeOffset)
326
+ settings.setValue("Time/fast_forward_speed", self.fast)
327
+ settings.setValue("Time/play_rate_step", self.play_rate_step)
328
+ settings.setValue("Automatic_backup", self.automaticBackup)
329
+ settings.setValue("behavioural_strings_separator", self.behav_seq_separator)
330
+ settings.setValue("close_the_same_current_event", self.close_the_same_current_event)
331
+ settings.setValue("confirm_sound", self.confirmSound)
332
+ settings.setValue("beep_every", self.beep_every)
333
+ settings.setValue("alert_nosubject", self.alertNoFocalSubject)
334
+ settings.setValue("tracking_cursor_above_event", self.trackingCursorAboveEvent)
335
+ settings.setValue("check_for_new_version", self.checkForNewVersion)
336
+ # settings.setValue(DISPLAY_SUBTITLES, self.config_param[DISPLAY_SUBTITLES])
337
+ settings.setValue("pause_before_addevent", self.pause_before_addevent)
338
+
339
+ if lastCheckForNewVersion:
340
+ settings.setValue("last_check_for_new_version", lastCheckForNewVersion)
341
+
342
+ # FFmpeg
343
+ settings.setValue("ffmpeg_cache_dir", self.ffmpeg_cache_dir)
344
+ # spectrogram
345
+ settings.setValue("spectrogram_color_map", self.spectrogram_color_map)
346
+ settings.setValue("spectrogram_time_interval", self.spectrogram_time_interval)
347
+ # plot colors
348
+ settings.setValue("plot_colors", "|".join(self.plot_colors))
349
+ # behavioral categories colors
350
+ settings.setValue("behav_category_colors", "|".join(self.behav_category_colors))
351
+
352
+ # recent projects
353
+ logging.debug("Save recent projects")
354
+
355
+ settings = QSettings(str(pl.Path.home() / ".boris_recent_projects"), QSettings.IniFormat)
356
+ settings.setValue("recent_projects", "|||".join(self.recent_projects))