boris-behav-obs 8.27.10__py2.py3-none-any.whl → 9.0.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- boris/about.py +7 -5
- boris/add_modifier.py +35 -35
- boris/add_modifier_ui.py +229 -129
- boris/advanced_event_filtering.py +3 -3
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/number_of_occurences.py +60 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +72 -0
- boris/analysis_plugins/time_budget.py +95 -0
- boris/behav_coding_map_creator.py +103 -108
- boris/behavior_binary_table.py +1 -1
- boris/behaviors_coding_map.py +8 -8
- boris/coding_pad.py +6 -6
- boris/config.py +6 -0
- boris/config_file.py +1 -1
- boris/connections.py +4 -2
- boris/converters.py +2 -3
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +2 -2
- boris/core.py +340 -94
- boris/core_qrc.py +16088 -13246
- boris/core_ui.py +922 -812
- boris/dialog.py +14 -13
- boris/duration_widget.py +5 -5
- boris/edit_event.py +1 -1
- boris/edit_event_ui.py +162 -88
- boris/event_operations.py +4 -25
- boris/events_cursor.py +17 -9
- boris/events_snapshots.py +5 -5
- boris/exclusion_matrix.py +1 -1
- boris/export_events.py +38 -28
- boris/export_observation.py +1 -1
- boris/external_processes.py +3 -5
- boris/geometric_measurement.py +49 -26
- boris/gui_utilities.py +31 -30
- boris/import_observations.py +2 -4
- boris/irr.py +1 -1
- boris/latency.py +1 -1
- boris/map_creator.py +77 -89
- boris/measurement_widget.py +4 -4
- boris/media_file.py +2 -4
- boris/menu_options.py +1 -3
- boris/modifiers_coding_map.py +4 -4
- boris/mpv2.py +0 -2
- boris/observation.py +124 -29
- boris/observation_operations.py +18 -40
- boris/observation_ui.py +566 -374
- boris/observations_list.py +6 -6
- boris/param_panel.py +2 -2
- boris/param_panel_ui.py +246 -141
- boris/player_dock_widget.py +16 -21
- boris/plot_data_module.py +6 -6
- boris/plot_events_rt.py +7 -8
- boris/plot_spectrogram_rt.py +7 -8
- boris/plot_waveform_rt.py +6 -7
- boris/plugins.py +79 -0
- boris/preferences.py +127 -17
- boris/preferences_ui.py +464 -240
- boris/project.py +69 -72
- boris/project_functions.py +233 -31
- boris/project_import_export.py +59 -67
- boris/project_ui.py +672 -440
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +2 -2
- boris/select_observations.py +2 -2
- boris/select_subj_behav.py +3 -3
- boris/state_events.py +1 -1
- boris/subjects_pad.py +5 -5
- boris/synthetic_time_budget.py +2 -2
- boris/time_budget_functions.py +15 -0
- boris/time_budget_widget.py +4 -4
- boris/transitions.py +34 -25
- boris/utilities.py +95 -2
- boris/version.py +2 -2
- boris/video_equalizer.py +4 -4
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +1 -1
- boris/view_df.py +106 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +9 -1
- {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.1.dist-info}/METADATA +5 -5
- boris_behav_obs-9.0.1.dist-info/RECORD +103 -0
- {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.1.dist-info}/WHEEL +1 -1
- boris/qdarkstyle/__init__.py +0 -479
- boris/qdarkstyle/__main__.py +0 -66
- boris/qdarkstyle/colorsystem.py +0 -38
- boris/qdarkstyle/dark/__init__.py +0 -1
- boris/qdarkstyle/dark/darkstyle_rc.py +0 -11379
- boris/qdarkstyle/dark/palette.py +0 -38
- boris/qdarkstyle/example/__init__.py +0 -4
- boris/qdarkstyle/example/__main__.py +0 -386
- boris/qdarkstyle/example/ui/__init__.py +0 -4
- boris/qdarkstyle/light/__init__.py +0 -1
- boris/qdarkstyle/light/lightstyle_rc.py +0 -11305
- boris/qdarkstyle/light/palette.py +0 -37
- boris/qdarkstyle/palette.py +0 -102
- boris/qdarkstyle/utils/__init__.py +0 -73
- boris/qdarkstyle/utils/__main__.py +0 -96
- boris/qdarkstyle/utils/images.py +0 -449
- boris/qdarkstyle/utils/scss.py +0 -318
- boris/vlc_local.py +0 -83
- boris_behav_obs-8.27.10.dist-info/RECORD +0 -114
- {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.1.dist-info}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.1.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-8.27.10.dist-info → boris_behav_obs-9.0.1.dist-info}/top_level.txt +0 -0
boris/qrc_boris.py
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# Resource object code
|
|
4
4
|
#
|
|
5
|
-
# Created by: The Resource Compiler for
|
|
5
|
+
# Created by: The Resource Compiler for PySide6 (Qt v5.13.0)
|
|
6
6
|
#
|
|
7
7
|
# WARNING! All changes made in this file will be lost!
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from PySide6 import QtCore
|
|
10
10
|
|
|
11
11
|
qt_resource_data = b"\
|
|
12
12
|
\x00\x00\x04\xba\
|
|
@@ -10369,7 +10369,7 @@ qt_resource_struct_v2 = b"\
|
|
|
10369
10369
|
\x00\x00\x01\x70\xe3\xd1\xd7\xb0\
|
|
10370
10370
|
"
|
|
10371
10371
|
|
|
10372
|
-
qt_version = [int(v) for v in QtCore.qVersion().split(
|
|
10372
|
+
qt_version = [int(v) for v in QtCore.qVersion().split(".")]
|
|
10373
10373
|
if qt_version < [5, 8, 0]:
|
|
10374
10374
|
rcc_version = 1
|
|
10375
10375
|
qt_resource_struct = qt_resource_struct_v1
|
|
@@ -10377,10 +10377,13 @@ else:
|
|
|
10377
10377
|
rcc_version = 2
|
|
10378
10378
|
qt_resource_struct = qt_resource_struct_v2
|
|
10379
10379
|
|
|
10380
|
+
|
|
10380
10381
|
def qInitResources():
|
|
10381
10382
|
QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
|
|
10382
10383
|
|
|
10384
|
+
|
|
10383
10385
|
def qCleanupResources():
|
|
10384
10386
|
QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
|
|
10385
10387
|
|
|
10388
|
+
|
|
10386
10389
|
qInitResources()
|
boris/qrc_boris5.py
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# Resource object code
|
|
4
4
|
#
|
|
5
|
-
# Created by: The Resource Compiler for
|
|
5
|
+
# Created by: The Resource Compiler for PySide6 (Qt v5.11.2)
|
|
6
6
|
#
|
|
7
7
|
# WARNING! All changes made in this file will be lost!
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from PySide6 import QtCore
|
|
10
10
|
|
|
11
11
|
qt_resource_data = b"\
|
|
12
12
|
\x00\x00\x04\xa6\
|
|
@@ -2559,7 +2559,7 @@ qt_resource_struct_v2 = b"\
|
|
|
2559
2559
|
\x00\x00\x01\x68\x32\x38\xb4\xfd\
|
|
2560
2560
|
"
|
|
2561
2561
|
|
|
2562
|
-
qt_version = [int(v) for v in QtCore.qVersion().split(
|
|
2562
|
+
qt_version = [int(v) for v in QtCore.qVersion().split(".")]
|
|
2563
2563
|
if qt_version < [5, 8, 0]:
|
|
2564
2564
|
rcc_version = 1
|
|
2565
2565
|
qt_resource_struct = qt_resource_struct_v1
|
|
@@ -2567,10 +2567,13 @@ else:
|
|
|
2567
2567
|
rcc_version = 2
|
|
2568
2568
|
qt_resource_struct = qt_resource_struct_v2
|
|
2569
2569
|
|
|
2570
|
+
|
|
2570
2571
|
def qInitResources():
|
|
2571
2572
|
QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
|
|
2572
2573
|
|
|
2574
|
+
|
|
2573
2575
|
def qCleanupResources():
|
|
2574
2576
|
QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
|
|
2575
2577
|
|
|
2578
|
+
|
|
2576
2579
|
qInitResources()
|
boris/select_modifiers.py
CHANGED
boris/select_observations.py
CHANGED
|
@@ -22,8 +22,8 @@ Copyright 2012-2024 Olivier Friard
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
from typing import Tuple
|
|
25
|
-
from
|
|
26
|
-
from
|
|
25
|
+
from PySide6.QtCore import Qt
|
|
26
|
+
from PySide6.QtWidgets import QAbstractItemView
|
|
27
27
|
|
|
28
28
|
from . import config as cfg
|
|
29
29
|
from . import gui_utilities, observations_list, project_functions
|
boris/select_subj_behav.py
CHANGED
|
@@ -24,9 +24,9 @@ import logging
|
|
|
24
24
|
from decimal import Decimal as dec
|
|
25
25
|
from typing import Optional
|
|
26
26
|
|
|
27
|
-
from
|
|
28
|
-
from
|
|
29
|
-
from
|
|
27
|
+
from PySide6.QtCore import Qt
|
|
28
|
+
from PySide6.QtGui import QFont
|
|
29
|
+
from PySide6.QtWidgets import QCheckBox, QListWidgetItem, QMessageBox
|
|
30
30
|
|
|
31
31
|
from . import config as cfg
|
|
32
32
|
from . import gui_utilities, param_panel, project_functions
|
boris/state_events.py
CHANGED
|
@@ -25,7 +25,7 @@ Module containing functions for state events
|
|
|
25
25
|
import time
|
|
26
26
|
from decimal import Decimal as dec
|
|
27
27
|
|
|
28
|
-
from
|
|
28
|
+
from PySide6.QtWidgets import QMessageBox, QAbstractItemView
|
|
29
29
|
|
|
30
30
|
from . import config as cfg
|
|
31
31
|
from . import dialog, project_functions, select_observations
|
boris/subjects_pad.py
CHANGED
|
@@ -19,17 +19,17 @@ Copyright 2012-2024 Olivier Friard
|
|
|
19
19
|
MA 02110-1301, USA.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
22
|
+
from PySide6.QtCore import Signal, QRect, QEvent, Qt
|
|
23
|
+
from PySide6.QtWidgets import QGridLayout, QPushButton, QHBoxLayout, QWidget
|
|
24
24
|
|
|
25
25
|
from . import config as cfg
|
|
26
26
|
from . import utilities as util
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class SubjectsPad(QWidget):
|
|
30
|
-
clickSignal =
|
|
31
|
-
sendEventSignal =
|
|
32
|
-
close_signal =
|
|
30
|
+
clickSignal = Signal(str)
|
|
31
|
+
sendEventSignal = Signal(QEvent)
|
|
32
|
+
close_signal = Signal(QRect)
|
|
33
33
|
|
|
34
34
|
def __init__(self, pj, filtered_subjects, parent=None):
|
|
35
35
|
super(SubjectsPad, self).__init__(parent)
|
boris/synthetic_time_budget.py
CHANGED
|
@@ -23,8 +23,8 @@ This file is part of BORIS.
|
|
|
23
23
|
import logging
|
|
24
24
|
import pathlib as pl
|
|
25
25
|
|
|
26
|
-
from
|
|
27
|
-
from
|
|
26
|
+
from PySide6.QtWidgets import QFileDialog, QMessageBox
|
|
27
|
+
from PySide6.QtGui import QFont, QTextOption, QTextCursor
|
|
28
28
|
|
|
29
29
|
from . import config as cfg
|
|
30
30
|
from . import (
|
boris/time_budget_functions.py
CHANGED
|
@@ -671,10 +671,14 @@ def time_budget_analysis(
|
|
|
671
671
|
categories: dict = {}
|
|
672
672
|
out: list = []
|
|
673
673
|
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
674
|
+
logging.debug(f"{subject=}")
|
|
675
|
+
|
|
674
676
|
out_cat: list = []
|
|
675
677
|
categories[subject] = {}
|
|
676
678
|
|
|
677
679
|
for behavior in parameters[cfg.SELECTED_BEHAVIORS]:
|
|
680
|
+
logging.debug(f"{behavior=}")
|
|
681
|
+
|
|
678
682
|
if parameters[cfg.INCLUDE_MODIFIERS]: # with modifiers
|
|
679
683
|
if parameters[cfg.EXCLUDE_NON_CODED_MODIFIERS]:
|
|
680
684
|
# get coded modifiers
|
|
@@ -1012,6 +1016,15 @@ def time_budget_analysis(
|
|
|
1012
1016
|
continue
|
|
1013
1017
|
|
|
1014
1018
|
if len(rows) % 2: # unpaired events
|
|
1019
|
+
"""
|
|
1020
|
+
print()
|
|
1021
|
+
print(f"{subject=}")
|
|
1022
|
+
print(f"{behavior=}")
|
|
1023
|
+
print()
|
|
1024
|
+
for row in rows:
|
|
1025
|
+
print(f"{row['observation']=} {row['occurence']=}")
|
|
1026
|
+
print()
|
|
1027
|
+
"""
|
|
1015
1028
|
out_cat.append(
|
|
1016
1029
|
{
|
|
1017
1030
|
"subject": subject,
|
|
@@ -1108,6 +1121,8 @@ def time_budget_analysis(
|
|
|
1108
1121
|
"-",
|
|
1109
1122
|
cfg.NA,
|
|
1110
1123
|
):
|
|
1124
|
+
# print(f"{categories[subject][category]["duration"]=}")
|
|
1125
|
+
# print(f"{behav["duration"]=}")
|
|
1111
1126
|
categories[subject][category]["duration"] += behav["duration"]
|
|
1112
1127
|
else:
|
|
1113
1128
|
categories[subject][category]["duration"] = cfg.NA
|
boris/time_budget_widget.py
CHANGED
|
@@ -37,8 +37,8 @@ except ModuleNotFoundError:
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
import tablib
|
|
40
|
-
from
|
|
41
|
-
from
|
|
40
|
+
from PySide6.QtCore import Qt
|
|
41
|
+
from PySide6.QtWidgets import (
|
|
42
42
|
QFileDialog,
|
|
43
43
|
QHBoxLayout,
|
|
44
44
|
QInputDialog,
|
|
@@ -631,8 +631,8 @@ def time_budget(self, mode: str, mode2: str = "list"):
|
|
|
631
631
|
else:
|
|
632
632
|
self.tb.excluded_behaviors_list.setVisible(False)
|
|
633
633
|
|
|
634
|
-
self.statusbar.showMessage(f"Time budget generated in {round(time.time() - t0, 3)} s")
|
|
635
|
-
logging.debug("Time budget generated"
|
|
634
|
+
self.statusbar.showMessage(f"Time budget generated in {round(time.time() - t0, 3)} s", 5000)
|
|
635
|
+
logging.debug("Time budget generated")
|
|
636
636
|
|
|
637
637
|
if mode == "by_behavior":
|
|
638
638
|
tb_fields = [
|
boris/transitions.py
CHANGED
|
@@ -24,8 +24,9 @@ import logging
|
|
|
24
24
|
import os
|
|
25
25
|
import subprocess
|
|
26
26
|
import tempfile
|
|
27
|
+
from pathlib import Path
|
|
27
28
|
|
|
28
|
-
from
|
|
29
|
+
from PySide6.QtWidgets import QFileDialog, QMessageBox
|
|
29
30
|
|
|
30
31
|
from . import config as cfg
|
|
31
32
|
from . import dialog, export_observation, select_subj_behav
|
|
@@ -170,6 +171,8 @@ def transitions_matrix(self, mode):
|
|
|
170
171
|
* number
|
|
171
172
|
* frequencies_after_behaviors
|
|
172
173
|
"""
|
|
174
|
+
logging.debug("flag transitions_matrix function")
|
|
175
|
+
|
|
173
176
|
# ask user observations to analyze
|
|
174
177
|
_, selected_observations = select_observations.select_observations2(
|
|
175
178
|
self, cfg.MULTIPLE, windows_title="Select observations for transitions matrix"
|
|
@@ -194,20 +197,20 @@ def transitions_matrix(self, mode):
|
|
|
194
197
|
|
|
195
198
|
flagMulti = False
|
|
196
199
|
if len(parameters[cfg.SELECTED_SUBJECTS]) == 1:
|
|
197
|
-
|
|
200
|
+
file_name, _ = QFileDialog().getSaveFileName(
|
|
198
201
|
None,
|
|
199
202
|
"Create matrix of transitions " + mode,
|
|
200
203
|
"",
|
|
201
204
|
"Transitions matrix files (*.txt *.tsv);;All files (*)",
|
|
202
205
|
)
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
if not file_name:
|
|
207
|
+
return
|
|
205
208
|
else:
|
|
206
|
-
exportDir = QFileDialog
|
|
209
|
+
exportDir = QFileDialog.getExistingDirectory(
|
|
207
210
|
self,
|
|
208
211
|
"Choose a directory to save the transitions matrices",
|
|
209
|
-
|
|
210
|
-
options=QFileDialog
|
|
212
|
+
str(Path.home()),
|
|
213
|
+
options=QFileDialog.ShowDirsOnly,
|
|
211
214
|
)
|
|
212
215
|
if not exportDir:
|
|
213
216
|
return
|
|
@@ -256,11 +259,11 @@ def transitions_matrix(self, mode):
|
|
|
256
259
|
QMessageBox.critical(self, cfg.programName, f"The file {nf} can not be saved")
|
|
257
260
|
else:
|
|
258
261
|
try:
|
|
259
|
-
with open(
|
|
262
|
+
with open(file_name, "w") as outfile:
|
|
260
263
|
outfile.write(observed_matrix)
|
|
261
264
|
|
|
262
265
|
except Exception:
|
|
263
|
-
QMessageBox.critical(self, cfg.programName, f"The file {
|
|
266
|
+
QMessageBox.critical(self, cfg.programName, f"The file {file_name} can not be saved")
|
|
264
267
|
|
|
265
268
|
|
|
266
269
|
def transitions_dot_script():
|
|
@@ -268,18 +271,17 @@ def transitions_dot_script():
|
|
|
268
271
|
create dot script (graphviz language) from transitions frequencies matrix
|
|
269
272
|
"""
|
|
270
273
|
|
|
271
|
-
|
|
274
|
+
file_names, _ = QFileDialog().getOpenFileNames(
|
|
272
275
|
None,
|
|
273
276
|
"Select one or more transitions matrix files",
|
|
274
277
|
"",
|
|
275
278
|
"Transitions matrix files (*.txt *.tsv);;All files (*)",
|
|
276
279
|
)
|
|
277
|
-
fileNames = fn[0] if type(fn) is tuple else fn
|
|
278
280
|
|
|
279
281
|
out = ""
|
|
280
282
|
|
|
281
|
-
for
|
|
282
|
-
with open(
|
|
283
|
+
for file_name in file_names:
|
|
284
|
+
with open(file_name, "r") as infile:
|
|
283
285
|
result, gv = create_transitions_gv_from_matrix(infile.read(), cutoff_all=0, cutoff_behavior=0, edge_label="percent_node")
|
|
284
286
|
if result:
|
|
285
287
|
QMessageBox.critical(
|
|
@@ -289,16 +291,24 @@ def transitions_dot_script():
|
|
|
289
291
|
)
|
|
290
292
|
return
|
|
291
293
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
+
try:
|
|
295
|
+
with open(file_name + ".gv", "w") as file_out:
|
|
296
|
+
file_out.write(gv)
|
|
297
|
+
except Exception:
|
|
298
|
+
QMessageBox.critical(
|
|
299
|
+
None,
|
|
300
|
+
cfg.programName,
|
|
301
|
+
("Error saving the file"),
|
|
302
|
+
)
|
|
303
|
+
return
|
|
294
304
|
|
|
295
|
-
out += f"<b>{
|
|
305
|
+
out += f"<b>{file_name}.gv</b> created<br>"
|
|
296
306
|
|
|
297
307
|
if out:
|
|
298
308
|
QMessageBox.information(
|
|
299
309
|
None,
|
|
300
310
|
cfg.programName,
|
|
301
|
-
(f"{out}<br><br>The DOT scripts can be used with Graphviz or WebGraphviz
|
|
311
|
+
(f"{out}<br><br>The DOT scripts can be used with the Graphviz package or WebGraphviz to generate diagram"),
|
|
302
312
|
)
|
|
303
313
|
|
|
304
314
|
|
|
@@ -322,17 +332,16 @@ def transitions_flow_diagram():
|
|
|
322
332
|
)
|
|
323
333
|
return
|
|
324
334
|
|
|
325
|
-
|
|
335
|
+
file_names, _ = QFileDialog().getOpenFileNames(
|
|
326
336
|
None,
|
|
327
337
|
"Select one or more transitions matrix files",
|
|
328
338
|
"",
|
|
329
339
|
"Transitions matrix files (*.txt *.tsv);;All files (*)",
|
|
330
340
|
)
|
|
331
|
-
fileNames = fn[0] if type(fn) is tuple else fn
|
|
332
341
|
|
|
333
342
|
out = ""
|
|
334
|
-
for
|
|
335
|
-
with open(
|
|
343
|
+
for file_name in file_names:
|
|
344
|
+
with open(file_name, "r") as infile:
|
|
336
345
|
result, gv = create_transitions_gv_from_matrix(infile.read(), cutoff_all=0, cutoff_behavior=0, edge_label="percent_node")
|
|
337
346
|
if result:
|
|
338
347
|
QMessageBox.critical(
|
|
@@ -342,15 +351,15 @@ def transitions_flow_diagram():
|
|
|
342
351
|
)
|
|
343
352
|
return
|
|
344
353
|
|
|
345
|
-
with open(tempfile.gettempdir() + os.sep + os.path.basename(
|
|
354
|
+
with open(tempfile.gettempdir() + os.sep + os.path.basename(file_name) + ".tmp.gv", "w") as f:
|
|
346
355
|
f.write(gv)
|
|
347
356
|
result = subprocess.getoutput(
|
|
348
|
-
(f'dot -Tpng -o "{
|
|
357
|
+
(f'dot -Tpng -o "{file_name}.png" ' f'"{tempfile.gettempdir() + os.sep + os.path.basename(file_name)}.tmp.gv"')
|
|
349
358
|
)
|
|
350
359
|
if not result:
|
|
351
|
-
out += f"<b>{
|
|
360
|
+
out += f"<b>{file_name}.png</b> created<br>"
|
|
352
361
|
else:
|
|
353
|
-
out += f"Problem with <b>{
|
|
362
|
+
out += f"Problem with <b>{file_name}</b><br>"
|
|
354
363
|
|
|
355
364
|
if out:
|
|
356
365
|
QMessageBox.information(None, cfg.programName, out)
|
boris/utilities.py
CHANGED
|
@@ -33,13 +33,19 @@ import subprocess
|
|
|
33
33
|
import sys
|
|
34
34
|
import urllib.parse
|
|
35
35
|
import wave
|
|
36
|
+
import exifread
|
|
37
|
+
import datetime
|
|
36
38
|
from decimal import Decimal as dec
|
|
37
39
|
from decimal import getcontext, ROUND_DOWN
|
|
38
40
|
from shutil import copyfile
|
|
39
41
|
from typing import Union, Tuple
|
|
40
42
|
|
|
43
|
+
from hachoir.parser import createParser
|
|
44
|
+
from hachoir.metadata import extractMetadata
|
|
45
|
+
|
|
46
|
+
|
|
41
47
|
import numpy as np
|
|
42
|
-
from
|
|
48
|
+
from PySide6.QtGui import QPixmap, QImage
|
|
43
49
|
|
|
44
50
|
from PIL.ImageQt import Image
|
|
45
51
|
|
|
@@ -65,6 +71,93 @@ except RuntimeError: # libmpv found but version too old
|
|
|
65
71
|
"""
|
|
66
72
|
|
|
67
73
|
|
|
74
|
+
def extract_exif_DateTimeOriginal(file_path: str) -> int:
|
|
75
|
+
"""
|
|
76
|
+
extract the EXIF DateTimeOriginal tag
|
|
77
|
+
return epoch time
|
|
78
|
+
if the tag is not available return -1
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
file_path (str): path of the media file
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
int: timestamp
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
with open(file_path, "rb") as f_in:
|
|
89
|
+
tags = exifread.process_file(f_in, details=False, stop_tag="EXIF DateTimeOriginal")
|
|
90
|
+
if "EXIF DateTimeOriginal" in tags:
|
|
91
|
+
date_time_original = (
|
|
92
|
+
f'{tags["EXIF DateTimeOriginal"].values[:4]}-'
|
|
93
|
+
f'{tags["EXIF DateTimeOriginal"].values[5:7]}-'
|
|
94
|
+
f'{tags["EXIF DateTimeOriginal"].values[8:10]} '
|
|
95
|
+
f'{tags["EXIF DateTimeOriginal"].values.split(" ")[-1]}'
|
|
96
|
+
)
|
|
97
|
+
return int(datetime.datetime.strptime(date_time_original, "%Y-%m-%d %H:%M:%S").timestamp())
|
|
98
|
+
else:
|
|
99
|
+
try:
|
|
100
|
+
# read from file name (YYYY-MM-DD_HHMMSS)
|
|
101
|
+
return int(datetime.datetime.strptime(pl.Path(file_path).stem, "%Y-%m-%d_%H%M%S").timestamp())
|
|
102
|
+
except Exception:
|
|
103
|
+
# read from file name (YYYY-MM-DD_HH:MM:SS)
|
|
104
|
+
return int(datetime.datetime.strptime(pl.Path(file_path).stem, "%Y-%m-%d_%H:%M:%S").timestamp())
|
|
105
|
+
|
|
106
|
+
except Exception:
|
|
107
|
+
return -1
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def extract_video_creation_date(file_path: str) -> int | None:
|
|
111
|
+
"""
|
|
112
|
+
returns the timestamp of the media creation date time with Hachoir
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
logging.debug(f"extract_video_creation_date for {file_path}")
|
|
116
|
+
|
|
117
|
+
if not pl.Path(file_path).is_file():
|
|
118
|
+
logging.debug(f"{file_path} not found")
|
|
119
|
+
return None
|
|
120
|
+
try:
|
|
121
|
+
parser = createParser(file_path)
|
|
122
|
+
metadata = extractMetadata(parser)
|
|
123
|
+
except Exception:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
if metadata.has("creation_date"):
|
|
127
|
+
if metadata.get("creation_date") == datetime.datetime(1904, 1, 1, 0, 0):
|
|
128
|
+
return None
|
|
129
|
+
return metadata.get("creation_date").timestamp()
|
|
130
|
+
else:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def extract_date_time_from_file_name(file_path: str) -> int:
|
|
135
|
+
"""
|
|
136
|
+
extract YYYY-MM-DD_HHMMSS or YYYY-MM-DD_HH:MM:SS from file name
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
patterns = (r"\d{4}-\d{2}-\d{2}_\d{6}", r"\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2}")
|
|
140
|
+
for pattern in patterns:
|
|
141
|
+
matches = re.findall(pattern, file_path)
|
|
142
|
+
|
|
143
|
+
if matches:
|
|
144
|
+
if pattern == r"\d{4}-\d{2}-\d{2}_\d{6}":
|
|
145
|
+
logging.debug(
|
|
146
|
+
f"extract_date_time_from_file_name timestamp from {file_path}: {int(datetime.datetime.strptime(matches[0], "%Y-%m-%d_%H%M%S").timestamp())}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return int(datetime.datetime.strptime(matches[0], "%Y-%m-%d_%H%M%S").timestamp())
|
|
150
|
+
|
|
151
|
+
if pattern == r"\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2}":
|
|
152
|
+
logging.debug(
|
|
153
|
+
f"extract_date_time_from_file_name timestamp from {file_path}: {int(datetime.datetime.strptime(matches[0], "%Y-%m-%d_%H:%M:%S").timestamp())}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return int(datetime.datetime.strptime(matches[0], "%Y-%m-%d_%H:%M:%S").timestamp())
|
|
157
|
+
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
68
161
|
def mpv_lib_version() -> Tuple[str, str, str]:
|
|
69
162
|
"""
|
|
70
163
|
Version of MPV library
|
|
@@ -438,7 +531,7 @@ def state_behavior_codes(ethogram: dict) -> list:
|
|
|
438
531
|
list: list of behavior codes defined as STATE event
|
|
439
532
|
|
|
440
533
|
"""
|
|
441
|
-
return [ethogram[x][cfg.BEHAVIOR_CODE] for x in ethogram if
|
|
534
|
+
return [ethogram[x][cfg.BEHAVIOR_CODE] for x in ethogram if ethogram[x][cfg.TYPE] == cfg.STATE_EVENT]
|
|
442
535
|
|
|
443
536
|
|
|
444
537
|
def point_behavior_codes(ethogram: dict) -> list:
|
boris/version.py
CHANGED
boris/video_equalizer.py
CHANGED
|
@@ -20,8 +20,8 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
from
|
|
24
|
-
from
|
|
23
|
+
from PySide6.QtWidgets import QDialog
|
|
24
|
+
from PySide6.QtCore import Signal, QEvent
|
|
25
25
|
from .video_equalizer_ui import Ui_Equalizer
|
|
26
26
|
|
|
27
27
|
|
|
@@ -30,8 +30,8 @@ class Video_equalizer(QDialog, Ui_Equalizer):
|
|
|
30
30
|
management of video equalizer: brightness, saturation, contrast, gamma and hue
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
sendEventSignal =
|
|
34
|
-
sendKeyPressSignal =
|
|
33
|
+
sendEventSignal = Signal(int, str, int)
|
|
34
|
+
sendKeyPressSignal = Signal(QEvent)
|
|
35
35
|
|
|
36
36
|
def __init__(self, equalizer, parent=None):
|
|
37
37
|
super().__init__(parent)
|