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/config_file.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
@@ -18,21 +18,17 @@ This file is part of BORIS.
18
18
  You should have received a copy of the GNU General Public License
19
19
  along with this program; if not see <http://www.gnu.org/licenses/>.
20
20
 
21
- """
22
-
23
- """
24
21
  Read and write the BORIS config file
25
22
  """
26
23
 
27
24
  import pathlib as pl
28
25
  import logging
29
26
  import time
30
- import os
31
27
 
32
28
  from . import config as cfg
33
29
  from . import dialog
34
30
 
35
- from PyQt5.QtCore import QByteArray, QSettings
31
+ from PySide6.QtCore import QByteArray, QSettings
36
32
 
37
33
 
38
34
  def read(self):
@@ -40,19 +36,19 @@ def read(self):
40
36
  read config file
41
37
  """
42
38
 
43
- iniFilePath = pl.Path.home() / pl.Path(".boris")
39
+ ini_fil_p_pe_p_path = pl.Path.home() / pl.Path(".boris")
44
40
 
45
- logging.debug(f"read config file: {iniFilePath}")
41
+ logging.debug(f"read config file: {ini_fil_p_pe_p_path}")
46
42
 
47
- if iniFilePath.is_file():
48
- settings = QSettings(str(iniFilePath), QSettings.IniFormat)
43
+ if ini_fil_p_pe_p_path.is_file():
44
+ settings = QSettings(str(ini_fil_p_pe_p_path), QSettings.IniFormat)
49
45
 
50
46
  try:
51
47
  self.config_param = settings.value("config")
52
48
  except Exception:
53
- self.config_param = None
54
- pass
55
- if self.config_param is None:
49
+ self.config_param = {}
50
+
51
+ if self.config_param == {}:
56
52
  self.config_param = cfg.INIT_PARAM
57
53
 
58
54
  # for back compatibility
@@ -76,7 +72,7 @@ def read(self):
76
72
  if not isinstance(self.saved_state, QByteArray):
77
73
  self.saved_state = None
78
74
 
79
- logging.debug(f"saved state: {self.saved_state}")
75
+ # logging.debug(f"saved state: {self.saved_state}")
80
76
 
81
77
  self.timeFormat = cfg.HHMMSS
82
78
  try:
@@ -86,11 +82,11 @@ def read(self):
86
82
 
87
83
  logging.debug(f"time format: {self.timeFormat}")
88
84
 
89
- self.fast = 10
85
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
90
86
  try:
91
- self.fast = int(settings.value("Time/fast_forward_speed"))
87
+ self.fast = float(settings.value("Time/fast_forward_speed"))
92
88
  except Exception:
93
- self.fast = 10
89
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
94
90
 
95
91
  logging.debug(f"Time/fast_forward_speed: {self.fast}")
96
92
 
@@ -172,25 +168,27 @@ def read(self):
172
168
 
173
169
  # check for new version
174
170
  self.checkForNewVersion = False
175
- try:
176
- if settings.value("check_for_new_version") is None:
177
- self.checkForNewVersion = (
178
- dialog.MessageDialog(
179
- cfg.programName,
180
- (
181
- "Allow BORIS to automatically check for new version and news?\n"
182
- "(An internet connection is required)\n"
183
- "You can change this option in the Preferences (File > Preferences)"
184
- ),
185
- [cfg.YES, cfg.NO],
171
+
172
+ if not self.no_first_launch_dialog:
173
+ try:
174
+ if settings.value("check_for_new_version") is None:
175
+ self.checkForNewVersion = (
176
+ dialog.MessageDialog(
177
+ cfg.programName,
178
+ (
179
+ "Allow BORIS to automatically check for new version and news?\n"
180
+ "(An internet connection is required)\n"
181
+ "You can change this option in the Preferences (File > Preferences)"
182
+ ),
183
+ [cfg.YES, cfg.NO],
184
+ )
185
+ == cfg.YES
186
186
  )
187
- == cfg.YES
188
- )
189
- else:
190
- self.checkForNewVersion = settings.value("check_for_new_version") == "true"
191
- except Exception:
192
- self.checkForNewVersion = False
193
- logging.debug(f"check_for_new_version: {self.checkForNewVersion}")
187
+ else:
188
+ self.checkForNewVersion = settings.value("check_for_new_version") == "true"
189
+ except Exception:
190
+ self.checkForNewVersion = False
191
+ logging.debug(f"Automatic check for new version: {self.checkForNewVersion}")
194
192
 
195
193
  # pause before add event
196
194
  self.pause_before_addevent = False
@@ -203,11 +201,11 @@ def read(self):
203
201
 
204
202
  if self.checkForNewVersion:
205
203
  if settings.value("last_check_for_new_version") and (
206
- int(time.mktime(time.localtime())) - int(settings.value("last_check_for_new_version"))
207
- > cfg.CHECK_NEW_VERSION_DELAY
204
+ int(time.mktime(time.localtime())) - int(settings.value("last_check_for_new_version")) > cfg.CHECK_NEW_VERSION_DELAY
208
205
  ):
209
206
  self.actionCheckUpdate_activated(flagMsgOnlyIfNew=True)
210
- logging.debug(f"last_check_for_new_version: {settings.value('last_check_for_new_version')}")
207
+
208
+ logging.debug(f"last check for new version: {settings.value('last_check_for_new_version')}")
211
209
 
212
210
  self.ffmpeg_cache_dir = ""
213
211
  try:
@@ -218,15 +216,6 @@ def read(self):
218
216
  self.ffmpeg_cache_dir = ""
219
217
  logging.debug(f"ffmpeg_cache_dir: {self.ffmpeg_cache_dir}")
220
218
 
221
- try:
222
- self.frame_bitmap_format = settings.value("frame_bitmap_format")
223
- if not self.frame_bitmap_format:
224
- self.frame_bitmap_format = cfg.FRAME_DEFAULT_BITMAP_FORMAT
225
- except Exception:
226
- self.frame_bitmap_format = cfg.FRAME_DEFAULT_BITMAP_FORMAT
227
-
228
- logging.debug(f"frame_bitmap_format: {self.frame_bitmap_format}")
229
-
230
219
  # spectrogram
231
220
  self.spectrogramHeight = 80
232
221
 
@@ -254,10 +243,7 @@ def read(self):
254
243
  if (
255
244
  dialog.MessageDialog(
256
245
  cfg.programName,
257
- (
258
- "The colors list contain colors that are very light.\n"
259
- "Do you want to reload the default colors list?"
260
- ),
246
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
261
247
  [cfg.NO, cfg.YES],
262
248
  )
263
249
  == cfg.YES
@@ -270,18 +256,11 @@ def read(self):
270
256
  except Exception:
271
257
  self.behav_category_colors = cfg.CATEGORY_COLORS_LIST
272
258
 
273
- if (
274
- "white" in self.behav_category_colors
275
- or "azure" in self.behav_category_colors
276
- or "snow" in self.behav_category_colors
277
- ):
259
+ if "white" in self.behav_category_colors or "azure" in self.behav_category_colors or "snow" in self.behav_category_colors:
278
260
  if (
279
261
  dialog.MessageDialog(
280
262
  cfg.programName,
281
- (
282
- "The colors list contain colors that are very light.\n"
283
- "Do you want to reload the default colors list?"
284
- ),
263
+ ("The colors list contain colors that are very light.\nDo you want to reload the default colors list?"),
285
264
  [cfg.NO, cfg.YES],
286
265
  )
287
266
  == cfg.YES
@@ -291,25 +270,28 @@ def read(self):
291
270
  else: # no .boris file found
292
271
  logging.info("No config file found")
293
272
  # ask user for checking for new version
294
- self.checkForNewVersion = (
295
- dialog.MessageDialog(
296
- cfg.programName,
297
- (
298
- "Allow BORIS to automatically check for new version?\n"
299
- "(An internet connection is required)\n"
300
- "You can change this option in the"
301
- " Preferences (File > Preferences)"
302
- ),
303
- [cfg.NO, cfg.YES],
273
+ if not self.no_first_launch_dialog:
274
+ self.checkForNewVersion = (
275
+ dialog.MessageDialog(
276
+ cfg.programName,
277
+ (
278
+ "Allow BORIS to automatically check for new version?\n"
279
+ "(An internet connection is required)\n"
280
+ "You can change this option in the"
281
+ " Preferences (File > Preferences)"
282
+ ),
283
+ [cfg.NO, cfg.YES],
284
+ )
285
+ == cfg.YES
304
286
  )
305
- == cfg.YES
306
- )
287
+ else:
288
+ self.checkForNewVersion = False
307
289
 
308
290
  # recent projects
309
- logging.info("read recent projects")
310
- iniFilePath = str(pl.Path.home() / ".boris_recent_projects")
311
- if os.path.isfile(iniFilePath):
312
- settings = QSettings(iniFilePath, QSettings.IniFormat)
291
+ logging.debug("read recent projects")
292
+ recent_projects_file_path = pl.Path.home() / ".boris_recent_projects"
293
+ if recent_projects_file_path.is_file():
294
+ settings = QSettings(str(recent_projects_file_path), QSettings.IniFormat)
313
295
  try:
314
296
  self.recent_projects = settings.value("recent_projects").split("|||")
315
297
  while "" in self.recent_projects:
@@ -323,14 +305,14 @@ def read(self):
323
305
 
324
306
  def save(self, lastCheckForNewVersion=0):
325
307
  """
326
- save config file
308
+ save config file in $HOME/.boris
327
309
  """
328
310
 
329
- iniFilePath = pl.Path.home() / pl.Path(".boris")
311
+ file_path = pl.Path.home() / pl.Path(".boris")
330
312
 
331
- logging.debug(f"save config file: {iniFilePath}")
313
+ logging.debug(f"save config file: {file_path}")
332
314
 
333
- settings = QSettings(str(iniFilePath), QSettings.IniFormat)
315
+ settings = QSettings(str(file_path), QSettings.IniFormat)
334
316
 
335
317
  settings.setValue("config", self.config_param)
336
318
 
@@ -343,7 +325,6 @@ def save(self, lastCheckForNewVersion=0):
343
325
  settings.setValue("Time/Repositioning_time_offset", self.repositioningTimeOffset)
344
326
  settings.setValue("Time/fast_forward_speed", self.fast)
345
327
  settings.setValue("Time/play_rate_step", self.play_rate_step)
346
- """settings.setValue("Save_media_file_path", self.saveMediaFilePath)"""
347
328
  settings.setValue("Automatic_backup", self.automaticBackup)
348
329
  settings.setValue("behavioural_strings_separator", self.behav_seq_separator)
349
330
  settings.setValue("close_the_same_current_event", self.close_the_same_current_event)
@@ -360,7 +341,6 @@ def save(self, lastCheckForNewVersion=0):
360
341
 
361
342
  # FFmpeg
362
343
  settings.setValue("ffmpeg_cache_dir", self.ffmpeg_cache_dir)
363
- settings.setValue("frame_bitmap_format", self.frame_bitmap_format)
364
344
  # spectrogram
365
345
  settings.setValue("spectrogram_color_map", self.spectrogram_color_map)
366
346
  settings.setValue("spectrogram_time_interval", self.spectrogram_time_interval)
@@ -370,7 +350,7 @@ def save(self, lastCheckForNewVersion=0):
370
350
  settings.setValue("behav_category_colors", "|".join(self.behav_category_colors))
371
351
 
372
352
  # recent projects
373
- logging.debug("save recent projects")
353
+ logging.debug("Save recent projects")
374
354
 
375
355
  settings = QSettings(str(pl.Path.home() / ".boris_recent_projects"), QSettings.IniFormat)
376
356
  settings.setValue("recent_projects", "|||".join(self.recent_projects))
boris/connections.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 program is free software; you can redistribute it and/or modify
7
7
  it under the terms of the GNU General Public License as published by
@@ -19,8 +19,8 @@ Copyright 2012-2023 Olivier Friard
19
19
  MA 02110-1301, USA.
20
20
  """
21
21
 
22
- from PyQt5.QtCore import Qt, QTimer
23
- from PyQt5.QtWidgets import QAction
22
+ from PySide6.QtCore import Qt, QTimer
23
+ from PySide6.QtGui import QAction
24
24
 
25
25
  from . import config as cfg
26
26
  from . import (
@@ -37,11 +37,11 @@ from . import (
37
37
  import_observations,
38
38
  irr,
39
39
  latency,
40
+ cooccurence,
40
41
  media_file,
41
42
  observation_operations,
42
43
  preferences,
43
44
  project_import_export,
44
- project_server,
45
45
  synthetic_time_budget,
46
46
  time_budget_widget,
47
47
  transitions,
@@ -70,16 +70,12 @@ def connections(self):
70
70
  self.actionExport_project.triggered.connect(lambda: project_import_export.export_project_as_pickle_object(self.pj))
71
71
  self.actionClose_project.triggered.connect(self.close_project)
72
72
 
73
- self.action_media_file_and_images_directories_relative_path.triggered.connect(
74
- self.set_media_files_path_relative_to_project_dir
75
- )
73
+ self.action_media_file_and_images_directories_relative_path.triggered.connect(self.set_media_files_path_relative_to_project_dir)
76
74
  self.action_data_files_relative_path.triggered.connect(self.set_data_files_path_relative_to_project_dir)
77
75
 
78
76
  self.action_remove_media_files_and_images_directories_path.triggered.connect(self.remove_media_files_path)
79
77
  self.action_remove_data_files_path.triggered.connect(self.remove_data_files_path)
80
78
 
81
- self.actionSend_project.triggered.connect(lambda: project_server.send_project_via_socket(self))
82
-
83
79
  self.menuCreate_subtitles_2.triggered.connect(self.create_subtitles)
84
80
 
85
81
  self.actionPreferences.triggered.connect(lambda: preferences.preferences(self))
@@ -87,19 +83,16 @@ def connections(self):
87
83
  self.actionQuit.triggered.connect(self.actionQuit_activated)
88
84
 
89
85
  # menu observations
90
- self.actionNew_observation.triggered.connect(
91
- lambda: observation_operations.new_observation(self, mode=cfg.NEW, obsId="")
92
- )
86
+ self.actionNew_observation.triggered.connect(lambda: observation_operations.new_observation(self, mode=cfg.NEW, obsId=""))
93
87
 
94
- self.actionOpen_observation.triggered.connect(
95
- lambda: observation_operations.open_observation(self, mode=cfg.OBS_START)
96
- )
88
+ self.actionOpen_observation.triggered.connect(lambda: observation_operations.open_observation(self, mode=cfg.OBS_START))
97
89
  self.actionView_observation.triggered.connect(lambda: observation_operations.open_observation(self, mode=cfg.VIEW))
98
90
  self.actionEdit_observation_2.triggered.connect(lambda: observation_operations.edit_observation(self))
99
91
  self.actionObservationsList.triggered.connect(lambda: observation_operations.observations_list(self))
100
92
 
101
93
  self.actionClose_observation.triggered.connect(lambda: observation_operations.close_observation(self))
102
94
 
95
+ self.action_create_observations.triggered.connect(lambda: observation_operations.create_observations(self))
103
96
  self.actionRemove_observations.triggered.connect(lambda: observation_operations.remove_observations(self))
104
97
 
105
98
  self.actionAdd_event.triggered.connect(lambda: event_operations.add_event(self))
@@ -109,20 +102,25 @@ def connections(self):
109
102
  self.actionFilter_events.triggered.connect(lambda: event_operations.filter_events(self))
110
103
  self.actionShow_all_events.triggered.connect(lambda: event_operations.show_all_events(self))
111
104
 
112
- self.actionExport_observations_list.triggered.connect(
113
- lambda: observation_operations.export_observations_list_clicked(self)
114
- )
105
+ # twevent header
106
+ # self.actionConfigure_twEvents_columns.triggered.connect(self.configure_twevents_columns)
107
+
108
+ # tv_events header
109
+ self.actionConfigure_tvevents_columns.triggered.connect(self.configure_tvevents_columns)
110
+
111
+ self.actionExport_observations_list.triggered.connect(lambda: observation_operations.export_observations_list_clicked(self))
115
112
 
116
113
  self.actionCheckStateEvents.triggered.connect(lambda: state_events.check_state_events(self, mode="all"))
117
- self.actionCheckStateEventsSingleObs.triggered.connect(
118
- lambda: state_events.check_state_events(self, mode="current")
119
- )
114
+ self.actionCheckStateEventsSingleObs.triggered.connect(lambda: state_events.check_state_events(self, mode="current"))
120
115
  self.actionClose_unpaired_events.triggered.connect(lambda: state_events.fix_unpaired_events(self))
116
+ self.actionAdd_frame_indexes.triggered.connect(lambda: event_operations.add_frame_indexes(self))
117
+
121
118
  self.actionRunEventOutside.triggered.connect(self.run_event_outside)
122
119
 
123
120
  self.actionSelect_observations.triggered.connect(lambda: event_operations.select_events_between_activated(self))
124
121
 
125
122
  self.actionEdit_selected_events.triggered.connect(lambda: event_operations.edit_selected_events(self))
123
+ self.action_add_comment.triggered.connect(lambda: event_operations.add_comment(self))
126
124
  self.actionEdit_event_time.triggered.connect(lambda: event_operations.edit_time_selected_events(self))
127
125
 
128
126
  self.actionCopy_events.triggered.connect(lambda: event_operations.copy_selected_events(self))
@@ -130,7 +128,7 @@ def connections(self):
130
128
 
131
129
  self.actionExplore_project.triggered.connect(lambda: project_functions.explore_project(self))
132
130
  self.actionFind_events.triggered.connect(lambda: event_operations.find_events(self))
133
- self.actionFind_replace_events.triggered.connect(self.find_replace_events)
131
+ self.actionFind_replace_events.triggered.connect(lambda: event_operations.find_replace_events(self))
134
132
  self.actionDelete_all_events.triggered.connect(lambda: event_operations.delete_all_events(self))
135
133
  self.actionDelete_selected_events.triggered.connect(lambda: event_operations.delete_selected_events(self))
136
134
 
@@ -163,12 +161,16 @@ def connections(self):
163
161
  self.actionNumber_of_transitions.triggered.connect(lambda: transitions.transitions_matrix(self, "number"))
164
162
 
165
163
  self.actionFrequencies_of_transitions_after_behaviors.triggered.connect(
166
- lambda: self.transitions_matrix("frequencies_after_behaviors")
164
+ lambda: transitions.transitions_matrix(self, "frequencies_after_behaviors")
167
165
  )
168
166
 
169
167
  # menu playback
170
168
  self.actionJumpTo.triggered.connect(self.jump_to)
169
+ self.action_deinterlace.triggered.connect(lambda: video_operations.deinterlace(self))
170
+ self.action_change_time_offset_of_players.triggered.connect(lambda: video_operations.change_player_offset(self))
171
171
  self.actionZoom_level.triggered.connect(lambda: video_operations.zoom_level(self))
172
+ self.actionRotate_current_video.triggered.connect(lambda: video_operations.rotate_displayed_video(self))
173
+
172
174
  self.actionDisplay_subtitles.triggered.connect(lambda: video_operations.display_subtitles(self))
173
175
  self.actionVideo_equalizer.triggered.connect(lambda: video_equalizer.video_equalizer_show(self))
174
176
 
@@ -176,9 +178,7 @@ def connections(self):
176
178
  self.action_block_dockwidgets.triggered.connect(self.block_dockwidgets)
177
179
 
178
180
  self.action_create_modifiers_coding_map.triggered.connect(self.modifiers_coding_map_creator)
179
- self.action_create_behaviors_coding_map.triggered.connect(
180
- lambda: behav_coding_map_creator.behaviors_coding_map_creator(self)
181
- )
181
+ self.action_create_behaviors_coding_map.triggered.connect(lambda: behav_coding_map_creator.behaviors_coding_map_creator(self))
182
182
 
183
183
  self.actionShow_spectrogram.triggered.connect(lambda: self.show_plot_widget("spectrogram", warning=True))
184
184
  self.actionShow_the_sound_waveform.triggered.connect(lambda: self.show_plot_widget("waveform", warning=True))
@@ -186,6 +186,7 @@ def connections(self):
186
186
 
187
187
  self.actionShow_data_files.triggered.connect(self.show_data_files)
188
188
  self.action_geometric_measurements.triggered.connect(lambda: geometric_measurement.show_widget(self))
189
+
189
190
  self.actionBehaviors_coding_map.triggered.connect(lambda: behaviors_coding_map.show_behaviors_coding_map(self))
190
191
 
191
192
  self.actionCoding_pad.triggered.connect(lambda: coding_pad.show_coding_pad(self))
@@ -195,24 +196,22 @@ def connections(self):
195
196
  self.actionAdd_image_overlay_on_video.triggered.connect(lambda: image_overlay.add_image_overlay(self))
196
197
  self.actionRemove_image_overlay.triggered.connect(lambda: image_overlay.remove_image_overlay(self))
197
198
 
199
+ self.actionMedia_file_information_2.triggered.connect(lambda: media_file.get_info(self))
198
200
  self.actionRecode_resize_video.triggered.connect(lambda: external_processes.ffmpeg_process(self, "reencode_resize"))
199
201
  self.actionRotate_video.triggered.connect(lambda: external_processes.ffmpeg_process(self, "rotate"))
200
202
  self.actionMerge_media_files.triggered.connect(lambda: external_processes.ffmpeg_process(self, "merge"))
201
- self.actionMedia_file_information_2.triggered.connect(lambda: media_file.get_info(self))
203
+ self.actionCreate_video_spectrogram.triggered.connect(lambda: external_processes.ffmpeg_process(self, "video_spectrogram"))
202
204
 
203
205
  self.actionCreate_transitions_flow_diagram.triggered.connect(transitions.transitions_dot_script)
204
206
  self.actionCreate_transitions_flow_diagram_2.triggered.connect(transitions.transitions_flow_diagram)
205
207
 
206
208
  # menu Analysis
209
+
207
210
  self.actionTime_budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior"))
208
- self.actionTime_budget_by_behaviors_category.triggered.connect(
209
- lambda: time_budget_widget.time_budget(self, mode="by_category")
210
- )
211
+ self.actionTime_budget_by_behaviors_category.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_category"))
211
212
 
212
213
  self.actionTime_budget_report.triggered.connect(lambda: synthetic_time_budget.synthetic_time_budget(self))
213
- self.actionSynthetic_binned_time_budget.triggered.connect(
214
- lambda: synthetic_time_budget.synthetic_binned_time_budget(self)
215
- )
214
+ self.actionSynthetic_binned_time_budget.triggered.connect(lambda: synthetic_time_budget.synthetic_binned_time_budget(self))
216
215
 
217
216
  self.actionBehavior_bar_plot.triggered.connect(lambda: self.behaviors_bar_plot(mode="list"))
218
217
  self.actionBehavior_bar_plot.setVisible(True)
@@ -226,6 +225,8 @@ def connections(self):
226
225
 
227
226
  self.action_latency.triggered.connect(lambda: latency.get_latency(self))
228
227
 
228
+ self.action_cooccurence.triggered.connect(lambda: cooccurence.get_cooccurence(self))
229
+
229
230
  # menu Help
230
231
  self.actionUser_guide.triggered.connect(self.actionUser_guide_triggered)
231
232
  self.actionAbout.triggered.connect(lambda: about.actionAbout_activated(self))
@@ -251,9 +252,7 @@ def connections(self):
251
252
  self.actionFrame_forward.triggered.connect(self.next_frame)
252
253
 
253
254
  self.actionCloseObs.triggered.connect(lambda: observation_operations.close_observation(self))
254
- self.actionCurrent_Time_Budget.triggered.connect(
255
- lambda: time_budget_widget.time_budget(self, mode="by_behavior", mode2="current")
256
- )
255
+ self.actionCurrent_Time_Budget.triggered.connect(lambda: time_budget_widget.time_budget(self, mode="by_behavior", mode2="current"))
257
256
  self.actionPlot_current_observation.triggered.connect(lambda: self.plot_events_triggered(mode="current"))
258
257
 
259
258
  self.actionPlot_current_time_budget.triggered.connect(lambda: self.behaviors_bar_plot(mode="current"))
@@ -261,10 +260,13 @@ def connections(self):
261
260
  self.actionFind_in_current_obs.triggered.connect(lambda: event_operations.find_events(self))
262
261
 
263
262
  # table Widget double click
264
- self.twEvents.itemDoubleClicked.connect(self.twEvents_doubleClicked)
263
+ # self.twEvents.itemDoubleClicked.connect(self.twEvents_doubleClicked)
265
264
  self.twEthogram.itemDoubleClicked.connect(self.twEthogram_doubleClicked)
266
265
  self.twSubjects.itemDoubleClicked.connect(self.twSubjects_doubleClicked)
267
266
 
267
+ # events tableview
268
+ self.tv_events.doubleClicked.connect(self.tv_events_doubleClicked)
269
+
268
270
  # Actions for twEthogram context menu
269
271
  self.twEthogram.setContextMenuPolicy(Qt.ActionsContextMenu)
270
272
  self.twEthogram.horizontalHeader().sortIndicatorChanged.connect(self.twEthogram_sorted)
@@ -272,7 +274,9 @@ def connections(self):
272
274
  self.actionViewBehavior.triggered.connect(self.view_behavior)
273
275
  self.twEthogram.addAction(self.actionViewBehavior)
274
276
 
275
- self.actionFilterBehaviors.triggered.connect(lambda: self.filter_behaviors(table=cfg.ETHOGRAM))
277
+ self.actionFilterBehaviors.triggered.connect(
278
+ lambda: self.filter_behaviors(table=cfg.ETHOGRAM, behavior_type=cfg.STATE_EVENT_TYPES + cfg.POINT_EVENT_TYPES)
279
+ )
276
280
  self.twEthogram.addAction(self.actionFilterBehaviors)
277
281
 
278
282
  self.actionShowAllBehaviors.triggered.connect(self.show_all_behaviors)
@@ -287,45 +291,96 @@ def connections(self):
287
291
  self.actionShowAllSubjects.triggered.connect(self.show_all_subjects)
288
292
  self.twSubjects.addAction(self.actionShowAllSubjects)
289
293
 
294
+ # actions for twEvents horizontal header menu
295
+ # tw_headers = self.twEvents.horizontalHeader()
296
+ # tw_headers.setContextMenuPolicy(Qt.ActionsContextMenu)
297
+ # tw_headers.addAction(self.actionConfigure_twEvents_columns)
298
+
299
+ tv_headers = self.tv_events.horizontalHeader()
300
+ tv_headers.setContextMenuPolicy(Qt.ActionsContextMenu)
301
+ tv_headers.addAction(self.actionConfigure_tvevents_columns)
302
+
290
303
  # Actions for twEvents menu
291
- self.twEvents.setContextMenuPolicy(Qt.ActionsContextMenu)
304
+ # self.twEvents.setContextMenuPolicy(Qt.ActionsContextMenu)
305
+
306
+ # self.twEvents.addAction(self.actionAdd_event)
307
+ # self.twEvents.addAction(self.actionEdit_selected_events)
308
+ # self.twEvents.addAction(self.actionEdit_event_time)
309
+
310
+ # self.twEvents.addAction(self.actionCopy_events)
311
+ # self.twEvents.addAction(self.actionPaste_events)
312
+
313
+ # separator2 = QAction(self)
314
+ # separator2.setSeparator(True)
315
+ # self.twEvents.addAction(separator2)
316
+
317
+ # self.twEvents.addAction(self.actionFind_events)
318
+ # self.twEvents.addAction(self.actionFind_replace_events)
319
+
320
+ # separator2 = QAction(self)
321
+ # separator2.setSeparator(True)
322
+ # self.twEvents.addAction(separator2)
292
323
 
293
- self.twEvents.addAction(self.actionAdd_event)
294
- self.twEvents.addAction(self.actionEdit_selected_events)
295
- self.twEvents.addAction(self.actionEdit_event_time)
324
+ # self.twEvents.addAction(self.actionFilter_events)
325
+ # self.twEvents.addAction(self.actionShow_all_events)
296
326
 
297
- self.twEvents.addAction(self.actionCopy_events)
298
- self.twEvents.addAction(self.actionPaste_events)
327
+ # separator2 = QAction(self)
328
+ # separator2.setSeparator(True)
329
+ # self.twEvents.addAction(separator2)
330
+
331
+ # self.twEvents.addAction(self.actionCheckStateEventsSingleObs)
332
+ # self.twEvents.addAction(self.actionClose_unpaired_events)
333
+
334
+ # self.twEvents.addAction(self.actionRunEventOutside)
335
+
336
+ # separator2 = QAction(self)
337
+ # separator2.setSeparator(True)
338
+ # self.twEvents.addAction(separator2)
339
+
340
+ # self.twEvents.addAction(self.actionDelete_selected_events)
341
+
342
+ # Actions for tv_events menu
343
+ self.tv_events.setContextMenuPolicy(Qt.ActionsContextMenu)
344
+
345
+ self.tv_events.addAction(self.actionAdd_event)
346
+ self.tv_events.addAction(self.actionEdit_selected_events)
347
+ self.tv_events.addAction(self.action_add_comment)
348
+
349
+ self.tv_events.addAction(self.actionEdit_event_time)
350
+
351
+ self.tv_events.addAction(self.actionCopy_events)
352
+ self.tv_events.addAction(self.actionPaste_events)
299
353
 
300
354
  separator2 = QAction(self)
301
355
  separator2.setSeparator(True)
302
- self.twEvents.addAction(separator2)
356
+ self.tv_events.addAction(separator2)
303
357
 
304
- self.twEvents.addAction(self.actionFind_events)
305
- self.twEvents.addAction(self.actionFind_replace_events)
358
+ self.tv_events.addAction(self.actionFind_events)
359
+ self.tv_events.addAction(self.actionFind_replace_events)
306
360
 
307
361
  separator2 = QAction(self)
308
362
  separator2.setSeparator(True)
309
- self.twEvents.addAction(separator2)
363
+ self.tv_events.addAction(separator2)
310
364
 
311
- self.twEvents.addAction(self.actionFilter_events)
312
- self.twEvents.addAction(self.actionShow_all_events)
365
+ self.tv_events.addAction(self.actionFilter_events)
366
+ self.tv_events.addAction(self.actionShow_all_events)
313
367
 
314
368
  separator2 = QAction(self)
315
369
  separator2.setSeparator(True)
316
- self.twEvents.addAction(separator2)
370
+ self.tv_events.addAction(separator2)
371
+
372
+ self.tv_events.addAction(self.actionCheckStateEventsSingleObs)
373
+ self.tv_events.addAction(self.actionClose_unpaired_events)
317
374
 
318
- self.twEvents.addAction(self.actionCheckStateEventsSingleObs)
319
- self.twEvents.addAction(self.actionClose_unpaired_events)
375
+ self.tv_events.addAction(self.actionAdd_frame_indexes)
320
376
 
321
- self.twEvents.addAction(self.actionRunEventOutside)
377
+ self.tv_events.addAction(self.actionRunEventOutside)
322
378
 
323
379
  separator2 = QAction(self)
324
380
  separator2.setSeparator(True)
325
- self.twEvents.addAction(separator2)
381
+ self.tv_events.addAction(separator2)
326
382
 
327
- self.twEvents.addAction(self.actionDelete_selected_events)
328
- # self.twEvents.addAction(self.actionDelete_all_events)
383
+ self.tv_events.addAction(self.actionDelete_selected_events)
329
384
 
330
385
  # Actions for twSubjects context menu
331
386
  self.actionDeselectCurrentSubject.triggered.connect(lambda: self.update_subject(""))