boris-behav-obs 9.3.1__tar.gz → 9.3.3__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.
- {boris_behav_obs-9.3.1/boris_behav_obs.egg-info → boris_behav_obs-9.3.3}/PKG-INFO +1 -1
- boris_behav_obs-9.3.3/boris/analysis_plugins/_latency.py +59 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/behav_coding_map_creator.py +2 -6
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/config.py +4 -1
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/core.py +21 -60
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/dialog.py +8 -3
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/gui_utilities.py +30 -4
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/media_file.py +12 -10
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/modifier_coding_map_creator.py +6 -21
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/observation.py +25 -20
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/observation_operations.py +6 -6
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/player_dock_widget.py +7 -5
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/plugins.py +4 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/project.py +69 -21
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/project_import_export.py +3 -1
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/utilities.py +35 -55
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/version.py +2 -2
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3/boris_behav_obs.egg-info}/PKG-INFO +1 -1
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris_behav_obs.egg-info/SOURCES.txt +1 -1
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/pyproject.toml +7 -2
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_utilities.py +105 -70
- boris_behav_obs-9.3.1/boris/1.py +0 -45
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/LICENSE.TXT +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/MANIFEST.in +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/README.TXT +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/README.md +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/__init__.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/__main__.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/about.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/add_modifier.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/add_modifier_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/advanced_event_filtering.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/analysis_plugins/__init__.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/analysis_plugins/number_of_occurences.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/analysis_plugins/time_budget.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/behavior_binary_table.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/behaviors_coding_map.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/boris_cli.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/cmd_arguments.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/coding_pad.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/config_file.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/connections.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/converters.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/converters_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/cooccurence.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/core_qrc.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/core_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/db_functions.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/dev.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/duration_widget.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/edit_event.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/edit_event_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/event_operations.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/events_cursor.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/events_snapshots.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/exclusion_matrix.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/export_events.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/export_observation.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/external_processes.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/geometric_measurement.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/image_overlay.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/import_observations.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/irr.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/latency.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/measurement_widget.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/menu_options.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/modifiers_coding_map.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/mpv-1.0.3.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/mpv.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/mpv2.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/observation_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/observations_list.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/otx_parser.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/param_panel.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/param_panel_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/plot_data_module.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/plot_events.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/plot_events_rt.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/plot_spectrogram_rt.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/plot_waveform_rt.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/portion/__init__.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/portion/const.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/portion/dict.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/portion/func.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/portion/interval.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/portion/io.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/preferences.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/preferences_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/project_functions.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/project_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/qrc_boris.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/qrc_boris5.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/select_modifiers.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/select_observations.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/select_subj_behav.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/state_events.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/subjects_pad.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/synthetic_time_budget.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/time_budget_functions.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/time_budget_widget.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/transitions.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/video_equalizer.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/video_equalizer_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/video_operations.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/view_df.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/view_df_ui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris/write_event.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris_behav_obs.egg-info/entry_points.txt +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris_behav_obs.egg-info/requires.txt +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/boris_behav_obs.egg-info/top_level.txt +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/setup.cfg +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_db_functions.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_export_observation.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_irr.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_observation_gui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_otx_parser.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_preferences_gui.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_project_functions.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_time_budget.py +0 -0
- {boris_behav_obs-9.3.1 → boris_behav_obs-9.3.3}/tests/test_utilities2.py +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS plugin
|
|
3
|
+
|
|
4
|
+
number of occurences of behaviors
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
__version__ = "0.0.1"
|
|
10
|
+
__version_date__ = "2025-04-10"
|
|
11
|
+
__plugin_name__ = "Behavior latency"
|
|
12
|
+
__author__ = "Olivier Friard - University of Torino - Italy"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
import itertools
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def run(df: pd.DataFrame):
|
|
19
|
+
"""
|
|
20
|
+
Latency of a behavior after another.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
df["start_time"] = pd.to_datetime(df["Start (s)"])
|
|
24
|
+
df["end_time"] = pd.to_datetime(df["Stop (s)"])
|
|
25
|
+
|
|
26
|
+
latency_by_subject: dict = {}
|
|
27
|
+
|
|
28
|
+
for subject, group in df.groupby("subject"):
|
|
29
|
+
behaviors = group["behavior"].tolist()
|
|
30
|
+
# combinations = []
|
|
31
|
+
# Utiliser itertools pour créer des combinaisons 2 à 2 des comportements
|
|
32
|
+
for comb in itertools.combinations(behaviors, 2):
|
|
33
|
+
# combinations.append(comb)
|
|
34
|
+
|
|
35
|
+
last_A_end_time = None
|
|
36
|
+
|
|
37
|
+
# Liste pour stocker les latences de chaque sujet
|
|
38
|
+
subject_latency = []
|
|
39
|
+
|
|
40
|
+
for index, row in group.iterrows():
|
|
41
|
+
if row["behavior"] == comb[0]:
|
|
42
|
+
# Si on rencontre un comportement A, on réinitialise le temps de fin du comportement A
|
|
43
|
+
last_A_end_time = row["end_time"]
|
|
44
|
+
subject_latency.append(None) # Pas de latence pour A
|
|
45
|
+
elif row["behavior"] == comb[1] and last_A_end_time is not None:
|
|
46
|
+
# Si on rencontre un comportement B et qu'on a déjà vu un A avant
|
|
47
|
+
latency_time = row["start_time"] - last_A_end_time
|
|
48
|
+
subject_latency.append(latency_time)
|
|
49
|
+
else:
|
|
50
|
+
# Si on rencontre un B mais sans A avant
|
|
51
|
+
subject_latency.append(None)
|
|
52
|
+
|
|
53
|
+
# Ajout des latences calculées au DataFrame
|
|
54
|
+
df.loc[group.index, f"latency {comb[1]} after {comb[0]}"] = subject_latency
|
|
55
|
+
|
|
56
|
+
# Calcul de la latence totale ou moyenne par sujet
|
|
57
|
+
latency_by_subject[(subject, comb)] = df.groupby("subject")["latency"].agg(["sum", "mean"])
|
|
58
|
+
|
|
59
|
+
return str(latency_by_subject)
|
|
@@ -24,6 +24,7 @@ import binascii
|
|
|
24
24
|
import io
|
|
25
25
|
import json
|
|
26
26
|
from pathlib import Path
|
|
27
|
+
import gui_utilities
|
|
27
28
|
|
|
28
29
|
from PySide6.QtCore import QBuffer, QByteArray, QIODevice, QLineF, QPoint, Qt, Signal
|
|
29
30
|
from PySide6.QtGui import QBrush, QColor, QIcon, QMouseEvent, QPen, QPixmap, QPolygonF, QAction
|
|
@@ -790,7 +791,6 @@ class BehaviorsMapCreatorWindow(QMainWindow):
|
|
|
790
791
|
|
|
791
792
|
if not self.fileName:
|
|
792
793
|
return
|
|
793
|
-
"""if os.path.splitext(self.fileName)[1] != ".behav_coding_map":"""
|
|
794
794
|
if Path(self.fileName).suffix != ".behav_coding_map":
|
|
795
795
|
self.fileName += ".behav_coding_map"
|
|
796
796
|
self.saveMap()
|
|
@@ -1106,10 +1106,6 @@ if __name__ == "__main__":
|
|
|
1106
1106
|
app = QApplication(sys.argv)
|
|
1107
1107
|
window = BehaviorsMapCreatorWindow(["North zone", "East zone", "South zone", "West zone"])
|
|
1108
1108
|
window.bcm_list = []
|
|
1109
|
-
|
|
1110
|
-
screen_geometry = app.primaryScreen().geometry()
|
|
1111
|
-
center_x = (screen_geometry.width() - window.width()) // 2
|
|
1112
|
-
center_y = (screen_geometry.height() - window.height()) // 2
|
|
1113
|
-
window.move(center_x, center_y)
|
|
1109
|
+
gui_utilities.resize_center(app, window, cfg.CODING_MAP_RESIZE_W, cfg.CODING_MAP_RESIZE_H)
|
|
1114
1110
|
window.show()
|
|
1115
1111
|
sys.exit(app.exec())
|
|
@@ -155,6 +155,7 @@ CHECK_PROJECT_INTEGRITY = "check_project_integrity"
|
|
|
155
155
|
YES = "Yes"
|
|
156
156
|
NO = "No"
|
|
157
157
|
CANCEL = "Cancel"
|
|
158
|
+
IGNORE = "Ignore"
|
|
158
159
|
APPEND = "Append"
|
|
159
160
|
CLOSE = "Close"
|
|
160
161
|
REPLACE = "Replace"
|
|
@@ -454,7 +455,8 @@ POINT = "POINT"
|
|
|
454
455
|
START = "START"
|
|
455
456
|
STOP = "STOP"
|
|
456
457
|
|
|
457
|
-
PLAYER1
|
|
458
|
+
PLAYER1 = "1"
|
|
459
|
+
PLAYER2 = "2"
|
|
458
460
|
ALL_PLAYERS = [str(x + 1) for x in range(N_PLAYER)]
|
|
459
461
|
|
|
460
462
|
VISUALIZE_SPECTROGRAM = "visualize_spectrogram"
|
|
@@ -701,6 +703,7 @@ EMPTY_PROJECT = {
|
|
|
701
703
|
ETHOGRAM: {},
|
|
702
704
|
OBSERVATIONS: {},
|
|
703
705
|
BEHAVIORAL_CATEGORIES: [],
|
|
706
|
+
BEHAVIORAL_CATEGORIES_CONF: {},
|
|
704
707
|
INDEPENDENT_VARIABLES: {},
|
|
705
708
|
CODING_MAP: {},
|
|
706
709
|
BEHAVIORS_CODING_MAP: [],
|
|
@@ -114,7 +114,6 @@ from . import cmd_arguments
|
|
|
114
114
|
|
|
115
115
|
from . import core_qrc
|
|
116
116
|
from .core_ui import Ui_MainWindow
|
|
117
|
-
import exifread
|
|
118
117
|
from . import config as cfg
|
|
119
118
|
from . import video_operations
|
|
120
119
|
|
|
@@ -135,8 +134,9 @@ __version__ = version.__version__
|
|
|
135
134
|
__version_date__ = version.__version_date__
|
|
136
135
|
|
|
137
136
|
# check minimal version of python
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
MIN_PYTHON_VERSION = "3.12"
|
|
138
|
+
if util.versiontuple(platform.python_version()) < util.versiontuple(MIN_PYTHON_VERSION):
|
|
139
|
+
msg = f"BORIS requires Python {MIN_PYTHON_VERSION}+! You are using Python v. {platform.python_version()}\n"
|
|
140
140
|
logging.critical(msg)
|
|
141
141
|
sys.exit()
|
|
142
142
|
|
|
@@ -237,19 +237,6 @@ class TableModel(QAbstractTableModel):
|
|
|
237
237
|
return self._data[row][event_idx]
|
|
238
238
|
|
|
239
239
|
|
|
240
|
-
"""
|
|
241
|
-
class ButtonEventFilter(QObject):
|
|
242
|
-
def eventFilter(self, obj, event):
|
|
243
|
-
print("event filter")
|
|
244
|
-
if isinstance(obj, QPushButton) and event.type() == QEvent.KeyPress:
|
|
245
|
-
print("keypress")
|
|
246
|
-
if event.key() in (Qt.Key_Enter, Qt.Key_Return, Qt.Key_Space):
|
|
247
|
-
print("enter sapce")
|
|
248
|
-
return False # Block the event
|
|
249
|
-
return super().eventFilter(obj, event)
|
|
250
|
-
"""
|
|
251
|
-
|
|
252
|
-
|
|
253
240
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
|
254
241
|
"""
|
|
255
242
|
Main BORIS window
|
|
@@ -301,7 +288,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
301
288
|
ext_data_timer_list: list = []
|
|
302
289
|
projectFileName: str = ""
|
|
303
290
|
mediaTotalLength = None
|
|
304
|
-
beep_every = 0
|
|
291
|
+
beep_every: float = 0.0
|
|
305
292
|
|
|
306
293
|
plot_colors = cfg.BEHAVIORS_PLOT_COLORS
|
|
307
294
|
behav_category_colors = cfg.CATEGORY_COLORS_LIST
|
|
@@ -315,19 +302,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
315
302
|
fast = 10
|
|
316
303
|
|
|
317
304
|
currentStates: dict = {}
|
|
318
|
-
subject_name_index = {}
|
|
305
|
+
subject_name_index: dict = {}
|
|
319
306
|
flag_slow = False
|
|
320
307
|
play_rate: float = 1
|
|
321
308
|
play_rate_step: float = 0.1
|
|
322
309
|
currentSubject: str = "" # contains the current subject of observation
|
|
323
|
-
coding_map_window_geometry = 0
|
|
324
310
|
|
|
325
311
|
# FFmpeg
|
|
326
|
-
memx
|
|
312
|
+
memx = -1
|
|
313
|
+
memy = -1
|
|
314
|
+
mem_player = -1
|
|
327
315
|
|
|
328
316
|
# path for ffmpeg/ffmpeg.exe program
|
|
329
|
-
ffmpeg_bin = ""
|
|
330
|
-
ffmpeg_cache_dir = ""
|
|
317
|
+
ffmpeg_bin: str = ""
|
|
318
|
+
ffmpeg_cache_dir: str = ""
|
|
331
319
|
|
|
332
320
|
# dictionary for FPS storing
|
|
333
321
|
fps = 0
|
|
@@ -390,9 +378,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
390
378
|
super(MainWindow, self).__init__(parent)
|
|
391
379
|
self.setupUi(self)
|
|
392
380
|
|
|
393
|
-
# disable trigger with RETURN or SPACE keys
|
|
394
|
-
"""filter_obj = ButtonEventFilter()
|
|
395
|
-
self.pb_live_obs.installEventFilter(filter_obj)"""
|
|
396
381
|
self.pb_live_obs.setFocusPolicy(Qt.NoFocus)
|
|
397
382
|
|
|
398
383
|
self.ffmpeg_bin = ffmpeg_bin
|
|
@@ -418,25 +403,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
418
403
|
self.tb_export.setMenu(self.menu)
|
|
419
404
|
"""
|
|
420
405
|
|
|
421
|
-
gui_utilities.set_icons(self, theme_mode=
|
|
406
|
+
gui_utilities.set_icons(self, theme_mode=gui_utilities.theme_mode())
|
|
422
407
|
|
|
423
408
|
self.setWindowTitle(f"{cfg.programName} ({__version__})")
|
|
424
409
|
|
|
425
|
-
self.w_obs_info.setVisible(False)
|
|
426
|
-
|
|
427
410
|
self.lbLogoBoris.setPixmap(QPixmap(":/logo"))
|
|
428
|
-
|
|
429
411
|
self.lbLogoBoris.setScaledContents(False)
|
|
430
412
|
self.lbLogoBoris.setAlignment(Qt.AlignCenter)
|
|
431
413
|
|
|
432
|
-
# self.lbLogoUnito.setPixmap(QPixmap(":/dbios_unito"))
|
|
433
|
-
# self.lbLogoUnito.setScaledContents(False)
|
|
434
|
-
# self.lbLogoUnito.setAlignment(Qt.AlignCenter)
|
|
435
|
-
|
|
436
414
|
self.toolBar.setEnabled(True)
|
|
437
415
|
|
|
438
416
|
# start with dock widget invisible
|
|
439
|
-
for w in
|
|
417
|
+
for w in (self.w_obs_info, self.dwEvents, self.dwEthogram, self.dwSubjects):
|
|
440
418
|
w.setVisible(False)
|
|
441
419
|
w.keyPressEvent = self.keyPressEvent
|
|
442
420
|
|
|
@@ -475,15 +453,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
475
453
|
self.lbTimeOffset.setMinimumWidth(160)
|
|
476
454
|
self.statusbar.addPermanentWidget(self.lbTimeOffset)
|
|
477
455
|
|
|
478
|
-
# play rate are now displayed in the main info widget
|
|
479
|
-
"""
|
|
480
|
-
# SPEED
|
|
481
|
-
self.lbSpeed = QLabel()
|
|
482
|
-
self.lbSpeed.setFrameStyle(QFrame.StyledPanel)
|
|
483
|
-
self.lbSpeed.setMinimumWidth(40)
|
|
484
|
-
self.statusbar.addPermanentWidget(self.lbSpeed)
|
|
485
|
-
"""
|
|
486
|
-
|
|
487
456
|
# set painter for tv_events to highlight current row
|
|
488
457
|
delegate = self.CustomItemDelegate()
|
|
489
458
|
self.tv_events.setItemDelegate(delegate)
|
|
@@ -499,14 +468,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
499
468
|
plugins.load_plugins(self)
|
|
500
469
|
plugins.add_plugins_to_menu(self)
|
|
501
470
|
|
|
502
|
-
def theme_mode(self):
|
|
503
|
-
"""
|
|
504
|
-
return the theme mode (dark or light) of the OS
|
|
505
|
-
"""
|
|
506
|
-
palette = QApplication.instance().palette()
|
|
507
|
-
color = palette.window().color()
|
|
508
|
-
return "dark" if color.value() < 128 else "light" # Dark mode if the color value is less than 128
|
|
509
|
-
|
|
510
471
|
class CustomItemDelegate(QStyledItemDelegate):
|
|
511
472
|
def paint(self, painter, option, index):
|
|
512
473
|
# Custom drawing logic here (overriding paint)
|
|
@@ -1506,7 +1467,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1506
1467
|
# one media
|
|
1507
1468
|
if self.dw_player[player].player.playlist_count == 1:
|
|
1508
1469
|
if new_time < self.dw_player[player].player.duration:
|
|
1509
|
-
|
|
1470
|
+
new_time_float = round(float(new_time), 3)
|
|
1471
|
+
|
|
1472
|
+
self.dw_player[player].player.seek(new_time_float, "absolute+exact")
|
|
1510
1473
|
|
|
1511
1474
|
if player == 0 and not self.user_move_slider:
|
|
1512
1475
|
self.video_slider.setValue(
|
|
@@ -3835,7 +3798,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3835
3798
|
if self.geometric_measurements_mode:
|
|
3836
3799
|
geometric_measurement.redraw_measurements(self)
|
|
3837
3800
|
|
|
3838
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
3801
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
3839
3802
|
|
|
3840
3803
|
def previous_frame(self) -> None:
|
|
3841
3804
|
"""
|
|
@@ -3859,7 +3822,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3859
3822
|
if self.geometric_measurements_mode:
|
|
3860
3823
|
geometric_measurement.redraw_measurements(self)
|
|
3861
3824
|
|
|
3862
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
3825
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
3863
3826
|
|
|
3864
3827
|
def run_event_outside(self):
|
|
3865
3828
|
"""
|
|
@@ -4463,7 +4426,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4463
4426
|
for data_timer in self.ext_data_timer_list:
|
|
4464
4427
|
data_timer.stop()
|
|
4465
4428
|
|
|
4466
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
4429
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
4467
4430
|
|
|
4468
4431
|
if msg:
|
|
4469
4432
|
self.lb_current_media_time.setText(msg)
|
|
@@ -5634,7 +5597,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5634
5597
|
for data_timer in self.ext_data_timer_list:
|
|
5635
5598
|
data_timer.start()
|
|
5636
5599
|
|
|
5637
|
-
self.actionPlay.setIcon(QIcon(f":/pause_{
|
|
5600
|
+
self.actionPlay.setIcon(QIcon(f":/pause_{gui_utilities.theme_mode()}"))
|
|
5638
5601
|
self.actionPlay.setText("Pause")
|
|
5639
5602
|
|
|
5640
5603
|
return True
|
|
@@ -5668,7 +5631,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5668
5631
|
for idx in self.plot_data:
|
|
5669
5632
|
self.timer_plot_data_out(self.plot_data[idx])
|
|
5670
5633
|
|
|
5671
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
5634
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
5672
5635
|
self.actionPlay.setText("Play")
|
|
5673
5636
|
|
|
5674
5637
|
def play_activated(self):
|
|
@@ -5823,8 +5786,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
5823
5786
|
|
|
5824
5787
|
|
|
5825
5788
|
def main():
|
|
5826
|
-
# QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
|
5827
|
-
|
|
5828
5789
|
app = QApplication(sys.argv)
|
|
5829
5790
|
app.setStyle("Fusion")
|
|
5830
5791
|
|
|
@@ -5917,7 +5878,7 @@ def main():
|
|
|
5917
5878
|
results.show()
|
|
5918
5879
|
|
|
5919
5880
|
window.show()
|
|
5920
|
-
window.raise_()
|
|
5881
|
+
window.raise_() # for overlapping widget (?)
|
|
5921
5882
|
|
|
5922
5883
|
if observation_to_open and "error" not in pj:
|
|
5923
5884
|
r = observation_operations.load_observation(window, obs_id=observation_to_open, mode=cfg.OBS_START)
|
|
@@ -71,7 +71,12 @@ from . import utilities as util
|
|
|
71
71
|
|
|
72
72
|
def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
73
73
|
"""
|
|
74
|
-
generic message dialog
|
|
74
|
+
show a generic message dialog and returns the text of the clicked button
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
title (str): Title of the dialog box
|
|
78
|
+
text (str): text of the dialog box
|
|
79
|
+
buttons (tuple): text for buttons
|
|
75
80
|
|
|
76
81
|
Return
|
|
77
82
|
str: text of the clicked button
|
|
@@ -83,8 +88,8 @@ def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
|
83
88
|
for button in buttons:
|
|
84
89
|
message.addButton(button, QMessageBox.YesRole)
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
message.
|
|
91
|
+
message.setWindowFlags(message.windowFlags() | Qt.WindowStaysOnTopHint)
|
|
92
|
+
message.exec()
|
|
88
93
|
return message.clickedButton().text()
|
|
89
94
|
|
|
90
95
|
|
|
@@ -22,10 +22,19 @@ Copyright 2012-2025 Olivier Friard
|
|
|
22
22
|
import pathlib as pl
|
|
23
23
|
import logging
|
|
24
24
|
from PySide6.QtCore import QSettings
|
|
25
|
-
from PySide6.QtWidgets import QWidget
|
|
25
|
+
from PySide6.QtWidgets import QWidget, QApplication
|
|
26
26
|
from PySide6.QtGui import QIcon
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def theme_mode() -> str:
|
|
30
|
+
"""
|
|
31
|
+
return the theme mode (dark or light) of the OS
|
|
32
|
+
"""
|
|
33
|
+
palette = QApplication.instance().palette()
|
|
34
|
+
color = palette.window().color()
|
|
35
|
+
return "dark" if color.value() < 128 else "light" # Dark mode if the color value is less than 128
|
|
36
|
+
|
|
37
|
+
|
|
29
38
|
def save_geometry(widget: QWidget, widget_name: str):
|
|
30
39
|
"""
|
|
31
40
|
save window geometry in ini file
|
|
@@ -44,6 +53,7 @@ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
|
|
|
44
53
|
"""
|
|
45
54
|
restore window geometry in ini file
|
|
46
55
|
"""
|
|
56
|
+
|
|
47
57
|
def default_resize(widget, default_width_height):
|
|
48
58
|
if default_width_height != (0, 0):
|
|
49
59
|
try:
|
|
@@ -51,15 +61,14 @@ def restore_geometry(widget: QWidget, widget_name: str, default_width_height):
|
|
|
51
61
|
except Exception:
|
|
52
62
|
logging.warning("Error during restoring default")
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
logging.debug(f'restore geometry function for {widget_name}')
|
|
64
|
+
logging.debug(f"restore geometry function for {widget_name}")
|
|
56
65
|
try:
|
|
57
66
|
ini_file_path = pl.Path.home() / pl.Path(".boris")
|
|
58
67
|
if ini_file_path.is_file():
|
|
59
68
|
settings = QSettings(str(ini_file_path), QSettings.IniFormat)
|
|
60
69
|
print(settings.value(f"{widget_name} geometry"))
|
|
61
70
|
widget.restoreGeometry(settings.value(f"{widget_name} geometry"))
|
|
62
|
-
logging.debug(f
|
|
71
|
+
logging.debug(f"geometry restored for {widget_name} {settings.value(f'{widget_name} geometry')}")
|
|
63
72
|
else:
|
|
64
73
|
default_resize(widget, default_width_height)
|
|
65
74
|
except Exception:
|
|
@@ -108,3 +117,20 @@ def set_icons(self, theme_mode: str) -> None:
|
|
|
108
117
|
self.action_geometric_measurements.setIcon(QIcon(f":/measurement_{theme_mode}"))
|
|
109
118
|
self.actionFind_in_current_obs.setIcon(QIcon(f":/find_{theme_mode}"))
|
|
110
119
|
self.actionExplore_project.setIcon(QIcon(f":/explore_{theme_mode}"))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def resize_center(app, window, width: int, height: int) -> None:
|
|
123
|
+
"""
|
|
124
|
+
resize and center window
|
|
125
|
+
"""
|
|
126
|
+
window.resize(width, height)
|
|
127
|
+
screen_geometry = app.primaryScreen().geometry()
|
|
128
|
+
if window.height() > screen_geometry.height():
|
|
129
|
+
window.resize(window.width(), int(screen_geometry.height() * 0.8))
|
|
130
|
+
if window.width() > screen_geometry.width():
|
|
131
|
+
window.resize(screen_geometry.width(), window.height())
|
|
132
|
+
# center
|
|
133
|
+
center_x = (screen_geometry.width() - window.width()) // 2
|
|
134
|
+
center_y = (screen_geometry.height() - window.height()) // 2
|
|
135
|
+
|
|
136
|
+
window.move(center_x, center_y)
|
|
@@ -20,11 +20,13 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
+
from PySide6.QtWidgets import QFileDialog
|
|
24
|
+
|
|
23
25
|
from . import config as cfg
|
|
24
26
|
from . import utilities as util
|
|
25
27
|
from . import dialog
|
|
26
28
|
from . import project_functions
|
|
27
|
-
from
|
|
29
|
+
from . import utilities as util
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
def get_info(self) -> None:
|
|
@@ -38,17 +40,17 @@ def get_info(self) -> None:
|
|
|
38
40
|
if "error" in r:
|
|
39
41
|
ffmpeg_output = f"File path: {media_full_path}<br><br>{r['error']}<br><br>"
|
|
40
42
|
else:
|
|
41
|
-
ffmpeg_output = f"<br><b>{r['analysis_program']
|
|
43
|
+
ffmpeg_output = f"<br><b>{r['analysis_program']} analysis</b><br>"
|
|
42
44
|
|
|
43
45
|
ffmpeg_output += (
|
|
44
46
|
f"File path: <b>{media_full_path}</b><br><br>"
|
|
45
47
|
f"Duration: {r['duration']} seconds ({util.convertTime(self.timeFormat, r['duration'])})<br>"
|
|
48
|
+
f"FPS: {r['fps']}<br>"
|
|
49
|
+
f"Resolution: {r['resolution']} pixels<br>"
|
|
46
50
|
f"Format long name: {r.get('format_long_name', cfg.NA)}<br>"
|
|
47
51
|
f"Creation time: {r.get('creation_time', cfg.NA)}<br>"
|
|
48
|
-
f"Resolution: {r['resolution']}<br>"
|
|
49
52
|
f"Number of frames: {r['frames_number']}<br>"
|
|
50
53
|
f"Bitrate: {util.smart_size_format(r['bitrate'])} <br>"
|
|
51
|
-
f"FPS: {r['fps']}<br>"
|
|
52
54
|
f"Has video: {r['has_video']}<br>"
|
|
53
55
|
f"Has audio: {r['has_audio']}<br>"
|
|
54
56
|
f"File size: {util.smart_size_format(r.get('file size', cfg.NA))}<br>"
|
|
@@ -70,6 +72,12 @@ def get_info(self) -> None:
|
|
|
70
72
|
|
|
71
73
|
mpv_output = (
|
|
72
74
|
"<b>MPV information</b><br>"
|
|
75
|
+
f"Duration: {dw.player.duration} seconds ({util.seconds2time(dw.player.duration)})<br>"
|
|
76
|
+
# "Position: {} %<br>"
|
|
77
|
+
f"FPS: {dw.player.container_fps}<br>"
|
|
78
|
+
# "Rate: {}<br>"
|
|
79
|
+
f"Resolution: {dw.player.width}x{dw.player.height} pixels<br>"
|
|
80
|
+
# "Scale: {}<br>"
|
|
73
81
|
f"Video format: {dw.player.video_format}<br>"
|
|
74
82
|
# "State: {}<br>"
|
|
75
83
|
# "Media Resource Location: {}<br>"
|
|
@@ -77,12 +85,6 @@ def get_info(self) -> None:
|
|
|
77
85
|
# "Track: {}/{}<br>"
|
|
78
86
|
f"Number of media in media list: {dw.player.playlist_count}<br>"
|
|
79
87
|
f"Current time position: {dw.player.time_pos}<br>"
|
|
80
|
-
f"Duration: {dw.player.duration}<br>"
|
|
81
|
-
# "Position: {} %<br>"
|
|
82
|
-
f"FPS: {dw.player.container_fps}<br>"
|
|
83
|
-
# "Rate: {}<br>"
|
|
84
|
-
f"Video size: {dw.player.width}x{dw.player.height}<br>"
|
|
85
|
-
# "Scale: {}<br>"
|
|
86
88
|
f"Aspect ratio: {round(dw.player.width / dw.player.height, 3)}<br>"
|
|
87
89
|
# "is seekable? {}<br>"
|
|
88
90
|
# "has_vout? {}<br>"
|
|
@@ -26,8 +26,9 @@ This file is part of BORIS.
|
|
|
26
26
|
import binascii
|
|
27
27
|
import io
|
|
28
28
|
import json
|
|
29
|
-
import
|
|
29
|
+
from pathlib import Path
|
|
30
30
|
import re
|
|
31
|
+
import gui_utilities
|
|
31
32
|
|
|
32
33
|
from PySide6.QtCore import (
|
|
33
34
|
Qt,
|
|
@@ -727,7 +728,8 @@ class ModifiersMapCreatorWindow(QMainWindow):
|
|
|
727
728
|
self.fileName = fn
|
|
728
729
|
|
|
729
730
|
if self.fileName:
|
|
730
|
-
if os.path.splitext(self.fileName)[1] != ".boris_map":
|
|
731
|
+
# if os.path.splitext(self.fileName)[1] != ".boris_map":
|
|
732
|
+
if Path(self.fileName).suffix != ".boris_map":
|
|
731
733
|
self.fileName += ".boris_map"
|
|
732
734
|
self.saveMap()
|
|
733
735
|
|
|
@@ -744,7 +746,7 @@ class ModifiersMapCreatorWindow(QMainWindow):
|
|
|
744
746
|
else:
|
|
745
747
|
self.fileName = fn
|
|
746
748
|
|
|
747
|
-
if self.fileName and
|
|
749
|
+
if self.fileName and Path(self.fileName).suffix() != ".boris_map":
|
|
748
750
|
self.fileName += ".boris_map"
|
|
749
751
|
|
|
750
752
|
if self.fileName:
|
|
@@ -1006,25 +1008,8 @@ if __name__ == "__main__":
|
|
|
1006
1008
|
|
|
1007
1009
|
app = QApplication(sys.argv)
|
|
1008
1010
|
window = ModifiersMapCreatorWindow()
|
|
1009
|
-
window.resize(800, 700)
|
|
1010
1011
|
|
|
1011
|
-
|
|
1012
|
-
print(f"{window.height()=}")
|
|
1013
|
-
|
|
1014
|
-
# Get the screen geometry (screen size and position)
|
|
1015
|
-
screen_geometry = app.primaryScreen().geometry()
|
|
1016
|
-
|
|
1017
|
-
print(f"{screen_geometry=}")
|
|
1018
|
-
|
|
1019
|
-
# Calculate the center of the screen
|
|
1020
|
-
center_x = (screen_geometry.width() - window.width()) // 2
|
|
1021
|
-
center_y = (screen_geometry.height() - window.height()) // 2
|
|
1022
|
-
|
|
1023
|
-
print(f"{center_x=}")
|
|
1024
|
-
print(f"{center_y=}")
|
|
1025
|
-
|
|
1026
|
-
# Move the widget to the center of the screen
|
|
1027
|
-
window.move(center_x, center_y)
|
|
1012
|
+
gui_utilities.resize_center(app, window, cfg.CODING_MAP_RESIZE_W, cfg.CODING_MAP_RESIZE_H)
|
|
1028
1013
|
|
|
1029
1014
|
window.show()
|
|
1030
1015
|
sys.exit(app.exec())
|
|
@@ -1008,27 +1008,30 @@ class Observation(QDialog, Ui_Form):
|
|
|
1008
1008
|
|
|
1009
1009
|
# check if observation id not empty
|
|
1010
1010
|
if not self.leObservationId.text():
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1011
|
+
QMessageBox.critical(
|
|
1012
|
+
self,
|
|
1013
|
+
cfg.programName,
|
|
1014
|
+
"The <b>observation id</b> is mandatory and must be unique.",
|
|
1015
|
+
)
|
|
1015
1016
|
return False
|
|
1016
1017
|
|
|
1017
1018
|
# check if observation_type
|
|
1018
1019
|
if not any((self.rb_media_files.isChecked(), self.rb_live.isChecked(), self.rb_images.isChecked())):
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1020
|
+
QMessageBox.critical(
|
|
1021
|
+
self,
|
|
1022
|
+
cfg.programName,
|
|
1023
|
+
"Choose an observation type.",
|
|
1024
|
+
)
|
|
1023
1025
|
return False
|
|
1024
1026
|
|
|
1025
1027
|
# check if offset is correct
|
|
1026
1028
|
if self.cb_time_offset.isChecked():
|
|
1027
1029
|
if self.obs_time_offset.get_time() is None:
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1030
|
+
QMessageBox.critical(
|
|
1031
|
+
self,
|
|
1032
|
+
cfg.programName,
|
|
1033
|
+
"Check the time offset value.",
|
|
1034
|
+
)
|
|
1032
1035
|
return False
|
|
1033
1036
|
|
|
1034
1037
|
if self.rb_media_files.isChecked(): # observation based on media file(s)
|
|
@@ -1051,18 +1054,20 @@ class Observation(QDialog, Ui_Form):
|
|
|
1051
1054
|
|
|
1052
1055
|
# check if player #1 is used
|
|
1053
1056
|
if not players_list or min(players_list) > 1:
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1057
|
+
QMessageBox.critical(
|
|
1058
|
+
self,
|
|
1059
|
+
cfg.programName,
|
|
1060
|
+
"A media file must be loaded in player #1",
|
|
1061
|
+
)
|
|
1058
1062
|
return False
|
|
1059
1063
|
|
|
1060
1064
|
# check if players are used in crescent order
|
|
1061
1065
|
if set(list(range(min(players_list), max(players_list) + 1))) != set(players_list):
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
+
QMessageBox.critical(
|
|
1067
|
+
self,
|
|
1068
|
+
cfg.programName,
|
|
1069
|
+
"Some player are not used. Please reorganize your media files",
|
|
1070
|
+
)
|
|
1066
1071
|
return False
|
|
1067
1072
|
|
|
1068
1073
|
# check if more media in player #1 and media in other players
|
|
@@ -888,7 +888,7 @@ def new_observation(self, mode: str = cfg.NEW, obsId: str = "") -> None:
|
|
|
888
888
|
self.pj[cfg.OBSERVATIONS][obsId][cfg.CLOSE_BEHAVIORS_BETWEEN_VIDEOS]
|
|
889
889
|
)
|
|
890
890
|
|
|
891
|
-
rv = observationWindow.
|
|
891
|
+
rv = observationWindow.exec()
|
|
892
892
|
|
|
893
893
|
# save geometry
|
|
894
894
|
gui_utilities.save_geometry(observationWindow, "new observation")
|
|
@@ -2012,7 +2012,7 @@ def initialize_new_media_observation(self) -> bool:
|
|
|
2012
2012
|
self.mpv_eof_reached_signal.connect(self.mpv_eof_reached)
|
|
2013
2013
|
self.video_click_signal.connect(self.player_clicked)
|
|
2014
2014
|
|
|
2015
|
-
self.actionPlay.setIcon(QIcon(f":/play_{
|
|
2015
|
+
self.actionPlay.setIcon(QIcon(f":/play_{gui_utilities.theme_mode()}"))
|
|
2016
2016
|
|
|
2017
2017
|
self.display_statusbar_info(self.observationId)
|
|
2018
2018
|
|
|
@@ -2406,15 +2406,15 @@ def event2media_file_name(observation: dict, timestamp: dec) -> Optional[str]:
|
|
|
2406
2406
|
"""
|
|
2407
2407
|
|
|
2408
2408
|
cumul_media_durations: list = [dec(0)]
|
|
2409
|
-
for media_file in observation[cfg.FILE][
|
|
2409
|
+
for media_file in observation[cfg.FILE][cfg.PLAYER1]:
|
|
2410
2410
|
media_duration = dec(str(observation[cfg.MEDIA_INFO][cfg.LENGTH][media_file]))
|
|
2411
|
-
cumul_media_durations.append(cumul_media_durations[-1] + media_duration)
|
|
2411
|
+
cumul_media_durations.append(round(cumul_media_durations[-1] + media_duration, 3))
|
|
2412
2412
|
|
|
2413
2413
|
cumul_media_durations.remove(dec(0))
|
|
2414
2414
|
|
|
2415
2415
|
# test if timestamp is at end of last media
|
|
2416
2416
|
if timestamp == cumul_media_durations[-1]:
|
|
2417
|
-
player_idx = len(observation[cfg.FILE][
|
|
2417
|
+
player_idx = len(observation[cfg.FILE][cfg.PLAYER1]) - 1
|
|
2418
2418
|
else:
|
|
2419
2419
|
player_idx = -1
|
|
2420
2420
|
for idx, value in enumerate(cumul_media_durations):
|
|
@@ -2424,7 +2424,7 @@ def event2media_file_name(observation: dict, timestamp: dec) -> Optional[str]:
|
|
|
2424
2424
|
break
|
|
2425
2425
|
|
|
2426
2426
|
if player_idx != -1:
|
|
2427
|
-
video_file_name = observation[cfg.FILE][
|
|
2427
|
+
video_file_name = observation[cfg.FILE][cfg.PLAYER1][player_idx]
|
|
2428
2428
|
else:
|
|
2429
2429
|
video_file_name = None
|
|
2430
2430
|
|