boris-behav-obs 9.0.8__tar.gz → 9.1__tar.gz

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 (119) hide show
  1. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/PKG-INFO +1 -1
  2. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/advanced_event_filtering.py +9 -4
  3. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/analysis_plugins/number_of_occurences.py +1 -2
  4. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/analysis_plugins/time_budget.py +1 -3
  5. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/behavior_binary_table.py +12 -0
  6. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/config.py +5 -0
  7. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/config_file.py +8 -8
  8. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/cooccurence.py +9 -1
  9. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/core.py +178 -62
  10. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/event_operations.py +0 -3
  11. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/events_snapshots.py +5 -5
  12. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/export_events.py +29 -13
  13. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/export_observation.py +17 -2
  14. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/observation_operations.py +37 -1
  15. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/param_panel.py +12 -0
  16. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/param_panel_ui.py +6 -0
  17. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/plot_events.py +13 -6
  18. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/project_functions.py +22 -12
  19. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/project_import_export.py +9 -9
  20. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/select_observations.py +1 -3
  21. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/select_subj_behav.py +12 -1
  22. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/synthetic_time_budget.py +8 -0
  23. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/time_budget_functions.py +20 -15
  24. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/time_budget_widget.py +18 -7
  25. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/utilities.py +3 -3
  26. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/version.py +2 -2
  27. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/write_event.py +2 -1
  28. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris_behav_obs.egg-info/PKG-INFO +1 -1
  29. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/pyproject.toml +2 -2
  30. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/LICENSE.TXT +0 -0
  31. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/MANIFEST.in +0 -0
  32. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/README.TXT +0 -0
  33. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/README.md +0 -0
  34. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/__init__.py +0 -0
  35. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/__main__.py +0 -0
  36. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/about.py +0 -0
  37. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/add_modifier.py +0 -0
  38. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/add_modifier_ui.py +0 -0
  39. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/analysis_plugins/__init__.py +0 -0
  40. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
  41. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/behav_coding_map_creator.py +0 -0
  42. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/behaviors_coding_map.py +0 -0
  43. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/boris_cli.py +0 -0
  44. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/cmd_arguments.py +0 -0
  45. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/coding_pad.py +0 -0
  46. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/connections.py +0 -0
  47. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/converters.py +0 -0
  48. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/converters_ui.py +0 -0
  49. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/core_qrc.py +0 -0
  50. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/core_ui.py +0 -0
  51. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/db_functions.py +0 -0
  52. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/dev.py +0 -0
  53. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/dialog.py +0 -0
  54. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/duration_widget.py +0 -0
  55. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/edit_event.py +0 -0
  56. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/edit_event_ui.py +0 -0
  57. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/events_cursor.py +0 -0
  58. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/exclusion_matrix.py +0 -0
  59. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/external_processes.py +0 -0
  60. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/geometric_measurement.py +0 -0
  61. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/gui_utilities.py +0 -0
  62. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/image_overlay.py +0 -0
  63. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/import_observations.py +0 -0
  64. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/irr.py +0 -0
  65. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/latency.py +0 -0
  66. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/map_creator.py +0 -0
  67. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/measurement_widget.py +0 -0
  68. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/media_file.py +0 -0
  69. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/menu_options.py +0 -0
  70. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/modifiers_coding_map.py +0 -0
  71. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/mpv-1.0.3.py +0 -0
  72. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/mpv.py +0 -0
  73. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/mpv2.py +0 -0
  74. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/observation.py +0 -0
  75. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/observation_ui.py +0 -0
  76. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/observations_list.py +0 -0
  77. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/otx_parser.py +0 -0
  78. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/player_dock_widget.py +0 -0
  79. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/plot_data_module.py +0 -0
  80. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/plot_events_rt.py +0 -0
  81. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/plot_spectrogram_rt.py +0 -0
  82. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/plot_waveform_rt.py +0 -0
  83. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/plugins.py +0 -0
  84. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/portion/__init__.py +0 -0
  85. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/portion/const.py +0 -0
  86. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/portion/dict.py +0 -0
  87. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/portion/func.py +0 -0
  88. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/portion/interval.py +0 -0
  89. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/portion/io.py +0 -0
  90. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/preferences.py +0 -0
  91. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/preferences_ui.py +0 -0
  92. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/project.py +0 -0
  93. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/project_ui.py +0 -0
  94. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/qrc_boris.py +0 -0
  95. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/qrc_boris5.py +0 -0
  96. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/select_modifiers.py +0 -0
  97. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/state_events.py +0 -0
  98. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/subjects_pad.py +0 -0
  99. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/transitions.py +0 -0
  100. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/video_equalizer.py +0 -0
  101. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/video_equalizer_ui.py +0 -0
  102. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/video_operations.py +0 -0
  103. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/view_df.py +0 -0
  104. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris/view_df_ui.py +0 -0
  105. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris_behav_obs.egg-info/SOURCES.txt +0 -0
  106. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
  107. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris_behav_obs.egg-info/entry_points.txt +0 -0
  108. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris_behav_obs.egg-info/requires.txt +0 -0
  109. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/boris_behav_obs.egg-info/top_level.txt +0 -0
  110. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/setup.cfg +0 -0
  111. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_db_functions.py +0 -0
  112. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_export_observation.py +0 -0
  113. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_irr.py +0 -0
  114. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_observation_gui.py +0 -0
  115. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_otx_parser.py +0 -0
  116. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_preferences_gui.py +0 -0
  117. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_project_functions.py +0 -0
  118. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_time_budget.py +0 -0
  119. {boris_behav_obs-9.0.8 → boris_behav_obs-9.1}/tests/test_utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: boris-behav-obs
3
- Version: 9.0.8
3
+ Version: 9.1
4
4
  Summary: BORIS - Behavioral Observation Research Interactive Software
5
5
  Author-email: Olivier Friard <olivier.friard@unito.it>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -235,7 +235,7 @@ class Advanced_event_filtering_dialog(QDialog):
235
235
  self.tw.clear()
236
236
 
237
237
  if flag_error or self.rb_details.isChecked():
238
- self.lb_results.setText(f"Results ({len(self.out)} event{'s'*(len(self.out) > 1)})")
238
+ self.lb_results.setText(f"Results ({len(self.out)} event{'s' * (len(self.out) > 1)})")
239
239
 
240
240
  self.tw.setRowCount(len(self.out))
241
241
  self.tw.setColumnCount(len(self.details_header)) # obs_id, comment, start, stop, duration
@@ -261,7 +261,7 @@ class Advanced_event_filtering_dialog(QDialog):
261
261
  ]
262
262
  )
263
263
 
264
- self.lb_results.setText(f"Results ({len(summary)} observation{'s'*(len(summary) > 1)})")
264
+ self.lb_results.setText(f"Results ({len(summary)} observation{'s' * (len(summary) > 1)})")
265
265
  self.tw.setRowCount(len(summary))
266
266
  self.tw.setColumnCount(len(self.summary_header)) # obs_id, mean, stdev
267
267
  self.tw.setHorizontalHeaderLabels(self.summary_header)
@@ -352,17 +352,22 @@ def event_filtering(self):
352
352
 
353
353
  max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
354
354
 
355
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
356
+
355
357
  parameters = select_subj_behav.choose_obs_subj_behav_category(
356
358
  self,
357
359
  selected_observations,
358
360
  start_coding=start_coding,
359
361
  end_coding=end_coding,
362
+ start_interval=start_interval,
363
+ end_interval=end_interval,
360
364
  maxTime=max_media_duration_all_obs,
361
365
  show_include_modifiers=False,
362
366
  show_exclude_non_coded_behaviors=False,
363
367
  by_category=False,
364
368
  n_observations=len(selected_observations),
365
369
  )
370
+
366
371
  if parameters == {}:
367
372
  return
368
373
 
@@ -380,7 +385,7 @@ def event_filtering(self):
380
385
  cursor.execute("SELECT MIN(start), MAX(stop) FROM aggregated_events")
381
386
  min_time, max_time = cursor.fetchone()
382
387
 
383
- if parameters[cfg.TIME_INTERVAL] == cfg.TIME_ARBITRARY_INTERVAL:
388
+ if parameters[cfg.TIME_INTERVAL] in (cfg.TIME_ARBITRARY_INTERVAL, cfg.TIME_OBS_INTERVAL):
384
389
  min_time = float(parameters[cfg.START_TIME])
385
390
  max_time = float(parameters[cfg.END_TIME])
386
391
 
@@ -443,6 +448,6 @@ def event_filtering(self):
443
448
 
444
449
  w = Advanced_event_filtering_dialog(events)
445
450
  w.lb_time_interval.setText(
446
- ("Time interval: " f"{util.smart_time_format(min_time, self.timeFormat)} - " f"{util.smart_time_format(max_time, self.timeFormat)}")
451
+ (f"Time interval: {util.smart_time_format(min_time, self.timeFormat)} - {util.smart_time_format(max_time, self.timeFormat)}")
447
452
  )
448
453
  w.exec_()
@@ -17,10 +17,9 @@ def run(df: pd.DataFrame):
17
17
  Calculate the number of occurrences of behaviors by subject.
18
18
  """
19
19
 
20
- string_results: str = ""
21
20
  df_results: pd.DataFrame = df.groupby(["Subject", "Behavior"])["Behavior"].count().reset_index(name="number of occurences")
22
21
 
23
- return df_results, string_results
22
+ return df_results
24
23
 
25
24
 
26
25
  def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
@@ -26,8 +26,6 @@ def run(df: pd.DataFrame):
26
26
  - % of total subject observation duration
27
27
  """
28
28
 
29
- string_results: str = "test\ntest1"
30
-
31
29
  group_by = ["Subject", "Behavior"]
32
30
 
33
31
  dfs = [
@@ -60,7 +58,7 @@ def run(df: pd.DataFrame):
60
58
  for df in dfs[1:]:
61
59
  merged_df = pd.merge(merged_df, df, on=group_by)
62
60
 
63
- return merged_df, string_results
61
+ return merged_df
64
62
 
65
63
 
66
64
  def main(df: pd.DataFrame, observations_list: list = [], parameters: dict = {}) -> pd.DataFrame:
@@ -79,6 +79,14 @@ def create_behavior_binary_table(pj: dict, selected_observations: list, paramete
79
79
  max_obs_length, _ = observation_operations.observation_length(pj, [obs_id])
80
80
  end_time = dec(max_obs_length)
81
81
 
82
+ if parameters_obs["time"] == cfg.TIME_OBS_INTERVAL:
83
+ obs_interval = pj[cfg.OBSERVATIONS][obs_id][cfg.OBSERVATION_TIME_INTERVAL]
84
+ offset = pj[cfg.OBSERVATIONS][obs_id][cfg.TIME_OFFSET]
85
+ start_time = dec(obs_interval[0]) + offset
86
+ # Use max observation length for end time if no interval is defined (=0)
87
+ max_obs_length, _ = observation_operations.observation_length(pj, [obs_id])
88
+ end_time = dec(obs_interval[1]) + offset if obs_interval[1] != 0 else dec(max_obs_length)
89
+
82
90
  if obs_id not in results_df:
83
91
  results_df[obs_id] = {}
84
92
 
@@ -201,11 +209,15 @@ def behavior_binary_table(self):
201
209
 
202
210
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
203
211
 
212
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
213
+
204
214
  parameters = select_subj_behav.choose_obs_subj_behav_category(
205
215
  self,
206
216
  selected_observations,
207
217
  start_coding=start_coding,
208
218
  end_coding=end_coding,
219
+ start_interval=start_interval,
220
+ end_interval=end_interval,
209
221
  maxTime=max_media_duration_all_obs,
210
222
  show_include_modifiers=True,
211
223
  show_exclude_non_coded_behaviors=True,
@@ -256,9 +256,13 @@ HAS_AUDIO_IDX = 6
256
256
  STATE_EVENT = "State event"
257
257
  STATE_EVENT_WITH_CODING_MAP = "State event with coding map"
258
258
 
259
+ STATE_EVENT_TYPES = [STATE_EVENT, STATE_EVENT_WITH_CODING_MAP]
260
+
259
261
  POINT_EVENT = "Point event"
260
262
  POINT_EVENT_WITH_CODING_MAP = "Point event with coding map"
261
263
 
264
+ POINT_EVENT_TYPES = [POINT_EVENT, POINT_EVENT_WITH_CODING_MAP]
265
+
262
266
  BEHAVIOR_TYPES = [
263
267
  POINT_EVENT,
264
268
  STATE_EVENT,
@@ -395,6 +399,7 @@ TIMESTAMP_idx = 3
395
399
 
396
400
  TIME_FULL_OBS = "full obs"
397
401
  TIME_EVENTS = "limit to events"
402
+ TIME_OBS_INTERVAL = "interval of observation"
398
403
  TIME_ARBITRARY_INTERVAL = "time interval"
399
404
 
400
405
  AVAILABLE_INDEP_VAR_TYPES = [NUMERIC, TEXT, SET_OF_VALUES, TIMESTAMP]
@@ -36,19 +36,19 @@ def read(self):
36
36
  read config file
37
37
  """
38
38
 
39
- iniFilePath = pl.Path.home() / pl.Path(".boris")
39
+ ini_fil_p_pe_p_path = pl.Path.home() / pl.Path(".boris")
40
40
 
41
- logging.debug(f"read config file: {iniFilePath}")
41
+ logging.debug(f"read config file: {ini_fil_p_pe_p_path}")
42
42
 
43
- if iniFilePath.is_file():
44
- 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)
45
45
 
46
46
  try:
47
47
  self.config_param = settings.value("config")
48
48
  except Exception:
49
- self.config_param = None
49
+ self.config_param = {}
50
50
 
51
- if self.config_param is None:
51
+ if self.config_param == {}:
52
52
  self.config_param = cfg.INIT_PARAM
53
53
 
54
54
  # for back compatibility
@@ -245,7 +245,7 @@ def read(self):
245
245
  if (
246
246
  dialog.MessageDialog(
247
247
  cfg.programName,
248
- ("The colors list contain colors that are very light.\n" "Do you want to reload the default colors list?"),
248
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
249
249
  [cfg.NO, cfg.YES],
250
250
  )
251
251
  == cfg.YES
@@ -262,7 +262,7 @@ def read(self):
262
262
  if (
263
263
  dialog.MessageDialog(
264
264
  cfg.programName,
265
- ("The colors list contain colors that are very light.\n" "Do you want to reload the default colors list?"),
265
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
266
266
  [cfg.NO, cfg.YES],
267
267
  )
268
268
  == cfg.YES
@@ -82,6 +82,8 @@ def get_cooccurence(self):
82
82
  if not_ok or not selected_observations:
83
83
  return
84
84
 
85
+ max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
86
+
85
87
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
86
88
  # exit with message if events do not have timestamp
87
89
  if start_coding.is_nan():
@@ -94,12 +96,18 @@ def get_cooccurence(self):
94
96
  )
95
97
  return
96
98
 
99
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
100
+
97
101
  while True:
98
102
  flag_ok: bool = True
99
103
  parameters = select_subj_behav.choose_obs_subj_behav_category(
100
104
  self,
101
105
  selected_observations,
102
- window_title="Select the behaviors",
106
+ start_coding=start_coding,
107
+ end_coding=end_coding,
108
+ start_interval=start_interval,
109
+ end_interval=end_interval,
110
+ maxTime=max_media_duration_all_obs,
103
111
  n_observations=len(selected_observations),
104
112
  show_include_modifiers=False,
105
113
  show_exclude_non_coded_behaviors=True,
@@ -52,7 +52,7 @@ from decimal import Decimal as dec
52
52
  from decimal import ROUND_DOWN
53
53
  import gzip
54
54
  from collections import deque
55
-
55
+ import pandas as pd
56
56
  import matplotlib
57
57
  import zipfile
58
58
  import shutil
@@ -60,7 +60,18 @@ import shutil
60
60
  matplotlib.use("QtAgg")
61
61
 
62
62
  import PySide6
63
- from PySide6.QtCore import Qt, QPoint, Signal, QEvent, QDateTime, QUrl, QAbstractTableModel, qVersion, QElapsedTimer
63
+ from PySide6.QtCore import (
64
+ Qt,
65
+ QPoint,
66
+ Signal,
67
+ QEvent,
68
+ QDateTime,
69
+ QUrl,
70
+ QAbstractTableModel,
71
+ qVersion,
72
+ QElapsedTimer,
73
+ QSettings,
74
+ )
64
75
  from PySide6.QtGui import QIcon, QPixmap, QFont, QKeyEvent, QDesktopServices, QColor, QPainter, QPolygon, QAction
65
76
  from PySide6.QtMultimedia import QSoundEffect
66
77
  from PySide6.QtWidgets import (
@@ -229,6 +240,19 @@ class TableModel(QAbstractTableModel):
229
240
  return self._data[row][event_idx]
230
241
 
231
242
 
243
+ """
244
+ class ButtonEventFilter(QObject):
245
+ def eventFilter(self, obj, event):
246
+ print("event filter")
247
+ if isinstance(obj, QPushButton) and event.type() == QEvent.KeyPress:
248
+ print("keypress")
249
+ if event.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Space):
250
+ print("enter sapce")
251
+ return False # Block the event
252
+ return super().eventFilter(obj, event)
253
+ """
254
+
255
+
232
256
  class MainWindow(QMainWindow, Ui_MainWindow):
233
257
  """
234
258
  Main BORIS window
@@ -369,6 +393,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
369
393
  super(MainWindow, self).__init__(parent)
370
394
  self.setupUi(self)
371
395
 
396
+ # disable trigger with RETURN or SPACE keys
397
+ """filter_obj = ButtonEventFilter()
398
+ self.pb_live_obs.installEventFilter(filter_obj)"""
399
+ self.pb_live_obs.setFocusPolicy(Qt.NoFocus)
400
+
372
401
  self.ffmpeg_bin = ffmpeg_bin
373
402
  # set icons
374
403
  self.setWindowIcon(QIcon(":/small_logo"))
@@ -771,7 +800,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
771
800
  title="Select the behaviors to show in the ethogram table",
772
801
  text="Behaviors to show in ethogram list",
773
802
  table=cfg.ETHOGRAM,
774
- behavior_type=[cfg.STATE_EVENT, cfg.POINT_EVENT],
803
+ behavior_type=cfg.STATE_EVENT_TYPES,
775
804
  ) -> Tuple[bool, list]:
776
805
  """
777
806
  allow user to:
@@ -793,7 +822,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
793
822
  if not self.pj[cfg.ETHOGRAM]:
794
823
  return True, []
795
824
 
796
- behavior_type = [x.upper() for x in behavior_type]
825
+ # behavior_type = [x.upper() for x in behavior_type]
797
826
 
798
827
  paramPanelWindow = param_panel.Param_panel()
799
828
  paramPanelWindow.setWindowTitle(title)
@@ -1382,9 +1411,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1382
1411
  ask user for updating
1383
1412
  """
1384
1413
 
1385
- versionURL = "https://www.boris.unito.it/static/ver4.dat"
1414
+ version_URL = "https://www.boris.unito.it/static/ver4.dat"
1386
1415
  try:
1387
- last_version = urllib.request.urlopen(versionURL).read().strip().decode("utf-8")
1416
+ last_version = urllib.request.urlopen(version_URL).read().strip().decode("utf-8")
1388
1417
  except Exception:
1389
1418
  QMessageBox.warning(self, cfg.programName, "Can not check for updates...")
1390
1419
  return
@@ -2264,8 +2293,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
2264
2293
  """
2265
2294
  populate table view with events
2266
2295
  """
2267
- logging.debug("populate tv_events")
2268
-
2269
2296
  model = self.tv_events.model()
2270
2297
  if model is not None:
2271
2298
  self.tv_events.setModel(None)
@@ -2275,7 +2302,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
2275
2302
  mem_behav: dict = {}
2276
2303
  state_events_list = util.state_behavior_codes(self.pj[cfg.ETHOGRAM])
2277
2304
 
2278
- state: list = [""] * len(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS])
2305
+ state = [""] * len(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS])
2279
2306
 
2280
2307
  for idx, row in enumerate(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]):
2281
2308
  code = row[cfg.PJ_OBS_FIELDS[self.playerType][cfg.BEHAVIOR_CODE]]
@@ -2341,16 +2368,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
2341
2368
 
2342
2369
  logging.debug(f"begin load events from obs in tableView: {obs_id}")
2343
2370
 
2344
- # print(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS])
2345
-
2371
+ # t1 = time.time()
2346
2372
  self.populate_tv_events(
2347
2373
  obs_id,
2348
- [s.capitalize() for s in cfg.TW_EVENTS_FIELDS[self.playerType]], # header
2374
+ [s.capitalize() for s in cfg.TW_EVENTS_FIELDS[self.playerType]],
2349
2375
  self.timeFormat,
2350
2376
  self.filtered_behaviors,
2351
2377
  self.filtered_subjects,
2352
2378
  )
2353
2379
 
2380
+ # print("load table view:", time.time() - t1)
2381
+
2354
2382
  return
2355
2383
 
2356
2384
  """
@@ -2692,11 +2720,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
2692
2720
  return
2693
2721
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
2694
2722
 
2723
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
2724
+
2695
2725
  parameters = select_subj_behav.choose_obs_subj_behav_category(
2696
2726
  self,
2697
2727
  selected_observations,
2698
2728
  start_coding=start_coding,
2699
2729
  end_coding=end_coding,
2730
+ start_interval=start_interval,
2731
+ end_interval=end_interval,
2700
2732
  maxTime=max_obs_length,
2701
2733
  show_exclude_non_coded_behaviors=True,
2702
2734
  by_category=False,
@@ -2791,11 +2823,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
2791
2823
 
2792
2824
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
2793
2825
 
2826
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
2827
+
2794
2828
  parameters = select_subj_behav.choose_obs_subj_behav_category(
2795
2829
  self,
2796
2830
  selected_observations,
2797
2831
  start_coding=start_coding,
2798
2832
  end_coding=end_coding,
2833
+ start_interval=start_interval,
2834
+ end_interval=end_interval,
2799
2835
  maxTime=max_obs_length if len(selected_observations) > 1 else selectedObsTotalMediaLength,
2800
2836
  show_include_modifiers=False,
2801
2837
  show_exclude_non_coded_behaviors=True,
@@ -3488,9 +3524,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3488
3524
  check project integrity
3489
3525
  to be used after opening or saving the current project
3490
3526
  """
3527
+
3528
+ logging.debug("check project integrity open save")
3529
+
3491
3530
  if self.automaticBackup:
3492
3531
  return
3493
3532
 
3533
+ logging.debug(
3534
+ f"{self.config_param[cfg.CHECK_PROJECT_INTEGRITY] if cfg.CHECK_PROJECT_INTEGRITY in self.config_param else "Check project integrity config NOT FOUND"=}"
3535
+ )
3536
+
3494
3537
  if self.config_param.get(cfg.CHECK_PROJECT_INTEGRITY, True):
3495
3538
  msg = project_functions.check_project_integrity(
3496
3539
  self.pj,
@@ -3712,6 +3755,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3712
3755
 
3713
3756
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
3714
3757
 
3758
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
3759
+
3715
3760
  if start_coding.is_nan():
3716
3761
  QMessageBox.critical(
3717
3762
  None,
@@ -3727,6 +3772,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3727
3772
  selected_observations,
3728
3773
  start_coding=start_coding,
3729
3774
  end_coding=end_coding,
3775
+ start_interval=start_interval,
3776
+ end_interval=end_interval,
3730
3777
  maxTime=max_media_duration_all_obs,
3731
3778
  show_include_modifiers=False,
3732
3779
  show_exclude_non_coded_behaviors=False,
@@ -3932,6 +3979,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3932
3979
  ethogram_idx = [x for x in self.pj[cfg.ETHOGRAM] if self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] == code][0]
3933
3980
 
3934
3981
  event = self.full_event(ethogram_idx)
3982
+ # MEDIA / LIVE
3983
+ """
3984
+ if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
3985
+ time_ = self.getLaps()
3986
+ """
3935
3987
 
3936
3988
  # IMAGES
3937
3989
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
@@ -3944,6 +3996,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3944
3996
 
3945
3997
  time_, cumulative_time = self.get_obs_time()
3946
3998
 
3999
+ """
4000
+ print(time_)
4001
+ print(cumulative_time)
4002
+ """
4003
+
3947
4004
  if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.MEDIA_CREATION_DATE_AS_OFFSET, False):
3948
4005
  write_event.write_event(self, event, time_)
3949
4006
  else:
@@ -4863,6 +4920,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4863
4920
 
4864
4921
  def keyPressEvent(self, event) -> None:
4865
4922
  """
4923
+ http://qt-project.org/doc/qt-5.0/qtcore/qt.html#Key-enum
4866
4924
  https://github.com/pyqt/python-qt5/blob/master/PySide6/qml/builtins.qmltypes
4867
4925
 
4868
4926
  ESC: 16777216
@@ -4922,6 +4980,19 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4922
4980
 
4923
4981
  flagPlayerPlaying = self.is_playing()
4924
4982
 
4983
+ # play / pause with space bar
4984
+ if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.LIVE:
4985
+ if ek in (Qt.Key_Space, Qt.Key_Enter, Qt.Key_Return):
4986
+ if self.liveObservationStarted:
4987
+ if (
4988
+ dialog.MessageDialog(cfg.programName, "Are you sure to stop the current live observation?", [cfg.YES, cfg.NO])
4989
+ == cfg.YES
4990
+ ):
4991
+ self.start_live_observation()
4992
+ else:
4993
+ self.start_live_observation()
4994
+ return
4995
+
4925
4996
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA:
4926
4997
  # speed down
4927
4998
  if ek == Qt.Key_End:
@@ -5172,6 +5243,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5172
5243
  else:
5173
5244
  write_event.write_event(self, event, memLaps)
5174
5245
 
5246
+ # write_event.write_event(self, event, memLaps)
5247
+
5175
5248
  elif subject_idx is not None:
5176
5249
  self.update_subject(self.pj[cfg.SUBJECTS][subject_idx][cfg.SUBJECT_NAME])
5177
5250
 
@@ -5525,7 +5598,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5525
5598
  def actionQuit_activated(self):
5526
5599
  self.close()
5527
5600
 
5528
- def play_video(self):
5601
+ def play_video(self) -> bool | None:
5529
5602
  """
5530
5603
  play video
5531
5604
  check if first player ended
@@ -5534,32 +5607,34 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5534
5607
  if self.geometric_measurements_mode:
5535
5608
  return
5536
5609
 
5537
- if self.playerType == cfg.MEDIA:
5538
- # check if player 1 is ended
5539
- for i, dw in enumerate(self.dw_player):
5540
- if (
5541
- str(i + 1) in self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE]
5542
- and self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][str(i + 1)]
5543
- ):
5544
- dw.player.pause = False
5610
+ if self.playerType != cfg.MEDIA:
5611
+ return
5545
5612
 
5546
- self.lb_player_status.clear()
5613
+ # check if player 1 is ended
5614
+ for i, dw in enumerate(self.dw_player):
5615
+ if (
5616
+ str(i + 1) in self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE]
5617
+ and self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][str(i + 1)]
5618
+ ):
5619
+ dw.player.pause = False
5547
5620
 
5548
- # if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.VISUALIZE_WAVEFORM, False) \
5549
- # or self.pj[cfg.OBSERVATIONS][self.observationId].get(VISUALIZE_SPECTROGRAM, False):
5621
+ self.lb_player_status.clear()
5550
5622
 
5551
- self.statusbar.showMessage("", 0)
5623
+ # if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.VISUALIZE_WAVEFORM, False) \
5624
+ # or self.pj[cfg.OBSERVATIONS][self.observationId].get(VISUALIZE_SPECTROGRAM, False):
5552
5625
 
5553
- self.plot_timer.start()
5626
+ self.statusbar.showMessage("", 0)
5554
5627
 
5555
- # start all timer for plotting data
5556
- for data_timer in self.ext_data_timer_list:
5557
- data_timer.start()
5628
+ self.plot_timer.start()
5558
5629
 
5559
- self.actionPlay.setIcon(QIcon(f":/pause_{self.theme_mode()}"))
5560
- self.actionPlay.setText("Pause")
5630
+ # start all timer for plotting data
5631
+ for data_timer in self.ext_data_timer_list:
5632
+ data_timer.start()
5561
5633
 
5562
- return True
5634
+ self.actionPlay.setIcon(QIcon(f":/pause_{self.theme_mode()}"))
5635
+ self.actionPlay.setText("Pause")
5636
+
5637
+ return True
5563
5638
 
5564
5639
  def pause_video(self, msg: str = "Player paused"):
5565
5640
  """
@@ -5597,12 +5672,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5597
5672
  """
5598
5673
  button 'play' activated
5599
5674
  """
5675
+ if not self.observationId:
5676
+ return
5600
5677
 
5601
- if self.observationId and self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA:
5602
- if not self.is_playing():
5603
- self.play_video()
5604
- else:
5605
- self.pause_video()
5678
+ if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] != cfg.MEDIA:
5679
+ return
5680
+
5681
+ if not self.is_playing():
5682
+ self.play_video()
5683
+ else:
5684
+ self.pause_video()
5606
5685
 
5607
5686
  def jumpBackward_activated(self):
5608
5687
  """
@@ -5707,11 +5786,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5707
5786
 
5708
5787
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
5709
5788
 
5789
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
5790
+
5710
5791
  parameters: dict = select_subj_behav.choose_obs_subj_behav_category(
5711
5792
  self,
5712
5793
  selected_observations,
5713
5794
  start_coding=start_coding,
5714
5795
  end_coding=end_coding,
5796
+ start_interval=start_interval,
5797
+ end_interval=end_interval,
5715
5798
  maxTime=max_media_duration_all_obs,
5716
5799
  by_category=False,
5717
5800
  n_observations=len(selected_observations),
@@ -5781,19 +5864,39 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5781
5864
  logging.debug(f"{df.info()}")
5782
5865
  logging.debug(f"{df.head()}")
5783
5866
 
5784
- df_results, str_results = plugin_module.main(df, observations_list=selected_observations, parameters=parameters)
5785
-
5786
- self.view_dataframe = view_df.View_df(
5787
- self.sender().text(), f"{plugin_module.__version__} ({plugin_module.__version_date__})", df_results
5788
- )
5789
- self.view_dataframe.show()
5790
-
5791
- if str_results:
5792
- self.results = dialog.Results_dialog()
5793
- self.results.setWindowTitle(self.sender().text())
5794
- self.results.ptText.clear()
5795
- self.results.ptText.appendPlainText(str_results)
5796
- self.results.show()
5867
+ # df_results, str_results = plugin_module.main(df, observations_list=selected_observations, parameters=parameters)
5868
+
5869
+ plugin_results = plugin_module.main(df, observations_list=selected_observations, parameters=parameters)
5870
+ # test if tuple: if not transform to tuple
5871
+ if not isinstance(plugin_results, tuple):
5872
+ plugin_results = tuple([plugin_results])
5873
+
5874
+ self.plugin_visu: list = []
5875
+ for result in plugin_results:
5876
+ if isinstance(result, str):
5877
+ self.plugin_visu.append(dialog.Results_dialog())
5878
+ self.plugin_visu[-1].setWindowTitle(self.sender().text())
5879
+ self.plugin_visu[-1].ptText.clear()
5880
+ self.plugin_visu[-1].ptText.appendPlainText(result)
5881
+ self.plugin_visu[-1].show()
5882
+ elif isinstance(result, pd.DataFrame):
5883
+ self.plugin_visu.append(
5884
+ view_df.View_df(self.sender().text(), f"{plugin_module.__version__} ({plugin_module.__version_date__})", result)
5885
+ )
5886
+ self.plugin_visu[-1].show()
5887
+ else:
5888
+ # result is not str nor dataframe
5889
+ QMessageBox.critical(
5890
+ None,
5891
+ cfg.programName,
5892
+ (
5893
+ f"Plugin returns an unknown object type: {type(result)}\n\n"
5894
+ "Plugins must return str and/or Pandas Dataframes.\n"
5895
+ "Check the plugin code."
5896
+ ),
5897
+ QMessageBox.Ok | QMessageBox.Default,
5898
+ QMessageBox.NoButton,
5899
+ )
5797
5900
 
5798
5901
 
5799
5902
  def main():
@@ -5864,18 +5967,31 @@ def main():
5864
5967
  window.load_project(project_path, project_changed, pj)
5865
5968
 
5866
5969
  # check project integrity
5867
- msg = project_functions.check_project_integrity(
5868
- pj,
5869
- "S",
5870
- project_path,
5871
- media_file_available=True,
5872
- )
5873
- if msg:
5874
- results = dialog.Results_dialog()
5875
- results.setWindowTitle("Project integrity results")
5876
- results.ptText.clear()
5877
- results.ptText.appendHtml(f"Some issues were found in the project<br><br>{msg}")
5878
- results.show()
5970
+ # read config
5971
+ config_param: dict = {}
5972
+ ini_file_path = pl.Path.home() / pl.Path(".boris")
5973
+ if ini_file_path.is_file():
5974
+ settings = QSettings(str(ini_file_path), QSettings.IniFormat)
5975
+ try:
5976
+ config_param = settings.value("config")
5977
+ except Exception:
5978
+ config_param = {}
5979
+ if config_param == {}:
5980
+ config_param = cfg.INIT_PARAM
5981
+
5982
+ if config_param.get(cfg.CHECK_PROJECT_INTEGRITY, True):
5983
+ msg = project_functions.check_project_integrity(
5984
+ pj,
5985
+ "S",
5986
+ project_path,
5987
+ media_file_available=True,
5988
+ )
5989
+ if msg:
5990
+ results = dialog.Results_dialog()
5991
+ results.setWindowTitle("Project integrity results")
5992
+ results.ptText.clear()
5993
+ results.ptText.appendHtml(f"Some issues were found in the project<br><br>{msg}")
5994
+ results.show()
5879
5995
 
5880
5996
  window.show()
5881
5997
  window.raise_()