boris-behav-obs 9.7.1__tar.gz → 9.7.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/PKG-INFO +3 -4
  2. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/README.md +2 -3
  3. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/about.py +5 -5
  4. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/config.py +7 -0
  5. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/config_file.py +3 -3
  6. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/core.py +20 -19
  7. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/ipc_mpv.py +2 -29
  8. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/observation.py +9 -1
  9. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/observation_operations.py +1 -1
  10. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/plot_data_module.py +2 -0
  11. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/preferences.py +6 -0
  12. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/preferences_ui.py +46 -28
  13. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/project.py +1 -1
  14. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/project_functions.py +8 -0
  15. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/utilities.py +20 -3
  16. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/version.py +2 -2
  17. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/video_operations.py +4 -0
  18. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris_behav_obs.egg-info/PKG-INFO +3 -4
  19. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/pyproject.toml +1 -1
  20. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/LICENSE.TXT +0 -0
  21. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/MANIFEST.in +0 -0
  22. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/README.TXT +0 -0
  23. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/__init__.py +0 -0
  24. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/__main__.py +0 -0
  25. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/add_modifier.py +0 -0
  26. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/add_modifier_ui.py +0 -0
  27. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/advanced_event_filtering.py +0 -0
  28. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/__init__.py +0 -0
  29. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/_latency.py +0 -0
  30. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/irr_cohen_kappa.py +0 -0
  31. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +0 -0
  32. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/irr_weighted_cohen_kappa.py +0 -0
  33. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +0 -0
  34. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/list_of_dataframe_columns.py +0 -0
  35. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/number_of_occurences.py +0 -0
  36. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
  37. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/analysis_plugins/time_budget.py +0 -0
  38. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/behav_coding_map_creator.py +0 -0
  39. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/behavior_binary_table.py +0 -0
  40. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/behaviors_coding_map.py +0 -0
  41. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/boris_cli.py +0 -0
  42. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/cmd_arguments.py +0 -0
  43. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/coding_pad.py +0 -0
  44. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/connections.py +0 -0
  45. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/converters.py +0 -0
  46. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/converters_ui.py +0 -0
  47. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/cooccurence.py +0 -0
  48. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/core_qrc.py +0 -0
  49. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/core_ui.py +0 -0
  50. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/db_functions.py +0 -0
  51. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/dev.py +0 -0
  52. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/dialog.py +0 -0
  53. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/duration_widget.py +0 -0
  54. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/edit_event.py +0 -0
  55. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/edit_event_ui.py +0 -0
  56. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/event_operations.py +0 -0
  57. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/events_cursor.py +0 -0
  58. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/events_snapshots.py +0 -0
  59. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/exclusion_matrix.py +0 -0
  60. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/export_events.py +0 -0
  61. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/export_observation.py +0 -0
  62. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/external_processes.py +0 -0
  63. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/geometric_measurement.py +0 -0
  64. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/gui_utilities.py +0 -0
  65. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/image_overlay.py +0 -0
  66. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/import_observations.py +0 -0
  67. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/irr.py +0 -0
  68. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/latency.py +0 -0
  69. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/measurement_widget.py +0 -0
  70. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/media_file.py +0 -0
  71. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/menu_options.py +0 -0
  72. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/modifier_coding_map_creator.py +0 -0
  73. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/modifiers_coding_map.py +0 -0
  74. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/mpv.py +0 -0
  75. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/mpv2.py +0 -0
  76. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/observation_ui.py +0 -0
  77. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/observations_list.py +0 -0
  78. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/otx_parser.py +0 -0
  79. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/param_panel.py +0 -0
  80. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/param_panel_ui.py +0 -0
  81. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/player_dock_widget.py +0 -0
  82. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/plot_events.py +0 -0
  83. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/plot_events_rt.py +0 -0
  84. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/plot_spectrogram_rt.py +0 -0
  85. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/plot_waveform_rt.py +0 -0
  86. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/plugins.py +0 -0
  87. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/portion/__init__.py +0 -0
  88. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/portion/const.py +0 -0
  89. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/portion/dict.py +0 -0
  90. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/portion/func.py +0 -0
  91. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/portion/interval.py +0 -0
  92. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/portion/io.py +0 -0
  93. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/project_import_export.py +0 -0
  94. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/project_ui.py +0 -0
  95. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/qrc_boris.py +0 -0
  96. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/qrc_boris5.py +0 -0
  97. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/select_modifiers.py +0 -0
  98. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/select_observations.py +0 -0
  99. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/select_subj_behav.py +0 -0
  100. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/state_events.py +0 -0
  101. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/subjects_pad.py +0 -0
  102. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/synthetic_time_budget.py +0 -0
  103. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/time_budget_functions.py +0 -0
  104. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/time_budget_widget.py +0 -0
  105. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/transitions.py +0 -0
  106. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/video_equalizer.py +0 -0
  107. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/video_equalizer_ui.py +0 -0
  108. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/view_df.py +0 -0
  109. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/view_df_ui.py +0 -0
  110. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris/write_event.py +0 -0
  111. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris_behav_obs.egg-info/SOURCES.txt +0 -0
  112. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
  113. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris_behav_obs.egg-info/entry_points.txt +0 -0
  114. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris_behav_obs.egg-info/requires.txt +0 -0
  115. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/boris_behav_obs.egg-info/top_level.txt +0 -0
  116. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/setup.cfg +0 -0
  117. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_db_functions.py +0 -0
  118. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_export_observation.py +0 -0
  119. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_irr.py +0 -0
  120. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_observation_gui.py +0 -0
  121. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_otx_parser.py +0 -0
  122. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_preferences_gui.py +0 -0
  123. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_project_functions.py +0 -0
  124. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_time_budget.py +0 -0
  125. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_utilities.py +0 -0
  126. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.5}/tests/test_utilities2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boris-behav-obs
3
- Version: 9.7.1
3
+ Version: 9.7.5
4
4
  Summary: BORIS - Behavioral Observation Research Interactive Software
5
5
  Author-email: Olivier Friard <olivier.friard@unito.it>
6
6
  License-Expression: GPL-3.0-only
@@ -44,15 +44,14 @@ BORIS (Behavioral Observation Research Interactive Software)
44
44
 
45
45
  BORIS is an easy-to-use event logging software for video/audio coding or live observations.
46
46
 
47
- BORIS is a free and open-source software available for GNU/Linux and Windows.
48
- You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to run the last version of BORIS are available, see [BORIS on MacOS](https://www.boris.unito.it/download_mac).
47
+ BORIS is a free and open-source software available for GNU/Linux, Windows and macOS.
49
48
 
50
49
  It provides also some analysis tools like time budget and some plotting functions.
51
50
 
52
51
  <!-- The BO-RIS paper has more than [![BORIS citations counter](https://penelope.unito.it/friard/boris_scopus_citations.png) citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications. -->
53
52
 
54
53
 
55
- The BORIS paper has more than 2407 citations in peer-reviewed scientific publications.
54
+ The BORIS paper has more than 2423 citations in peer-reviewed scientific publications.
56
55
 
57
56
 
58
57
 
@@ -6,15 +6,14 @@ BORIS (Behavioral Observation Research Interactive Software)
6
6
 
7
7
  BORIS is an easy-to-use event logging software for video/audio coding or live observations.
8
8
 
9
- BORIS is a free and open-source software available for GNU/Linux and Windows.
10
- You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to run the last version of BORIS are available, see [BORIS on MacOS](https://www.boris.unito.it/download_mac).
9
+ BORIS is a free and open-source software available for GNU/Linux, Windows and macOS.
11
10
 
12
11
  It provides also some analysis tools like time budget and some plotting functions.
13
12
 
14
13
  <!-- The BO-RIS paper has more than [![BORIS citations counter](https://penelope.unito.it/friard/boris_scopus_citations.png) citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications. -->
15
14
 
16
15
 
17
- The BORIS paper has more than 2407 citations in peer-reviewed scientific publications.
16
+ The BORIS paper has more than 2423 citations in peer-reviewed scientific publications.
18
17
 
19
18
 
20
19
 
@@ -40,7 +40,7 @@ def actionAbout_activated(self):
40
40
  About dialog
41
41
  """
42
42
 
43
- programs_versions: list = ["MPV media player"]
43
+ programs_versions: list[str] = ["MPV media player"]
44
44
 
45
45
  mpv_lib_version, mpv_lib_file_path, mpv_api_version = util.mpv_lib_version()
46
46
  programs_versions.append(
@@ -53,13 +53,13 @@ def actionAbout_activated(self):
53
53
 
54
54
  # ffmpeg
55
55
  if self.ffmpeg_bin == "ffmpeg" and sys.platform.startswith("linux"):
56
- ffmpeg_true_path = subprocess.getoutput("which ffmpeg")
56
+ ffmpeg_true_path: str = subprocess.getoutput("which ffmpeg")
57
57
  else:
58
58
  ffmpeg_true_path = self.ffmpeg_bin
59
59
  programs_versions.extend(
60
60
  [
61
61
  "\nFFmpeg",
62
- subprocess.getoutput(f'"{self.ffmpeg_bin}" -version').split("\n")[0],
62
+ subprocess.getoutput(cmd=f'"{self.ffmpeg_bin}" -version').split(sep="\n")[0],
63
63
  f"Path: {ffmpeg_true_path}",
64
64
  "https://www.ffmpeg.org",
65
65
  ]
@@ -75,11 +75,11 @@ def actionAbout_activated(self):
75
75
  programs_versions.extend(["\nPandas", f"version {pd.__version__}", "https://pandas.pydata.org"])
76
76
 
77
77
  # graphviz
78
- gv_result = subprocess.getoutput("dot -V")
78
+ gv_result = subprocess.getoutput(cmd="dot -V")
79
79
 
80
80
  programs_versions.extend(["\nGraphViz", gv_result if "graphviz" in gv_result else "not installed", "https://www.graphviz.org/"])
81
81
 
82
- about_dialog = QMessageBox()
82
+ about_dialog: QMessageBox = QMessageBox()
83
83
  about_dialog.setIconPixmap(QPixmap(":/boris_unito"))
84
84
 
85
85
  about_dialog.setWindowTitle(f"About {cfg.programName}")
@@ -132,6 +132,8 @@ POINT_EVENT_PLOT_COLOR = "black"
132
132
 
133
133
  CHAR_FORBIDDEN_IN_MODIFIERS = "(|),`~"
134
134
 
135
+ FAST_FORWARD_DEFAULT_VALUE:float = 10.0
136
+
135
137
  ADAPT_FAST_JUMP = "adapt_fast_jump"
136
138
  ADAPT_FAST_JUMP_DEFAULT = False
137
139
 
@@ -419,6 +421,10 @@ MPV_HWDEC_AUTOSAFE = "auto-safe"
419
421
  MPV_HWDEC_OPTIONS = (MPV_HWDEC_AUTO, MPV_HWDEC_AUTOSAFE, MPV_HWDEC_NO)
420
422
  MPV_HWDEC_DEFAULT_VALUE = MPV_HWDEC_AUTO
421
423
 
424
+ # frame step size (disabled)
425
+ # FRAME_STEP_SIZE: str = "frame_step_size"
426
+ # FRAME_STEP_SIZE_DEFAULT_VALUE: int = 1
427
+
422
428
  ANALYSIS_PLUGINS = "analysis_plugins"
423
429
  EXCLUDED_PLUGINS = "excluded_plugins"
424
430
  PERSONAL_PLUGINS_DIR = "personal_plugins_dir"
@@ -730,6 +736,7 @@ INIT_PARAM = {
730
736
  MPV_HWDEC: MPV_HWDEC_DEFAULT_VALUE,
731
737
  PROJECT_FILE_INDENTATION: PROJECT_FILE_INDENTATION_DEFAULT_VALUE,
732
738
  f"{MEDIA} tw fields": MEDIA_TW_EVENTS_FIELDS_DEFAULT,
739
+ # FRAME_STEP_SIZE: FRAME_STEP_SIZE_DEFAULT_VALUE,
733
740
  }
734
741
 
735
742
  SDIS_EXT = "sds"
@@ -82,11 +82,11 @@ def read(self):
82
82
 
83
83
  logging.debug(f"time format: {self.timeFormat}")
84
84
 
85
- self.fast = 10
85
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
86
86
  try:
87
- self.fast = int(settings.value("Time/fast_forward_speed"))
87
+ self.fast = float(settings.value("Time/fast_forward_speed"))
88
88
  except Exception:
89
- self.fast = 10
89
+ self.fast = cfg.FAST_FORWARD_DEFAULT_VALUE
90
90
 
91
91
  logging.debug(f"Time/fast_forward_speed: {self.fast}")
92
92
 
@@ -43,7 +43,6 @@ import locale
43
43
  import tempfile
44
44
  import time
45
45
  import urllib.request
46
- from typing import Union, Tuple
47
46
  from decimal import Decimal as dec
48
47
  from decimal import ROUND_DOWN
49
48
  import gzip
@@ -242,7 +241,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
242
241
  current_player: int = 0 # id of the selected (left click) video player
243
242
 
244
243
  mem_media_name: str = "" # record current media name. Use to check if media changed
245
- mem_playlist_index: Union[int, None] = None
244
+ mem_playlist_index: int | None = None
246
245
  saved_state = None
247
246
  user_move_slider: bool = False
248
247
  observationId: str = "" # current observation id
@@ -739,7 +738,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
739
738
  text: str = "Behaviors to show in ethogram list",
740
739
  table: str = cfg.ETHOGRAM,
741
740
  behavior_type: list = cfg.STATE_EVENT_TYPES,
742
- ) -> Tuple[bool, list]:
741
+ ) -> tuple[bool, list]:
743
742
  """
744
743
  allow user to:
745
744
  filter behaviors in ethogram widget
@@ -3682,14 +3681,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3682
3681
  """
3683
3682
  show next frame
3684
3683
  """
3684
+ # frame_step_size = self.config_param.get(cfg.FRAME_STEP_SIZE, cfg.FRAME_STEP_SIZE_DEFAULT_VALUE)
3685
+
3685
3686
  if self.playerType == cfg.IMAGES:
3686
3687
  if self.image_idx < len(self.images_list) - 1:
3687
- self.image_idx += 1
3688
+ self.image_idx += 1 # frame_step_size
3688
3689
  self.extract_frame(self.dw_player[0])
3689
3690
 
3690
3691
  if self.playerType == cfg.MEDIA:
3691
3692
  for dw in self.dw_player:
3693
+ # for _ in range(frame_step_size):
3692
3694
  dw.player.frame_step()
3695
+ # time.sleep(0.5)
3693
3696
 
3694
3697
  if self.geometric_measurements_mode:
3695
3698
  self.extract_frame(dw)
@@ -3866,7 +3869,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3866
3869
  write_event.write_event(self, event, cumulative_time)
3867
3870
  # write_event.write_event(self, event, self.getLaps())
3868
3871
 
3869
- def get_frame_index(self, player_idx: int = 0) -> Union[int, str]:
3872
+ def get_frame_index(self, player_idx: int = 0) -> int | str:
3870
3873
  """
3871
3874
  returns frame index for player player_idx
3872
3875
  """
@@ -3947,7 +3950,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3947
3950
  slider_position = self.video_slider.value() / (cfg.SLIDER_MAXIMUM - 1)
3948
3951
  if self.dw_player[0].player.duration is None:
3949
3952
  return
3950
- video_position = slider_position * self.dw_player[0].player.duration
3953
+ print(f"{slider_position=}")
3954
+
3955
+ d = self.dw_player[0].player.duration
3956
+ print(f"{d=}")
3957
+ if d is None:
3958
+ return
3959
+ video_position = slider_position * d
3960
+ #video_position = slider_position * self.dw_player[0].player.duration
3951
3961
  # self.dw_player[0].player.command("seek", str(video_position), "absolute")
3952
3962
  self.dw_player[0].player.seek(video_position, "absolute")
3953
3963
 
@@ -4142,7 +4152,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4142
4152
  self.dw_player[n_player].player.playlist_pos = self.dw_player[n_player].player.playlist_count - 1
4143
4153
  self.seek_mediaplayer(self.dw_player[n_player].media_durations[-1], player=n_player)
4144
4154
 
4145
- def mpv_timer_out(self, value: Union[float, None] = None, scroll_slider=True):
4155
+ def mpv_timer_out(self, value: float | None = None, scroll_slider=True):
4146
4156
  """
4147
4157
  print the media current position and total length for MPV player
4148
4158
  scroll video slider to video position
@@ -4152,13 +4162,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4152
4162
  if not self.observationId:
4153
4163
  return
4154
4164
 
4155
- print("mpv timer out")
4156
-
4157
4165
  cumulative_time_pos = self.getLaps()
4158
- print(f"{cumulative_time_pos=}")
4159
4166
  # get frame index
4160
4167
  frame_idx = self.get_frame_index()
4161
- print(f"{frame_idx=}")
4162
4168
  # frame_idx = 0
4163
4169
 
4164
4170
  if value is None: # ipc mpv
@@ -4237,9 +4243,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4237
4243
  self.show_current_states_in_subjects_table()
4238
4244
 
4239
4245
  # current media name
4240
- print(f"{self.dw_player[0].player.playlist_pos=}")
4241
- print(f"{self.dw_player[0].player.playlist=}")
4242
-
4243
4246
  if self.dw_player[0].player.playlist_pos is not None:
4244
4247
  current_media_name = Path(self.dw_player[0].player.playlist[self.dw_player[0].player.playlist_pos]["filename"]).name
4245
4248
  current_playlist_index = self.dw_player[0].player.playlist_pos
@@ -4315,8 +4318,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4315
4318
 
4316
4319
  self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
4317
4320
 
4318
- print(f"{msg=}")
4319
-
4320
4321
  if msg:
4321
4322
  self.lb_current_media_time.setText(msg)
4322
4323
 
@@ -4488,7 +4489,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4488
4489
  for x in self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
4489
4490
  )
4490
4491
 
4491
- def choose_behavior(self, obs_key) -> Union[None, str]:
4492
+ def choose_behavior(self, obs_key) -> None | str:
4492
4493
  """
4493
4494
  fill listwidget with all behaviors coded by key
4494
4495
 
@@ -4520,7 +4521,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4520
4521
  else:
4521
4522
  return None
4522
4523
 
4523
- def choose_subject(self, subject_key) -> Union[None, str]:
4524
+ def choose_subject(self, subject_key) -> None | str:
4524
4525
  """
4525
4526
  fill listwidget with all subjects coded by key
4526
4527
 
@@ -4608,7 +4609,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4608
4609
 
4609
4610
  return dec(str(round(mem_laps / 1000, 3)))
4610
4611
 
4611
- def get_obs_time(self, n_player: int = 0) -> Tuple[dec, dec | None]:
4612
+ def get_obs_time(self, n_player: int = 0) -> tuple[dec, dec | None]:
4612
4613
  """
4613
4614
  returns time in current media and cumulative time from begining of observation
4614
4615
  do not add time offset
@@ -36,6 +36,7 @@ class IPC_MPV:
36
36
  self.process = subprocess.Popen(
37
37
  [
38
38
  "mpv",
39
+ "--ontop",
39
40
  "--no-border",
40
41
  "--osc=no", # no on screen commands
41
42
  "--input-ipc-server=" + self.socket_path,
@@ -48,30 +49,6 @@ class IPC_MPV:
48
49
  stderr=subprocess.PIPE,
49
50
  )
50
51
 
51
- '''
52
- def init_socket(self):
53
- """
54
- Initialize the JSON IPC socket.
55
- """
56
- print("init socket")
57
- self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
58
- QTimer.singleShot(5000, self.connect_socket) # Allow time for mpv to initialize
59
- '''
60
-
61
- '''
62
- def connect_socket(self):
63
- """
64
- Connect to the mpv IPC socket.
65
- """
66
- print("connect socket")
67
- try:
68
- self.sock.connect(self.socket_path)
69
- print("Connected to mpv IPC server.")
70
- except socket.error as e:
71
- print(f"Failed to connect to mpv IPC server: {e}")
72
- print("end of connect_socket fucntion")
73
- '''
74
-
75
52
  def send_command(self, command):
76
53
  """
77
54
  Send a JSON command to the mpv IPC server.
@@ -92,9 +69,7 @@ class IPC_MPV:
92
69
  # Parse the response as JSON
93
70
  response_data = json.loads(response.decode("utf-8"))
94
71
  if response_data["error"] != "success":
95
- print(f"send command: {command}")
96
- print(f"{response_data=}")
97
- print()
72
+ logging.warning(f"send command: {command} response data: {response_data}")
98
73
  # Return the 'data' field which contains the playback position
99
74
  return response_data.get("data")
100
75
  except FileNotFoundError:
@@ -128,7 +103,6 @@ class IPC_MPV:
128
103
 
129
104
  @pause.setter
130
105
  def pause(self, value):
131
- print(f"set pause to {value}")
132
106
  return self.send_command({"command": ["set_property", "pause", value]})
133
107
 
134
108
  @property
@@ -168,7 +142,6 @@ class IPC_MPV:
168
142
  @property
169
143
  def playback_time(self):
170
144
  playback_time_ = self.send_command({"command": ["get_property", "playback-time"]})
171
- print(f"playback_time: {playback_time_}")
172
145
  return playback_time_
173
146
 
174
147
  def frame_step(self):
@@ -42,6 +42,7 @@ from PySide6.QtWidgets import (
42
42
  QApplication,
43
43
  QMenu,
44
44
  QListWidgetItem,
45
+ QHeaderView
45
46
  )
46
47
 
47
48
  from . import config as cfg
@@ -75,7 +76,10 @@ class AssignConverter(QDialog):
75
76
  self.cbb[-1].addItems(["None"] + sorted(converters.keys()))
76
77
 
77
78
  if column_idx in col_conv:
78
- self.cbb[-1].setCurrentIndex((["None"] + sorted(converters.keys())).index(col_conv[column_idx]))
79
+ if col_conv[column_idx] in (["None"] + sorted(converters.keys())):
80
+ self.cbb[-1].setCurrentIndex((["None"] + sorted(converters.keys())).index(col_conv[column_idx]))
81
+ else:
82
+ self.cbb[-1].setCurrentIndex(0)
79
83
  else:
80
84
  self.cbb[-1].setCurrentIndex(0)
81
85
  hbox.addWidget(self.cbb[-1])
@@ -224,6 +228,10 @@ class Observation(QDialog, Ui_Form):
224
228
  self.pbCancel.clicked.connect(self.pbCancel_clicked)
225
229
 
226
230
  self.tw_data_files.cellDoubleClicked[int, int].connect(self.tw_data_files_cellDoubleClicked)
231
+ self.tw_data_files.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
232
+
233
+ self.twVideo1.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
234
+
227
235
 
228
236
  self.mediaDurations, self.mediaFPS, self.mediaHasVideo, self.mediaHasAudio, self.media_creation_time = {}, {}, {}, {}, {}
229
237
 
@@ -1964,7 +1964,7 @@ def initialize_new_media_observation(self) -> bool:
1964
1964
 
1965
1965
  menu_options.update_menu(self)
1966
1966
 
1967
- if not self.MPV_IPC_MODE:
1967
+ if self.MPV_IPC_MODE:
1968
1968
  # activate timer
1969
1969
  self.ipc_mpv_timer = QTimer()
1970
1970
  self.ipc_mpv_timer.setInterval(500)
@@ -134,6 +134,8 @@ class Plot_data(QWidget):
134
134
  column_converter=column_converter,
135
135
  )
136
136
 
137
+ print(f"{error_msg=}")
138
+
137
139
  if not result:
138
140
  self.error_msg = error_msg
139
141
  return
@@ -221,6 +221,9 @@ def preferences(self):
221
221
  preferencesWindow.cbConfirmSound.setChecked(self.confirmSound)
222
222
  # beep every
223
223
  preferencesWindow.sbBeepEvery.setValue(self.beep_every)
224
+ # frame step size
225
+ #preferencesWindow.sb_frame_step_size.setValue(self.config_param.get(cfg.FRAME_STEP_SIZE, cfg.FRAME_STEP_SIZE_DEFAULT_VALUE))
226
+
224
227
  # alert no focal subject
225
228
  preferencesWindow.cbAlertNoFocalSubject.setChecked(self.alertNoFocalSubject)
226
229
  # tracking cursor above event
@@ -418,6 +421,9 @@ def preferences(self):
418
421
 
419
422
  self.beep_every = preferencesWindow.sbBeepEvery.value()
420
423
 
424
+ # frame step size
425
+ #self.config_param[cfg.FRAME_STEP_SIZE] = preferencesWindow.sb_frame_step_size.value()
426
+
421
427
  self.alertNoFocalSubject = preferencesWindow.cbAlertNoFocalSubject.isChecked()
422
428
 
423
429
  self.trackingCursorAboveEvent = preferencesWindow.cbTrackingCursorAboveEvent.isChecked()
@@ -3,7 +3,7 @@
3
3
  ################################################################################
4
4
  ## Form generated from reading UI file 'preferences.ui'
5
5
  ##
6
- ## Created by: Qt User Interface Compiler version 6.9.0
6
+ ## Created by: Qt User Interface Compiler version 6.10.0
7
7
  ##
8
8
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
9
9
  ################################################################################
@@ -145,11 +145,8 @@ class Ui_prefDialog(object):
145
145
 
146
146
  self.horizontalLayout_4.addWidget(self.label_4)
147
147
 
148
- self.sbffSpeed = QSpinBox(self.tab_observations)
148
+ self.sbffSpeed = QDoubleSpinBox(self.tab_observations)
149
149
  self.sbffSpeed.setObjectName(u"sbffSpeed")
150
- self.sbffSpeed.setMinimum(0)
151
- self.sbffSpeed.setMaximum(10000)
152
- self.sbffSpeed.setValue(10)
153
150
 
154
151
  self.horizontalLayout_4.addWidget(self.sbffSpeed)
155
152
 
@@ -244,6 +241,26 @@ class Ui_prefDialog(object):
244
241
 
245
242
  self.verticalLayout.addWidget(self.cb_pause_before_addevent)
246
243
 
244
+ self.horizontalLayout_23 = QHBoxLayout()
245
+ self.horizontalLayout_23.setObjectName(u"horizontalLayout_23")
246
+ self.label_24 = QLabel(self.tab_observations)
247
+ self.label_24.setObjectName(u"label_24")
248
+ self.label_24.setEnabled(False)
249
+
250
+ self.horizontalLayout_23.addWidget(self.label_24)
251
+
252
+ self.sb_frame_step_size = QSpinBox(self.tab_observations)
253
+ self.sb_frame_step_size.setObjectName(u"sb_frame_step_size")
254
+ self.sb_frame_step_size.setEnabled(False)
255
+ self.sb_frame_step_size.setMinimum(1)
256
+ self.sb_frame_step_size.setMaximum(1000)
257
+ self.sb_frame_step_size.setValue(1)
258
+
259
+ self.horizontalLayout_23.addWidget(self.sb_frame_step_size)
260
+
261
+
262
+ self.verticalLayout.addLayout(self.horizontalLayout_23)
263
+
247
264
  self.verticalSpacer_4 = QSpacerItem(20, 391, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
248
265
 
249
266
  self.verticalLayout.addItem(self.verticalSpacer_4)
@@ -256,35 +273,35 @@ class Ui_prefDialog(object):
256
273
  self.splitter_2 = QSplitter(self.tab_analysis_plugins)
257
274
  self.splitter_2.setObjectName(u"splitter_2")
258
275
  self.splitter_2.setOrientation(Qt.Orientation.Horizontal)
259
- self.widget = QWidget(self.splitter_2)
260
- self.widget.setObjectName(u"widget")
261
- self.verticalLayout_11 = QVBoxLayout(self.widget)
276
+ self.layoutWidget = QWidget(self.splitter_2)
277
+ self.layoutWidget.setObjectName(u"layoutWidget")
278
+ self.verticalLayout_11 = QVBoxLayout(self.layoutWidget)
262
279
  self.verticalLayout_11.setObjectName(u"verticalLayout_11")
263
280
  self.verticalLayout_11.setContentsMargins(0, 0, 0, 0)
264
- self.label_13 = QLabel(self.widget)
281
+ self.label_13 = QLabel(self.layoutWidget)
265
282
  self.label_13.setObjectName(u"label_13")
266
283
 
267
284
  self.verticalLayout_11.addWidget(self.label_13)
268
285
 
269
- self.lv_all_plugins = QListWidget(self.widget)
286
+ self.lv_all_plugins = QListWidget(self.layoutWidget)
270
287
  self.lv_all_plugins.setObjectName(u"lv_all_plugins")
271
288
 
272
289
  self.verticalLayout_11.addWidget(self.lv_all_plugins)
273
290
 
274
- self.label_15 = QLabel(self.widget)
291
+ self.label_15 = QLabel(self.layoutWidget)
275
292
  self.label_15.setObjectName(u"label_15")
276
293
 
277
294
  self.verticalLayout_11.addWidget(self.label_15)
278
295
 
279
296
  self.horizontalLayout_16 = QHBoxLayout()
280
297
  self.horizontalLayout_16.setObjectName(u"horizontalLayout_16")
281
- self.le_personal_plugins_dir = QLineEdit(self.widget)
298
+ self.le_personal_plugins_dir = QLineEdit(self.layoutWidget)
282
299
  self.le_personal_plugins_dir.setObjectName(u"le_personal_plugins_dir")
283
300
  self.le_personal_plugins_dir.setReadOnly(True)
284
301
 
285
302
  self.horizontalLayout_16.addWidget(self.le_personal_plugins_dir)
286
303
 
287
- self.pb_browse_plugins_dir = QPushButton(self.widget)
304
+ self.pb_browse_plugins_dir = QPushButton(self.layoutWidget)
288
305
  self.pb_browse_plugins_dir.setObjectName(u"pb_browse_plugins_dir")
289
306
 
290
307
  self.horizontalLayout_16.addWidget(self.pb_browse_plugins_dir)
@@ -292,49 +309,49 @@ class Ui_prefDialog(object):
292
309
 
293
310
  self.verticalLayout_11.addLayout(self.horizontalLayout_16)
294
311
 
295
- self.lw_personal_plugins = QListWidget(self.widget)
312
+ self.lw_personal_plugins = QListWidget(self.layoutWidget)
296
313
  self.lw_personal_plugins.setObjectName(u"lw_personal_plugins")
297
314
 
298
315
  self.verticalLayout_11.addWidget(self.lw_personal_plugins)
299
316
 
300
- self.splitter_2.addWidget(self.widget)
317
+ self.splitter_2.addWidget(self.layoutWidget)
301
318
  self.splitter = QSplitter(self.splitter_2)
302
319
  self.splitter.setObjectName(u"splitter")
303
320
  self.splitter.setOrientation(Qt.Orientation.Vertical)
304
- self.widget1 = QWidget(self.splitter)
305
- self.widget1.setObjectName(u"widget1")
306
- self.verticalLayout_12 = QVBoxLayout(self.widget1)
321
+ self.layoutWidget1 = QWidget(self.splitter)
322
+ self.layoutWidget1.setObjectName(u"layoutWidget1")
323
+ self.verticalLayout_12 = QVBoxLayout(self.layoutWidget1)
307
324
  self.verticalLayout_12.setObjectName(u"verticalLayout_12")
308
325
  self.verticalLayout_12.setContentsMargins(0, 0, 0, 0)
309
- self.label_14 = QLabel(self.widget1)
326
+ self.label_14 = QLabel(self.layoutWidget1)
310
327
  self.label_14.setObjectName(u"label_14")
311
328
 
312
329
  self.verticalLayout_12.addWidget(self.label_14)
313
330
 
314
- self.pte_plugin_description = QPlainTextEdit(self.widget1)
331
+ self.pte_plugin_description = QPlainTextEdit(self.layoutWidget1)
315
332
  self.pte_plugin_description.setObjectName(u"pte_plugin_description")
316
333
  self.pte_plugin_description.setReadOnly(True)
317
334
 
318
335
  self.verticalLayout_12.addWidget(self.pte_plugin_description)
319
336
 
320
- self.splitter.addWidget(self.widget1)
321
- self.widget2 = QWidget(self.splitter)
322
- self.widget2.setObjectName(u"widget2")
323
- self.verticalLayout_14 = QVBoxLayout(self.widget2)
337
+ self.splitter.addWidget(self.layoutWidget1)
338
+ self.layoutWidget2 = QWidget(self.splitter)
339
+ self.layoutWidget2.setObjectName(u"layoutWidget2")
340
+ self.verticalLayout_14 = QVBoxLayout(self.layoutWidget2)
324
341
  self.verticalLayout_14.setObjectName(u"verticalLayout_14")
325
342
  self.verticalLayout_14.setContentsMargins(0, 0, 0, 0)
326
- self.label_23 = QLabel(self.widget2)
343
+ self.label_23 = QLabel(self.layoutWidget2)
327
344
  self.label_23.setObjectName(u"label_23")
328
345
 
329
346
  self.verticalLayout_14.addWidget(self.label_23)
330
347
 
331
- self.pte_plugin_code = QPlainTextEdit(self.widget2)
348
+ self.pte_plugin_code = QPlainTextEdit(self.layoutWidget2)
332
349
  self.pte_plugin_code.setObjectName(u"pte_plugin_code")
333
350
  self.pte_plugin_code.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
334
351
 
335
352
  self.verticalLayout_14.addWidget(self.pte_plugin_code)
336
353
 
337
- self.splitter.addWidget(self.widget2)
354
+ self.splitter.addWidget(self.layoutWidget2)
338
355
  self.splitter_2.addWidget(self.splitter)
339
356
 
340
357
  self.verticalLayout_15.addWidget(self.splitter_2)
@@ -676,7 +693,7 @@ class Ui_prefDialog(object):
676
693
 
677
694
  self.retranslateUi(prefDialog)
678
695
 
679
- self.tabWidget.setCurrentIndex(2)
696
+ self.tabWidget.setCurrentIndex(1)
680
697
 
681
698
 
682
699
  QMetaObject.connectSlotsByName(prefDialog)
@@ -707,6 +724,7 @@ class Ui_prefDialog(object):
707
724
  self.cbTrackingCursorAboveEvent.setText(QCoreApplication.translate("prefDialog", u"Tracking cursor above current event", None))
708
725
  self.cbAlertNoFocalSubject.setText(QCoreApplication.translate("prefDialog", u"Alert if focal subject is not set", None))
709
726
  self.cb_pause_before_addevent.setText(QCoreApplication.translate("prefDialog", u"Pause media before \"Add event\" command", None))
727
+ self.label_24.setText(QCoreApplication.translate("prefDialog", u"Frame step size", None))
710
728
  self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_observations), QCoreApplication.translate("prefDialog", u"Observations", None))
711
729
  self.label_13.setText(QCoreApplication.translate("prefDialog", u"BORIS plugins", None))
712
730
  self.label_15.setText(QCoreApplication.translate("prefDialog", u"Personal plugins", None))
@@ -1977,7 +1977,7 @@ class projectDialog(QDialog, Ui_dlgProject):
1977
1977
  self.pj[cfg.INDEPENDENT_VARIABLES] = dict(self.indVar)
1978
1978
 
1979
1979
  # converters
1980
- converters = {}
1980
+ converters:dict = {}
1981
1981
  for row in range(self.tw_converters.rowCount()):
1982
1982
  converters[self.tw_converters.item(row, 0).text()] = {
1983
1983
  "name": self.tw_converters.item(row, 0).text(),
@@ -1425,6 +1425,7 @@ def open_project_json(project_file_name: str) -> tuple:
1425
1425
  pj[cfg.OBSERVATIONS][obs][cfg.TYPE] = cfg.MEDIA
1426
1426
 
1427
1427
  # convert old media list in new one
1428
+ d1: dict = {}
1428
1429
  if len(pj[cfg.OBSERVATIONS][obs][cfg.FILE]):
1429
1430
  d1 = {cfg.PLAYER1: [pj[cfg.OBSERVATIONS][obs][cfg.FILE][0]]}
1430
1431
 
@@ -1470,6 +1471,13 @@ def open_project_json(project_file_name: str) -> tuple:
1470
1471
  pj[cfg.PROJECT_VERSION] = cfg.project_format_version
1471
1472
  projectChanged = True
1472
1473
 
1474
+ # check if behavioral categories are stored as a list
1475
+ if isinstance(pj[cfg.BEHAVIORAL_CATEGORIES_CONF], list):
1476
+ # convert to dict
1477
+ pj[cfg.BEHAVIORAL_CATEGORIES_CONF] = {str(idx): {"name": bc} for idx, bc in enumerate(pj[cfg.BEHAVIORAL_CATEGORIES_CONF])}
1478
+ logging.info("Behavioral categories was converted from a list to a dictionary")
1479
+ projectChanged = True
1480
+
1473
1481
  # add category key if not found
1474
1482
  for idx in pj[cfg.ETHOGRAM]:
1475
1483
  if cfg.BEHAVIOR_CATEGORY not in pj[cfg.ETHOGRAM][idx]:
@@ -471,20 +471,37 @@ def txt2np_array(
471
471
  conv_name = column_converter[column_idx]
472
472
 
473
473
  function = f"""def {conv_name}(INPUT):\n"""
474
- function += """ INPUT = INPUT.decode("utf-8") if isinstance(INPUT, bytes) else INPUT"""
474
+ function += """ INPUT = INPUT.decode("utf-8") if isinstance(INPUT, bytes) else INPUT\n\n"""
475
475
  for line in converters[conv_name]["code"].split("\n"):
476
476
  function += f" {line}\n"
477
477
  function += """ return OUTPUT"""
478
478
 
479
+
480
+ print('=============')
481
+ print(function)
482
+ print('=============')
483
+
484
+ import types
485
+ mod = types.ModuleType("converter_module")
486
+ exec(function, mod.__dict__)
487
+
488
+ '''
479
489
  try:
480
490
  exec(function)
481
491
  except Exception:
482
492
  return False, f"error in converter: {sys.exc_info()[1]}", np.array([])
483
493
 
484
- np_converters[column_idx - 1] = locals()[conv_name]
494
+ print(f"{converters=}")
495
+ print(f"{column_converter=}")
496
+ print(locals())
497
+ print(f"{conv_name=}")
498
+ '''
499
+
500
+ #np_converters[column_idx - 1] = locals()['conv_name']
501
+ np_converters[column_idx - 1] = getattr(mod, conv_name)
485
502
 
486
503
  else:
487
- return False, f"converter {cfg.converters_param[column_idx]} not found", np.array([])
504
+ return False, f"converter {column_converter[column_idx]} not found", np.array([])
488
505
 
489
506
  # snif txt file
490
507
  try:
@@ -20,5 +20,5 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- __version__ = "9.7.1"
24
- __version_date__ = "2025-10-22"
23
+ __version__ = "9.7.5"
24
+ __version_date__ = "2025-11-07"
@@ -231,6 +231,10 @@ def display_zoom_level(self) -> None:
231
231
  """
232
232
  msg: str = "Zoom level: <b>"
233
233
  for player in self.dw_player:
234
+ vz = player.player.video_zoom
235
+ if vz is None:
236
+ self.lb_zoom_level.setText("-")
237
+ return
234
238
  msg += f"{2**player.player.video_zoom:.1f} "
235
239
  msg += "</b>"
236
240
  self.lb_zoom_level.setText(msg)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boris-behav-obs
3
- Version: 9.7.1
3
+ Version: 9.7.5
4
4
  Summary: BORIS - Behavioral Observation Research Interactive Software
5
5
  Author-email: Olivier Friard <olivier.friard@unito.it>
6
6
  License-Expression: GPL-3.0-only
@@ -44,15 +44,14 @@ BORIS (Behavioral Observation Research Interactive Software)
44
44
 
45
45
  BORIS is an easy-to-use event logging software for video/audio coding or live observations.
46
46
 
47
- BORIS is a free and open-source software available for GNU/Linux and Windows.
48
- You can not longer run BORIS natively on MacOS (since v.8). Some alternatives to run the last version of BORIS are available, see [BORIS on MacOS](https://www.boris.unito.it/download_mac).
47
+ BORIS is a free and open-source software available for GNU/Linux, Windows and macOS.
49
48
 
50
49
  It provides also some analysis tools like time budget and some plotting functions.
51
50
 
52
51
  <!-- The BO-RIS paper has more than [![BORIS citations counter](https://penelope.unito.it/friard/boris_scopus_citations.png) citations](https://www.boris.unito.it/citations) in peer-reviewed scientific publications. -->
53
52
 
54
53
 
55
- The BORIS paper has more than 2407 citations in peer-reviewed scientific publications.
54
+ The BORIS paper has more than 2423 citations in peer-reviewed scientific publications.
56
55
 
57
56
 
58
57
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "boris-behav-obs"
3
- version = "9.7.1"
3
+ version = "9.7.5"
4
4
  description = "BORIS - Behavioral Observation Research Interactive Software"
5
5
  authors = [{ name="Olivier Friard", email="olivier.friard@unito.it" }]
6
6
  readme = "README.md"