boris-behav-obs 8.16.6__py3-none-any.whl → 9.7.2__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 +108 -49
  24. boris/config_file.py +58 -67
  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 +2106 -1277
  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 +304 -123
  79. boris/preferences_ui.py +684 -227
  80. boris/project.py +293 -270
  81. boris/project_functions.py +618 -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.2.dist-info/METADATA +140 -0
  104. boris_behav_obs-9.7.2.dist-info/RECORD +109 -0
  105. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.2.dist-info}/WHEEL +1 -1
  106. boris_behav_obs-9.7.2.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.2.dist-info/licenses}/LICENSE.TXT +0 -0
  125. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.2.dist-info}/top_level.txt +0 -0
@@ -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
@@ -21,12 +21,10 @@ Copyright 2012-2023 Olivier Friard
21
21
 
22
22
  import os
23
23
  import pathlib
24
- import re
25
24
  from decimal import Decimal as dec
26
25
 
27
26
  import tablib
28
- from PyQt5.QtCore import Qt
29
- from PyQt5.QtWidgets import QFileDialog, QInputDialog, QMessageBox
27
+ from PySide6.QtWidgets import QFileDialog, QInputDialog, QMessageBox
30
28
 
31
29
  from . import observation_operations
32
30
 
@@ -38,9 +36,7 @@ from . import config as cfg
38
36
  from . import select_subj_behav
39
37
 
40
38
 
41
- def create_behavior_binary_table(
42
- pj: dict, selected_observations: list, parameters_obs: dict, time_interval: float
43
- ) -> dict:
39
+ def create_behavior_binary_table(pj: dict, selected_observations: list, parameters_obs: dict, time_interval: float) -> dict:
44
40
  """
45
41
  create behavior binary table
46
42
 
@@ -57,17 +53,12 @@ def create_behavior_binary_table(
57
53
 
58
54
  results_df = {}
59
55
 
60
- state_behavior_codes = [
61
- x for x in util.state_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]
62
- ]
63
- point_behavior_codes = [
64
- x for x in util.point_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]
65
- ]
56
+ state_behavior_codes = [x for x in util.state_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]]
57
+ point_behavior_codes = [x for x in util.point_behavior_codes(pj[cfg.ETHOGRAM]) if x in parameters_obs[cfg.SELECTED_BEHAVIORS]]
66
58
  if not state_behavior_codes and not point_behavior_codes:
67
59
  return {"error": True, "msg": "No events selected"}
68
60
 
69
61
  for obs_id in selected_observations:
70
-
71
62
  start_time = parameters_obs[cfg.START_TIME]
72
63
  end_time = parameters_obs[cfg.END_TIME]
73
64
 
@@ -78,7 +69,6 @@ def create_behavior_binary_table(
78
69
  end_time = dec(max_obs_length)
79
70
 
80
71
  if parameters_obs["time"] == cfg.TIME_EVENTS:
81
-
82
72
  try:
83
73
  start_time = dec(pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS][0][0])
84
74
  except Exception:
@@ -89,23 +79,29 @@ def create_behavior_binary_table(
89
79
  max_obs_length, _ = observation_operations.observation_length(pj, [obs_id])
90
80
  end_time = dec(max_obs_length)
91
81
 
82
+ if parameters_obs["time"] == cfg.TIME_OBS_INTERVAL:
83
+ obs_interval = pj[cfg.OBSERVATIONS][obs_id].get(cfg.OBSERVATION_TIME_INTERVAL, [0, 0])
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] not in (0, None) else dec(max_obs_length)
89
+
92
90
  if obs_id not in results_df:
93
91
  results_df[obs_id] = {}
94
92
 
95
93
  for subject in parameters_obs[cfg.SELECTED_SUBJECTS]:
96
-
97
94
  # extract tuple (behavior, modifier)
98
95
  behav_modif_list = [
99
96
  (idx[2], idx[3])
100
97
  for idx in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
101
- if idx[1] == (subject if subject != cfg.NO_FOCAL_SUBJECT else "")
102
- and idx[2] in parameters_obs[cfg.SELECTED_BEHAVIORS]
98
+ if idx[1] == (subject if subject != cfg.NO_FOCAL_SUBJECT else "") and idx[2] in parameters_obs[cfg.SELECTED_BEHAVIORS]
103
99
  ]
104
100
 
105
101
  # extract observed subjects NOT USED at the moment
106
- observed_subjects = [
102
+ """observed_subjects = [
107
103
  event[cfg.EVENT_SUBJECT_FIELD_IDX] for event in pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
108
- ]
104
+ ]"""
109
105
 
110
106
  # add selected behavior if not found in (behavior, modifier)
111
107
  if not parameters_obs[cfg.EXCLUDE_BEHAVIORS]:
@@ -127,17 +123,12 @@ def create_behavior_binary_table(
127
123
  sel_subject_dict = {"": {cfg.SUBJECT_NAME: ""}}
128
124
  else:
129
125
  sel_subject_dict = dict(
130
- [
131
- (idx, pj[cfg.SUBJECTS][idx])
132
- for idx in pj[cfg.SUBJECTS]
133
- if pj[cfg.SUBJECTS][idx][cfg.SUBJECT_NAME] == subject
134
- ]
126
+ [(idx, pj[cfg.SUBJECTS][idx]) for idx in pj[cfg.SUBJECTS] if pj[cfg.SUBJECTS][idx][cfg.SUBJECT_NAME] == subject]
135
127
  )
136
128
 
137
129
  row_idx = 0
138
130
  t = start_time
139
131
  while t <= end_time:
140
-
141
132
  # state events
142
133
  current_states = util.get_current_states_modifiers_by_subject_2(
143
134
  state_behavior_codes, pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS], sel_subject_dict, t
@@ -175,8 +166,8 @@ def behavior_binary_table(self):
175
166
  None,
176
167
  cfg.programName,
177
168
  (
178
- "Depending of the length of your observations "
179
- "the execution of this function may be very long.<br>"
169
+ "Depending on the length of yours observations "
170
+ "the execution of this function may take a long time.<br>"
180
171
  "The program interface may freeze, be patient. <br>"
181
172
  ),
182
173
  )
@@ -214,24 +205,26 @@ def behavior_binary_table(self):
214
205
  return
215
206
  """
216
207
 
217
- max_media_duration_all_obs, _ = observation_operations.media_duration(
218
- self.pj[cfg.OBSERVATIONS], selected_observations
219
- )
208
+ max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
220
209
 
221
210
  start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
222
211
 
212
+ start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
213
+
223
214
  parameters = select_subj_behav.choose_obs_subj_behav_category(
224
215
  self,
225
216
  selected_observations,
226
217
  start_coding=start_coding,
227
218
  end_coding=end_coding,
219
+ start_interval=start_interval,
220
+ end_interval=end_interval,
228
221
  maxTime=max_media_duration_all_obs,
229
- flagShowIncludeModifiers=True,
230
- flagShowExcludeBehaviorsWoEvents=True,
222
+ show_include_modifiers=True,
223
+ show_exclude_non_coded_behaviors=True,
231
224
  by_category=False,
232
225
  n_observations=len(selected_observations),
233
226
  )
234
- if parameters == {}:
227
+ if not parameters:
235
228
  return
236
229
  if not parameters[cfg.SELECTED_SUBJECTS] or not parameters[cfg.SELECTED_BEHAVIORS]:
237
230
  QMessageBox.warning(None, cfg.programName, "Select subject(s) and behavior(s) to analyze")
@@ -253,7 +246,6 @@ def behavior_binary_table(self):
253
246
  file_formats = [cfg.TSV, cfg.CSV, cfg.ODS, cfg.XLSX, cfg.XLS, cfg.HTML]
254
247
 
255
248
  if len(selected_observations) == 1:
256
-
257
249
  file_name, filter_ = QFileDialog().getSaveFileName(None, "Save results", "", ";;".join(file_formats))
258
250
  if not file_name:
259
251
  return
@@ -265,18 +257,15 @@ def behavior_binary_table(self):
265
257
  # check if file with new extension already exists
266
258
  if pathlib.Path(file_name).is_file():
267
259
  if (
268
- dialog.MessageDialog(
269
- cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE]
270
- )
260
+ dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE])
271
261
  == cfg.CANCEL
272
262
  ):
273
263
  return
274
264
  else:
275
-
276
265
  item, ok = QInputDialog.getItem(None, "Save results", "Available formats", file_formats, 0, False)
277
266
  if not ok:
278
267
  return
279
- """output_format = re.sub(".* \(\*\.", "", item)[:-1]"""
268
+
280
269
  output_format = cfg.FILE_NAME_SUFFIX[item]
281
270
 
282
271
  export_dir = QFileDialog().getExistingDirectory(
@@ -287,17 +276,11 @@ def behavior_binary_table(self):
287
276
 
288
277
  mem_command = ""
289
278
  for obs_id in results_df:
290
-
291
279
  for subject in results_df[obs_id]:
292
-
293
280
  if len(selected_observations) > 1:
294
- file_name_with_subject = (
295
- str(pathlib.Path(export_dir) / util.safeFileName(obs_id + "_" + subject)) + "." + output_format
296
- )
281
+ file_name_with_subject = str(pathlib.Path(export_dir) / util.safeFileName(obs_id + "_" + subject)) + "." + output_format
297
282
  else:
298
- file_name_with_subject = (
299
- str(os.path.splitext(file_name)[0] + util.safeFileName("_" + subject)) + "." + output_format
300
- )
283
+ file_name_with_subject = str(os.path.splitext(file_name)[0] + util.safeFileName("_" + subject)) + "." + output_format
301
284
 
302
285
  # check if file with new extension already exists
303
286
  if mem_command != cfg.OVERWRITE_ALL and pathlib.Path(file_name_with_subject).is_file():
@@ -313,10 +296,10 @@ def behavior_binary_table(self):
313
296
  if mem_command in ["Skip", "Skip all"]:
314
297
  continue
315
298
 
316
- if output_format in ["csv", "tsv", "html"]:
299
+ if output_format in [cfg.CSV_EXT, cfg.TSV_EXT, cfg.HTML]:
317
300
  with open(file_name_with_subject, "wb") as f:
318
301
  f.write(str.encode(results_df[obs_id][subject].export(output_format)))
319
302
 
320
- if output_format in ["ods", "xlsx", "xls"]:
303
+ if output_format in [cfg.ODS_EXT, cfg.XLSX_EXT, cfg.XLS_EXT]:
321
304
  with open(file_name_with_subject, "wb") as f:
322
305
  f.write(results_df[obs_id][subject].export(output_format))
@@ -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,9 +20,12 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- from PyQt5.QtGui import *
24
- from PyQt5.QtCore import *
25
- from PyQt5.QtWidgets import (
23
+ import json
24
+ import binascii
25
+
26
+ from PySide6.QtGui import QMouseEvent, QPixmap, QPolygonF, QColor, QBrush, QPen
27
+ from PySide6.QtCore import Qt, Signal, QEvent, QPoint
28
+ from PySide6.QtWidgets import (
26
29
  QLabel,
27
30
  QHBoxLayout,
28
31
  QGraphicsView,
@@ -38,20 +41,17 @@ from PyQt5.QtWidgets import (
38
41
  QApplication,
39
42
  )
40
43
 
41
- import json
42
- import binascii
43
44
  from . import config as cfg
44
45
 
45
- codeSeparator = ","
46
- penWidth = 0
46
+ codeSeparator: str = ","
47
+ penWidth: int = 0
47
48
  penStyle = Qt.NoPen
48
49
 
49
50
 
50
51
  class BehaviorsCodingMapWindowClass(QWidget):
51
52
  class View(QGraphicsView):
52
-
53
- mousePress = pyqtSignal(QMouseEvent)
54
- mouseMove = pyqtSignal(QMouseEvent)
53
+ mousePress = Signal(QMouseEvent)
54
+ mouseMove = Signal(QMouseEvent)
55
55
 
56
56
  def eventFilter(self, source, event):
57
57
  if event.type() == QEvent.MouseMove:
@@ -72,9 +72,9 @@ class BehaviorsCodingMapWindowClass(QWidget):
72
72
  self.viewport().installEventFilter(self)
73
73
  self.setMouseTracking(True)
74
74
 
75
- clickSignal = pyqtSignal(str, list) # click signal to be sent to mainwindow
76
- keypressSignal = pyqtSignal(QEvent)
77
- close_signal = pyqtSignal(str)
75
+ clickSignal = Signal(str, list) # click signal to be sent to mainwindow
76
+ keypressSignal = Signal(QEvent)
77
+ close_signal = Signal(str)
78
78
 
79
79
  def __init__(self, behaviors_coding_map, idx=0):
80
80
  super(BehaviorsCodingMapWindowClass, self).__init__()
@@ -103,7 +103,7 @@ class BehaviorsCodingMapWindowClass(QWidget):
103
103
  self.leareaCode = QLineEdit(self)
104
104
  hBoxLayout1.addWidget(self.leareaCode)
105
105
 
106
- self.btClose = QPushButton("Close")
106
+ self.btClose = QPushButton(cfg.CLOSE)
107
107
  self.btClose.clicked.connect(self.close)
108
108
  hBoxLayout1.addWidget(self.btClose)
109
109
 
@@ -140,13 +140,13 @@ class BehaviorsCodingMapWindowClass(QWidget):
140
140
  codes.append(areaCode)
141
141
  self.leareaCode.setText(", ".join(codes))
142
142
 
143
- def viewMousePressEvent(self, event):
143
+ def viewMousePressEvent(self, event) -> None:
144
144
  """
145
145
  insert clicked areas codes
146
146
  """
147
147
 
148
148
  test = self.view.mapToScene(event.pos()).toPoint()
149
- to_be_sent = []
149
+ to_be_sent: list = []
150
150
 
151
151
  for areaCode, pg in self.polygonsList2:
152
152
  if pg.contains(test):
@@ -227,7 +227,6 @@ def show_behaviors_coding_map(self):
227
227
 
228
228
 
229
229
  if __name__ == "__main__":
230
-
231
230
  import sys
232
231
 
233
232
  app = QApplication(sys.argv)
boris/boris_cli.py CHANGED
@@ -3,7 +3,7 @@ BORIS CLI
3
3
 
4
4
  Behavioral Observation Research Interactive Software Command Line Interface
5
5
 
6
- Copyright 2012-2023 Olivier Friard
6
+ Copyright 2012-2025 Olivier Friard
7
7
 
8
8
  This program is free software; you can redistribute it and/or modify
9
9
  it under the terms of the GNU General Public License as published by
@@ -91,9 +91,7 @@ commands_usage = {
91
91
  parser = argparse.ArgumentParser(description="BORIS CLI")
92
92
  parser.add_argument("-v", "--version", action="store_true", dest="version", help="BORIS version")
93
93
  parser.add_argument("-p", "--project", action="store", dest="project_file", help="Project file path")
94
- parser.add_argument(
95
- "-o", "--observation", nargs="*", action="store", default=[], dest="observation_id", help="Observation id"
96
- )
94
+ parser.add_argument("-o", "--observation", nargs="*", action="store", default=[], dest="observation_id", help="Observation id")
97
95
  parser.add_argument("-i", "--info", action="store_true", dest="project_info", help="Project information")
98
96
  parser.add_argument("-c", "--command", nargs="*", action="store", dest="command", help="Command to execute")
99
97
 
@@ -117,7 +115,6 @@ if args.command:
117
115
  sys.exit()
118
116
 
119
117
  if args.project_file:
120
-
121
118
  if not args.command:
122
119
  print("Project path: {}".format(args.project_file))
123
120
 
@@ -164,9 +161,7 @@ if args.project_info:
164
161
  print("Subjects\n========")
165
162
  print("Number of subjects: {}".format(len(pj[SUBJECTS])))
166
163
  for idx in utilities.sorted_keys(pj[SUBJECTS]):
167
- print(
168
- "Name: {}\tDescription: {}".format(pj[SUBJECTS][idx]["name"], pj[SUBJECTS][idx]["description"])
169
- )
164
+ print("Name: {}\tDescription: {}".format(pj[SUBJECTS][idx]["name"], pj[SUBJECTS][idx]["description"]))
170
165
  print()
171
166
 
172
167
  print("Observations\n============")
@@ -179,7 +174,6 @@ if args.project_info:
179
174
  for observation_id in observations_id_list:
180
175
  print("Observation id: {}".format(observation_id))
181
176
  if pj[OBSERVATIONS][observation_id][EVENTS]:
182
-
183
177
  for event in pj[OBSERVATIONS][observation_id][EVENTS]:
184
178
  print("\t".join([str(x) for x in event]))
185
179
  else:
@@ -190,7 +184,6 @@ if args.project_info:
190
184
  sys.exit()
191
185
 
192
186
  if args.command:
193
-
194
187
  print("Command: {}\n".format(" ".join(args.command)))
195
188
 
196
189
  if not pj:
@@ -198,20 +191,16 @@ if args.command:
198
191
  sys.exit()
199
192
 
200
193
  if "check_state_events" in args.command:
201
-
202
194
  if not observations_id_list:
203
195
  print("No observation selected. Command applied on all observations found in project\n")
204
196
  observations_id_list = all_observations(pj)
205
197
 
206
198
  for observation_id in observations_id_list:
207
- ret, msg = project_functions.check_state_events_obs(
208
- observation_id, pj[ETHOGRAM], pj[OBSERVATIONS][observation_id], HHMMSS
209
- )
199
+ ret, msg = project_functions.check_state_events_obs(observation_id, pj[ETHOGRAM], pj[OBSERVATIONS][observation_id], HHMMSS)
210
200
  print("{}: {}".format(observation_id, cleanhtml(msg)))
211
201
  sys.exit()
212
202
 
213
203
  if "export_events" in args.command:
214
-
215
204
  if not observations_id_list:
216
205
  print("No observation selected. Command applied on all observations found in project\n")
217
206
  observations_id_list = [idx for idx in pj[OBSERVATIONS]]
@@ -261,21 +250,14 @@ if args.command:
261
250
  if len(args.command) > 2:
262
251
  include_modifiers = "TRUE" in args.command[2].upper()
263
252
 
264
- K, out = irr.cohen_kappa(
265
- cursor, observations_id_list[0], observations_id_list[1], interval, subjects, include_modifiers
266
- )
253
+ K, out = irr.cohen_kappa(cursor, observations_id_list[0], observations_id_list[1], interval, subjects, include_modifiers)
267
254
 
268
- print(
269
- ("Cohen's Kappa - Index of Inter-Rater Reliability\n\n" "Interval time: {interval:.3f} s\n").format(
270
- interval=interval
271
- )
272
- )
255
+ print(("Cohen's Kappa - Index of Inter-Rater Reliability\n\nInterval time: {interval:.3f} s\n").format(interval=interval))
273
256
 
274
257
  print(out)
275
258
  sys.exit()
276
259
 
277
260
  if "subtitles" in args.command:
278
-
279
261
  if not observations_id_list:
280
262
  print("No observation selected. Command applied on all observations found in project\n")
281
263
  observations_id_list = all_observations(pj)
@@ -309,7 +291,6 @@ if args.command:
309
291
  sys.exit()
310
292
 
311
293
  if "plot_events" in args.command[0]:
312
-
313
294
  if not observations_id_list:
314
295
  print("No observation selected. Command applied on all observations found in project\n")
315
296
  observations_id_list = all_observations(pj)
boris/cmd_arguments.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
 
7
7
  This program is free software; you can redistribute it and/or modify
@@ -30,9 +30,20 @@ def parse_arguments():
30
30
  parser = OptionParser(usage=usage)
31
31
 
32
32
  parser.add_option("-d", "--debug", action="store_true", default=False, dest="debug", help="Use debugging mode")
33
+ parser.add_option("-q", "--quit", action="store_true", default=False, dest="quit", help="Quit after launch")
33
34
  parser.add_option("-v", "--version", action="store_true", default=False, dest="version", help="Print version")
34
35
  parser.add_option("-n", "--nosplashscreen", action="store_true", default=False, help="No splash screen")
35
36
  parser.add_option("-p", "--project", action="store", default="", dest="project", help="Project file")
36
37
  parser.add_option("-o", "--observation", action="store", default="", dest="observation", help="Observation id")
38
+ parser.add_option("-i", "--ipc", action="store_true", default="", dest="ipc", help="MPV IPC mode")
39
+
40
+ parser.add_option(
41
+ "-f",
42
+ "--no-first-launch-dialog",
43
+ action="store_true",
44
+ default=False,
45
+ dest="no_first_launch_dialog",
46
+ help="No first launch dialog (for new version automatic check)",
47
+ )
37
48
 
38
49
  return parser.parse_args()
boris/coding_pad.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
- from PyQt5.QtCore import Qt, pyqtSignal, QEvent, QRect
23
- from PyQt5.QtGui import QFont
24
- from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QGridLayout, QComboBox, QMessageBox
22
+ from PySide6.QtCore import Qt, Signal, QEvent, QRect
23
+ from PySide6.QtGui import QFont
24
+ from PySide6.QtWidgets import QWidget, QPushButton, QHBoxLayout, QGridLayout, QComboBox, QMessageBox
25
25
 
26
26
  from . import config as cfg
27
27
  from . import utilities as util
@@ -38,10 +38,9 @@ class Button(QWidget):
38
38
 
39
39
 
40
40
  class CodingPad(QWidget):
41
-
42
- clickSignal = pyqtSignal(str)
43
- sendEventSignal = pyqtSignal(QEvent)
44
- close_signal = pyqtSignal(QRect, dict)
41
+ clickSignal = Signal(str)
42
+ sendEventSignal = Signal(QEvent)
43
+ close_signal = Signal(QRect, dict)
45
44
 
46
45
  def __init__(self, pj: dict, filtered_behaviors, parent=None):
47
46
  super().__init__(parent)
@@ -56,9 +55,7 @@ class CodingPad(QWidget):
56
55
 
57
56
  self.preferences: dict = {"button font size": 20, "button color": cfg.BEHAVIOR_CATEGORY}
58
57
 
59
- self.button_css: str = (
60
- "min-width: 50px; min-height:50px; font-weight: bold; max-height:5000px; max-width: 5000px;"
61
- )
58
+ self.button_css: str = "min-width: 50px; min-height:50px; font-weight: bold; max-height:5000px; max-width: 5000px;"
62
59
 
63
60
  self.setWindowTitle("Coding pad")
64
61
 
@@ -95,6 +92,7 @@ class CodingPad(QWidget):
95
92
  # combobox for coding pad configuration
96
93
  vlayout = QHBoxLayout()
97
94
  self.cb_config = QComboBox()
95
+ self.cb_config.setFocusPolicy(Qt.NoFocus)
98
96
  self.cb_config.addItems(
99
97
  [
100
98
  "Choose an option to configure",
@@ -109,9 +107,7 @@ class CodingPad(QWidget):
109
107
  vlayout.addWidget(self.cb_config)
110
108
  self.grid.addLayout(vlayout, 0, 1, 1, 1)
111
109
 
112
- self.all_behaviors = [
113
- self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
114
- ]
110
+ self.all_behaviors = [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])]
115
111
 
116
112
  # behavioral category colors
117
113
  self.unique_behavioral_categories = sorted(
@@ -126,8 +122,7 @@ class CodingPad(QWidget):
126
122
  behaviorsList = [
127
123
  [self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CATEGORY], self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE]]
128
124
  for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
129
- if cfg.BEHAVIOR_CATEGORY in self.pj[cfg.ETHOGRAM][x]
130
- and self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in self.filtered_behaviors
125
+ if cfg.BEHAVIOR_CATEGORY in self.pj[cfg.ETHOGRAM][x] and self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] in self.filtered_behaviors
131
126
  ]
132
127
 
133
128
  # square grid dimension
@@ -144,7 +139,6 @@ class CodingPad(QWidget):
144
139
  self.button_configuration()
145
140
 
146
141
  def addWidget(self, behavior_code: str, i: int, j: int) -> None:
147
-
148
142
  self.grid.addWidget(Button(), i, j)
149
143
  index = self.grid.count() - 1
150
144
  widget = self.grid.itemAt(index).widget()
@@ -166,7 +160,6 @@ class CodingPad(QWidget):
166
160
  behavior_code = self.grid.itemAt(index).widget().pushButton.text()
167
161
 
168
162
  if self.preferences["button color"] == cfg.BEHAVIOR_CATEGORY:
169
-
170
163
  behav_cat = [
171
164
  self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CATEGORY]
172
165
  for x in self.pj[cfg.ETHOGRAM]
@@ -184,11 +177,7 @@ class CodingPad(QWidget):
184
177
  if self.preferences["button color"] == "behavior":
185
178
  # behavioral categories are not defined
186
179
  behavior_position = int(
187
- [
188
- x
189
- for x in util.sorted_keys(self.pj[cfg.ETHOGRAM])
190
- if self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] == behavior_code
191
- ][0]
180
+ [x for x in util.sorted_keys(self.pj[cfg.ETHOGRAM]) if self.pj[cfg.ETHOGRAM][x][cfg.BEHAVIOR_CODE] == behavior_code][0]
192
181
  )
193
182
 
194
183
  # behavior button color
@@ -197,18 +186,14 @@ class CodingPad(QWidget):
197
186
  if behav_color is not None:
198
187
  color = behav_color
199
188
  else:
200
- color = self.behavior_colors_list[behavior_position % len(self.behavior_colors_list)].replace(
201
- "tab:", ""
202
- )
189
+ color = self.behavior_colors_list[behavior_position % len(self.behavior_colors_list)].replace("tab:", "")
203
190
 
204
191
  if self.preferences["button color"] == "no color":
205
192
  color = ""
206
193
 
207
194
  # set checkable if state behavior
208
195
  self.grid.itemAt(index).widget().pushButton.setCheckable(behavior_code in state_behaviors_list)
209
- self.grid.itemAt(index).widget().pushButton.setStyleSheet(
210
- self.button_css + (f"background-color: {color};" if color else "")
211
- )
196
+ self.grid.itemAt(index).widget().pushButton.setStyleSheet(self.button_css + (f"background-color: {color};" if color else ""))
212
197
  font = QFont("Arial", self.preferences["button font size"])
213
198
  self.grid.itemAt(index).widget().pushButton.setFont(font)
214
199
 
@@ -251,10 +236,7 @@ def show_coding_pad(self):
251
236
  return
252
237
 
253
238
  if hasattr(self, "codingpad"):
254
-
255
- self.codingpad.filtered_behaviors = [
256
- self.twEthogram.item(i, 1).text() for i in range(self.twEthogram.rowCount())
257
- ]
239
+ self.codingpad.filtered_behaviors = [self.twEthogram.item(i, 1).text() for i in range(self.twEthogram.rowCount())]
258
240
  if not self.codingpad.filtered_behaviors:
259
241
  QMessageBox.warning(self, cfg.programName, "No behaviors to show!")
260
242
  return
@@ -265,7 +247,6 @@ def show_coding_pad(self):
265
247
  self.codingpad.behavioral_category_colors_list = self.behav_category_colors
266
248
 
267
249
  else: # coding pad does not exist
268
-
269
250
  filtered_behaviors = [self.twEthogram.item(i, 1).text() for i in range(self.twEthogram.rowCount())]
270
251
  if not filtered_behaviors:
271
252
  QMessageBox.warning(self, cfg.programName, "No behaviors to show!")
@@ -279,6 +260,7 @@ def show_coding_pad(self):
279
260
 
280
261
  self.codingpad.setWindowFlags(Qt.WindowStaysOnTopHint)
281
262
  self.codingpad.sendEventSignal.connect(self.signal_from_widget)
263
+
282
264
  self.codingpad.clickSignal.connect(self.click_signal_from_coding_pad)
283
265
  self.codingpad.close_signal.connect(self.close_signal_from_coding_pad)
284
266
  self.codingpad.show()