boris-behav-obs 8.9.16__py3-none-any.whl → 9.7.6__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 (129) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +36 -39
  4. boris/add_modifier.py +122 -109
  5. boris/add_modifier_ui.py +239 -135
  6. boris/advanced_event_filtering.py +81 -45
  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 +42 -49
  23. boris/config.py +161 -77
  24. boris/config_file.py +63 -83
  25. boris/connections.py +112 -57
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2511 -1824
  30. boris/core_qrc.py +15895 -10185
  31. boris/core_ui.py +946 -792
  32. boris/db_functions.py +21 -41
  33. boris/dev.py +134 -0
  34. boris/dialog.py +505 -244
  35. boris/duration_widget.py +15 -20
  36. boris/edit_event.py +84 -28
  37. boris/edit_event_ui.py +214 -78
  38. boris/event_operations.py +517 -415
  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 +213 -583
  43. boris/export_observation.py +98 -611
  44. boris/external_processes.py +156 -97
  45. boris/geometric_measurement.py +652 -287
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +9 -9
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +26 -63
  51. boris/latency.py +34 -25
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +52 -84
  54. boris/menu_options.py +17 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv.py +1 -0
  58. boris/mpv2.py +732 -705
  59. boris/observation.py +655 -310
  60. boris/observation_operations.py +1036 -404
  61. boris/observation_ui.py +584 -356
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -80
  64. boris/param_panel.py +31 -16
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +90 -60
  67. boris/plot_data_module.py +43 -46
  68. boris/plot_events.py +127 -90
  69. boris/plot_events_rt.py +17 -31
  70. boris/plot_spectrogram_rt.py +95 -30
  71. boris/plot_waveform_rt.py +32 -21
  72. boris/plugins.py +431 -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 +306 -83
  80. boris/preferences_ui.py +685 -228
  81. boris/project.py +448 -293
  82. boris/project_functions.py +689 -254
  83. boris/project_import_export.py +213 -222
  84. boris/project_ui.py +674 -438
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +74 -48
  88. boris/select_observations.py +20 -199
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +53 -37
  91. boris/subjects_pad.py +6 -9
  92. boris/synthetic_time_budget.py +45 -28
  93. boris/time_budget_functions.py +171 -171
  94. boris/time_budget_widget.py +84 -114
  95. boris/transitions.py +41 -47
  96. boris/utilities.py +766 -266
  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 +125 -28
  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.6.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
  106. {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/boris_ui.py +0 -886
  111. boris/converters.ui +0 -289
  112. boris/core.qrc +0 -35
  113. boris/core.ui +0 -1543
  114. boris/edit_event.ui +0 -175
  115. boris/icons/logo_eye.ico +0 -0
  116. boris/map_creator.py +0 -850
  117. boris/observation.ui +0 -773
  118. boris/param_panel.ui +0 -379
  119. boris/preferences.ui +0 -537
  120. boris/project.ui +0 -1069
  121. boris/project_server.py +0 -236
  122. boris/vlc.py +0 -10343
  123. boris/vlc_local.py +0 -90
  124. boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
  125. boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
  126. boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
  127. boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
  128. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  129. {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.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,42 +25,39 @@ 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
30
- import matplotlib.font_manager as font_manager
31
+
31
32
  import matplotlib.pyplot as plt
32
- import matplotlib.transforms as mtransforms
33
33
  import numpy as np
34
- from matplotlib import colors as mcolors
35
34
  from matplotlib.dates import (
36
- HOURLY,
37
- MICROSECONDLY,
38
- MINUTELY,
39
- MONTHLY,
40
- SECONDLY,
41
- WEEKLY,
42
35
  DateFormatter,
43
- RRuleLocator,
44
- rrulewrapper,
45
36
  )
46
37
 
47
38
  from . import config as cfg
48
39
  from . import db_functions, project_functions, observation_operations
49
40
  from . import utilities as util
50
41
 
42
+ # matplotlib.pyplot.switch_backend("Qt5Agg")
43
+
51
44
 
52
45
  def default_value(ethogram, behavior, parameter):
53
46
  """
54
47
  return value for duration in case of point event
55
48
  """
56
49
  default_value_ = 0
57
- if project_functions.event_type(behavior, ethogram) == "POINT EVENT" and parameter in ["duration"]:
50
+ if project_functions.event_type(behavior, ethogram) in cfg.POINT_EVENT_TYPES and parameter in ["duration"]:
58
51
  default_value_ = "NA"
59
52
  return default_value_
60
53
 
61
54
 
62
55
  def init_behav_modif(
63
- ethogram: dict, selected_subjects: list, distinct_behav_modif, include_modifiers, parameters
56
+ ethogram: dict,
57
+ selected_subjects: list,
58
+ distinct_behav_modif,
59
+ include_modifiers,
60
+ parameters,
64
61
  ) -> dict:
65
62
  """
66
63
  initialize dictionary with subject, behaviors and modifiers
@@ -70,7 +67,6 @@ def init_behav_modif(
70
67
  for subj in selected_subjects:
71
68
  behaviors[subj] = {}
72
69
  for behav_modif in distinct_behav_modif:
73
-
74
70
  behav, modif = behav_modif
75
71
  behav_modif_str = "|".join(behav_modif) if modif else behav
76
72
 
@@ -88,7 +84,7 @@ def init_behav(ethogram: dict, selected_subjects: list, distinct_behaviors, para
88
84
  initialize dictionary with subject, behaviors and modifiers
89
85
  """
90
86
 
91
- behaviors = {}
87
+ behaviors: dict = {}
92
88
  for subj in selected_subjects:
93
89
  behaviors[subj] = {}
94
90
  for behavior in distinct_behaviors:
@@ -122,15 +118,12 @@ def create_behaviors_bar_plot(
122
118
 
123
119
  selected_subjects = param[cfg.SELECTED_SUBJECTS]
124
120
  selected_behaviors = param[cfg.SELECTED_BEHAVIORS]
125
- time_interval = param["time"]
126
121
  start_time = param[cfg.START_TIME]
127
122
  end_time = param[cfg.END_TIME]
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,10 +132,12 @@ 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
- cursor.execute("SELECT distinct behavior FROM aggregated_events WHERE observation = ?", (obs_id,))
137
+ cursor.execute(
138
+ "SELECT distinct behavior FROM aggregated_events WHERE observation = ?",
139
+ (obs_id,),
140
+ )
146
141
  distinct_behav = [rows["behavior"] for rows in cursor.fetchall()]
147
142
 
148
143
  # add selected behaviors that are not observed
@@ -154,7 +149,10 @@ def create_behaviors_bar_plot(
154
149
  """
155
150
 
156
151
  # distinct subjects
157
- cursor.execute("SELECT distinct subject FROM aggregated_events WHERE observation = ?", (obs_id,))
152
+ cursor.execute(
153
+ "SELECT distinct subject FROM aggregated_events WHERE observation = ?",
154
+ (obs_id,),
155
+ )
158
156
  distinct_subjects = [rows["subject"] for rows in cursor.fetchall()]
159
157
 
160
158
  behaviors = init_behav(pj[cfg.ETHOGRAM], distinct_subjects, distinct_behav, parameters)
@@ -198,7 +196,7 @@ def create_behaviors_bar_plot(
198
196
  except Exception:
199
197
  max_time = float(obs_length)
200
198
 
201
- if param["time"] == cfg.TIME_ARBITRARY_INTERVAL:
199
+ if param["time"] in (cfg.TIME_ARBITRARY_INTERVAL, cfg.TIME_OBS_INTERVAL):
202
200
  min_time = float(start_time)
203
201
  max_time = float(end_time)
204
202
 
@@ -234,15 +232,10 @@ def create_behaviors_bar_plot(
234
232
  )
235
233
 
236
234
  for ax_idx, subject in enumerate(sorted(distinct_subjects)):
237
-
238
235
  for behavior in distinct_behav:
239
-
240
236
  # number of occurences
241
237
  cursor.execute(
242
- (
243
- "SELECT COUNT(*) AS count FROM aggregated_events "
244
- "WHERE observation = ? AND subject = ? AND behavior = ?"
245
- ),
238
+ ("SELECT COUNT(*) AS count FROM aggregated_events WHERE observation = ? AND subject = ? AND behavior = ?"),
246
239
  (
247
240
  obs_id,
248
241
  subject,
@@ -253,7 +246,7 @@ def create_behaviors_bar_plot(
253
246
  behaviors[subject][behavior]["number of occurences"] = 0 if row["count"] is None else row["count"]
254
247
 
255
248
  # total duration
256
- if cfg.STATE in project_functions.event_type(behavior, pj[cfg.ETHOGRAM]):
249
+ if project_functions.event_type(behavior, pj[cfg.ETHOGRAM]) in cfg.STATE_EVENT_TYPES:
257
250
  cursor.execute(
258
251
  (
259
252
  "SELECT SUM(stop - start) AS duration FROM aggregated_events "
@@ -268,27 +261,49 @@ def create_behaviors_bar_plot(
268
261
  for row in cursor.fetchall():
269
262
  behaviors[subject][behavior]["duration"] = 0 if row["duration"] is None else row["duration"]
270
263
 
271
- durations, n_occurences, colors, x_labels, colors_duration, x_labels_duration = [], [], [], [], [], []
264
+ (
265
+ durations,
266
+ n_occurences,
267
+ colors,
268
+ x_labels,
269
+ colors_duration,
270
+ x_labels_duration,
271
+ ) = ([], [], [], [], [], [])
272
272
 
273
273
  for behavior in sorted(distinct_behav):
274
-
275
274
  if param[cfg.EXCLUDE_BEHAVIORS] and behaviors[subject][behavior]["number of occurences"] == 0:
276
275
  continue
277
276
 
278
277
  n_occurences.append(behaviors[subject][behavior]["number of occurences"])
279
278
  x_labels.append(behavior)
280
- try:
281
- colors.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
282
- except Exception:
283
- colors.append("darkgray")
284
279
 
285
- if cfg.STATE in project_functions.event_type(behavior, pj[cfg.ETHOGRAM]):
286
- durations.append(behaviors[subject][behavior]["duration"])
287
- x_labels_duration.append(behavior)
280
+ # color
281
+ behav_idx = [k for k in pj[cfg.ETHOGRAM] if pj[cfg.ETHOGRAM][k]["code"] == behavior][0]
282
+ col = None
283
+ if cfg.COLOR in pj[cfg.ETHOGRAM][behav_idx]:
284
+ col = util.behavior_user_color(pj[cfg.ETHOGRAM], behavior)
285
+ if col is not None:
286
+ colors.append(col)
287
+ else:
288
288
  try:
289
- colors_duration.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
289
+ colors.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
290
290
  except Exception:
291
- colors_duration.append("darkgray")
291
+ colors.append("darkgray")
292
+
293
+ if project_functions.event_type(behavior, pj[cfg.ETHOGRAM]) in cfg.STATE_EVENT_TYPES:
294
+ durations.append(behaviors[subject][behavior]["duration"])
295
+ x_labels_duration.append(behavior)
296
+
297
+ col = None
298
+ if cfg.COLOR in pj[cfg.ETHOGRAM][behav_idx]:
299
+ col = util.behavior_user_color(pj[cfg.ETHOGRAM], behavior)
300
+ if col is not None:
301
+ colors_duration.append(col)
302
+ else:
303
+ try:
304
+ colors_duration.append(util.behavior_color(plot_colors, all_behaviors.index(behavior)))
305
+ except Exception:
306
+ colors_duration.append("darkgray")
292
307
 
293
308
  # width = 0.35 # the width of the bars: can also be len(x) sequence
294
309
 
@@ -341,10 +356,15 @@ def create_behaviors_bar_plot(
341
356
 
342
357
 
343
358
  def create_events_plot(
344
- pj, selected_observations, parameters, plot_colors=cfg.BEHAVIORS_PLOT_COLORS, plot_directory="", file_format="png"
359
+ self,
360
+ selected_observations,
361
+ parameters,
362
+ plot_colors=cfg.BEHAVIORS_PLOT_COLORS,
363
+ plot_directory="",
364
+ file_format="png",
345
365
  ):
346
366
  """
347
- create a time diagram plot (sort of gantt chart)
367
+ create a time diagram plot (like a gantt chart)
348
368
  with matplotlib barh function (https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.pyplot.barh.html)
349
369
  """
350
370
 
@@ -355,9 +375,7 @@ def create_events_plot(
355
375
  start_time = parameters[cfg.START_TIME]
356
376
  end_time = parameters[cfg.END_TIME]
357
377
 
358
- ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
359
- pj, selected_subjects, selected_observations, selected_behaviors
360
- )
378
+ ok, msg, db_connector = db_functions.load_aggregated_events_in_db(self.pj, selected_subjects, selected_observations, selected_behaviors)
361
379
 
362
380
  if not ok:
363
381
  return False, msg, None
@@ -379,14 +397,13 @@ def create_events_plot(
379
397
  distinct_behav_modif = sorted(distinct_behav_modif)
380
398
  max_len = len(distinct_behav_modif)
381
399
 
382
- all_behaviors = [pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(pj[cfg.ETHOGRAM])]
400
+ all_behaviors = [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]
383
401
 
384
402
  par1 = 1
385
403
  bar_height = 0.5
386
- init = dt.datetime(2017, 1, 1)
404
+ epoch_date = dt.datetime(2017, 1, 1)
387
405
 
388
406
  for obs_id in selected_observations:
389
-
390
407
  if len(selected_subjects) > 1:
391
408
  fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True)
392
409
  else:
@@ -394,9 +411,7 @@ def create_events_plot(
394
411
  axs = np.ndarray(shape=(1), dtype=type(ax))
395
412
  axs[0] = ax
396
413
 
397
- ok, msg, db_connector = db_functions.load_aggregated_events_in_db(
398
- pj, selected_subjects, [obs_id], selected_behaviors
399
- )
414
+ ok, msg, db_connector = db_functions.load_aggregated_events_in_db(self.pj, selected_subjects, [obs_id], selected_behaviors)
400
415
 
401
416
  cursor = db_connector.cursor()
402
417
  # if modifiers not to be included set modifiers to ""
@@ -417,7 +432,7 @@ def create_events_plot(
417
432
  max_len = len(distinct_behav_modif)
418
433
 
419
434
  # time
420
- obs_length = observation_operations.observation_total_length(pj[cfg.OBSERVATIONS][obs_id])
435
+ obs_length = observation_operations.observation_total_length(self.pj[cfg.OBSERVATIONS][obs_id])
421
436
  if obs_length == -1: # media length not available
422
437
  interval = cfg.TIME_EVENTS
423
438
 
@@ -425,13 +440,20 @@ def create_events_plot(
425
440
  min_time = 0.0
426
441
  max_time = float(obs_length)
427
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
+
428
450
  if interval == cfg.TIME_EVENTS:
429
451
  try:
430
- min_time = float(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0]) # first event
452
+ min_time = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0]) # first event
431
453
  except Exception:
432
454
  min_time = 0.0
433
455
  try:
434
- max_time = float(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][-1][0]) # last event
456
+ max_time = float(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][-1][0]) # last event
435
457
  except Exception:
436
458
  max_time = float(obs_length)
437
459
 
@@ -478,10 +500,10 @@ def create_events_plot(
478
500
 
479
501
  ylabels = [" ".join(x) for x in distinct_behav_modif]
480
502
  for ax_idx, subject in enumerate(selected_subjects):
481
-
482
503
  if parameters["exclude behaviors"]:
483
504
  cursor.execute(
484
- "SELECT DISTINCT behavior, modifiers FROM aggregated_events WHERE subject = ?", (subject,)
505
+ "SELECT DISTINCT behavior, modifiers FROM aggregated_events WHERE subject = ?",
506
+ (subject,),
485
507
  )
486
508
  distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()]
487
509
 
@@ -508,10 +530,7 @@ def create_events_plot(
508
530
 
509
531
  # total duration
510
532
  cursor.execute(
511
- (
512
- "SELECT start, stop FROM aggregated_events "
513
- "WHERE subject = ? AND behavior = ? AND modifiers = ?"
514
- ),
533
+ ("SELECT start, stop FROM aggregated_events WHERE subject = ? AND behavior = ? AND modifiers = ?"),
515
534
  (
516
535
  subject,
517
536
  behavior,
@@ -521,22 +540,36 @@ def create_events_plot(
521
540
  for row in cursor.fetchall():
522
541
  bars[behavior_modifiers_str].append((row["start"], row["stop"]))
523
542
 
524
- start_date = matplotlib.dates.date2num(init + dt.timedelta(seconds=row["start"]))
525
- end_date = matplotlib.dates.date2num(
526
- init
527
- + dt.timedelta(
528
- seconds=row["stop"] + cfg.POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"])
543
+ if self.timeFormat == cfg.HHMMSS:
544
+ start_date = matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=row["start"]))
545
+ end_date = matplotlib.dates.date2num(
546
+ epoch_date + dt.timedelta(seconds=row["stop"] + cfg.POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))
529
547
  )
530
- )
531
- try:
532
- bar_color = util.behavior_color(plot_colors, all_behaviors.index(behavior))
533
- except Exception:
534
- bar_color = "darkgray"
548
+ if self.timeFormat == cfg.S:
549
+ start_date = row["start"]
550
+ end_date = row["stop"]
551
+
552
+ # color
553
+ behav_idx = [k for k in self.pj[cfg.ETHOGRAM] if self.pj[cfg.ETHOGRAM][k]["code"] == behavior][0]
554
+ col = None
555
+ if cfg.COLOR in self.pj[cfg.ETHOGRAM][behav_idx]:
556
+ col = util.behavior_user_color(self.pj[cfg.ETHOGRAM], behavior)
557
+ if col is not None:
558
+ bar_color = col
559
+ else:
560
+ try:
561
+ bar_color = util.behavior_color(plot_colors, all_behaviors.index(behavior))
562
+ except Exception:
563
+ bar_color = "darkgray"
535
564
  bar_color = cfg.POINT_EVENT_PLOT_COLOR if row["stop"] == row["start"] else bar_color
536
565
 
537
566
  # sage colors removed from matplotlib colors list
538
- if bar_color in ["sage", "darksage", "lightsage"]:
539
- bar_color = {"darksage": "#598556", "lightsage": "#bcecac", "sage": "#87ae73"}[bar_color]
567
+ if bar_color in ("sage", "darksage", "lightsage"):
568
+ bar_color = {
569
+ "darksage": "#598556",
570
+ "lightsage": "#bcecac",
571
+ "sage": "#87ae73",
572
+ }[bar_color]
540
573
 
541
574
  try:
542
575
  axs[ax_idx].barh(
@@ -569,26 +602,30 @@ def create_events_plot(
569
602
 
570
603
  axs[ax_idx].set_yticklabels(ylabels, fontdict={"fontsize": 10})
571
604
 
572
- axs[ax_idx].set_ylabel("Behaviors" + " (modifiers)" * include_modifiers, fontdict={"fontsize": 10})
573
-
574
- """
575
- axs[ax_idx].set_xlim(
576
- left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)),
577
- right=matplotlib.dates.date2num(init + dt.timedelta(seconds=max_time + 1)),
578
- )
579
- """
580
- axs[ax_idx].set_xlim(
581
- left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)),
582
- right=matplotlib.dates.date2num(init + dt.timedelta(seconds=max_time)),
605
+ axs[ax_idx].set_ylabel(
606
+ "Behaviors" + " (modifiers)" * include_modifiers,
607
+ fontdict={"fontsize": 10},
583
608
  )
584
609
 
610
+ if self.timeFormat == cfg.HHMMSS:
611
+ axs[ax_idx].set_xlim(
612
+ left=matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=min_time)),
613
+ right=matplotlib.dates.date2num(epoch_date + dt.timedelta(seconds=max_time)),
614
+ )
615
+
585
616
  axs[ax_idx].grid(color="g", linestyle=":")
586
- axs[ax_idx].xaxis_date()
587
- axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S"))
588
- axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12})
617
+ if self.timeFormat == cfg.HHMMSS:
618
+ axs[ax_idx].xaxis_date()
619
+ axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S"))
620
+ axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12})
621
+ if self.timeFormat == cfg.S:
622
+ axs[ax_idx].set_xlabel("Time (s)", fontdict={"fontsize": 12})
623
+
589
624
  axs[ax_idx].invert_yaxis()
590
625
 
591
- fig.autofmt_xdate()
626
+ if self.timeFormat == cfg.HHMMSS:
627
+ fig.autofmt_xdate()
628
+
592
629
  plt.tight_layout()
593
630
 
594
631
  if len(selected_observations) > 1:
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,33 +25,34 @@ 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
51
52
 
52
53
  self.events_mem = {"init": 0}
53
54
 
54
- self.cursor_color = "red" # default cursor color
55
+ self.cursor_color = cfg.REALTIME_PLOT_CURSOR_COLOR # default cursor color
55
56
  self.observation_type = cfg.MEDIA
56
57
  self.groupby = "behaviors" # group results by "behaviors" or "modifiers"
57
58
 
@@ -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] = []