boris-behav-obs 8.16.6__py3-none-any.whl → 9.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +24 -40
  4. boris/add_modifier.py +88 -80
  5. boris/add_modifier_ui.py +235 -131
  6. boris/advanced_event_filtering.py +23 -29
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +16 -34
  23. boris/config.py +101 -49
  24. boris/config_file.py +55 -64
  25. boris/connections.py +105 -58
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2108 -1275
  30. boris/core_qrc.py +15892 -10829
  31. boris/core_ui.py +941 -806
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +134 -0
  34. boris/dialog.py +461 -242
  35. boris/duration_widget.py +9 -14
  36. boris/edit_event.py +61 -31
  37. boris/edit_event_ui.py +208 -97
  38. boris/event_operations.py +405 -281
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +180 -203
  43. boris/export_observation.py +60 -73
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +427 -218
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +4 -4
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +304 -0
  50. boris/irr.py +20 -57
  51. boris/latency.py +31 -24
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +17 -19
  54. boris/menu_options.py +16 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv2.py +127 -36
  58. boris/observation.py +493 -210
  59. boris/observation_operations.py +1010 -391
  60. boris/observation_ui.py +573 -363
  61. boris/observations_list.py +51 -58
  62. boris/otx_parser.py +74 -68
  63. boris/param_panel.py +45 -59
  64. boris/param_panel_ui.py +254 -138
  65. boris/player_dock_widget.py +91 -56
  66. boris/plot_data_module.py +18 -53
  67. boris/plot_events.py +56 -153
  68. boris/plot_events_rt.py +16 -30
  69. boris/plot_spectrogram_rt.py +80 -56
  70. boris/plot_waveform_rt.py +23 -48
  71. boris/plugins.py +431 -0
  72. boris/portion/__init__.py +18 -8
  73. boris/portion/const.py +35 -18
  74. boris/portion/dict.py +5 -5
  75. boris/portion/func.py +2 -2
  76. boris/portion/interval.py +21 -41
  77. boris/portion/io.py +41 -32
  78. boris/preferences.py +298 -123
  79. boris/preferences_ui.py +664 -225
  80. boris/project.py +293 -270
  81. boris/project_functions.py +610 -537
  82. boris/project_import_export.py +204 -213
  83. boris/project_ui.py +673 -441
  84. boris/qrc_boris.py +6 -3
  85. boris/qrc_boris5.py +6 -3
  86. boris/select_modifiers.py +62 -90
  87. boris/select_observations.py +19 -197
  88. boris/select_subj_behav.py +67 -39
  89. boris/state_events.py +51 -33
  90. boris/subjects_pad.py +6 -8
  91. boris/synthetic_time_budget.py +25 -17
  92. boris/time_budget_functions.py +169 -169
  93. boris/time_budget_widget.py +71 -86
  94. boris/transitions.py +41 -41
  95. boris/utilities.py +562 -222
  96. boris/version.py +3 -3
  97. boris/video_equalizer.py +16 -14
  98. boris/video_equalizer_ui.py +199 -130
  99. boris/video_operations.py +78 -28
  100. boris/view_df.py +104 -0
  101. boris/view_df_ui.py +75 -0
  102. boris/write_event.py +240 -136
  103. boris_behav_obs-9.7.1.dist-info/METADATA +140 -0
  104. boris_behav_obs-9.7.1.dist-info/RECORD +109 -0
  105. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.1.dist-info}/WHEEL +1 -1
  106. boris_behav_obs-9.7.1.dist-info/entry_points.txt +2 -0
  107. boris/README.TXT +0 -22
  108. boris/add_modifier.ui +0 -323
  109. boris/converters.ui +0 -289
  110. boris/core.qrc +0 -37
  111. boris/core.ui +0 -1571
  112. boris/edit_event.ui +0 -233
  113. boris/icons/logo_eye.ico +0 -0
  114. boris/map_creator.py +0 -982
  115. boris/observation.ui +0 -814
  116. boris/param_panel.ui +0 -379
  117. boris/preferences.ui +0 -537
  118. boris/project.ui +0 -1074
  119. boris/vlc_local.py +0 -90
  120. boris_behav_obs-8.16.6.dist-info/LICENSE.TXT +0 -674
  121. boris_behav_obs-8.16.6.dist-info/METADATA +0 -134
  122. boris_behav_obs-8.16.6.dist-info/RECORD +0 -106
  123. boris_behav_obs-8.16.6.dist-info/entry_points.txt +0 -2
  124. {boris → boris_behav_obs-9.7.1.dist-info/licenses}/LICENSE.TXT +0 -0
  125. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.1.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,7 @@
1
- #!/usr/bin/env python3
2
1
  """
3
2
  BORIS
4
3
  Behavioral Observation Research Interactive Software
5
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
6
5
 
7
6
  This file is part of BORIS.
8
7
 
@@ -23,10 +22,10 @@ This file is part of BORIS.
23
22
 
24
23
  import logging
25
24
 
26
- from PyQt5.QtCore import pyqtSignal
25
+ from PySide6.QtCore import Signal
27
26
 
28
- # from PyQt5.QtGui import *
29
- from PyQt5.QtWidgets import (
27
+ # from PySide6.QtGui import *
28
+ from PySide6.QtWidgets import (
30
29
  QApplication,
31
30
  QWidget,
32
31
  QRadioButton,
@@ -40,14 +39,14 @@ from PyQt5.QtWidgets import (
40
39
  QFileDialog,
41
40
  QMessageBox,
42
41
  )
43
- from boris import dialog
44
- from boris.config import YES, NO, CANCEL, programName
42
+ from . import dialog
43
+ from . import config as cfg
45
44
 
46
45
 
47
46
  class wgMeasurement(QWidget):
48
47
  """ """
49
48
 
50
- closeSignal, clearSignal = pyqtSignal(), pyqtSignal()
49
+ closeSignal, clearSignal = Signal(), Signal()
51
50
  flagSaved = True
52
51
  draw_mem = []
53
52
 
@@ -112,7 +111,7 @@ class wgMeasurement(QWidget):
112
111
  self.pbSave = QPushButton("Save results", clicked=self.pbSave_clicked)
113
112
  hbox3.addWidget(self.pbSave)
114
113
 
115
- self.pbClose = QPushButton("Close", clicked=self.pbClose_clicked)
114
+ self.pbClose = QPushButton(cfg.CLOSE, clicked=self.pbClose_clicked)
116
115
  hbox3.addWidget(self.pbClose)
117
116
 
118
117
  vbox.addLayout(hbox3)
@@ -128,13 +127,13 @@ class wgMeasurement(QWidget):
128
127
  def pbClose_clicked(self):
129
128
  if not self.flagSaved:
130
129
  response = dialog.MessageDialog(
131
- programName,
130
+ cfg.programName,
132
131
  "The current results are not saved. Do you want to save results before closing?",
133
- [YES, NO, CANCEL],
132
+ [cfg.YES, cfg.NO, cfg.CANCEL],
134
133
  )
135
- if response == YES:
134
+ if response == cfg.YES:
136
135
  self.pbSave_clicked()
137
- if response == CANCEL:
136
+ if response == cfg.CANCEL:
138
137
  return
139
138
  self.closeSignal.emit()
140
139
 
@@ -143,19 +142,16 @@ class wgMeasurement(QWidget):
143
142
  save results
144
143
  """
145
144
  if self.pte.toPlainText():
146
- fileName, _ = QFileDialog().getSaveFileName(
147
- self, "Save measurement results", "", "Text files (*.txt);;All files (*)"
148
- )
145
+ fileName, _ = QFileDialog().getSaveFileName(self, "Save measurement results", "", "Text files (*.txt);;All files (*)")
149
146
  if fileName:
150
147
  with open(fileName, "w") as f:
151
148
  f.write(self.pte.toPlainText())
152
149
  self.flagSaved = True
153
150
  else:
154
- QMessageBox.information(self, programName, "There are no results to save")
151
+ QMessageBox.information(self, cfg.programName, "There are no results to save")
155
152
 
156
153
 
157
154
  if __name__ == "__main__":
158
-
159
155
  import sys
160
156
 
161
157
  app = QApplication(sys.argv)
boris/media_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
 
@@ -20,13 +20,13 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- import logging
23
+ from PySide6.QtWidgets import QFileDialog
24
24
 
25
25
  from . import config as cfg
26
26
  from . import utilities as util
27
27
  from . import dialog
28
28
  from . import project_functions
29
- from PyQt5.QtWidgets import QFileDialog
29
+ from . import utilities as util
30
30
 
31
31
 
32
32
  def get_info(self) -> None:
@@ -34,22 +34,23 @@ def get_info(self) -> None:
34
34
  show info about media file (current media file if an observation is opened)
35
35
  """
36
36
 
37
- def media_analysis_str(ffmpeg_bin, media_full_path):
38
-
37
+ def media_analysis_str(ffmpeg_bin: str, media_full_path: str) -> str:
39
38
  r = util.accurate_media_analysis(ffmpeg_bin, media_full_path)
40
39
 
41
40
  if "error" in r:
42
41
  ffmpeg_output = f"File path: {media_full_path}<br><br>{r['error']}<br><br>"
43
42
  else:
44
- ffmpeg_output = f"<br><b>{r['analysis_program'] } analysis</b><br>"
43
+ ffmpeg_output = f"<br><b>{r['analysis_program']} analysis</b><br>"
45
44
 
46
45
  ffmpeg_output += (
47
46
  f"File path: <b>{media_full_path}</b><br><br>"
48
47
  f"Duration: {r['duration']} seconds ({util.convertTime(self.timeFormat, r['duration'])})<br>"
49
- f"Resolution: {r['resolution']}<br>"
48
+ f"FPS: {r['fps']}<br>"
49
+ f"Resolution: {r['resolution']} pixels<br>"
50
+ f"Format long name: {r.get('format_long_name', cfg.NA)}<br>"
51
+ f"Creation time: {r.get('creation_time', cfg.NA)}<br>"
50
52
  f"Number of frames: {r['frames_number']}<br>"
51
53
  f"Bitrate: {util.smart_size_format(r['bitrate'])} <br>"
52
- f"FPS: {r['fps']}<br>"
53
54
  f"Has video: {r['has_video']}<br>"
54
55
  f"Has audio: {r['has_audio']}<br>"
55
56
  f"File size: {util.smart_size_format(r.get('file size', cfg.NA))}<br>"
@@ -60,7 +61,6 @@ def get_info(self) -> None:
60
61
  return ffmpeg_output
61
62
 
62
63
  if self.observationId and self.playerType == cfg.MEDIA:
63
-
64
64
  tot_output: str = ""
65
65
 
66
66
  for i, dw in enumerate(self.dw_player):
@@ -72,6 +72,12 @@ def get_info(self) -> None:
72
72
 
73
73
  mpv_output = (
74
74
  "<b>MPV information</b><br>"
75
+ f"Duration: {dw.player.duration} seconds ({util.seconds2time(dw.player.duration)})<br>"
76
+ # "Position: {} %<br>"
77
+ f"FPS: {dw.player.container_fps}<br>"
78
+ # "Rate: {}<br>"
79
+ f"Resolution: {dw.player.width}x{dw.player.height} pixels<br>"
80
+ # "Scale: {}<br>"
75
81
  f"Video format: {dw.player.video_format}<br>"
76
82
  # "State: {}<br>"
77
83
  # "Media Resource Location: {}<br>"
@@ -79,12 +85,6 @@ def get_info(self) -> None:
79
85
  # "Track: {}/{}<br>"
80
86
  f"Number of media in media list: {dw.player.playlist_count}<br>"
81
87
  f"Current time position: {dw.player.time_pos}<br>"
82
- f"duration: {dw.player.duration}<br>"
83
- # "Position: {} %<br>"
84
- f"FPS: {dw.player.container_fps}<br>"
85
- # "Rate: {}<br>"
86
- f"Video size: {dw.player.width}x{dw.player.height}<br>"
87
- # "Scale: {}<br>"
88
88
  f"Aspect ratio: {round(dw.player.width / dw.player.height, 3)}<br>"
89
89
  # "is seekable? {}<br>"
90
90
  # "has_vout? {}<br>"
@@ -101,9 +101,7 @@ def get_info(self) -> None:
101
101
  tot_output += mpv_output + ffmpeg_output + "<br><hr>"
102
102
 
103
103
  else: # no open observation
104
-
105
- fn = QFileDialog().getOpenFileNames(self, "Select a media file", "", "Media files (*)")
106
- file_paths = fn[0] if type(fn) is tuple else fn
104
+ file_paths, _ = QFileDialog().getOpenFileNames(self, "Select a media file", "", "Media files (*)")
107
105
  if not file_paths:
108
106
  return
109
107
 
@@ -112,6 +110,6 @@ def get_info(self) -> None:
112
110
  tot_output += media_analysis_str(self.ffmpeg_bin, file_path)
113
111
 
114
112
  self.results = dialog.Results_dialog()
115
- self.results.setWindowTitle(cfg.programName + " - Media file information")
113
+ self.results.setWindowTitle(f"{cfg.programName} - Media file information")
116
114
  self.results.ptText.appendHtml(tot_output)
117
115
  self.results.show()
boris/menu_options.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,9 +19,9 @@ Copyright 2012-2023 Olivier Friard
19
19
  MA 02110-1301, USA.
20
20
  """
21
21
 
22
-
23
22
  import logging
24
23
  from . import config as cfg
24
+ from PySide6.QtCore import QSize
25
25
 
26
26
 
27
27
  def update_windows_title(self):
@@ -57,6 +57,13 @@ def update_menu(self):
57
57
 
58
58
  update_windows_title(self)
59
59
 
60
+ self.toolBar.setIconSize(
61
+ QSize(
62
+ self.config_param.get(cfg.TOOLBAR_ICON_SIZE, cfg.DEFAULT_TOOLBAR_ICON_SIZE_VALUE),
63
+ self.config_param.get(cfg.TOOLBAR_ICON_SIZE, cfg.DEFAULT_TOOLBAR_ICON_SIZE_VALUE),
64
+ )
65
+ )
66
+
60
67
  # enabled if project loaded
61
68
  for action in (
62
69
  self.actionEdit_project,
@@ -65,9 +72,9 @@ def update_menu(self):
65
72
  self.actionExport_project,
66
73
  self.actionCheck_project,
67
74
  self.actionClose_project,
68
- self.actionSend_project,
69
75
  self.actionNew_observation,
70
76
  self.actionRemove_path_from_media_files,
77
+ self.action_create_observations,
71
78
  self.action_obs_list,
72
79
  self.actionExport_observations_list,
73
80
  self.actionExplore_project,
@@ -136,6 +143,8 @@ def update_menu(self):
136
143
  self.actionJumpForward,
137
144
  self.actionJumpBackward,
138
145
  self.actionJumpTo,
146
+ self.action_change_time_offset_of_players,
147
+ self.action_deinterlace,
139
148
  self.actionZoom_level,
140
149
  self.actionRotate_current_video,
141
150
  self.actionDisplay_subtitles,
@@ -157,12 +166,12 @@ def update_menu(self):
157
166
  self.menuImage_overlay_on_video_2,
158
167
  self.actionAdd_image_overlay_on_video,
159
168
  self.actionRemove_image_overlay,
169
+ self.actionAdd_frame_indexes,
160
170
  ):
161
-
162
171
  action.setEnabled(self.playerType == cfg.MEDIA)
163
172
 
164
173
  # geometric measurements
165
- self.action_geometric_measurements.setEnabled(observation_is_active and self.geometric_measurements_mode == False)
174
+ self.action_geometric_measurements.setEnabled(observation_is_active and self.geometric_measurements_mode is False)
166
175
  self.actionCoding_pad.setEnabled(observation_is_active)
167
176
  self.actionSubjects_pad.setEnabled(observation_is_active)
168
177
  self.actionBehaviors_coding_map.setEnabled(observation_is_active)
@@ -188,6 +197,7 @@ def update_menu(self):
188
197
  self.action_behavior_binary_table,
189
198
  self.action_advanced_event_filtering,
190
199
  self.action_latency,
200
+ self.action_cooccurence,
191
201
  self.menuPlot_events,
192
202
  self.menuInter_rater_reliability,
193
203
  self.menuSimilarities,
@@ -197,7 +207,7 @@ def update_menu(self):
197
207
  w.setEnabled(project_contains_obs)
198
208
 
199
209
  # statusbar labels
200
- for w in [self.lbTimeOffset, self.lbSpeed, self.lb_obs_time_interval]:
210
+ for w in (self.lbTimeOffset, self.lb_obs_time_interval):
201
211
  w.setVisible(self.playerType == cfg.MEDIA)
202
212
 
203
213
  logging.debug("function: menu_options finished")