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.
Files changed (125) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +24 -36
  4. boris/add_modifier.py +88 -80
  5. boris/add_modifier_ui.py +235 -131
  6. boris/advanced_event_filtering.py +23 -29
  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 +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +16 -34
  23. boris/config.py +102 -50
  24. boris/config_file.py +55 -64
  25. boris/connections.py +105 -58
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2108 -1275
  30. boris/core_qrc.py +15892 -10829
  31. boris/core_ui.py +941 -806
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +27 -7
  34. boris/dialog.py +461 -242
  35. boris/duration_widget.py +9 -14
  36. boris/edit_event.py +61 -31
  37. boris/edit_event_ui.py +208 -97
  38. boris/event_operations.py +405 -281
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +180 -203
  43. boris/export_observation.py +60 -73
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +427 -218
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +4 -4
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +304 -0
  50. boris/irr.py +20 -57
  51. boris/latency.py +31 -24
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +17 -19
  54. boris/menu_options.py +16 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv2.py +128 -35
  58. boris/observation.py +493 -210
  59. boris/observation_operations.py +1010 -391
  60. boris/observation_ui.py +573 -363
  61. boris/observations_list.py +51 -58
  62. boris/otx_parser.py +74 -68
  63. boris/param_panel.py +45 -59
  64. boris/param_panel_ui.py +254 -138
  65. boris/player_dock_widget.py +91 -56
  66. boris/plot_data_module.py +18 -53
  67. boris/plot_events.py +56 -153
  68. boris/plot_events_rt.py +16 -30
  69. boris/plot_spectrogram_rt.py +80 -56
  70. boris/plot_waveform_rt.py +23 -48
  71. boris/plugins.py +431 -0
  72. boris/portion/__init__.py +18 -8
  73. boris/portion/const.py +35 -18
  74. boris/portion/dict.py +5 -5
  75. boris/portion/func.py +2 -2
  76. boris/portion/interval.py +21 -41
  77. boris/portion/io.py +41 -32
  78. boris/preferences.py +298 -123
  79. boris/preferences_ui.py +664 -225
  80. boris/project.py +293 -270
  81. boris/project_functions.py +610 -537
  82. boris/project_import_export.py +204 -213
  83. boris/project_ui.py +673 -441
  84. boris/qrc_boris.py +6 -3
  85. boris/qrc_boris5.py +6 -3
  86. boris/select_modifiers.py +62 -90
  87. boris/select_observations.py +19 -197
  88. boris/select_subj_behav.py +67 -39
  89. boris/state_events.py +51 -33
  90. boris/subjects_pad.py +6 -8
  91. boris/synthetic_time_budget.py +42 -26
  92. boris/time_budget_functions.py +169 -169
  93. boris/time_budget_widget.py +77 -89
  94. boris/transitions.py +41 -41
  95. boris/utilities.py +562 -222
  96. boris/version.py +3 -3
  97. boris/video_equalizer.py +16 -14
  98. boris/video_equalizer_ui.py +199 -130
  99. boris/video_operations.py +78 -28
  100. boris/view_df.py +104 -0
  101. boris/view_df_ui.py +75 -0
  102. boris/write_event.py +240 -136
  103. boris_behav_obs-9.7.1.dist-info/METADATA +140 -0
  104. boris_behav_obs-9.7.1.dist-info/RECORD +109 -0
  105. {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.1.dist-info}/WHEEL +1 -1
  106. boris_behav_obs-9.7.1.dist-info/entry_points.txt +2 -0
  107. boris/README.TXT +0 -22
  108. boris/add_modifier.ui +0 -323
  109. boris/converters.ui +0 -289
  110. boris/core.qrc +0 -37
  111. boris/core.ui +0 -1571
  112. boris/edit_event.ui +0 -233
  113. boris/icons/logo_eye.ico +0 -0
  114. boris/map_creator.py +0 -982
  115. boris/observation.ui +0 -814
  116. boris/param_panel.ui +0 -379
  117. boris/preferences.ui +0 -537
  118. boris/project.ui +0 -1074
  119. boris/vlc_local.py +0 -90
  120. boris_behav_obs-8.16.5.dist-info/LICENSE.TXT +0 -674
  121. boris_behav_obs-8.16.5.dist-info/METADATA +0 -134
  122. boris_behav_obs-8.16.5.dist-info/RECORD +0 -107
  123. boris_behav_obs-8.16.5.dist-info/entry_points.txt +0 -2
  124. {boris → boris_behav_obs-9.7.1.dist-info/licenses}/LICENSE.TXT +0 -0
  125. {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.1.dist-info}/top_level.txt +0 -0
boris/plot_events.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
 
@@ -25,8 +25,10 @@ import pathlib as pl
25
25
 
26
26
  import matplotlib
27
27
 
28
- matplotlib.use("Qt5Agg")
28
+ matplotlib.use("QtAgg")
29
+
29
30
  import matplotlib.dates
31
+
30
32
  import matplotlib.pyplot as plt
31
33
  import numpy as np
32
34
  from matplotlib.dates import (
@@ -37,15 +39,15 @@ from . import config as cfg
37
39
  from . import db_functions, project_functions, observation_operations
38
40
  from . import utilities as util
39
41
 
42
+ # matplotlib.pyplot.switch_backend("Qt5Agg")
43
+
40
44
 
41
45
  def default_value(ethogram, behavior, parameter):
42
46
  """
43
47
  return value for duration in case of point event
44
48
  """
45
49
  default_value_ = 0
46
- if project_functions.event_type(
47
- behavior, ethogram
48
- ) == "POINT EVENT" and parameter in ["duration"]:
50
+ if project_functions.event_type(behavior, ethogram) in cfg.POINT_EVENT_TYPES and parameter in ["duration"]:
49
51
  default_value_ = "NA"
50
52
  return default_value_
51
53
 
@@ -65,7 +67,6 @@ def init_behav_modif(
65
67
  for subj in selected_subjects:
66
68
  behaviors[subj] = {}
67
69
  for behav_modif in distinct_behav_modif:
68
-
69
70
  behav, modif = behav_modif
70
71
  behav_modif_str = "|".join(behav_modif) if modif else behav
71
72
 
@@ -73,16 +74,12 @@ def init_behav_modif(
73
74
  behaviors[subj][behav_modif_str] = {}
74
75
 
75
76
  for parameter in parameters:
76
- behaviors[subj][behav_modif_str][parameter[0]] = default_value(
77
- ethogram, behav_modif_str, parameter[0]
78
- )
77
+ behaviors[subj][behav_modif_str][parameter[0]] = default_value(ethogram, behav_modif_str, parameter[0])
79
78
 
80
79
  return behaviors
81
80
 
82
81
 
83
- def init_behav(
84
- ethogram: dict, selected_subjects: list, distinct_behaviors, parameters
85
- ) -> dict:
82
+ def init_behav(ethogram: dict, selected_subjects: list, distinct_behaviors, parameters) -> dict:
86
83
  """
87
84
  initialize dictionary with subject, behaviors and modifiers
88
85
  """
@@ -94,9 +91,7 @@ def init_behav(
94
91
  if behavior not in behaviors[subj]:
95
92
  behaviors[subj][behavior] = {}
96
93
  for parameter in parameters:
97
- behaviors[subj][behavior][parameter] = default_value(
98
- ethogram, behavior, parameter
99
- )
94
+ behaviors[subj][behavior][parameter] = default_value(ethogram, behavior, parameter)
100
95
  return behaviors
101
96
 
102
97
 
@@ -128,9 +123,7 @@ def create_behaviors_bar_plot(
128
123
 
129
124
  parameters = ["duration", "number of occurences"]
130
125
 
131
- ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
132
- pj, selected_subjects, selected_observations, selected_behaviors
133
- )
126
+ ok, msg, db_connector = db_functions.load_aggregated_events_in_db(pj, selected_subjects, selected_observations, selected_behaviors)
134
127
 
135
128
  if not ok:
136
129
  return {"error": True, "message": msg}
@@ -139,7 +132,6 @@ def create_behaviors_bar_plot(
139
132
  all_behaviors = util.all_behaviors(pj[cfg.ETHOGRAM])
140
133
 
141
134
  for obs_id in selected_observations:
142
-
143
135
  cursor = db_connector.cursor()
144
136
  # distinct behaviors
145
137
  cursor.execute(
@@ -163,16 +155,12 @@ def create_behaviors_bar_plot(
163
155
  )
164
156
  distinct_subjects = [rows["subject"] for rows in cursor.fetchall()]
165
157
 
166
- behaviors = init_behav(
167
- pj[cfg.ETHOGRAM], distinct_subjects, distinct_behav, parameters
168
- )
158
+ behaviors = init_behav(pj[cfg.ETHOGRAM], distinct_subjects, distinct_behav, parameters)
169
159
 
170
160
  # plot creation
171
161
  if len(distinct_subjects) > 1:
172
162
  fig, axs = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)
173
- fig2, axs2 = plt.subplots(
174
- nrows=1, ncols=len(distinct_subjects), sharey=True
175
- )
163
+ fig2, axs2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)
176
164
 
177
165
  else:
178
166
  fig, ax = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True)
@@ -190,9 +178,7 @@ def create_behaviors_bar_plot(
190
178
  cursor.execute("UPDATE aggregated_events SET modifiers = ''")
191
179
 
192
180
  # time
193
- obs_length = observation_operations.observation_total_length(
194
- pj[cfg.OBSERVATIONS][obs_id]
195
- )
181
+ obs_length = observation_operations.observation_total_length(pj[cfg.OBSERVATIONS][obs_id])
196
182
  if obs_length == -1:
197
183
  obs_length = 0
198
184
 
@@ -210,7 +196,7 @@ def create_behaviors_bar_plot(
210
196
  except Exception:
211
197
  max_time = float(obs_length)
212
198
 
213
- if param["time"] == cfg.TIME_ARBITRARY_INTERVAL:
199
+ if param["time"] in (cfg.TIME_ARBITRARY_INTERVAL, cfg.TIME_OBS_INTERVAL):
214
200
  min_time = float(start_time)
215
201
  max_time = float(end_time)
216
202
 
@@ -246,15 +232,10 @@ def create_behaviors_bar_plot(
246
232
  )
247
233
 
248
234
  for ax_idx, subject in enumerate(sorted(distinct_subjects)):
249
-
250
235
  for behavior in distinct_behav:
251
-
252
236
  # number of occurences
253
237
  cursor.execute(
254
- (
255
- "SELECT COUNT(*) AS count FROM aggregated_events "
256
- "WHERE observation = ? AND subject = ? AND behavior = ?"
257
- ),
238
+ ("SELECT COUNT(*) AS count FROM aggregated_events WHERE observation = ? AND subject = ? AND behavior = ?"),
258
239
  (
259
240
  obs_id,
260
241
  subject,
@@ -262,14 +243,10 @@ def create_behaviors_bar_plot(
262
243
  ),
263
244
  )
264
245
  for row in cursor.fetchall():
265
- behaviors[subject][behavior]["number of occurences"] = (
266
- 0 if row["count"] is None else row["count"]
267
- )
246
+ behaviors[subject][behavior]["number of occurences"] = 0 if row["count"] is None else row["count"]
268
247
 
269
248
  # total duration
270
- if cfg.STATE in project_functions.event_type(
271
- behavior, pj[cfg.ETHOGRAM]
272
- ):
249
+ if project_functions.event_type(behavior, pj[cfg.ETHOGRAM]) in cfg.STATE_EVENT_TYPES:
273
250
  cursor.execute(
274
251
  (
275
252
  "SELECT SUM(stop - start) AS duration FROM aggregated_events "
@@ -282,9 +259,7 @@ def create_behaviors_bar_plot(
282
259
  ),
283
260
  )
284
261
  for row in cursor.fetchall():
285
- behaviors[subject][behavior]["duration"] = (
286
- 0 if row["duration"] is None else row["duration"]
287
- )
262
+ behaviors[subject][behavior]["duration"] = 0 if row["duration"] is None else row["duration"]
288
263
 
289
264
  (
290
265
  durations,
@@ -296,24 +271,14 @@ def create_behaviors_bar_plot(
296
271
  ) = ([], [], [], [], [], [])
297
272
 
298
273
  for behavior in sorted(distinct_behav):
299
-
300
- if (
301
- param[cfg.EXCLUDE_BEHAVIORS]
302
- and behaviors[subject][behavior]["number of occurences"] == 0
303
- ):
274
+ if param[cfg.EXCLUDE_BEHAVIORS] and behaviors[subject][behavior]["number of occurences"] == 0:
304
275
  continue
305
276
 
306
- n_occurences.append(
307
- behaviors[subject][behavior]["number of occurences"]
308
- )
277
+ n_occurences.append(behaviors[subject][behavior]["number of occurences"])
309
278
  x_labels.append(behavior)
310
279
 
311
280
  # color
312
- behav_idx = [
313
- k
314
- for k in pj[cfg.ETHOGRAM]
315
- if pj[cfg.ETHOGRAM][k]["code"] == behavior
316
- ][0]
281
+ behav_idx = [k for k in pj[cfg.ETHOGRAM] if pj[cfg.ETHOGRAM][k]["code"] == behavior][0]
317
282
  col = None
318
283
  if cfg.COLOR in pj[cfg.ETHOGRAM][behav_idx]:
319
284
  col = util.behavior_user_color(pj[cfg.ETHOGRAM], behavior)
@@ -321,17 +286,11 @@ def create_behaviors_bar_plot(
321
286
  colors.append(col)
322
287
  else:
323
288
  try:
324
- colors.append(
325
- util.behavior_color(
326
- plot_colors, all_behaviors.index(behavior)
327
- )
328
- )
289
+ colors.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
329
290
  except Exception:
330
291
  colors.append("darkgray")
331
292
 
332
- if cfg.STATE in project_functions.event_type(
333
- behavior, pj[cfg.ETHOGRAM]
334
- ):
293
+ if project_functions.event_type(behavior, pj[cfg.ETHOGRAM]) in cfg.STATE_EVENT_TYPES:
335
294
  durations.append(behaviors[subject][behavior]["duration"])
336
295
  x_labels_duration.append(behavior)
337
296
 
@@ -342,11 +301,7 @@ def create_behaviors_bar_plot(
342
301
  colors_duration.append(col)
343
302
  else:
344
303
  try:
345
- colors_duration.append(
346
- util.behavior_color(
347
- plot_colors, all_behaviors.index(behavior)
348
- )
349
- )
304
+ colors_duration.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
350
305
  except Exception:
351
306
  colors_duration.append("darkgray")
352
307
 
@@ -372,9 +327,7 @@ def create_behaviors_bar_plot(
372
327
  axs[ax_idx].set_title(f"{subject}")
373
328
 
374
329
  axs[ax_idx].set_xticks(np.arange(len(durations)))
375
- axs[ax_idx].set_xticklabels(
376
- x_labels_duration, rotation="vertical", fontsize=8
377
- )
330
+ axs[ax_idx].set_xticklabels(x_labels_duration, rotation="vertical", fontsize=8)
378
331
 
379
332
  if ax_idx == 0:
380
333
  axs2[ax_idx].set_ylabel("Number of occurences")
@@ -392,12 +345,8 @@ def create_behaviors_bar_plot(
392
345
 
393
346
  if plot_directory:
394
347
  # output_file_name = f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.{output_format}"
395
- fig.savefig(
396
- f"{pl.Path(plot_directory) / util.safeFileName(obs_id)}.duration.{output_format}"
397
- )
398
- fig2.savefig(
399
- f"{pl.Path(plot_directory) / util.safeFileName(obs_id)}.number_of_occurences.{output_format}"
400
- )
348
+ fig.savefig(f"{pl.Path(plot_directory) / util.safeFileName(obs_id)}.duration.{output_format}")
349
+ fig2.savefig(f"{pl.Path(plot_directory) / util.safeFileName(obs_id)}.number_of_occurences.{output_format}")
401
350
  plt.close()
402
351
  else:
403
352
  fig.show()
@@ -426,9 +375,7 @@ def create_events_plot(
426
375
  start_time = parameters[cfg.START_TIME]
427
376
  end_time = parameters[cfg.END_TIME]
428
377
 
429
- ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
430
- self.pj, selected_subjects, selected_observations, selected_behaviors
431
- )
378
+ ok, msg, db_connector = db_functions.load_aggregated_events_in_db(self.pj, selected_subjects, selected_observations, selected_behaviors)
432
379
 
433
380
  if not ok:
434
381
  return False, msg, None
@@ -440,9 +387,7 @@ def create_events_plot(
440
387
  cursor.execute("UPDATE aggregated_events SET modifiers = ''")
441
388
 
442
389
  cursor.execute("SELECT DISTINCT behavior, modifiers FROM aggregated_events")
443
- distinct_behav_modif = [
444
- [rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()
445
- ]
390
+ distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()]
446
391
 
447
392
  # add selected behaviors that are not observed
448
393
  for behav in selected_behaviors:
@@ -452,31 +397,21 @@ def create_events_plot(
452
397
  distinct_behav_modif = sorted(distinct_behav_modif)
453
398
  max_len = len(distinct_behav_modif)
454
399
 
455
- all_behaviors = [
456
- self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]
457
- for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
458
- ]
400
+ all_behaviors = [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]
459
401
 
460
402
  par1 = 1
461
403
  bar_height = 0.5
462
404
  epoch_date = dt.datetime(2017, 1, 1)
463
405
 
464
406
  for obs_id in selected_observations:
465
-
466
407
  if len(selected_subjects) > 1:
467
- fig, axs = plt.subplots(
468
- figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True
469
- )
408
+ fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True)
470
409
  else:
471
- fig, ax = plt.subplots(
472
- figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True
473
- )
410
+ fig, ax = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True)
474
411
  axs = np.ndarray(shape=(1), dtype=type(ax))
475
412
  axs[0] = ax
476
413
 
477
- ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
478
- self.pj, selected_subjects, [obs_id], selected_behaviors
479
- )
414
+ ok, msg, db_connector = db_functions.load_aggregated_events_in_db(self.pj, selected_subjects, [obs_id], selected_behaviors)
480
415
 
481
416
  cursor = db_connector.cursor()
482
417
  # if modifiers not to be included set modifiers to ""
@@ -485,9 +420,7 @@ def create_events_plot(
485
420
  cursor = db_connector.cursor()
486
421
 
487
422
  cursor.execute("SELECT DISTINCT behavior, modifiers FROM aggregated_events")
488
- distinct_behav_modif = [
489
- [rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()
490
- ]
423
+ distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()]
491
424
 
492
425
  # add selected behaviors that are not observed
493
426
  if not parameters["exclude behaviors"]:
@@ -499,9 +432,7 @@ def create_events_plot(
499
432
  max_len = len(distinct_behav_modif)
500
433
 
501
434
  # time
502
- obs_length = observation_operations.observation_total_length(
503
- self.pj[cfg.OBSERVATIONS][obs_id]
504
- )
435
+ obs_length = observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id])
505
436
  if obs_length == -1: # media length not available
506
437
  interval = cfg.TIME_EVENTS
507
438
 
@@ -509,17 +440,20 @@ def create_events_plot(
509
440
  min_time = 0.0
510
441
  max_time = float(obs_length)
511
442
 
443
+ if interval == cfg.TIME_OBS_INTERVAL:
444
+ obs_interval = self.pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
445
+ offset = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET])
446
+ min_time = float(obs_interval[0]) + offset
447
+ # Use max media duration for max time if no interval is defined (=0)
448
+ max_time = float(obs_interval[1]) + offset if obs_interval[1] != 0 else float(obs_length)
449
+
512
450
  if interval == cfg.TIME_EVENTS:
513
451
  try:
514
- min_time = float(
515
- self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0]
516
- ) # first event
452
+ min_time = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0]) # first event
517
453
  except Exception:
518
454
  min_time = 0.0
519
455
  try:
520
- max_time = float(
521
- self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][-1][0]
522
- ) # last event
456
+ max_time = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][-1][0]) # last event
523
457
  except Exception:
524
458
  max_time = float(obs_length)
525
459
 
@@ -566,15 +500,12 @@ def create_events_plot(
566
500
 
567
501
  ylabels = [" ".join(x) for x in distinct_behav_modif]
568
502
  for ax_idx, subject in enumerate(selected_subjects):
569
-
570
503
  if parameters["exclude behaviors"]:
571
504
  cursor.execute(
572
505
  "SELECT DISTINCT behavior, modifiers FROM aggregated_events WHERE subject = ?",
573
506
  (subject,),
574
507
  )
575
- distinct_behav_modif = [
576
- [rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()
577
- ]
508
+ distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()]
578
509
 
579
510
  # add selected behaviors that are not observed
580
511
  if not parameters["exclude behaviors"]:
@@ -594,17 +525,12 @@ def create_events_plot(
594
525
  i = 0
595
526
  for behavior_modifiers in distinct_behav_modif:
596
527
  behavior, modifiers = behavior_modifiers
597
- behavior_modifiers_str = (
598
- "|".join(behavior_modifiers) if modifiers else behavior
599
- )
528
+ behavior_modifiers_str = "|".join(behavior_modifiers) if modifiers else behavior
600
529
  bars[behavior_modifiers_str] = []
601
530
 
602
531
  # total duration
603
532
  cursor.execute(
604
- (
605
- "SELECT start, stop FROM aggregated_events "
606
- "WHERE subject = ? AND behavior = ? AND modifiers = ?"
607
- ),
533
+ ("SELECT start, stop FROM aggregated_events WHERE subject = ? AND behavior = ? AND modifiers = ?"),
608
534
  (
609
535
  subject,
610
536
  behavior,
@@ -615,27 +541,16 @@ def create_events_plot(
615
541
  bars[behavior_modifiers_str].append((row["start"], row["stop"]))
616
542
 
617
543
  if self.timeFormat == cfg.HHMMSS:
618
- start_date = matplotlib.dates.date2num(
619
- epoch_date + dt.timedelta(seconds=row["start"])
620
- )
544
+ start_date = matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=row["start"]))
621
545
  end_date = matplotlib.dates.date2num(
622
- epoch_date
623
- + dt.timedelta(
624
- seconds=row["stop"]
625
- + cfg.POINT_EVENT_PLOT_DURATION
626
- * (row["stop"] == row["start"])
627
- )
546
+ epoch_date + dt.timedelta(seconds=row["stop"] + cfg.POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))
628
547
  )
629
548
  if self.timeFormat == cfg.S:
630
549
  start_date = row["start"]
631
550
  end_date = row["stop"]
632
551
 
633
552
  # color
634
- behav_idx = [
635
- k
636
- for k in self.pj[cfg.ETHOGRAM]
637
- if self.pj[cfg.ETHOGRAM][k]["code"] == behavior
638
- ][0]
553
+ behav_idx = [k for k in self.pj[cfg.ETHOGRAM] if self.pj[cfg.ETHOGRAM][k]["code"] == behavior][0]
639
554
  col = None
640
555
  if cfg.COLOR in self.pj[cfg.ETHOGRAM][behav_idx]:
641
556
  col = util.behavior_user_color(self.pj[cfg.ETHOGRAM], behavior)
@@ -643,16 +558,10 @@ def create_events_plot(
643
558
  bar_color = col
644
559
  else:
645
560
  try:
646
- bar_color = util.behavior_color(
647
- plot_colors, all_behaviors.index(behavior)
648
- )
561
+ bar_color = util.behavior_color(plot_colors, all_behaviors.index(behavior))
649
562
  except Exception:
650
563
  bar_color = "darkgray"
651
- bar_color = (
652
- cfg.POINT_EVENT_PLOT_COLOR
653
- if row["stop"] == row["start"]
654
- else bar_color
655
- )
564
+ bar_color = cfg.POINT_EVENT_PLOT_COLOR if row["stop"] == row["start"] else bar_color
656
565
 
657
566
  # sage colors removed from matplotlib colors list
658
567
  if bar_color in ("sage", "darksage", "lightsage"):
@@ -700,12 +609,8 @@ def create_events_plot(
700
609
 
701
610
  if self.timeFormat == cfg.HHMMSS:
702
611
  axs[ax_idx].set_xlim(
703
- left=matplotlib.dates.date2num(
704
- epoch_date + dt.timedelta(seconds=min_time)
705
- ),
706
- right=matplotlib.dates.date2num(
707
- epoch_date + dt.timedelta(seconds=max_time)
708
- ),
612
+ left=matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=min_time)),
613
+ right=matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=max_time)),
709
614
  )
710
615
 
711
616
  axs[ax_idx].grid(color="g", linestyle=":")
@@ -724,8 +629,6 @@ def create_events_plot(
724
629
  plt.tight_layout()
725
630
 
726
631
  if len(selected_observations) > 1:
727
- plt.savefig(
728
- f"{pl.Path(plot_directory) / util.safeFileName(obs_id)}.{file_format}"
729
- )
632
+ plt.savefig(f"{pl.Path(plot_directory) / util.safeFileName(obs_id)}.{file_format}")
730
633
  else:
731
634
  plt.show()
boris/plot_events_rt.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
 
@@ -25,26 +25,27 @@ Plot events in real time
25
25
 
26
26
  import matplotlib
27
27
 
28
- matplotlib.use("Qt5Agg")
28
+ matplotlib.use("QtAgg")
29
+
29
30
  import numpy as np
30
- from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
31
- from PyQt5.QtCore import pyqtSignal, QEvent
32
- from PyQt5 import Qt
33
- from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
31
+ from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
32
+ from PySide6.QtCore import Signal, QEvent, Qt
33
+ from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
34
34
 
35
35
  from matplotlib.figure import Figure
36
36
 
37
37
  from . import config as cfg
38
38
 
39
+ # matplotlib.pyplot.switch_backend("Qt5Agg")
39
40
 
40
- class Plot_events_RT(QWidget):
41
41
 
42
+ class Plot_events_RT(QWidget):
42
43
  # send keypress event to mainwindow
43
- sendEvent = pyqtSignal(QEvent)
44
+ sendEvent = Signal(QEvent)
44
45
 
45
46
  def __init__(self):
46
47
  super().__init__()
47
- self.setWindowTitle(f"Events plot")
48
+ self.setWindowTitle("Events plot")
48
49
 
49
50
  self.interval = 60 # default interval of visualization (in seconds)
50
51
  self.time_mem = -1
@@ -65,13 +66,9 @@ class Plot_events_RT(QWidget):
65
66
 
66
67
  hlayout1 = QHBoxLayout()
67
68
  hlayout1.addWidget(QLabel("Time interval"))
68
- hlayout1.addWidget(
69
- QPushButton("+", self, clicked=lambda: self.time_interval_changed(1), focusPolicy=Qt.Qt.NoFocus)
70
- )
71
- hlayout1.addWidget(
72
- QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.Qt.NoFocus)
73
- )
74
- self.pb_mode = QPushButton("Include modifiers", self, clicked=self.change_mode, focusPolicy=Qt.Qt.NoFocus)
69
+ hlayout1.addWidget(QPushButton("+", self, clicked=lambda: self.time_interval_changed(1), focusPolicy=Qt.NoFocus))
70
+ hlayout1.addWidget(QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.NoFocus))
71
+ self.pb_mode = QPushButton("Include modifiers", self, clicked=self.change_mode, focusPolicy=Qt.NoFocus)
75
72
  hlayout1.addWidget(self.pb_mode)
76
73
  layout.addLayout(hlayout1)
77
74
 
@@ -147,13 +144,11 @@ class Plot_events_RT(QWidget):
147
144
  intervals_behav[group(event[1], event[2], event[3])] = [(0, 0)]
148
145
 
149
146
  for event in events:
150
-
151
147
  time_, subject, code, modifier = event[:4]
152
148
  key = group(subject, code, modifier)
153
149
 
154
150
  # check if code is state
155
151
  if code in self.state_events_list:
156
-
157
152
  if key in mem_behav and mem_behav[key] is not None:
158
153
  # stop interval
159
154
 
@@ -172,22 +167,16 @@ class Plot_events_RT(QWidget):
172
167
  mem_behav[key] = time_
173
168
 
174
169
  else: # point event
175
-
176
170
  if start <= time_ <= end:
177
- intervals_behav[key].append(
178
- (float(time_), float(time_) + self.point_event_plot_duration * 50)
179
- ) # point event -> 1 s
171
+ intervals_behav[key].append((float(time_), float(time_) + self.point_event_plot_duration * 50)) # point event -> 1 s
180
172
 
181
173
  # check if intervals are closed
182
174
  for k in mem_behav:
183
175
  if mem_behav[k] is not None: # interval open
184
176
  if self.observation_type == cfg.LIVE:
185
- intervals_behav[k].append(
186
- (float(mem_behav[k]), float((end + start) / 2))
187
- ) # close interval with current time
177
+ intervals_behav[k].append((float(mem_behav[k]), float((end + start) / 2))) # close interval with current time
188
178
 
189
179
  elif self.observation_type == cfg.MEDIA:
190
-
191
180
  intervals_behav[k].append((float(mem_behav[k]), float(end))) # close interval with end value
192
181
 
193
182
  return intervals_behav
@@ -201,9 +190,7 @@ class Plot_events_RT(QWidget):
201
190
  force_plot (bool): force plot even if media paused
202
191
  """
203
192
 
204
- self.events = self.aggregate_events(
205
- self.events_list, current_time - self.interval / 2, current_time + self.interval / 2
206
- )
193
+ self.events = self.aggregate_events(self.events_list, current_time - self.interval / 2, current_time + self.interval / 2)
207
194
 
208
195
  if not force_plot and current_time == self.time_mem:
209
196
  return
@@ -211,7 +198,6 @@ class Plot_events_RT(QWidget):
211
198
  self.time_mem = current_time
212
199
 
213
200
  if self.events != self.events_mem:
214
-
215
201
  left, duration = {}, {}
216
202
  for k in self.events:
217
203
  left[k] = []