boris-behav-obs 9.3.4__tar.gz → 9.4__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.4/boris_behav_obs.egg-info → boris_behav_obs-9.4}/PKG-INFO +1 -1
- boris_behav_obs-9.4/boris/1.py +45 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/about.py +5 -4
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/config.py +2 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/config_file.py +1 -1
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/converters.py +1 -3
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/core.py +107 -168
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/dialog.py +10 -31
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/edit_event.py +47 -27
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/edit_event_ui.py +69 -36
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/event_operations.py +50 -57
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/observation.py +1 -2
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/observation_operations.py +16 -3
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/project_functions.py +1 -1
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/select_observations.py +7 -1
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/state_events.py +1 -1
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/utilities.py +170 -75
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/version.py +2 -2
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4/boris_behav_obs.egg-info}/PKG-INFO +1 -1
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/SOURCES.txt +1 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/pyproject.toml +2 -2
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/LICENSE.TXT +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/MANIFEST.in +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/README.TXT +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/README.md +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/__init__.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/__main__.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/add_modifier.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/add_modifier_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/advanced_event_filtering.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/analysis_plugins/__init__.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/analysis_plugins/_latency.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/analysis_plugins/number_of_occurences.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/analysis_plugins/time_budget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/behav_coding_map_creator.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/behavior_binary_table.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/behaviors_coding_map.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/boris_cli.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/cmd_arguments.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/coding_pad.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/connections.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/converters_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/cooccurence.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/core_qrc.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/core_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/db_functions.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/dev.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/duration_widget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/events_cursor.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/events_snapshots.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/exclusion_matrix.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/export_events.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/export_observation.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/external_processes.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/geometric_measurement.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/gui_utilities.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/image_overlay.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/import_observations.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/irr.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/latency.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/measurement_widget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/media_file.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/menu_options.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/modifier_coding_map_creator.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/modifiers_coding_map.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/mpv-1.0.3.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/mpv.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/mpv2.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/observation_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/observations_list.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/otx_parser.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/param_panel.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/param_panel_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/player_dock_widget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/plot_data_module.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/plot_events.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/plot_events_rt.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/plot_spectrogram_rt.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/plot_waveform_rt.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/plugins.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/portion/__init__.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/portion/const.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/portion/dict.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/portion/func.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/portion/interval.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/portion/io.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/preferences.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/preferences_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/project.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/project_import_export.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/project_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/qrc_boris.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/qrc_boris5.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/select_modifiers.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/select_subj_behav.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/subjects_pad.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/synthetic_time_budget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/time_budget_functions.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/time_budget_widget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/transitions.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/video_equalizer.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/video_equalizer_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/video_operations.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/view_df.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/view_df_ui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris/write_event.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/entry_points.txt +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/requires.txt +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/top_level.txt +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/setup.cfg +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_db_functions.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_export_observation.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_irr.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_observation_gui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_otx_parser.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_preferences_gui.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_project_functions.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_time_budget.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_utilities.py +0 -0
- {boris_behav_obs-9.3.4 → boris_behav_obs-9.4}/tests/test_utilities2.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
3
|
+
import mpv
|
|
4
|
+
|
|
5
|
+
player = mpv.MPV()
|
|
6
|
+
|
|
7
|
+
player.loop = True
|
|
8
|
+
player.play('/home/olivier/gdrive_sync/src/python/generate_video_test/video1.mp4')
|
|
9
|
+
player.wait_until_playing()
|
|
10
|
+
|
|
11
|
+
font = ImageFont.truetype('DejaVuSans.ttf', 40)
|
|
12
|
+
|
|
13
|
+
overlay = player.create_image_overlay()
|
|
14
|
+
|
|
15
|
+
img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
|
|
16
|
+
d = ImageDraw.Draw(img)
|
|
17
|
+
d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
|
|
18
|
+
#d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
|
|
19
|
+
|
|
20
|
+
pos = 100
|
|
21
|
+
|
|
22
|
+
overlay.update(img, pos=(2*pos, pos))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
while not player.core_idle:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
'''
|
|
30
|
+
for pos in range(0, 500, 5):
|
|
31
|
+
ts = player.time_pos
|
|
32
|
+
if ts is None:
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
|
|
36
|
+
d = ImageDraw.Draw(img)
|
|
37
|
+
d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
|
|
38
|
+
d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
|
|
39
|
+
|
|
40
|
+
overlay.update(img, pos=(2*pos, pos))
|
|
41
|
+
time.sleep(0.05)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
overlay.remove()
|
|
45
|
+
'''
|
|
@@ -31,7 +31,6 @@ from . import config as cfg
|
|
|
31
31
|
from . import utilities as util
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
from PySide6.QtCore import qVersion
|
|
35
34
|
from PySide6.QtGui import QPixmap
|
|
36
35
|
from PySide6.QtWidgets import QMessageBox
|
|
37
36
|
|
|
@@ -41,8 +40,6 @@ def actionAbout_activated(self):
|
|
|
41
40
|
About dialog
|
|
42
41
|
"""
|
|
43
42
|
|
|
44
|
-
import PySide6
|
|
45
|
-
|
|
46
43
|
programs_versions: list = ["MPV media player"]
|
|
47
44
|
|
|
48
45
|
mpv_lib_version, mpv_lib_file_path, mpv_api_version = util.mpv_lib_version()
|
|
@@ -108,6 +105,7 @@ def actionAbout_activated(self):
|
|
|
108
105
|
'<a href="https://besjournals.onlinelibrary.wiley.com/doi/full/10.1111/2041-210X.12584">DOI:10.1111/2041-210X.12584</a>'
|
|
109
106
|
)
|
|
110
107
|
)
|
|
108
|
+
"""
|
|
111
109
|
n = "\n"
|
|
112
110
|
current_system = platform.uname()
|
|
113
111
|
details = (
|
|
@@ -123,8 +121,11 @@ def actionAbout_activated(self):
|
|
|
123
121
|
f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
|
|
124
122
|
f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb\n\n"
|
|
125
123
|
)
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
details = util.get_systeminfo()
|
|
126
127
|
|
|
127
|
-
details += n.join(programs_versions)
|
|
128
|
+
details += "\n".join(programs_versions)
|
|
128
129
|
"""
|
|
129
130
|
memory_in_use = f"{utilities.rss_memory_used(self.pid)} Mb" if utilities.rss_memory_used(self.pid) != -1 else "Not available"
|
|
130
131
|
percent_memory_in_use = (f"({utilities.rss_memory_percent_used(self.pid):.1f} % of total memory)"
|
|
@@ -37,6 +37,7 @@ SECONDS_PER_DAY: int = 86_400
|
|
|
37
37
|
HOUR_CUTOFF: int = 7 * 24
|
|
38
38
|
DATE_CUTOFF: int = HOUR_CUTOFF * 60 * 60 # 1 week
|
|
39
39
|
|
|
40
|
+
# cutoff for displaying time in HH:MM:SS.zzz format
|
|
40
41
|
SMART_TIME_CUTOFF_DEFAULT: int = 300
|
|
41
42
|
|
|
42
43
|
# minimal project version for handling observations from images
|
|
@@ -218,6 +219,7 @@ OVERLAY = "video overlay"
|
|
|
218
219
|
|
|
219
220
|
|
|
220
221
|
USE_EXIF_DATE = "use_exif_date"
|
|
222
|
+
SUBSTRACT_FIRST_EXIF_DATE = "substract_first_exif_date"
|
|
221
223
|
TIME_LAPSE = "time_lapse_delay"
|
|
222
224
|
|
|
223
225
|
|
|
@@ -22,8 +22,6 @@ This file is part of BORIS.
|
|
|
22
22
|
|
|
23
23
|
import sys
|
|
24
24
|
import json
|
|
25
|
-
import urllib.error
|
|
26
|
-
import urllib.parse
|
|
27
25
|
import urllib.request
|
|
28
26
|
|
|
29
27
|
|
|
@@ -314,7 +312,7 @@ def load_converters_from_file_repo(self, mode: str):
|
|
|
314
312
|
QMessageBox.critical(
|
|
315
313
|
self,
|
|
316
314
|
"BORIS",
|
|
317
|
-
(f"The code of {converter_name} converter produces an error:
|
|
315
|
+
(f"The code of {converter_name} converter produces an error: <br><b>{sys.exc_info()[1]}</b>"),
|
|
318
316
|
)
|
|
319
317
|
|
|
320
318
|
self.tw_converters.setRowCount(self.tw_converters.rowCount() + 1)
|
|
@@ -19,34 +19,31 @@ This file is part of BORIS.
|
|
|
19
19
|
along with this program; if not see <http://www.gnu.org/licenses/>.
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
|
+
# ruff: noqa: E402
|
|
22
23
|
|
|
23
24
|
import os
|
|
24
25
|
import sys
|
|
26
|
+
import pathlib as pl
|
|
25
27
|
|
|
26
|
-
os.environ["PATH"] = os.path.dirname(__file__) + os.sep + "misc" + os.pathsep + os.environ["PATH"]
|
|
28
|
+
# os.environ["PATH"] = os.path.dirname(__file__) + os.sep + "misc" + os.pathsep + os.environ["PATH"]
|
|
27
29
|
|
|
30
|
+
os.environ["PATH"] = str(pl.Path(__file__).parent / "misc") + os.pathsep + os.environ["PATH"]
|
|
28
31
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
|
|
29
32
|
|
|
30
|
-
|
|
31
33
|
import datetime
|
|
32
|
-
|
|
33
34
|
import json
|
|
34
35
|
import logging
|
|
35
|
-
import pathlib as pl
|
|
36
36
|
import platform
|
|
37
37
|
import re
|
|
38
38
|
import PIL.Image
|
|
39
39
|
import PIL.ImageEnhance
|
|
40
|
+
from PIL.ImageQt import Image
|
|
40
41
|
import subprocess
|
|
41
|
-
|
|
42
42
|
import locale
|
|
43
43
|
import tempfile
|
|
44
44
|
import time
|
|
45
|
-
import urllib.error
|
|
46
|
-
import urllib.parse
|
|
47
45
|
import urllib.request
|
|
48
46
|
from typing import Union, Tuple
|
|
49
|
-
|
|
50
47
|
from decimal import Decimal as dec
|
|
51
48
|
from decimal import ROUND_DOWN
|
|
52
49
|
import gzip
|
|
@@ -57,7 +54,6 @@ import shutil
|
|
|
57
54
|
|
|
58
55
|
matplotlib.use("QtAgg")
|
|
59
56
|
|
|
60
|
-
import PySide6
|
|
61
57
|
from PySide6.QtCore import (
|
|
62
58
|
Qt,
|
|
63
59
|
QPoint,
|
|
@@ -66,7 +62,6 @@ from PySide6.QtCore import (
|
|
|
66
62
|
QDateTime,
|
|
67
63
|
QUrl,
|
|
68
64
|
QAbstractTableModel,
|
|
69
|
-
qVersion,
|
|
70
65
|
QElapsedTimer,
|
|
71
66
|
QSettings,
|
|
72
67
|
)
|
|
@@ -88,7 +83,28 @@ from PySide6.QtWidgets import (
|
|
|
88
83
|
QStyledItemDelegate,
|
|
89
84
|
QTableWidgetItem,
|
|
90
85
|
)
|
|
91
|
-
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
from . import cmd_arguments
|
|
89
|
+
|
|
90
|
+
# parse command line arguments
|
|
91
|
+
(options, args) = cmd_arguments.parse_arguments()
|
|
92
|
+
|
|
93
|
+
# set logging parameters
|
|
94
|
+
if options.debug:
|
|
95
|
+
logging.basicConfig(
|
|
96
|
+
format="%(asctime)s,%(msecs)d %(module)s l.%(lineno)d %(levelname)s %(message)s",
|
|
97
|
+
datefmt="%H:%M:%S",
|
|
98
|
+
level=logging.DEBUG,
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
logging.basicConfig(
|
|
102
|
+
format="%(asctime)s,%(msecs)d %(message)s",
|
|
103
|
+
datefmt="%H:%M:%S",
|
|
104
|
+
level=logging.INFO,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
from . import utilities as util
|
|
92
108
|
|
|
93
109
|
from . import dialog
|
|
94
110
|
from . import gui_utilities
|
|
@@ -105,21 +121,15 @@ from . import plot_waveform_rt
|
|
|
105
121
|
from . import plot_events_rt
|
|
106
122
|
from . import plugins
|
|
107
123
|
from . import project_functions
|
|
108
|
-
|
|
109
124
|
from . import select_observations
|
|
110
125
|
from . import subjects_pad
|
|
111
126
|
from . import version
|
|
112
127
|
from . import event_operations
|
|
113
|
-
from . import cmd_arguments
|
|
114
|
-
|
|
115
128
|
from . import core_qrc
|
|
116
129
|
from .core_ui import Ui_MainWindow
|
|
117
130
|
from . import config as cfg
|
|
118
131
|
from . import video_operations
|
|
119
|
-
|
|
120
132
|
from . import project
|
|
121
|
-
from . import utilities as util
|
|
122
|
-
|
|
123
133
|
from . import menu_options as menu_options
|
|
124
134
|
from . import connections as connections
|
|
125
135
|
from . import config_file
|
|
@@ -128,7 +138,7 @@ from . import observation_operations
|
|
|
128
138
|
from . import write_event
|
|
129
139
|
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
logging.debug("test")
|
|
132
142
|
|
|
133
143
|
__version__ = version.__version__
|
|
134
144
|
__version_date__ = version.__version_date__
|
|
@@ -143,44 +153,13 @@ if util.versiontuple(platform.python_version()) < util.versiontuple(MIN_PYTHON_V
|
|
|
143
153
|
if sys.platform == "darwin": # for MacOS
|
|
144
154
|
os.environ["LC_ALL"] = "en_US.UTF-8"
|
|
145
155
|
|
|
146
|
-
# parse command line arguments
|
|
147
|
-
(options, args) = cmd_arguments.parse_arguments()
|
|
148
|
-
|
|
149
|
-
# set logging parameters
|
|
150
|
-
if options.debug:
|
|
151
|
-
logging.basicConfig(
|
|
152
|
-
format="%(asctime)s,%(msecs)d %(module)s l.%(lineno)d %(levelname)s %(message)s",
|
|
153
|
-
datefmt="%H:%M:%S",
|
|
154
|
-
level=logging.DEBUG,
|
|
155
|
-
)
|
|
156
|
-
else:
|
|
157
|
-
logging.basicConfig(
|
|
158
|
-
format="%(asctime)s,%(msecs)d %(message)s",
|
|
159
|
-
datefmt="%H:%M:%S",
|
|
160
|
-
level=logging.INFO,
|
|
161
|
-
)
|
|
162
156
|
|
|
163
157
|
if options.version:
|
|
164
158
|
print(f"version {__version__} release date: {__version_date__}")
|
|
165
159
|
sys.exit(0)
|
|
166
160
|
|
|
167
|
-
|
|
168
161
|
logging.debug("BORIS started")
|
|
169
|
-
logging.info(
|
|
170
|
-
logging.info(f"Operating system: {platform.uname().system} {platform.uname().release} {platform.uname().version}")
|
|
171
|
-
logging.info(f"CPU: {platform.uname().machine} {platform.uname().processor}")
|
|
172
|
-
logging.info(f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})")
|
|
173
|
-
logging.info(f"Qt {qVersion()} - PySide {PySide6.__version__}")
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
(r, memory) = util.mem_info()
|
|
177
|
-
if not r:
|
|
178
|
-
logging.info(
|
|
179
|
-
(
|
|
180
|
-
f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
|
|
181
|
-
f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb"
|
|
182
|
-
)
|
|
183
|
-
)
|
|
162
|
+
logging.info(util.get_systeminfo())
|
|
184
163
|
|
|
185
164
|
|
|
186
165
|
def excepthook(exception_type, exception_value, traceback_object):
|
|
@@ -308,9 +287,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
308
287
|
play_rate_step: float = 0.1
|
|
309
288
|
currentSubject: str = "" # contains the current subject of observation
|
|
310
289
|
|
|
311
|
-
#
|
|
312
|
-
memx = -1
|
|
313
|
-
memy = -1
|
|
290
|
+
# geometric measurements
|
|
314
291
|
mem_player = -1
|
|
315
292
|
|
|
316
293
|
# path for ffmpeg/ffmpeg.exe program
|
|
@@ -339,6 +316,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
339
316
|
|
|
340
317
|
mem_hash_obs: int = 0
|
|
341
318
|
|
|
319
|
+
# variables for list of observations
|
|
320
|
+
data: list = []
|
|
321
|
+
not_paired: list = []
|
|
322
|
+
|
|
342
323
|
'''
|
|
343
324
|
def add_button_menu(self, data, menu_obj):
|
|
344
325
|
"""
|
|
@@ -1342,21 +1323,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1342
1323
|
self.pj[cfg.BEHAVIORS_CODING_MAP][idx] = dict(behav_coding_map)
|
|
1343
1324
|
return
|
|
1344
1325
|
|
|
1345
|
-
"""
|
|
1346
|
-
QMessageBox.critical(
|
|
1347
|
-
None,
|
|
1348
|
-
cfg.programName,
|
|
1349
|
-
(
|
|
1350
|
-
"The current project already contains a behaviors coding map "
|
|
1351
|
-
f"with the same name (<b>{behav_coding_map['name']}</b>)"
|
|
1352
|
-
),
|
|
1353
|
-
QMessageBox.Ok | QMessageBox.Default,
|
|
1354
|
-
QMessageBox.NoButton,
|
|
1355
|
-
)
|
|
1356
|
-
|
|
1357
|
-
return
|
|
1358
|
-
"""
|
|
1359
|
-
|
|
1360
1326
|
self.pj[cfg.BEHAVIORS_CODING_MAP].append(behav_coding_map)
|
|
1361
1327
|
QMessageBox.information(
|
|
1362
1328
|
self,
|
|
@@ -1375,7 +1341,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1375
1341
|
try:
|
|
1376
1342
|
last_version = urllib.request.urlopen(version_URL).read().strip().decode("utf-8")
|
|
1377
1343
|
except Exception:
|
|
1378
|
-
QMessageBox.warning(self, cfg.programName, "Can not check for updates
|
|
1344
|
+
QMessageBox.warning(self, cfg.programName, "Can not check for updates. Check your connection.")
|
|
1379
1345
|
return
|
|
1380
1346
|
|
|
1381
1347
|
# record check timestamp
|
|
@@ -1526,7 +1492,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1526
1492
|
jt.setWindowTitle("Jump to specific time")
|
|
1527
1493
|
jt.label.setText("Set the time")
|
|
1528
1494
|
|
|
1529
|
-
if jt.
|
|
1495
|
+
if jt.exec():
|
|
1530
1496
|
new_time = jt.time_widget.get_time()
|
|
1531
1497
|
if new_time < 0:
|
|
1532
1498
|
return
|
|
@@ -1704,7 +1670,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1704
1670
|
QMessageBox.critical(
|
|
1705
1671
|
None,
|
|
1706
1672
|
cfg.programName,
|
|
1707
|
-
("The picture directory
|
|
1673
|
+
("The picture directory has changed since the creation of observation."),
|
|
1708
1674
|
QMessageBox.Ok | QMessageBox.Default,
|
|
1709
1675
|
QMessageBox.NoButton,
|
|
1710
1676
|
)
|
|
@@ -1718,15 +1684,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1718
1684
|
# extract EXIF tag
|
|
1719
1685
|
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False):
|
|
1720
1686
|
date_time_original = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
1687
|
+
|
|
1721
1688
|
if date_time_original != -1:
|
|
1722
1689
|
msg += f"<br>EXIF Date/Time Original: <b>{datetime.datetime.fromtimestamp(date_time_original):%Y-%m-%d %H:%M:%S}</b>"
|
|
1723
|
-
else:
|
|
1724
|
-
msg += "<br>EXIF Date/Time Original: <b>NA</b>"
|
|
1725
1690
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1691
|
+
if self.image_idx == 0:
|
|
1692
|
+
self.image_time_ref = date_time_original
|
|
1728
1693
|
|
|
1729
|
-
if date_time_original != -1:
|
|
1730
1694
|
if self.image_time_ref is not None:
|
|
1731
1695
|
seconds_from_1st = date_time_original - self.image_time_ref
|
|
1732
1696
|
|
|
@@ -1736,6 +1700,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1736
1700
|
seconds_from_1st_formated = seconds_from_1st
|
|
1737
1701
|
|
|
1738
1702
|
else:
|
|
1703
|
+
msg += "<br>EXIF Date/Time Original: <b>NA</b>"
|
|
1739
1704
|
seconds_from_1st_formated = cfg.NA
|
|
1740
1705
|
|
|
1741
1706
|
msg += f"<br>Time from 1st image: <b>{seconds_from_1st_formated}</b>"
|
|
@@ -2311,11 +2276,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
2311
2276
|
self.tv_events.setModel(model)
|
|
2312
2277
|
|
|
2313
2278
|
# column width
|
|
2314
|
-
|
|
2315
|
-
self.tv_events.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
|
2316
|
-
|
|
2317
|
-
# self.table.setSortingEnabled(True)
|
|
2318
|
-
# self.table.sortByColumn(0, Qt.AscendingOrder)
|
|
2279
|
+
self.tv_events.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
|
2319
2280
|
|
|
2320
2281
|
def load_tw_events(self, obs_id) -> None:
|
|
2321
2282
|
"""
|
|
@@ -2343,76 +2304,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
2343
2304
|
|
|
2344
2305
|
return
|
|
2345
2306
|
|
|
2346
|
-
"""
|
|
2347
|
-
DISABLED tableview component is used
|
|
2348
|
-
|
|
2349
|
-
logging.debug(f"begin load events from obs in tablewidget: {obs_id}")
|
|
2350
|
-
|
|
2351
|
-
t1 = time.time()
|
|
2352
|
-
|
|
2353
|
-
self.twEvents.clear()
|
|
2354
|
-
|
|
2355
|
-
self.twEvents.setColumnCount(len(cfg.TW_EVENTS_FIELDS[self.playerType]))
|
|
2356
|
-
self.twEvents.setHorizontalHeaderLabels([s.capitalize() for s in cfg.TW_EVENTS_FIELDS[self.playerType]])
|
|
2357
|
-
|
|
2358
|
-
for idx, field in enumerate(cfg.TW_EVENTS_FIELDS[self.playerType]):
|
|
2359
|
-
if field not in self.config_param.get(f"{self.playerType} tw fields", cfg.TW_EVENTS_FIELDS[self.playerType]):
|
|
2360
|
-
self.twEvents.horizontalHeader().hideSection(idx)
|
|
2361
|
-
else:
|
|
2362
|
-
self.twEvents.horizontalHeader().showSection(idx)
|
|
2363
|
-
|
|
2364
|
-
self.twEvents.setRowCount(len(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]))
|
|
2365
|
-
if self.filtered_behaviors or self.filtered_subjects:
|
|
2366
|
-
self.twEvents.setRowCount(0)
|
|
2367
|
-
row = 0
|
|
2368
|
-
|
|
2369
|
-
for event_idx, event in enumerate(self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]):
|
|
2370
|
-
if self.filtered_behaviors and event[cfg.PJ_OBS_FIELDS[self.playerType][cfg.BEHAVIOR_CODE]] not in self.filtered_behaviors:
|
|
2371
|
-
continue
|
|
2372
|
-
|
|
2373
|
-
if self.filtered_subjects and event[cfg.PJ_OBS_FIELDS[self.playerType][cfg.SUBJECT]] not in self.filtered_subjects:
|
|
2374
|
-
continue
|
|
2375
|
-
|
|
2376
|
-
if self.filtered_behaviors or self.filtered_subjects:
|
|
2377
|
-
self.twEvents.insertRow(self.twEvents.rowCount())
|
|
2378
|
-
|
|
2379
|
-
for field_type in cfg.TW_EVENTS_FIELDS[self.playerType]:
|
|
2380
|
-
if field_type in cfg.PJ_EVENTS_FIELDS[self.playerType]:
|
|
2381
|
-
field = event_operations.read_event_field(event, self.playerType, field_type)
|
|
2382
|
-
|
|
2383
|
-
if field_type == cfg.TIME:
|
|
2384
|
-
item = QTableWidgetItem(str(util.convertTime(self.timeFormat, field)))
|
|
2385
|
-
|
|
2386
|
-
# add index of project events
|
|
2387
|
-
item.setData(Qt.UserRole, event_idx)
|
|
2388
|
-
self.twEvents.setItem(row, cfg.TW_OBS_FIELD[self.playerType][field_type], item)
|
|
2389
|
-
continue
|
|
2390
|
-
|
|
2391
|
-
if field_type in (cfg.IMAGE_INDEX, cfg.FRAME_INDEX):
|
|
2392
|
-
field = str(field)
|
|
2393
|
-
|
|
2394
|
-
self.twEvents.setItem(
|
|
2395
|
-
row,
|
|
2396
|
-
cfg.TW_OBS_FIELD[self.playerType][field_type],
|
|
2397
|
-
QTableWidgetItem(field),
|
|
2398
|
-
)
|
|
2399
|
-
|
|
2400
|
-
else:
|
|
2401
|
-
self.twEvents.setItem(
|
|
2402
|
-
row,
|
|
2403
|
-
cfg.TW_OBS_FIELD[self.playerType][field_type],
|
|
2404
|
-
QTableWidgetItem(""),
|
|
2405
|
-
)
|
|
2406
|
-
|
|
2407
|
-
row += 1
|
|
2408
|
-
|
|
2409
|
-
self.update_events_start_stop()
|
|
2410
|
-
|
|
2411
|
-
print("load twevent:", time.time() - t1)
|
|
2412
|
-
|
|
2413
|
-
logging.debug("end load events from obs")
|
|
2414
|
-
"""
|
|
2415
|
-
|
|
2416
2307
|
def close_tool_windows(self):
|
|
2417
2308
|
"""
|
|
2418
2309
|
close tool windows:
|
|
@@ -3493,7 +3384,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3493
3384
|
return
|
|
3494
3385
|
|
|
3495
3386
|
logging.debug(
|
|
3496
|
-
f"{self.config_param[cfg.CHECK_PROJECT_INTEGRITY] if cfg.CHECK_PROJECT_INTEGRITY in self.config_param else
|
|
3387
|
+
f"{self.config_param[cfg.CHECK_PROJECT_INTEGRITY] if cfg.CHECK_PROJECT_INTEGRITY in self.config_param else 'Check project integrity config NOT FOUND'=}"
|
|
3497
3388
|
)
|
|
3498
3389
|
|
|
3499
3390
|
if self.config_param.get(cfg.CHECK_PROJECT_INTEGRITY, True):
|
|
@@ -4707,7 +4598,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4707
4598
|
self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False)
|
|
4708
4599
|
and util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx]) != -1
|
|
4709
4600
|
):
|
|
4710
|
-
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4601
|
+
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4602
|
+
# check if first value must be substracted
|
|
4603
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.SUBSTRACT_FIRST_EXIF_DATE, True):
|
|
4604
|
+
time_ -= self.image_time_ref
|
|
4711
4605
|
|
|
4712
4606
|
elif self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0):
|
|
4713
4607
|
time_ = (self.image_idx + 1) * self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0)
|
|
@@ -4765,7 +4659,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4765
4659
|
self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False)
|
|
4766
4660
|
and util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx]) != -1
|
|
4767
4661
|
):
|
|
4768
|
-
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4662
|
+
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4663
|
+
# check if first value must be substracte
|
|
4664
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.SUBSTRACT_FIRST_EXIF_DATE, True):
|
|
4665
|
+
time_ -= self.image_time_ref
|
|
4769
4666
|
|
|
4770
4667
|
elif self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0):
|
|
4771
4668
|
time_ = (self.image_idx + 1) * self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0)
|
|
@@ -5791,21 +5688,54 @@ def main():
|
|
|
5791
5688
|
|
|
5792
5689
|
locale.setlocale(locale.LC_NUMERIC, "C")
|
|
5793
5690
|
|
|
5794
|
-
# splashscreen
|
|
5795
|
-
# no splashscreen for Mac because it can mask the first use dialog box
|
|
5796
|
-
|
|
5797
|
-
if (not options.nosplashscreen) and (sys.platform != "darwin"):
|
|
5798
|
-
start = time.time()
|
|
5799
|
-
splash = QSplashScreen(QPixmap(":/splash"))
|
|
5800
|
-
splash.show()
|
|
5801
|
-
splash.raise_()
|
|
5802
|
-
app.processEvents()
|
|
5803
|
-
while time.time() - start < 1:
|
|
5804
|
-
time.sleep(0.001)
|
|
5805
|
-
|
|
5806
5691
|
# check FFmpeg
|
|
5807
5692
|
ret, msg = util.check_ffmpeg_path()
|
|
5808
5693
|
if not ret:
|
|
5694
|
+
if sys.platform.startswith("win"):
|
|
5695
|
+
QMessageBox.warning(
|
|
5696
|
+
None,
|
|
5697
|
+
cfg.programName,
|
|
5698
|
+
"FFmpeg is not available.<br>It will be downloaded",
|
|
5699
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
5700
|
+
QMessageBox.NoButton,
|
|
5701
|
+
)
|
|
5702
|
+
|
|
5703
|
+
# download ffmpeg and ffprobe from https://github.com/boris-behav-obs/boris-behav-obs.github.io/releases/download/files/
|
|
5704
|
+
url = "https://github.com/boris-behav-obs/boris-behav-obs.github.io/releases/download/files/"
|
|
5705
|
+
|
|
5706
|
+
# search where to download ffmpeg
|
|
5707
|
+
ffmpeg_dir = pl.Path(__file__).parent / "misc"
|
|
5708
|
+
|
|
5709
|
+
logging.debug(f"{ffmpeg_dir=}")
|
|
5710
|
+
|
|
5711
|
+
if not ffmpeg_dir.is_dir():
|
|
5712
|
+
logging.info(f"Creating {ffmpeg_dir} directory")
|
|
5713
|
+
ffmpeg_dir.mkdir(parents=True, exist_ok=True)
|
|
5714
|
+
|
|
5715
|
+
for file_ in ("ffmpeg.exe", "ffprobe.exe"):
|
|
5716
|
+
local_filename = ffmpeg_dir / file_
|
|
5717
|
+
logging.info(f"Downloading {file_}...")
|
|
5718
|
+
try:
|
|
5719
|
+
urllib.request.urlretrieve(url + file_, local_filename)
|
|
5720
|
+
except Exception:
|
|
5721
|
+
logging.critical("The FFmpeg program can not be downloaded! Check your connection.")
|
|
5722
|
+
QMessageBox.warning(
|
|
5723
|
+
None,
|
|
5724
|
+
cfg.programName,
|
|
5725
|
+
"The FFmpeg program can not be downloaded!\nCheck your connection.",
|
|
5726
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
5727
|
+
QMessageBox.NoButton,
|
|
5728
|
+
)
|
|
5729
|
+
sys.exit(3)
|
|
5730
|
+
|
|
5731
|
+
logging.info(f"File downloaded as {local_filename}")
|
|
5732
|
+
|
|
5733
|
+
# re-test for ffmpeg
|
|
5734
|
+
ret, msg = util.check_ffmpeg_path()
|
|
5735
|
+
|
|
5736
|
+
if ret:
|
|
5737
|
+
ffmpeg_bin = msg
|
|
5738
|
+
else:
|
|
5809
5739
|
QMessageBox.critical(
|
|
5810
5740
|
None,
|
|
5811
5741
|
cfg.programName,
|
|
@@ -5814,8 +5744,17 @@ def main():
|
|
|
5814
5744
|
QMessageBox.NoButton,
|
|
5815
5745
|
)
|
|
5816
5746
|
sys.exit(3)
|
|
5817
|
-
|
|
5818
|
-
|
|
5747
|
+
|
|
5748
|
+
# splashscreen
|
|
5749
|
+
# no splashscreen for Mac because it can mask the first use dialog box
|
|
5750
|
+
if (not options.nosplashscreen) and (sys.platform != "darwin"):
|
|
5751
|
+
start = time.time()
|
|
5752
|
+
splash = QSplashScreen(QPixmap(":/splash"))
|
|
5753
|
+
splash.show()
|
|
5754
|
+
splash.raise_()
|
|
5755
|
+
app.processEvents()
|
|
5756
|
+
while time.time() - start < 1:
|
|
5757
|
+
time.sleep(0.001)
|
|
5819
5758
|
|
|
5820
5759
|
app.setApplicationName(cfg.programName)
|
|
5821
5760
|
|