boris-behav-obs 9.7.1__tar.gz → 9.7.7__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.7}/PKG-INFO +3 -4
  2. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/README.md +2 -3
  3. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/about.py +5 -5
  4. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/add_modifier_ui.py +47 -29
  5. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/config.py +7 -0
  6. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/config_file.py +3 -3
  7. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/core.py +89 -55
  8. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/ipc_mpv.py +52 -31
  9. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/observation.py +8 -1
  10. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/observation_operations.py +23 -6
  11. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/plot_data_module.py +2 -0
  12. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/preferences.py +9 -0
  13. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/preferences_ui.py +46 -28
  14. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/project.py +1 -1
  15. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/project_functions.py +27 -12
  16. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/utilities.py +23 -4
  17. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/version.py +2 -2
  18. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/video_operations.py +8 -1
  19. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris_behav_obs.egg-info/PKG-INFO +3 -4
  20. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/pyproject.toml +1 -1
  21. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/LICENSE.TXT +0 -0
  22. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/MANIFEST.in +0 -0
  23. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/README.TXT +0 -0
  24. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/__init__.py +0 -0
  25. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/__main__.py +0 -0
  26. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/add_modifier.py +0 -0
  27. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/advanced_event_filtering.py +0 -0
  28. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/__init__.py +0 -0
  29. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/_latency.py +0 -0
  30. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/irr_cohen_kappa.py +0 -0
  31. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +0 -0
  32. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/irr_weighted_cohen_kappa.py +0 -0
  33. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +0 -0
  34. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/list_of_dataframe_columns.py +0 -0
  35. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/number_of_occurences.py +0 -0
  36. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
  37. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/analysis_plugins/time_budget.py +0 -0
  38. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/behav_coding_map_creator.py +0 -0
  39. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/behavior_binary_table.py +0 -0
  40. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/behaviors_coding_map.py +0 -0
  41. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/boris_cli.py +0 -0
  42. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/cmd_arguments.py +0 -0
  43. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/coding_pad.py +0 -0
  44. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/connections.py +0 -0
  45. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/converters.py +0 -0
  46. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/converters_ui.py +0 -0
  47. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/cooccurence.py +0 -0
  48. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/core_qrc.py +0 -0
  49. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/core_ui.py +0 -0
  50. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/db_functions.py +0 -0
  51. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/dev.py +0 -0
  52. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/dialog.py +0 -0
  53. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/duration_widget.py +0 -0
  54. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/edit_event.py +0 -0
  55. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/edit_event_ui.py +0 -0
  56. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/event_operations.py +0 -0
  57. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/events_cursor.py +0 -0
  58. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/events_snapshots.py +0 -0
  59. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/exclusion_matrix.py +0 -0
  60. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/export_events.py +0 -0
  61. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/export_observation.py +0 -0
  62. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/external_processes.py +0 -0
  63. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/geometric_measurement.py +0 -0
  64. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/gui_utilities.py +0 -0
  65. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/image_overlay.py +0 -0
  66. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/import_observations.py +0 -0
  67. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/irr.py +0 -0
  68. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/latency.py +0 -0
  69. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/measurement_widget.py +0 -0
  70. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/media_file.py +0 -0
  71. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/menu_options.py +0 -0
  72. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/modifier_coding_map_creator.py +0 -0
  73. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/modifiers_coding_map.py +0 -0
  74. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/mpv.py +0 -0
  75. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/mpv2.py +0 -0
  76. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/observation_ui.py +0 -0
  77. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/observations_list.py +0 -0
  78. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/otx_parser.py +0 -0
  79. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/param_panel.py +0 -0
  80. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/param_panel_ui.py +0 -0
  81. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/player_dock_widget.py +0 -0
  82. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/plot_events.py +0 -0
  83. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/plot_events_rt.py +0 -0
  84. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/plot_spectrogram_rt.py +0 -0
  85. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/plot_waveform_rt.py +0 -0
  86. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/plugins.py +0 -0
  87. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/portion/__init__.py +0 -0
  88. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/portion/const.py +0 -0
  89. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/portion/dict.py +0 -0
  90. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/portion/func.py +0 -0
  91. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/portion/interval.py +0 -0
  92. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/portion/io.py +0 -0
  93. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/project_import_export.py +0 -0
  94. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/project_ui.py +0 -0
  95. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/qrc_boris.py +0 -0
  96. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/qrc_boris5.py +0 -0
  97. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/select_modifiers.py +0 -0
  98. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/select_observations.py +0 -0
  99. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/select_subj_behav.py +0 -0
  100. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/state_events.py +0 -0
  101. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/subjects_pad.py +0 -0
  102. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/synthetic_time_budget.py +0 -0
  103. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/time_budget_functions.py +0 -0
  104. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/time_budget_widget.py +0 -0
  105. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/transitions.py +0 -0
  106. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/video_equalizer.py +0 -0
  107. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/video_equalizer_ui.py +0 -0
  108. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/view_df.py +0 -0
  109. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/view_df_ui.py +0 -0
  110. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris/write_event.py +0 -0
  111. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris_behav_obs.egg-info/SOURCES.txt +0 -0
  112. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
  113. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris_behav_obs.egg-info/entry_points.txt +0 -0
  114. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris_behav_obs.egg-info/requires.txt +0 -0
  115. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/boris_behav_obs.egg-info/top_level.txt +0 -0
  116. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/setup.cfg +0 -0
  117. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_db_functions.py +0 -0
  118. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_export_observation.py +0 -0
  119. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_irr.py +0 -0
  120. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_observation_gui.py +0 -0
  121. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_otx_parser.py +0 -0
  122. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_preferences_gui.py +0 -0
  123. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_project_functions.py +0 -0
  124. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_time_budget.py +0 -0
  125. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/tests/test_utilities.py +0 -0
  126. {boris_behav_obs-9.7.1 → boris_behav_obs-9.7.7}/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.7
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}")
@@ -3,7 +3,7 @@
3
3
  ################################################################################
4
4
  ## Form generated from reading UI file 'add_modifier.ui'
5
5
  ##
6
- ## Created by: Qt User Interface Compiler version 6.8.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
  ################################################################################
@@ -24,18 +24,16 @@ class Ui_Dialog(object):
24
24
  def setupUi(self, Dialog):
25
25
  if not Dialog.objectName():
26
26
  Dialog.setObjectName(u"Dialog")
27
- Dialog.resize(1088, 654)
28
- self.verticalLayout_5 = QVBoxLayout(Dialog)
29
- self.verticalLayout_5.setObjectName(u"verticalLayout_5")
27
+ Dialog.resize(1339, 789)
28
+ self.verticalLayout_4 = QVBoxLayout(Dialog)
29
+ self.verticalLayout_4.setObjectName(u"verticalLayout_4")
30
30
  self.cb_ask_at_stop = QCheckBox(Dialog)
31
31
  self.cb_ask_at_stop.setObjectName(u"cb_ask_at_stop")
32
32
 
33
- self.verticalLayout_5.addWidget(self.cb_ask_at_stop)
33
+ self.verticalLayout_4.addWidget(self.cb_ask_at_stop)
34
34
 
35
- self.verticalLayout_4 = QVBoxLayout()
36
- self.verticalLayout_4.setObjectName(u"verticalLayout_4")
37
- self.horizontalLayout_5 = QHBoxLayout()
38
- self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
35
+ self.horizontalLayout_8 = QHBoxLayout()
36
+ self.horizontalLayout_8.setObjectName(u"horizontalLayout_8")
39
37
  self.verticalLayout_2 = QVBoxLayout()
40
38
  self.verticalLayout_2.setObjectName(u"verticalLayout_2")
41
39
  self.lbModifier = QLabel(Dialog)
@@ -69,7 +67,7 @@ class Ui_Dialog(object):
69
67
  self.verticalLayout_2.addItem(self.verticalSpacer)
70
68
 
71
69
 
72
- self.horizontalLayout_5.addLayout(self.verticalLayout_2)
70
+ self.horizontalLayout_8.addLayout(self.verticalLayout_2)
73
71
 
74
72
  self.verticalLayout_3 = QVBoxLayout()
75
73
  self.verticalLayout_3.setObjectName(u"verticalLayout_3")
@@ -88,7 +86,7 @@ class Ui_Dialog(object):
88
86
  self.verticalLayout_3.addItem(self.verticalSpacer_2)
89
87
 
90
88
 
91
- self.horizontalLayout_5.addLayout(self.verticalLayout_3)
89
+ self.horizontalLayout_8.addLayout(self.verticalLayout_3)
92
90
 
93
91
  self.verticalLayout = QVBoxLayout()
94
92
  self.verticalLayout.setObjectName(u"verticalLayout")
@@ -102,30 +100,42 @@ class Ui_Dialog(object):
102
100
 
103
101
  self.verticalLayout.addWidget(self.tabWidgetModifiersSets)
104
102
 
103
+ self.horizontalLayout_5 = QHBoxLayout()
104
+ self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
105
105
  self.lb_name = QLabel(Dialog)
106
106
  self.lb_name.setObjectName(u"lb_name")
107
107
 
108
- self.verticalLayout.addWidget(self.lb_name)
108
+ self.horizontalLayout_5.addWidget(self.lb_name)
109
109
 
110
110
  self.le_name = QLineEdit(Dialog)
111
111
  self.le_name.setObjectName(u"le_name")
112
112
 
113
- self.verticalLayout.addWidget(self.le_name)
113
+ self.horizontalLayout_5.addWidget(self.le_name)
114
114
 
115
+
116
+ self.verticalLayout.addLayout(self.horizontalLayout_5)
117
+
118
+ self.horizontalLayout_6 = QHBoxLayout()
119
+ self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
115
120
  self.lb_description = QLabel(Dialog)
116
121
  self.lb_description.setObjectName(u"lb_description")
117
122
 
118
- self.verticalLayout.addWidget(self.lb_description)
123
+ self.horizontalLayout_6.addWidget(self.lb_description)
119
124
 
120
125
  self.le_description = QLineEdit(Dialog)
121
126
  self.le_description.setObjectName(u"le_description")
122
127
 
123
- self.verticalLayout.addWidget(self.le_description)
128
+ self.horizontalLayout_6.addWidget(self.le_description)
129
+
124
130
 
131
+ self.verticalLayout.addLayout(self.horizontalLayout_6)
132
+
133
+ self.horizontalLayout_7 = QHBoxLayout()
134
+ self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
125
135
  self.lbType = QLabel(Dialog)
126
136
  self.lbType.setObjectName(u"lbType")
127
137
 
128
- self.verticalLayout.addWidget(self.lbType)
138
+ self.horizontalLayout_7.addWidget(self.lbType)
129
139
 
130
140
  self.cbType = QComboBox(Dialog)
131
141
  self.cbType.addItem("")
@@ -134,7 +144,14 @@ class Ui_Dialog(object):
134
144
  self.cbType.addItem("")
135
145
  self.cbType.setObjectName(u"cbType")
136
146
 
137
- self.verticalLayout.addWidget(self.cbType)
147
+ self.horizontalLayout_7.addWidget(self.cbType)
148
+
149
+ self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
150
+
151
+ self.horizontalLayout_7.addItem(self.horizontalSpacer_3)
152
+
153
+
154
+ self.verticalLayout.addLayout(self.horizontalLayout_7)
138
155
 
139
156
  self.lbValues = QLabel(Dialog)
140
157
  self.lbValues.setObjectName(u"lbValues")
@@ -198,28 +215,32 @@ class Ui_Dialog(object):
198
215
 
199
216
  self.horizontalLayout_4 = QHBoxLayout()
200
217
  self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
201
-
202
- self.verticalLayout.addLayout(self.horizontalLayout_4)
203
-
204
218
  self.pb_add_subjects = QPushButton(Dialog)
205
219
  self.pb_add_subjects.setObjectName(u"pb_add_subjects")
206
220
 
207
- self.verticalLayout.addWidget(self.pb_add_subjects)
221
+ self.horizontalLayout_4.addWidget(self.pb_add_subjects)
208
222
 
209
223
  self.pb_load_file = QPushButton(Dialog)
210
224
  self.pb_load_file.setObjectName(u"pb_load_file")
211
225
 
212
- self.verticalLayout.addWidget(self.pb_load_file)
226
+ self.horizontalLayout_4.addWidget(self.pb_load_file)
227
+
228
+ self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
229
+
230
+ self.horizontalLayout_4.addItem(self.horizontalSpacer_2)
231
+
232
+
233
+ self.verticalLayout.addLayout(self.horizontalLayout_4)
213
234
 
214
235
  self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
215
236
 
216
237
  self.verticalLayout.addItem(self.verticalSpacer_3)
217
238
 
218
239
 
219
- self.horizontalLayout_5.addLayout(self.verticalLayout)
240
+ self.horizontalLayout_8.addLayout(self.verticalLayout)
220
241
 
221
242
 
222
- self.verticalLayout_4.addLayout(self.horizontalLayout_5)
243
+ self.verticalLayout_4.addLayout(self.horizontalLayout_8)
223
244
 
224
245
  self.horizontalLayout_2 = QHBoxLayout()
225
246
  self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
@@ -241,9 +262,6 @@ class Ui_Dialog(object):
241
262
  self.verticalLayout_4.addLayout(self.horizontalLayout_2)
242
263
 
243
264
 
244
- self.verticalLayout_5.addLayout(self.verticalLayout_4)
245
-
246
-
247
265
  self.retranslateUi(Dialog)
248
266
 
249
267
  self.tabWidgetModifiersSets.setCurrentIndex(-1)
@@ -256,8 +274,8 @@ class Ui_Dialog(object):
256
274
  Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Set modifiers", None))
257
275
  self.cb_ask_at_stop.setText(QCoreApplication.translate("Dialog", u"Ask for modifier(s) when behavior stops", None))
258
276
  self.lbModifier.setText(QCoreApplication.translate("Dialog", u"Modifier", None))
259
- self.lbCode.setText(QCoreApplication.translate("Dialog", u"Key code", None))
260
- self.lbCodeHelp.setText(QCoreApplication.translate("Dialog", u"Key code is case sensitive. Type one character or a function key (F1, F2... F12)", None))
277
+ self.lbCode.setText(QCoreApplication.translate("Dialog", u"Shortcut", None))
278
+ self.lbCodeHelp.setText(QCoreApplication.translate("Dialog", u"The shortcut is case sensitive. Type one character or a function key (F1, F2... F12)", None))
261
279
  self.pbAddModifier.setText(QCoreApplication.translate("Dialog", u"->", None))
262
280
  self.pbModifyModifier.setText(QCoreApplication.translate("Dialog", u"<-", None))
263
281
  self.lb_name.setText(QCoreApplication.translate("Dialog", u"Set name", None))
@@ -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
 
@@ -41,9 +41,9 @@ from PIL.ImageQt import Image
41
41
  import subprocess
42
42
  import locale
43
43
  import tempfile
44
+ import math
44
45
  import time
45
46
  import urllib.request
46
- from typing import Union, Tuple
47
47
  from decimal import Decimal as dec
48
48
  from decimal import ROUND_DOWN
49
49
  import gzip
@@ -54,17 +54,7 @@ import shutil
54
54
 
55
55
  matplotlib.use("QtAgg")
56
56
 
57
- from PySide6.QtCore import (
58
- Qt,
59
- QPoint,
60
- Signal,
61
- QEvent,
62
- QDateTime,
63
- QUrl,
64
- QAbstractTableModel,
65
- QElapsedTimer,
66
- QSettings,
67
- )
57
+ from PySide6.QtCore import Qt, QPoint, Signal, QEvent, QDateTime, QUrl, QAbstractTableModel, QElapsedTimer, QSettings, QTimer
68
58
  from PySide6.QtGui import QIcon, QPixmap, QFont, QKeyEvent, QDesktopServices, QColor, QPainter, QPolygon, QAction
69
59
  from PySide6.QtMultimedia import QSoundEffect
70
60
  from PySide6.QtWidgets import (
@@ -242,7 +232,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
242
232
  current_player: int = 0 # id of the selected (left click) video player
243
233
 
244
234
  mem_media_name: str = "" # record current media name. Use to check if media changed
245
- mem_playlist_index: Union[int, None] = None
235
+ mem_playlist_index: int | None = None
246
236
  saved_state = None
247
237
  user_move_slider: bool = False
248
238
  observationId: str = "" # current observation id
@@ -739,7 +729,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
739
729
  text: str = "Behaviors to show in ethogram list",
740
730
  table: str = cfg.ETHOGRAM,
741
731
  behavior_type: list = cfg.STATE_EVENT_TYPES,
742
- ) -> Tuple[bool, list]:
732
+ ) -> tuple[bool, list]:
743
733
  """
744
734
  allow user to:
745
735
  filter behaviors in ethogram widget
@@ -1510,15 +1500,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1510
1500
  logging.debug("previous media file")
1511
1501
 
1512
1502
  if self.playerType == cfg.MEDIA:
1513
- if len(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][cfg.PLAYER1]) == 1:
1514
- return
1503
+ #if len(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][cfg.PLAYER1]) == 1:
1504
+ # self.seek_mediaplayer(dec(0))
1505
+ # return
1515
1506
 
1516
1507
  # check if media not first media
1517
1508
  if self.dw_player[0].player.playlist_pos > 0:
1518
1509
  self.dw_player[0].player.playlist_prev()
1519
1510
 
1520
1511
  elif self.dw_player[0].player.playlist_count == 1:
1521
- self.statusbar.showMessage("There is only one media file", 5000)
1512
+ self.seek_mediaplayer(dec(0))
1513
+ #self.statusbar.showMessage("There is only one media file", 5000)
1522
1514
 
1523
1515
  if hasattr(self, "spectro"):
1524
1516
  self.spectro.memChunk = -1
@@ -3682,14 +3674,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3682
3674
  """
3683
3675
  show next frame
3684
3676
  """
3677
+ # frame_step_size = self.config_param.get(cfg.FRAME_STEP_SIZE, cfg.FRAME_STEP_SIZE_DEFAULT_VALUE)
3678
+
3685
3679
  if self.playerType == cfg.IMAGES:
3686
3680
  if self.image_idx < len(self.images_list) - 1:
3687
- self.image_idx += 1
3681
+ self.image_idx += 1 # frame_step_size
3688
3682
  self.extract_frame(self.dw_player[0])
3689
3683
 
3690
3684
  if self.playerType == cfg.MEDIA:
3691
3685
  for dw in self.dw_player:
3686
+ # for _ in range(frame_step_size):
3692
3687
  dw.player.frame_step()
3688
+ # time.sleep(0.5)
3693
3689
 
3694
3690
  if self.geometric_measurements_mode:
3695
3691
  self.extract_frame(dw)
@@ -3866,7 +3862,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3866
3862
  write_event.write_event(self, event, cumulative_time)
3867
3863
  # write_event.write_event(self, event, self.getLaps())
3868
3864
 
3869
- def get_frame_index(self, player_idx: int = 0) -> Union[int, str]:
3865
+ def get_frame_index(self, player_idx: int = 0) -> int | str:
3870
3866
  """
3871
3867
  returns frame index for player player_idx
3872
3868
  """
@@ -3947,7 +3943,14 @@ class MainWindow(QMainWindow, Ui_MainWindow):
3947
3943
  slider_position = self.video_slider.value() / (cfg.SLIDER_MAXIMUM - 1)
3948
3944
  if self.dw_player[0].player.duration is None:
3949
3945
  return
3950
- video_position = slider_position * self.dw_player[0].player.duration
3946
+ print(f"{slider_position=}")
3947
+
3948
+ d = self.dw_player[0].player.duration
3949
+ print(f"{d=}")
3950
+ if d is None:
3951
+ return
3952
+ video_position = slider_position * d
3953
+ # video_position = slider_position * self.dw_player[0].player.duration
3951
3954
  # self.dw_player[0].player.command("seek", str(video_position), "absolute")
3952
3955
  self.dw_player[0].player.seek(video_position, "absolute")
3953
3956
 
@@ -4142,7 +4145,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4142
4145
  self.dw_player[n_player].player.playlist_pos = self.dw_player[n_player].player.playlist_count - 1
4143
4146
  self.seek_mediaplayer(self.dw_player[n_player].media_durations[-1], player=n_player)
4144
4147
 
4145
- def mpv_timer_out(self, value: Union[float, None] = None, scroll_slider=True):
4148
+ def activate_main_window(self):
4149
+ """
4150
+ activate main window in order to capture keyboard events
4151
+ called only in IPC mode
4152
+ """
4153
+ # check if eof reached
4154
+ #print(f"{self.dw_player[0].player.playlist_pos=}")
4155
+ #print(f"{self.dw_player[0].player.playlist_count=}")
4156
+ if self.dw_player[0].player.eof_reached and self.dw_player[0].player.core_idle:
4157
+ logging.debug("end of playlist reached")
4158
+ if self.dw_player[0].player.playlist_pos is not None and self.dw_player[0].player.playlist_count is not None:
4159
+ if self.dw_player[0].player.playlist_pos == self.dw_player[0].player.playlist_count - 1:
4160
+ self.pause_video()
4161
+ self.activateWindow()
4162
+
4163
+ def mpv_timer_out(self, value: float | None = None, scroll_slider=True):
4146
4164
  """
4147
4165
  print the media current position and total length for MPV player
4148
4166
  scroll video slider to video position
@@ -4152,13 +4170,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4152
4170
  if not self.observationId:
4153
4171
  return
4154
4172
 
4155
- print("mpv timer out")
4156
-
4157
4173
  cumulative_time_pos = self.getLaps()
4158
- print(f"{cumulative_time_pos=}")
4159
4174
  # get frame index
4160
4175
  frame_idx = self.get_frame_index()
4161
- print(f"{frame_idx=}")
4162
4176
  # frame_idx = 0
4163
4177
 
4164
4178
  if value is None: # ipc mpv
@@ -4205,8 +4219,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4205
4219
  ct = self.getLaps(n_player=n_player)
4206
4220
 
4207
4221
  # sync players 2..8 if time diff >= 1 s
4208
- if abs(ct0 - (ct + dec(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO][cfg.OFFSET][str(n_player + 1)]))) >= 1:
4209
- self.sync_time(n_player, ct0) # self.seek_mediaplayer(ct0, n_player)
4222
+ if not math.isnan(ct) and not math.isnan(ct0):
4223
+ if abs(ct0 - (ct + dec(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.MEDIA_INFO][cfg.OFFSET][str(n_player + 1)]))) >= 1:
4224
+ self.sync_time(n_player, ct0) # self.seek_mediaplayer(ct0, n_player)
4210
4225
 
4211
4226
  currentTimeOffset = dec(cumulative_time_pos + self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TIME_OFFSET])
4212
4227
 
@@ -4237,12 +4252,11 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4237
4252
  self.show_current_states_in_subjects_table()
4238
4253
 
4239
4254
  # current media name
4240
- print(f"{self.dw_player[0].player.playlist_pos=}")
4241
- print(f"{self.dw_player[0].player.playlist=}")
4242
-
4243
- if self.dw_player[0].player.playlist_pos is not None:
4244
- current_media_name = Path(self.dw_player[0].player.playlist[self.dw_player[0].player.playlist_pos]["filename"]).name
4245
- current_playlist_index = self.dw_player[0].player.playlist_pos
4255
+ playlist = self.dw_player[0].player.playlist
4256
+ playlist_pos = self.dw_player[0].player.playlist_pos
4257
+ if playlist is not None and playlist_pos is not None:
4258
+ current_media_name = Path(playlist[playlist_pos]["filename"]).name
4259
+ current_playlist_index = playlist_pos
4246
4260
  else:
4247
4261
  current_media_name = ""
4248
4262
  current_playlist_index = None
@@ -4315,8 +4329,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4315
4329
 
4316
4330
  self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
4317
4331
 
4318
- print(f"{msg=}")
4319
-
4320
4332
  if msg:
4321
4333
  self.lb_current_media_time.setText(msg)
4322
4334
 
@@ -4488,7 +4500,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4488
4500
  for x in self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]
4489
4501
  )
4490
4502
 
4491
- def choose_behavior(self, obs_key) -> Union[None, str]:
4503
+ def choose_behavior(self, obs_key) -> None | str:
4492
4504
  """
4493
4505
  fill listwidget with all behaviors coded by key
4494
4506
 
@@ -4520,7 +4532,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4520
4532
  else:
4521
4533
  return None
4522
4534
 
4523
- def choose_subject(self, subject_key) -> Union[None, str]:
4535
+ def choose_subject(self, subject_key) -> None | str:
4524
4536
  """
4525
4537
  fill listwidget with all subjects coded by key
4526
4538
 
@@ -4602,13 +4614,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4602
4614
 
4603
4615
  if self.playerType == cfg.MEDIA:
4604
4616
  # cumulative time
4617
+ if self.dw_player[n_player].player.time_pos is None:
4618
+ return dec("NaN")
4605
4619
  mem_laps = sum(self.dw_player[n_player].media_durations[0 : self.dw_player[n_player].player.playlist_pos]) + (
4606
4620
  0 if self.dw_player[n_player].player.time_pos is None else self.dw_player[n_player].player.time_pos * 1000
4607
4621
  )
4608
4622
 
4609
4623
  return dec(str(round(mem_laps / 1000, 3)))
4610
4624
 
4611
- def get_obs_time(self, n_player: int = 0) -> Tuple[dec, dec | None]:
4625
+ def get_obs_time(self, n_player: int = 0) -> tuple[dec, dec | None]:
4612
4626
  """
4613
4627
  returns time in current media and cumulative time from begining of observation
4614
4628
  do not add time offset
@@ -4621,7 +4635,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
4621
4635
  """
4622
4636
 
4623
4637
  if not self.observationId:
4624
- return dec("0")
4638
+ return dec("0"), dec("0")
4625
4639
 
4626
4640
  if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.LIVE:
4627
4641
  if "finished" in self.pb_live_obs.text():
@@ -5457,7 +5471,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5457
5471
  check if first player ended
5458
5472
  """
5459
5473
 
5460
- print("play_video")
5474
+ logging.debug("play_video")
5461
5475
 
5462
5476
  if self.geometric_measurements_mode:
5463
5477
  return
@@ -5465,6 +5479,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5465
5479
  if self.playerType != cfg.MEDIA:
5466
5480
  return
5467
5481
 
5482
+ # check if playlist is ended. If it is ended restart playlist from beginning
5483
+ if self.dw_player[0].player.eof_reached and self.dw_player[0].player.core_idle:
5484
+ if self.dw_player[0].player.playlist_pos is not None and self.dw_player[0].player.playlist_count is not None:
5485
+ if self.dw_player[0].player.playlist_pos == self.dw_player[0].player.playlist_count - 1:
5486
+ self.seek_mediaplayer(dec(0))
5487
+
5488
+
5468
5489
  # check if player 1 is ended
5469
5490
  for i, dw in enumerate(self.dw_player):
5470
5491
  if (
@@ -5475,9 +5496,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5475
5496
 
5476
5497
  self.lb_player_status.clear()
5477
5498
 
5478
- # if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.VISUALIZE_WAVEFORM, False) \
5479
- # or self.pj[cfg.OBSERVATIONS][self.observationId].get(VISUALIZE_SPECTROGRAM, False):
5480
-
5481
5499
  self.statusbar.showMessage("", 0)
5482
5500
 
5483
5501
  if self.ipc_mpv_timer is not None:
@@ -5554,16 +5572,19 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5554
5572
 
5555
5573
  decrement = self.fast * self.play_rate if self.config_param.get(cfg.ADAPT_FAST_JUMP, cfg.ADAPT_FAST_JUMP_DEFAULT) else self.fast
5556
5574
 
5557
- new_time = (
5558
- sum(self.dw_player[0].media_durations[0 : self.dw_player[0].player.playlist_pos]) / 1000
5559
- + self.dw_player[0].player.playback_time
5560
- - decrement
5561
- )
5575
+ try:
5576
+ new_time = (
5577
+ sum(self.dw_player[0].media_durations[0 : self.dw_player[0].player.playlist_pos]) / 1000
5578
+ + self.dw_player[0].player.playback_time
5579
+ - decrement
5580
+ )
5581
+ except Exception:
5582
+ return
5562
5583
 
5563
5584
  if new_time < decrement:
5564
5585
  new_time = 0
5565
5586
 
5566
- self.seek_mediaplayer(new_time)
5587
+ self.seek_mediaplayer(dec(new_time))
5567
5588
 
5568
5589
  self.update_visualizations()
5569
5590
 
@@ -5577,13 +5598,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
5577
5598
 
5578
5599
  logging.info(f"Jump forward for {increment} seconds")
5579
5600
 
5580
- new_time = (
5581
- sum(self.dw_player[0].media_durations[0 : self.dw_player[0].player.playlist_pos]) / 1000
5582
- + self.dw_player[0].player.playback_time
5583
- + increment
5584
- )
5601
+ try:
5602
+ new_time = (
5603
+ sum(self.dw_player[0].media_durations[0 : self.dw_player[0].player.playlist_pos]) / 1000
5604
+ + self.dw_player[0].player.playback_time
5605
+ + increment
5606
+ )
5607
+ except Exception:
5608
+ return
5585
5609
 
5586
- self.seek_mediaplayer(new_time)
5610
+ self.seek_mediaplayer(dec(new_time))
5587
5611
 
5588
5612
  self.update_visualizations()
5589
5613
 
@@ -5839,6 +5863,15 @@ def main():
5839
5863
  )
5840
5864
  sys.exit()
5841
5865
 
5866
+ if sys.platform.startswith("darwin"):
5867
+ QMessageBox.warning(
5868
+ None,
5869
+ cfg.programName,
5870
+ (f"This version of BORIS for macOS is still EXPERIMENTAL and should be used at your own risk."),
5871
+ QMessageBox.Ok | QMessageBox.Default,
5872
+ QMessageBox.NoButton,
5873
+ )
5874
+
5842
5875
  window.show()
5843
5876
  window.raise_() # for overlapping widget (?)
5844
5877
 
@@ -5865,3 +5898,4 @@ def main():
5865
5898
  del window
5866
5899
 
5867
5900
  sys.exit(return_code)
5901
+