boris-behav-obs 8.16.5__py3-none-any.whl → 9.7.12__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.
Files changed (126) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +28 -40
  4. boris/add_modifier.py +88 -80
  5. boris/add_modifier_ui.py +266 -144
  6. boris/advanced_event_filtering.py +23 -29
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_export_to_feral.py +225 -0
  9. boris/analysis_plugins/_latency.py +59 -0
  10. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  11. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  13. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  14. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  15. boris/analysis_plugins/number_of_occurences.py +22 -0
  16. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  17. boris/analysis_plugins/time_budget.py +61 -0
  18. boris/behav_coding_map_creator.py +235 -236
  19. boris/behavior_binary_table.py +33 -50
  20. boris/behaviors_coding_map.py +17 -18
  21. boris/boris_cli.py +6 -25
  22. boris/cmd_arguments.py +12 -1
  23. boris/coding_pad.py +19 -36
  24. boris/config.py +109 -50
  25. boris/config_file.py +58 -67
  26. boris/connections.py +105 -58
  27. boris/converters.py +13 -37
  28. boris/converters_ui.py +187 -110
  29. boris/cooccurence.py +250 -0
  30. boris/core.py +2174 -1303
  31. boris/core_qrc.py +15892 -10829
  32. boris/core_ui.py +941 -806
  33. boris/db_functions.py +17 -42
  34. boris/dev.py +27 -7
  35. boris/dialog.py +461 -242
  36. boris/duration_widget.py +9 -14
  37. boris/edit_event.py +61 -31
  38. boris/edit_event_ui.py +208 -97
  39. boris/event_operations.py +405 -281
  40. boris/events_cursor.py +25 -17
  41. boris/events_snapshots.py +36 -82
  42. boris/exclusion_matrix.py +4 -9
  43. boris/export_events.py +180 -203
  44. boris/export_observation.py +60 -73
  45. boris/external_processes.py +123 -98
  46. boris/geometric_measurement.py +427 -218
  47. boris/gui_utilities.py +91 -14
  48. boris/image_overlay.py +4 -4
  49. boris/import_observations.py +190 -98
  50. boris/ipc_mpv.py +325 -0
  51. boris/irr.py +20 -57
  52. boris/latency.py +31 -24
  53. boris/measurement_widget.py +14 -18
  54. boris/media_file.py +17 -19
  55. boris/menu_options.py +16 -6
  56. boris/modifier_coding_map_creator.py +1013 -0
  57. boris/modifiers_coding_map.py +7 -9
  58. boris/mpv2.py +128 -35
  59. boris/observation.py +501 -211
  60. boris/observation_operations.py +1037 -393
  61. boris/observation_ui.py +573 -363
  62. boris/observations_list.py +51 -58
  63. boris/otx_parser.py +74 -68
  64. boris/param_panel.py +45 -59
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +91 -56
  67. boris/plot_data_module.py +20 -53
  68. boris/plot_events.py +56 -153
  69. boris/plot_events_rt.py +16 -30
  70. boris/plot_spectrogram_rt.py +83 -56
  71. boris/plot_waveform_rt.py +27 -49
  72. boris/plugins.py +468 -0
  73. boris/portion/__init__.py +18 -8
  74. boris/portion/const.py +35 -18
  75. boris/portion/dict.py +5 -5
  76. boris/portion/func.py +2 -2
  77. boris/portion/interval.py +21 -41
  78. boris/portion/io.py +41 -32
  79. boris/preferences.py +307 -123
  80. boris/preferences_ui.py +686 -227
  81. boris/project.py +294 -271
  82. boris/project_functions.py +626 -537
  83. boris/project_import_export.py +204 -213
  84. boris/project_ui.py +673 -441
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +62 -90
  88. boris/select_observations.py +19 -197
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +51 -33
  91. boris/subjects_pad.py +7 -9
  92. boris/synthetic_time_budget.py +42 -26
  93. boris/time_budget_functions.py +169 -169
  94. boris/time_budget_widget.py +77 -89
  95. boris/transitions.py +41 -41
  96. boris/utilities.py +594 -226
  97. boris/version.py +3 -3
  98. boris/video_equalizer.py +16 -14
  99. boris/video_equalizer_ui.py +199 -130
  100. boris/video_operations.py +86 -28
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +240 -136
  104. boris_behav_obs-9.7.12.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.12.dist-info/RECORD +110 -0
  106. {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.12.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.12.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/converters.ui +0 -289
  111. boris/core.qrc +0 -37
  112. boris/core.ui +0 -1571
  113. boris/edit_event.ui +0 -233
  114. boris/icons/logo_eye.ico +0 -0
  115. boris/map_creator.py +0 -982
  116. boris/observation.ui +0 -814
  117. boris/param_panel.ui +0 -379
  118. boris/preferences.ui +0 -537
  119. boris/project.ui +0 -1074
  120. boris/vlc_local.py +0 -90
  121. boris_behav_obs-8.16.5.dist-info/LICENSE.TXT +0 -674
  122. boris_behav_obs-8.16.5.dist-info/METADATA +0 -134
  123. boris_behav_obs-8.16.5.dist-info/RECORD +0 -107
  124. boris_behav_obs-8.16.5.dist-info/entry_points.txt +0 -2
  125. {boris → boris_behav_obs-9.7.12.dist-info/licenses}/LICENSE.TXT +0 -0
  126. {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.12.dist-info}/top_level.txt +0 -0
boris/config.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
@@ -22,8 +22,23 @@ This file is part of BORIS.
22
22
 
23
23
  programName: str = "BORIS"
24
24
 
25
+ # socket for MPV IPC mode
26
+ MPV_SOCKET = "/tmp/mpvsocket"
27
+
28
+
29
+ ETHOGRAM_REPOSITORY_URL = "https://www.boris.unito.it/static/ethograms"
30
+
25
31
  project_format_version: str = "7.0"
26
32
 
33
+ SECONDS_PER_DAY: int = 86_400
34
+
35
+ # cutoff value for displaying HH:MM:SS.zzz or YYYY-mm-DD HH:MM:SS:ZZZ
36
+ HOUR_CUTOFF: int = 7 * 24
37
+ DATE_CUTOFF: int = HOUR_CUTOFF * 60 * 60 # 1 week
38
+
39
+ # cutoff for displaying time in HH:MM:SS.zzz format
40
+ SMART_TIME_CUTOFF_DEFAULT: int = 300
41
+
27
42
  # minimal project version for handling observations from images
28
43
  IMAGES_OBS_PROJECT_MIN_VERSION: tuple = (8, 6)
29
44
 
@@ -37,11 +52,12 @@ MAX_UNDO_QUEUE: int = 25
37
52
 
38
53
  NA: str = "NA"
39
54
 
40
- REALTIME_PLOT_CURSOR_COLOR = "red"
55
+ REALTIME_PLOT_CURSOR_COLOR: str = "red"
41
56
 
42
- DYNAMIC_TIME_CUTOFF_DEFAULT = 300
57
+ DARKER_DIFFERENCE = 5
43
58
 
44
59
  CTRL_KEY: str = "Ctrl"
60
+ ALT_KEY: str = "Alt"
45
61
 
46
62
  SPECTRO_TIMER: int = 500
47
63
 
@@ -72,6 +88,7 @@ EVENTS = "events"
72
88
  TIME_OFFSET = "time offset"
73
89
 
74
90
  CODING_MAP = "coding_map"
91
+ CODING_MAP_sp = "coding map" # space between words (no underscore)
75
92
  BEHAVIORS_CODING_MAP = "behaviors_coding_map"
76
93
  SUBJECTS = "subjects_conf"
77
94
  ETHOGRAM = "behaviors_conf"
@@ -115,20 +132,34 @@ POINT_EVENT_PLOT_COLOR = "black"
115
132
 
116
133
  CHAR_FORBIDDEN_IN_MODIFIERS = "(|),`~"
117
134
 
135
+ FAST_FORWARD_DEFAULT_VALUE: float = 10.0
136
+
118
137
  ADAPT_FAST_JUMP = "adapt_fast_jump"
119
138
  ADAPT_FAST_JUMP_DEFAULT = False
120
139
 
140
+ TIME = "time"
141
+ BEHAVIOR_CODE = "code"
142
+ SUBJECT = "subject"
143
+ MODIFIER = "modifier"
144
+ COMMENT = "comment"
145
+ BEHAVIOR_KEY = "key"
146
+ SUBJECT_KEY = "key"
147
+ BEHAVIOR_CATEGORY = "category"
148
+
121
149
 
122
- subjectsFields = ["key", SUBJECT_NAME, DESCRIPTION]
150
+ subjectsFields = [SUBJECT_KEY, SUBJECT_NAME, DESCRIPTION]
123
151
 
124
152
  UNPAIRED = "UNPAIRED"
125
153
  DISPLAY_SUBTITLES = "display_subtitles"
154
+ CHECK_PROJECT_INTEGRITY = "check_project_integrity"
126
155
 
127
156
 
128
157
  YES = "Yes"
129
158
  NO = "No"
130
159
  CANCEL = "Cancel"
160
+ IGNORE = "Ignore"
131
161
  APPEND = "Append"
162
+ CLOSE = "Close"
132
163
  REPLACE = "Replace"
133
164
  REMOVE = "Remove"
134
165
  SAVE = "Save"
@@ -173,13 +204,6 @@ DATA_PLOT_FIELDS = {
173
204
  }
174
205
  DATA_PLOT_STYLES = ["b-", "r-", "g-", "bo", "ro", "go"]
175
206
 
176
- TIME = "time"
177
- BEHAVIOR_CODE = "code"
178
- SUBJECT = "subject"
179
- MODIFIER = "modifier"
180
- COMMENT = "comment"
181
- BEHAVIOR_KEY = "key"
182
- BEHAVIOR_CATEGORY = "category"
183
207
 
184
208
  FILE_PATH = "file_path"
185
209
 
@@ -188,27 +212,19 @@ IMAGE_PATH = "image path"
188
212
  FRAME_INDEX = "frame index"
189
213
 
190
214
  ZOOM_LEVEL = "zoom level"
215
+ PAN_X = "pan-x"
216
+ PAN_Y = "pan-y"
191
217
  ROTATION_ANGLE = "rotation angle"
192
218
  DISPLAY_MEDIA_SUBTITLES = "display subtitles"
193
219
  OVERLAY = "video overlay"
194
220
 
195
221
 
196
222
  USE_EXIF_DATE = "use_exif_date"
223
+ SUBSTRACT_FIRST_EXIF_DATE = "substract_first_exif_date"
197
224
  TIME_LAPSE = "time_lapse_delay"
198
225
 
199
226
 
200
227
  # fields for event configuration
201
- """
202
- fields = {
203
- "type": 0,
204
- "key": 1,
205
- "code": 2,
206
- "description": 3,
207
- "modifiers": 4,
208
- "excluded": 5,
209
- "coding map": 6,
210
- }
211
- """
212
228
 
213
229
  ETHOGRAM_TABLE_COLUMNS: dict = {
214
230
  0: "key",
@@ -233,18 +249,6 @@ behavioursFields: dict = {
233
249
  "excluded": 7,
234
250
  "coding map": 8,
235
251
  }
236
- """
237
- ETHOGRAM_FIELDS = [
238
- "type",
239
- "key",
240
- "code",
241
- "description",
242
- "category",
243
- "modifiers",
244
- "excluded",
245
- "coding map",
246
- ]
247
- """
248
252
  ETHOGRAM_EDITABLE_FIELDS: tuple = ("key", "code", "description")
249
253
 
250
254
  PROJECT_BEHAVIORS_KEY_FIELD_IDX = 1
@@ -254,13 +258,20 @@ MEDIA_FILE_PATH_IDX = 2
254
258
  HAS_AUDIO_IDX = 6
255
259
 
256
260
  STATE_EVENT = "State event"
261
+ STATE_EVENT_WITH_CODING_MAP = "State event with coding map"
262
+
263
+ STATE_EVENT_TYPES = [STATE_EVENT, STATE_EVENT_WITH_CODING_MAP]
264
+
257
265
  POINT_EVENT = "Point event"
266
+ POINT_EVENT_WITH_CODING_MAP = "Point event with coding map"
267
+
268
+ POINT_EVENT_TYPES = [POINT_EVENT, POINT_EVENT_WITH_CODING_MAP]
258
269
 
259
270
  BEHAVIOR_TYPES = [
260
- "Point event",
261
- "State event",
262
- "Point event with coding map",
263
- "State event with coding map",
271
+ POINT_EVENT,
272
+ STATE_EVENT,
273
+ POINT_EVENT_WITH_CODING_MAP,
274
+ STATE_EVENT_WITH_CODING_MAP,
264
275
  ]
265
276
 
266
277
  DEFAULT_BEHAVIOR_TYPE = "Point event"
@@ -272,7 +283,7 @@ MEDIA_TW_EVENTS_FIELDS_DEFAULT = ("time", FRAME_INDEX, "subject", "code", "type"
272
283
  MEDIA_TW_EVENTS_FIELDS = ("time", FRAME_INDEX, "subject", "code", "type", "modifier", "comment")
273
284
  # MEDIA_TW_EVENTS_FIELDS = ("time", "subject", "code", "type", "modifier", "comment")
274
285
  LIVE_TW_EVENTS_FIELDS = ("time", "subject", "code", "type", "modifier", "comment")
275
- IMAGES_TW_EVENTS_FIELDS = ("time", "subject", "code", "type", "modifier", "comment", IMAGE_INDEX, "image path")
286
+ IMAGES_TW_EVENTS_FIELDS = ("time", "subject", "code", "type", "modifier", "comment", IMAGE_INDEX, IMAGE_PATH)
276
287
 
277
288
  TW_EVENTS_FIELDS = {
278
289
  MEDIA: MEDIA_TW_EVENTS_FIELDS,
@@ -294,7 +305,7 @@ for observation_type in TW_EVENTS_FIELDS:
294
305
  # fields for project events list
295
306
  MEDIA_PJ_EVENTS_FIELDS = (TIME, "subject", "code", "modifier", "comment", FRAME_INDEX)
296
307
  LIVE_PJ_EVENTS_FIELDS = (TIME, "subject", "code", "modifier", "comment")
297
- IMAGES_PJ_EVENTS_FIELDS = (TIME, "subject", "code", "modifier", "comment", "image index", "image path")
308
+ IMAGES_PJ_EVENTS_FIELDS = (TIME, "subject", "code", "modifier", "comment", IMAGE_INDEX, IMAGE_PATH)
298
309
 
299
310
  PJ_EVENTS_FIELDS = {
300
311
  MEDIA: MEDIA_PJ_EVENTS_FIELDS,
@@ -305,9 +316,6 @@ PJ_EVENTS_FIELDS = {
305
316
  VIEWER_IMAGES: IMAGES_PJ_EVENTS_FIELDS,
306
317
  }
307
318
 
308
- # fields for independent variable definition
309
- tw_indVarFields = ["label", "description", "type", "default value", "possible values"]
310
-
311
319
 
312
320
  PJ_OBS_FIELDS = {}
313
321
  for observation_type in PJ_EVENTS_FIELDS:
@@ -315,12 +323,17 @@ for observation_type in PJ_EVENTS_FIELDS:
315
323
  for idx, field in enumerate(PJ_EVENTS_FIELDS[observation_type]):
316
324
  PJ_OBS_FIELDS[observation_type][field] = idx
317
325
 
326
+
327
+ # fields for independent variable definition
328
+ tw_indVarFields = ["label", "description", "type", "default value", "possible values"]
329
+
330
+
318
331
  EVENT_TIME_FIELD_IDX = 0
319
332
  EVENT_SUBJECT_FIELD_IDX = 1
320
333
  EVENT_BEHAVIOR_FIELD_IDX = 2
321
334
  EVENT_MODIFIER_FIELD_IDX = 3
322
335
  EVENT_COMMENT_FIELD_IDX = 4
323
- EVENT_STATUS_FIELD_IDX = 5
336
+ EVENT_STATUS_FIELD_IDX = -1
324
337
  # EVENT_IMAGEIDX_FIELD_IDX = 6
325
338
  # EVENT_IMAGEPATH_FIELD_IDX = 7
326
339
 
@@ -345,6 +358,14 @@ START_FROM_CURRENT_EPOCH_TIME = "start_from_current_epoch_time"
345
358
 
346
359
  SCAN_SAMPLING_TIME = "scan_sampling_time"
347
360
 
361
+ POINT_OBJECT = "Point"
362
+ SEGMENT_OBJECT = "Segment"
363
+ ANGLE_OBJECT = "Angle"
364
+ ORIENTED_ANGLE_OBJECT = "Oriented angle"
365
+ POLYGON_OBJECT = "Polygon"
366
+ POLYLINE_OBJECT = "Polyline"
367
+
368
+
348
369
  NEW = "new"
349
370
  LIST = "list"
350
371
  EDIT = "edit"
@@ -364,6 +385,7 @@ SELECTED_BEHAVIORS = "selected behaviors"
364
385
  SELECTED_SUBJECTS = "selected subjects"
365
386
  INCLUDE_MODIFIERS = "include modifiers"
366
387
  EXCLUDE_BEHAVIORS = "exclude behaviors"
388
+ EXCLUDE_NON_CODED_MODIFIERS = "exclude_non_coded_modifiers"
367
389
  EXCLUDED_BEHAVIORS = "excluded behaviors"
368
390
  TIME_INTERVAL = "time"
369
391
  START_TIME = "start time"
@@ -381,6 +403,7 @@ TIMESTAMP_idx = 3
381
403
 
382
404
  TIME_FULL_OBS = "full obs"
383
405
  TIME_EVENTS = "limit to events"
406
+ TIME_OBS_INTERVAL = "interval of observation"
384
407
  TIME_ARBITRARY_INTERVAL = "time interval"
385
408
 
386
409
  AVAILABLE_INDEP_VAR_TYPES = [NUMERIC, TEXT, SET_OF_VALUES, TIMESTAMP]
@@ -390,16 +413,31 @@ OBSERVATIONS = "observations"
390
413
 
391
414
  CLOSE_BEHAVIORS_BETWEEN_VIDEOS = "close_behaviors_between_videos"
392
415
 
393
- MPV_HWDEC_OPTIONS = ("auto", "auto-safe", "no")
394
- MPV_HWDEC_DEFAULT_VALUE = "auto"
416
+ # MPV hardware decode
395
417
  MPV_HWDEC = "mpv_hwdec"
418
+ MPV_HWDEC_NO = "no"
419
+ MPV_HWDEC_AUTO = "auto"
420
+ MPV_HWDEC_AUTOSAFE = "auto-safe"
421
+ MPV_HWDEC_OPTIONS = (MPV_HWDEC_AUTO, MPV_HWDEC_AUTOSAFE, MPV_HWDEC_NO)
422
+ MPV_HWDEC_DEFAULT_VALUE = MPV_HWDEC_AUTO
423
+
424
+ # frame step size (disabled)
425
+ # FRAME_STEP_SIZE: str = "frame_step_size"
426
+ # FRAME_STEP_SIZE_DEFAULT_VALUE: int = 1
396
427
 
428
+ ANALYSIS_PLUGINS = "analysis_plugins"
429
+ EXCLUDED_PLUGINS = "excluded_plugins"
430
+ PERSONAL_PLUGINS_DIR = "personal_plugins_dir"
397
431
 
398
432
  PROJECT_FILE_INDENTATION = "project file indentation"
399
433
  PROJECT_FILE_INDENTATION_COMBO_OPTIONS = ("None", "Newline", "Tab", "2 spaces", "4 spaces")
400
434
  PROJECT_FILE_INDENTATION_OPTIONS = (None, 0, "\t", 2, 4)
401
435
  PROJECT_FILE_INDENTATION_DEFAULT_VALUE = None
402
436
 
437
+ TOOLBAR_ICON_SIZE = "toolbar icon size"
438
+ DEFAULT_TOOLBAR_ICON_SIZE_VALUE = 24
439
+
440
+
403
441
  VIDEO_VIEWER = 0
404
442
  PICTURE_VIEWER = 1
405
443
 
@@ -416,6 +454,7 @@ LENGTH = "length"
416
454
  FPS = "fps"
417
455
  HAS_AUDIO = "hasAudio"
418
456
  HAS_VIDEO = "hasVideo"
457
+ MEDIA_CREATION_TIME = "media_creation_time"
419
458
 
420
459
  STATE = "STATE"
421
460
  POINT = "POINT"
@@ -423,12 +462,15 @@ POINT = "POINT"
423
462
  START = "START"
424
463
  STOP = "STOP"
425
464
 
426
- PLAYER1, PLAYER2 = "1", "2"
465
+ PLAYER1 = "1"
466
+ PLAYER2 = "2"
427
467
  ALL_PLAYERS = [str(x + 1) for x in range(N_PLAYER)]
428
468
 
429
469
  VISUALIZE_SPECTROGRAM = "visualize_spectrogram"
430
470
  VISUALIZE_WAVEFORM = "visualize_waveform"
471
+ MEDIA_CREATION_DATE_AS_OFFSET = "media_creation_date_as_offset"
431
472
 
473
+ MEDIA_SCAN_SAMPLING_DURATION = "media_scan_sampling_duration"
432
474
  IMAGE_DISPLAY_DURATION = "image_display_duration"
433
475
 
434
476
  # plot type
@@ -436,6 +478,9 @@ WAVEFORM_PLOT = "waveform"
436
478
  SPECTROGRAM_PLOT = "spectrogram"
437
479
  EVENTS_PLOT = "plot_events"
438
480
 
481
+ PLAYING = "playing"
482
+ PAUSED = "paused"
483
+ STOPPED = "stopped"
439
484
 
440
485
  POINT_EVENT_ST_DURATION = 0.5
441
486
 
@@ -446,6 +491,8 @@ SLIDER_MAXIMUM = 1000
446
491
 
447
492
  FRAME_DEFAULT_CACHE_SIZE = 1
448
493
 
494
+ EXCLUDED = "excluded"
495
+
449
496
  # modifiers
450
497
  MODIFIERS = "modifiers"
451
498
  SINGLE_SELECTION = 0
@@ -489,6 +536,16 @@ NO_COLOR_CODING_PAD = "#777777"
489
536
  SPECTROGRAM_COLOR_MAPS = ["viridis", "inferno", "plasma", "magma", "gray", "YlOrRd"]
490
537
  SPECTROGRAM_DEFAULT_COLOR_MAP = "viridis"
491
538
  SPECTROGRAM_DEFAULT_TIME_INTERVAL = 10
539
+ SPECTROGRAM_WINDOW_TYPE = "SPECTROGRAM_WINDOW_TYPE"
540
+ SPECTROGRAM_DEFAULT_WINDOW_TYPE = "hanning"
541
+ SPECTROGRAM_NFFT = "SPECTROGRAM_NFFT"
542
+ SPECTROGRAM_DEFAULT_NFFT = "1024"
543
+ SPECTROGRAM_NOVERLAP = "SPECTROGRAM_NOVERLAP"
544
+ SPECTROGRAM_DEFAULT_NOVERLAP = 900
545
+ SPECTROGRAM_VMIN = "SPECTROGRAM_VMIN"
546
+ SPECTROGRAM_DEFAULT_VMIN = -100
547
+ SPECTROGRAM_VMAX = "SPECTROGRAM_VMAX"
548
+ SPECTROGRAM_DEFAULT_VMAX = -20
492
549
 
493
550
  # see matplotlib.colors.cnames.keys()
494
551
  # https://xkcd.com/color/rgb/
@@ -663,6 +720,7 @@ EMPTY_PROJECT = {
663
720
  ETHOGRAM: {},
664
721
  OBSERVATIONS: {},
665
722
  BEHAVIORAL_CATEGORIES: [],
723
+ BEHAVIORAL_CATEGORIES_CONF: {},
666
724
  INDEPENDENT_VARIABLES: {},
667
725
  CODING_MAP: {},
668
726
  BEHAVIORS_CODING_MAP: [],
@@ -678,15 +736,16 @@ INIT_PARAM = {
678
736
  MPV_HWDEC: MPV_HWDEC_DEFAULT_VALUE,
679
737
  PROJECT_FILE_INDENTATION: PROJECT_FILE_INDENTATION_DEFAULT_VALUE,
680
738
  f"{MEDIA} tw fields": MEDIA_TW_EVENTS_FIELDS_DEFAULT,
739
+ # FRAME_STEP_SIZE: FRAME_STEP_SIZE_DEFAULT_VALUE,
681
740
  }
682
741
 
683
742
  SDIS_EXT = "sds"
684
743
  TBS_EXT = "tbs"
685
744
  TSV_EXT = "tsv"
686
- CSV_EXT = "cvs"
745
+ CSV_EXT = "csv"
687
746
  RDS_EXT = "rds"
688
747
  PANDAS_DF_EXT = "pkl"
689
- HMTL_EXT = "html"
748
+ HTML_EXT = "html"
690
749
  SQL_EXT = "sql"
691
750
  ODS_EXT = "ods"
692
751
  XLS_EXT = "xls"
@@ -716,7 +775,7 @@ FILE_NAME_SUFFIX = {
716
775
  XLSX: XLSX_EXT,
717
776
  XLSX_WB: XLSX_EXT,
718
777
  XLS: XLS_EXT,
719
- HTML: HMTL_EXT,
778
+ HTML: HTML_EXT,
720
779
  PANDAS_DF: PANDAS_DF_EXT,
721
780
  RDS: RDS_EXT,
722
781
  SQL: SQL_EXT,
boris/config_file.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
@@ -18,21 +18,17 @@ This file is part of BORIS.
18
18
  You should have received a copy of the GNU General Public License
19
19
  along with this program; if not see <http://www.gnu.org/licenses/>.
20
20
 
21
- """
22
-
23
- """
24
21
  Read and write the BORIS config file
25
22
  """
26
23
 
27
24
  import pathlib as pl
28
25
  import logging
29
26
  import time
30
- import os
31
27
 
32
28
  from . import config as cfg
33
29
  from . import dialog
34
30
 
35
- from PyQt5.QtCore import QByteArray, QSettings
31
+ from PySide6.QtCore import QByteArray, QSettings
36
32
 
37
33
 
38
34
  def read(self):
@@ -40,19 +36,19 @@ def read(self):
40
36
  read config file
41
37
  """
42
38
 
43
- iniFilePath = pl.Path.home() / pl.Path(".boris")
39
+ ini_fil_p_pe_p_path = pl.Path.home() / pl.Path(".boris")
44
40
 
45
- logging.debug(f"read config file: {iniFilePath}")
41
+ logging.debug(f"read config file: {ini_fil_p_pe_p_path}")
46
42
 
47
- if iniFilePath.is_file():
48
- settings = QSettings(str(iniFilePath), QSettings.IniFormat)
43
+ if ini_fil_p_pe_p_path.is_file():
44
+ settings = QSettings(str(ini_fil_p_pe_p_path), QSettings.IniFormat)
49
45
 
50
46
  try:
51
47
  self.config_param = settings.value("config")
52
48
  except Exception:
53
- self.config_param = None
54
- pass
55
- if self.config_param is None:
49
+ self.config_param = {}
50
+
51
+ if self.config_param == {}:
56
52
  self.config_param = cfg.INIT_PARAM
57
53
 
58
54
  # for back compatibility
@@ -76,7 +72,7 @@ def read(self):
76
72
  if not isinstance(self.saved_state, QByteArray):
77
73
  self.saved_state = None
78
74
 
79
- logging.debug(f"saved state: {self.saved_state}")
75
+ # logging.debug(f"saved state: {self.saved_state}")
80
76
 
81
77
  self.timeFormat = cfg.HHMMSS
82
78
  try:
@@ -86,11 +82,11 @@ def read(self):
86
82
 
87
83
  logging.debug(f"time format: {self.timeFormat}")
88
84
 
89
- self.fast = 10
85
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
90
86
  try:
91
- self.fast = int(settings.value("Time/fast_forward_speed"))
87
+ self.fast = float(settings.value("Time/fast_forward_speed"))
92
88
  except Exception:
93
- self.fast = 10
89
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
94
90
 
95
91
  logging.debug(f"Time/fast_forward_speed: {self.fast}")
96
92
 
@@ -172,25 +168,27 @@ def read(self):
172
168
 
173
169
  # check for new version
174
170
  self.checkForNewVersion = False
175
- try:
176
- if settings.value("check_for_new_version") is None:
177
- self.checkForNewVersion = (
178
- dialog.MessageDialog(
179
- cfg.programName,
180
- (
181
- "Allow BORIS to automatically check for new version and news?\n"
182
- "(An internet connection is required)\n"
183
- "You can change this option in the Preferences (File > Preferences)"
184
- ),
185
- [cfg.YES, cfg.NO],
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
186
  )
187
- == cfg.YES
188
- )
189
- else:
190
- self.checkForNewVersion = settings.value("check_for_new_version") == "true"
191
- except Exception:
192
- self.checkForNewVersion = False
193
- logging.debug(f"check_for_new_version: {self.checkForNewVersion}")
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}")
194
192
 
195
193
  # pause before add event
196
194
  self.pause_before_addevent = False
@@ -203,11 +201,11 @@ def read(self):
203
201
 
204
202
  if self.checkForNewVersion:
205
203
  if settings.value("last_check_for_new_version") and (
206
- int(time.mktime(time.localtime())) - int(settings.value("last_check_for_new_version"))
207
- > cfg.CHECK_NEW_VERSION_DELAY
204
+ int(time.mktime(time.localtime())) - int(settings.value("last_check_for_new_version")) > cfg.CHECK_NEW_VERSION_DELAY
208
205
  ):
209
206
  self.actionCheckUpdate_activated(flagMsgOnlyIfNew=True)
210
- logging.debug(f"last_check_for_new_version: {settings.value('last_check_for_new_version')}")
207
+
208
+ logging.debug(f"last check for new version: {settings.value('last_check_for_new_version')}")
211
209
 
212
210
  self.ffmpeg_cache_dir = ""
213
211
  try:
@@ -245,10 +243,7 @@ def read(self):
245
243
  if (
246
244
  dialog.MessageDialog(
247
245
  cfg.programName,
248
- (
249
- "The colors list contain colors that are very light.\n"
250
- "Do you want to reload the default colors list?"
251
- ),
246
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
252
247
  [cfg.NO, cfg.YES],
253
248
  )
254
249
  == cfg.YES
@@ -261,18 +256,11 @@ def read(self):
261
256
  except Exception:
262
257
  self.behav_category_colors = cfg.CATEGORY_COLORS_LIST
263
258
 
264
- if (
265
- "white" in self.behav_category_colors
266
- or "azure" in self.behav_category_colors
267
- or "snow" in self.behav_category_colors
268
- ):
259
+ if "white" in self.behav_category_colors or "azure" in self.behav_category_colors or "snow" in self.behav_category_colors:
269
260
  if (
270
261
  dialog.MessageDialog(
271
262
  cfg.programName,
272
- (
273
- "The colors list contain colors that are very light.\n"
274
- "Do you want to reload the default colors list?"
275
- ),
263
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
276
264
  [cfg.NO, cfg.YES],
277
265
  )
278
266
  == cfg.YES
@@ -282,25 +270,28 @@ def read(self):
282
270
  else: # no .boris file found
283
271
  logging.info("No config file found")
284
272
  # ask user for checking for new version
285
- self.checkForNewVersion = (
286
- dialog.MessageDialog(
287
- cfg.programName,
288
- (
289
- "Allow BORIS to automatically check for new version?\n"
290
- "(An internet connection is required)\n"
291
- "You can change this option in the"
292
- " Preferences (File > Preferences)"
293
- ),
294
- [cfg.NO, cfg.YES],
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
295
286
  )
296
- == cfg.YES
297
- )
287
+ else:
288
+ self.checkForNewVersion = False
298
289
 
299
290
  # recent projects
300
- logging.info("read recent projects")
301
- iniFilePath = str(pl.Path.home() / ".boris_recent_projects")
302
- if os.path.isfile(iniFilePath):
303
- settings = QSettings(iniFilePath, QSettings.IniFormat)
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)
304
295
  try:
305
296
  self.recent_projects = settings.value("recent_projects").split("|||")
306
297
  while "" in self.recent_projects: