boris-behav-obs 9.7.15__py3-none-any.whl → 9.8.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 (80) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +4 -3
  4. boris/add_modifier.py +1 -1
  5. boris/advanced_event_filtering.py +1 -1
  6. boris/analysis_plugins/irr_weighted_cohen_kappa.py +2 -2
  7. boris/behav_coding_map_creator.py +1 -1
  8. boris/behavior_binary_table.py +1 -1
  9. boris/behaviors_coding_map.py +1 -1
  10. boris/boris_cli.py +1 -1
  11. boris/cmd_arguments.py +1 -1
  12. boris/coding_pad.py +1 -1
  13. boris/config.py +10 -1
  14. boris/config_file.py +18 -19
  15. boris/connections.py +12 -13
  16. boris/converters.py +1 -1
  17. boris/cooccurence.py +1 -1
  18. boris/core.py +41 -42
  19. boris/core_qrc.py +1830 -1967
  20. boris/core_ui.py +1 -1
  21. boris/db_functions.py +5 -14
  22. boris/dialog.py +24 -24
  23. boris/edit_event.py +1 -1
  24. boris/event_operations.py +1 -1
  25. boris/events_cursor.py +1 -1
  26. boris/events_snapshots.py +133 -78
  27. boris/exclusion_matrix.py +1 -1
  28. boris/export_events.py +49 -43
  29. boris/export_observation.py +1 -1
  30. boris/external_processes.py +1 -1
  31. boris/geometric_measurement.py +1 -1
  32. boris/gui_utilities.py +1 -1
  33. boris/image_overlay.py +1 -1
  34. boris/import_observations.py +1 -1
  35. boris/ipc_mpv.py +1 -1
  36. boris/irr.py +1 -1
  37. boris/latency.py +1 -1
  38. boris/measurement_widget.py +1 -1
  39. boris/media_file.py +1 -1
  40. boris/menu_options.py +14 -12
  41. boris/modifier_coding_map_creator.py +1 -1
  42. boris/modifiers_coding_map.py +1 -1
  43. boris/observation.py +13 -14
  44. boris/observation_operations.py +1 -1
  45. boris/observations_list.py +1 -1
  46. boris/otx_parser.py +1 -1
  47. boris/param_panel.py +1 -1
  48. boris/player_dock_widget.py +1 -1
  49. boris/plot_data_module.py +1 -1
  50. boris/plot_events.py +1 -1
  51. boris/plot_events_rt.py +1 -1
  52. boris/plot_spectrogram_rt.py +2 -2
  53. boris/plot_waveform_rt.py +1 -1
  54. boris/plugins.py +1 -1
  55. boris/preferences.py +1 -1
  56. boris/project.py +1 -1
  57. boris/project_functions.py +12 -12
  58. boris/project_import_export.py +1 -1
  59. boris/select_modifiers.py +1 -1
  60. boris/select_observations.py +22 -23
  61. boris/select_subj_behav.py +4 -4
  62. boris/state_events.py +1 -1
  63. boris/subjects_pad.py +1 -1
  64. boris/synthetic_time_budget.py +1 -1
  65. boris/time_budget_functions.py +1 -1
  66. boris/time_budget_widget.py +1 -1
  67. boris/transitions.py +1 -1
  68. boris/utilities.py +1 -1
  69. boris/version.py +3 -3
  70. boris/video_equalizer.py +1 -1
  71. boris/video_operations.py +1 -1
  72. boris/view_df.py +28 -4
  73. boris/write_event.py +1 -1
  74. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/METADATA +2 -2
  75. boris_behav_obs-9.8.2.dist-info/RECORD +110 -0
  76. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/WHEEL +1 -1
  77. boris_behav_obs-9.7.15.dist-info/RECORD +0 -110
  78. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/entry_points.txt +0 -0
  79. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/licenses/LICENSE.TXT +0 -0
  80. {boris_behav_obs-9.7.15.dist-info → boris_behav_obs-9.8.2.dist-info}/top_level.txt +0 -0
boris/core.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2025 Olivier Friard
4
+ Copyright 2012-2026 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
@@ -25,8 +25,6 @@ import os
25
25
  import sys
26
26
  from pathlib import Path
27
27
 
28
- # os.environ["PATH"] = os.path.dirname(__file__) + os.sep + "misc" + os.pathsep + os.environ["PATH"]
29
-
30
28
  os.environ["PATH"] = str(Path(__file__).parent / "misc") + os.pathsep + os.environ["PATH"]
31
29
  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
32
30
 
@@ -431,7 +429,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
431
429
  self.tv_events.setItemDelegate(events_cursor.StyledItemDelegateTriangle(self.events_current_row))
432
430
 
433
431
  connections.connections(self)
434
- self.config_param = cfg.INIT_PARAM
432
+ self.config_param = dict(cfg.INIT_PARAM)
435
433
  config_file.read(self)
436
434
  menu_options.update_menu(self)
437
435
 
@@ -1626,7 +1624,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1626
1624
  self.currentSubject = subject
1627
1625
  self.lbFocalSubject.setText(f" Focal subject: <b>{self.currentSubject}</b>")
1628
1626
 
1629
- def getCurrentMediaByFrame(self, player: str, requiredFrame: int, fps: float):
1627
+ def getCurrentMediaByFrame(self, player: str, requiredFrame: int, fps: float) -> tuple[str, int]:
1630
1628
  """
1631
1629
  Args:
1632
1630
  player (str): player
@@ -1637,7 +1635,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1637
1635
  currentMedia
1638
1636
  frameCurrentMedia
1639
1637
  """
1640
- currentMedia, frameCurrentMedia = "", 0
1638
+ currentMedia: str = ""
1639
+ frameCurrentMedia: int = 0
1641
1640
  frameMs = 1000 / fps
1642
1641
  for idx, media in enumerate(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][player]):
1643
1642
  if requiredFrame * frameMs < sum(self.dw_player[int(player) - 1].media_durations[0 : idx + 1]):
@@ -1646,7 +1645,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1646
1645
  break
1647
1646
  return currentMedia, round(frameCurrentMedia)
1648
1647
 
1649
- def extract_frame(self, dw):
1648
+ def extract_frame(self, dw) -> None:
1650
1649
  """
1651
1650
  for MEDIA obs: extract frame from video and visualize it in frame_viewer
1652
1651
  for IMAGES obs: load picture and visualize it in frame_viewer, extract EXIF Date/Time Original tag if available
@@ -1729,23 +1728,23 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1729
1728
  # show current states in subjects table
1730
1729
  self.show_current_states_in_subjects_table()
1731
1730
 
1732
- def frame_image_clicked(self, n_player, event):
1731
+ def frame_image_clicked(self, n_player, event) -> None:
1733
1732
  geometric_measurement.image_clicked(self, n_player, event)
1734
1733
 
1735
- def timer_plot_data_out(self, w):
1734
+ def timer_plot_data_out(self, w) -> None:
1736
1735
  """
1737
1736
  update plot in w (Plot_data class)
1738
1737
  triggered by timers in self.ext_data_timer_list
1739
1738
  """
1740
1739
  w.update_plot(self.getLaps())
1741
1740
 
1742
- def signal_from_widget(self, event):
1741
+ def signal_from_widget(self, event) -> None:
1743
1742
  """
1744
1743
  receive signal from widget
1745
1744
  """
1746
1745
  self.keyPressEvent(event)
1747
1746
 
1748
- def reload_frame(self):
1747
+ def reload_frame(self) -> None:
1749
1748
  """
1750
1749
  receive signal to reload frames from geometric measurements
1751
1750
  """
@@ -1761,7 +1760,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1761
1760
 
1762
1761
  geometric_measurement.redraw_measurements(self)
1763
1762
 
1764
- def save_picture_with_measurements(self, mode: str):
1763
+ def save_picture_with_measurements(self, mode: str) -> None:
1765
1764
  """
1766
1765
  receive signal to save picture from geometric measurements
1767
1766
  """
@@ -1946,7 +1945,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1946
1945
 
1947
1946
  pixmap.save(str(Path(output_dir) / file_name.with_suffix(".jpg")), "JPG")
1948
1947
 
1949
- def resize_dw(self, dw_id):
1948
+ def resize_dw(self, dw_id) -> None:
1950
1949
  """
1951
1950
  dockwidget was resized. Adapt overlay if any
1952
1951
  """
@@ -4923,21 +4922,21 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4923
4922
  self.update_project_zoom_pan_values()
4924
4923
 
4925
4924
  # frame-by-frame mode
4926
- if ek == 47 or (ek == Qt.Key_Left and modifier != cfg.CTRL_KEY): # / one frame back
4925
+ if ek == 47 or (ek == Qt.Key.Key_Left and modifier != cfg.CTRL_KEY): # / one frame back
4927
4926
  self.previous_frame()
4928
4927
  return
4929
4928
 
4930
- if ek == 42 or (ek == Qt.Key_Right and modifier != cfg.CTRL_KEY): # * read next frame
4929
+ if ek == 42 or (ek == Qt.Key.Key_Right and modifier != cfg.CTRL_KEY): # * read next frame
4931
4930
  self.next_frame()
4932
4931
  return
4933
4932
 
4934
4933
  if self.playerType in (cfg.MEDIA, cfg.IMAGES):
4935
4934
  # next media file (page up)
4936
- if ek == Qt.Key_PageUp:
4935
+ if ek == Qt.Key.Key_PageUp:
4937
4936
  self.next_media_file()
4938
4937
 
4939
4938
  # previous media file (page down)
4940
- if ek == Qt.Key_PageDown:
4939
+ if ek == Qt.Key.Key_PageDown:
4941
4940
  self.previous_media_file()
4942
4941
 
4943
4942
  if not self.pj[cfg.ETHOGRAM]:
@@ -4980,20 +4979,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4980
4979
  return
4981
4980
 
4982
4981
  if (
4983
- ((ek in range(33, 256)) and (ek not in [Qt.Key_Plus, Qt.Key_Minus]))
4982
+ ((ek in range(33, 256)) and (ek not in [Qt.Key.Key_Plus, Qt.Key.Key_Minus]))
4984
4983
  or (ek in cfg.function_keys)
4985
- or (ek == Qt.Key_Enter and event.text())
4984
+ or (ek == Qt.Key.Key_Enter and event.text())
4986
4985
  ): # click from coding pad or subjects pad
4987
4986
  if ek in cfg.function_keys:
4988
4987
  ek_unichr = cfg.function_keys[ek]
4989
- elif ek != Qt.Key_Enter:
4988
+ elif ek != Qt.Key.Key_Enter:
4990
4989
  ek_unichr = ek_text
4991
- elif ek == Qt.Key_Enter and event.text(): # click from coding pad or subjects pad
4990
+ elif ek == Qt.Key.Key_Enter and event.text(): # click from coding pad or subjects pad
4992
4991
  ek_unichr = ek_text
4993
4992
 
4994
4993
  logging.debug(f"{ek_unichr = }")
4995
4994
 
4996
- if ek == Qt.Key_Enter and event.text(): # click from coding pad or subjects pad
4995
+ if ek == Qt.Key.Key_Enter and event.text(): # click from coding pad or subjects pad
4997
4996
  ek_unichr = ""
4998
4997
 
4999
4998
  if "#subject#" in event.text():
@@ -5035,7 +5034,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5035
5034
  r = dialog.MessageDialog(
5036
5035
  cfg.programName,
5037
5036
  "This key defines a behavior and a subject. Choose one",
5038
- ["&Behavior", "&Subject", cfg.CANCEL],
5037
+ ("&Behavior", "&Subject", cfg.CANCEL),
5039
5038
  )
5040
5039
  if r == cfg.CANCEL:
5041
5040
  return
@@ -5088,7 +5087,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5088
5087
  "The focal subject is not defined. Do you want to continue?\n"
5089
5088
  "Use Preferences menu option to modify this behaviour."
5090
5089
  ),
5091
- [cfg.YES, cfg.NO],
5090
+ (cfg.YES, cfg.NO),
5092
5091
  )
5093
5092
 
5094
5093
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.MEDIA and flagPlayerPlaying:
@@ -5260,7 +5259,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5260
5259
  dialog.MessageDialog(
5261
5260
  cfg.programName,
5262
5261
  f"<b>{self.find_dialog.findText.text()}</b> not found. Search from beginning?",
5263
- [cfg.YES, cfg.NO],
5262
+ (cfg.YES, cfg.NO),
5264
5263
  )
5265
5264
  == cfg.YES
5266
5265
  ):
@@ -5281,15 +5280,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5281
5280
  self.find_replace_dialog.close()
5282
5281
  return
5283
5282
  if self.find_replace_dialog.combo_fields.currentIndex() == 0: # choose a field
5284
- dialog.MessageDialog(cfg.programName, "Choose a field.", ["OK"])
5283
+ dialog.MessageDialog(cfg.programName, "Choose a field.", (cfg.OK,))
5285
5284
  return
5286
5285
 
5287
5286
  if not self.find_replace_dialog.findText.text():
5288
- dialog.MessageDialog(cfg.programName, "There is nothing to find.", ["OK"])
5287
+ dialog.MessageDialog(cfg.programName, "There is nothing to find.", (cfg.OK,))
5289
5288
  return
5290
5289
 
5291
5290
  if self.find_replace_dialog.cbFindInSelectedEvents.isChecked() and not len(self.find_replace_dialog.rowsToFind):
5292
- dialog.MessageDialog(cfg.programName, "There are no selected events", [cfg.OK])
5291
+ dialog.MessageDialog(cfg.programName, "There are no selected events", (cfg.OK,))
5293
5292
  return
5294
5293
 
5295
5294
  fields_list: list = []
@@ -5297,7 +5296,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5297
5296
  # check if find and replace contain valid behavior codes
5298
5297
  for bh in (self.find_replace_dialog.findText.text(), self.find_replace_dialog.replaceText.text()):
5299
5298
  if bh not in util.all_subjects(self.pj[cfg.SUBJECTS]):
5300
- dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid subject name", [cfg.OK])
5299
+ dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid subject name", (cfg.OK,))
5301
5300
  return
5302
5301
  fields_list.append(cfg.PJ_OBS_FIELDS[self.playerType][cfg.SUBJECT])
5303
5302
 
@@ -5305,7 +5304,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5305
5304
  # check if find and replace contain valid behavior codes
5306
5305
  for bh in (self.find_replace_dialog.findText.text(), self.find_replace_dialog.replaceText.text()):
5307
5306
  if bh not in util.all_behaviors(self.pj[cfg.ETHOGRAM]):
5308
- dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid behavior code", [cfg.OK])
5307
+ dialog.MessageDialog(cfg.programName, f"<b>{bh}</b> is not a valid behavior code", (cfg.OK,))
5309
5308
  return
5310
5309
  fields_list.append(cfg.PJ_OBS_FIELDS[self.playerType][cfg.BEHAVIOR_CODE])
5311
5310
 
@@ -5403,7 +5402,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5403
5402
  self.find_replace_dialog.close()
5404
5403
 
5405
5404
  if msg == "FIND_REPLACE_ALL":
5406
- dialog.MessageDialog(cfg.programName, f"{number_replacement} substitution(s).", [cfg.OK])
5405
+ dialog.MessageDialog(cfg.programName, f"{number_replacement} substitution(s).", (cfg.OK,))
5407
5406
  self.find_replace_dialog.close()
5408
5407
 
5409
5408
  def closeEvent(self, event):
@@ -5433,7 +5432,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5433
5432
  dialog.MessageDialog(
5434
5433
  cfg.programName,
5435
5434
  "BORIS is doing some job. What do you want to do?",
5436
- ["Wait", "Quit BORIS"],
5435
+ ("Wait", "Quit BORIS"),
5437
5436
  )
5438
5437
  == "Wait"
5439
5438
  ):
@@ -5452,7 +5451,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5452
5451
  response = dialog.MessageDialog(
5453
5452
  cfg.programName,
5454
5453
  "What to do about the current unsaved project?",
5455
- [cfg.SAVE, cfg.DISCARD, cfg.CANCEL],
5454
+ (cfg.SAVE, cfg.DISCARD, cfg.CANCEL),
5456
5455
  )
5457
5456
 
5458
5457
  if response == cfg.SAVE:
@@ -5779,8 +5778,8 @@ def main():
5779
5778
  None,
5780
5779
  cfg.programName,
5781
5780
  "FFmpeg is not available.<br>Go to http://www.ffmpeg.org to download it",
5782
- QMessageBox.Ok | QMessageBox.Default,
5783
- QMessageBox.NoButton,
5781
+ QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
5782
+ QMessageBox.StandardButton.NoButton,
5784
5783
  )
5785
5784
  sys.exit(3)
5786
5785
 
@@ -5833,7 +5832,7 @@ def main():
5833
5832
  config_param: dict = {}
5834
5833
  ini_file_path = Path.home() / Path(".boris")
5835
5834
  if ini_file_path.is_file():
5836
- settings = QSettings(str(ini_file_path), QSettings.IniFormat)
5835
+ settings = QSettings(str(ini_file_path), QSettings.Format.IniFormat)
5837
5836
  try:
5838
5837
  config_param = settings.value("config")
5839
5838
  except Exception:
@@ -5866,8 +5865,8 @@ def main():
5866
5865
  None,
5867
5866
  cfg.programName,
5868
5867
  ("The mpv command is not available on the path"),
5869
- QMessageBox.Ok | QMessageBox.Default,
5870
- QMessageBox.NoButton,
5868
+ QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
5869
+ QMessageBox.StandardButton.NoButton,
5871
5870
  )
5872
5871
  sys.exit()
5873
5872
 
@@ -5876,8 +5875,8 @@ def main():
5876
5875
  None,
5877
5876
  cfg.programName,
5878
5877
  ("This version of BORIS for macOS is still EXPERIMENTAL and should be used at your own risk."),
5879
- QMessageBox.Ok | QMessageBox.Default,
5880
- QMessageBox.NoButton,
5878
+ QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
5879
+ QMessageBox.StandardButton.NoButton,
5881
5880
  )
5882
5881
 
5883
5882
  window.show()
@@ -5890,8 +5889,8 @@ def main():
5890
5889
  None,
5891
5890
  cfg.programName,
5892
5891
  (f"Error opening observation: <b>{observation_to_open}</b><br>{r.split(':')[1]}"),
5893
- QMessageBox.Ok | QMessageBox.Default,
5894
- QMessageBox.NoButton,
5892
+ QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Default,
5893
+ QMessageBox.StandardButton.NoButton,
5895
5894
  )
5896
5895
 
5897
5896
  if not options.nosplashscreen and (sys.platform != "darwin"):