boris-behav-obs 9.3.4__py2.py3-none-any.whl → 9.3.5__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/config.py +2 -0
- boris/core.py +21 -103
- boris/dialog.py +6 -6
- boris/edit_event.py +47 -27
- boris/edit_event_ui.py +69 -36
- boris/event_operations.py +50 -57
- boris/observation.py +1 -2
- boris/observation_operations.py +16 -3
- boris/project_functions.py +1 -1
- boris/select_observations.py +7 -1
- boris/state_events.py +1 -1
- boris/version.py +2 -2
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.3.5.dist-info}/METADATA +1 -1
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.3.5.dist-info}/RECORD +18 -18
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.3.5.dist-info}/WHEEL +1 -1
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.3.5.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.3.5.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.3.5.dist-info}/top_level.txt +0 -0
boris/config.py
CHANGED
|
@@ -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
|
|
boris/core.py
CHANGED
|
@@ -308,9 +308,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
308
308
|
play_rate_step: float = 0.1
|
|
309
309
|
currentSubject: str = "" # contains the current subject of observation
|
|
310
310
|
|
|
311
|
-
#
|
|
312
|
-
memx = -1
|
|
313
|
-
memy = -1
|
|
311
|
+
# geometric measurements
|
|
314
312
|
mem_player = -1
|
|
315
313
|
|
|
316
314
|
# path for ffmpeg/ffmpeg.exe program
|
|
@@ -339,6 +337,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
339
337
|
|
|
340
338
|
mem_hash_obs: int = 0
|
|
341
339
|
|
|
340
|
+
# variables for list of observations
|
|
341
|
+
data: list = []
|
|
342
|
+
not_paired: list = []
|
|
343
|
+
|
|
342
344
|
'''
|
|
343
345
|
def add_button_menu(self, data, menu_obj):
|
|
344
346
|
"""
|
|
@@ -1342,21 +1344,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1342
1344
|
self.pj[cfg.BEHAVIORS_CODING_MAP][idx] = dict(behav_coding_map)
|
|
1343
1345
|
return
|
|
1344
1346
|
|
|
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
1347
|
self.pj[cfg.BEHAVIORS_CODING_MAP].append(behav_coding_map)
|
|
1361
1348
|
QMessageBox.information(
|
|
1362
1349
|
self,
|
|
@@ -1526,7 +1513,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1526
1513
|
jt.setWindowTitle("Jump to specific time")
|
|
1527
1514
|
jt.label.setText("Set the time")
|
|
1528
1515
|
|
|
1529
|
-
if jt.
|
|
1516
|
+
if jt.exec():
|
|
1530
1517
|
new_time = jt.time_widget.get_time()
|
|
1531
1518
|
if new_time < 0:
|
|
1532
1519
|
return
|
|
@@ -1704,7 +1691,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1704
1691
|
QMessageBox.critical(
|
|
1705
1692
|
None,
|
|
1706
1693
|
cfg.programName,
|
|
1707
|
-
("The picture directory
|
|
1694
|
+
("The picture directory has changed since the creation of observation."),
|
|
1708
1695
|
QMessageBox.Ok | QMessageBox.Default,
|
|
1709
1696
|
QMessageBox.NoButton,
|
|
1710
1697
|
)
|
|
@@ -1718,15 +1705,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1718
1705
|
# extract EXIF tag
|
|
1719
1706
|
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False):
|
|
1720
1707
|
date_time_original = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
1708
|
+
|
|
1721
1709
|
if date_time_original != -1:
|
|
1722
1710
|
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
1711
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1712
|
+
if self.image_idx == 0:
|
|
1713
|
+
self.image_time_ref = date_time_original
|
|
1728
1714
|
|
|
1729
|
-
if date_time_original != -1:
|
|
1730
1715
|
if self.image_time_ref is not None:
|
|
1731
1716
|
seconds_from_1st = date_time_original - self.image_time_ref
|
|
1732
1717
|
|
|
@@ -1736,6 +1721,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
1736
1721
|
seconds_from_1st_formated = seconds_from_1st
|
|
1737
1722
|
|
|
1738
1723
|
else:
|
|
1724
|
+
msg += "<br>EXIF Date/Time Original: <b>NA</b>"
|
|
1739
1725
|
seconds_from_1st_formated = cfg.NA
|
|
1740
1726
|
|
|
1741
1727
|
msg += f"<br>Time from 1st image: <b>{seconds_from_1st_formated}</b>"
|
|
@@ -2311,11 +2297,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
2311
2297
|
self.tv_events.setModel(model)
|
|
2312
2298
|
|
|
2313
2299
|
# column width
|
|
2314
|
-
|
|
2315
|
-
self.tv_events.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
|
2316
|
-
|
|
2317
|
-
# self.table.setSortingEnabled(True)
|
|
2318
|
-
# self.table.sortByColumn(0, Qt.AscendingOrder)
|
|
2300
|
+
self.tv_events.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
|
|
2319
2301
|
|
|
2320
2302
|
def load_tw_events(self, obs_id) -> None:
|
|
2321
2303
|
"""
|
|
@@ -2343,76 +2325,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
2343
2325
|
|
|
2344
2326
|
return
|
|
2345
2327
|
|
|
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
2328
|
def close_tool_windows(self):
|
|
2417
2329
|
"""
|
|
2418
2330
|
close tool windows:
|
|
@@ -3493,7 +3405,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
3493
3405
|
return
|
|
3494
3406
|
|
|
3495
3407
|
logging.debug(
|
|
3496
|
-
f"{self.config_param[cfg.CHECK_PROJECT_INTEGRITY] if cfg.CHECK_PROJECT_INTEGRITY in self.config_param else
|
|
3408
|
+
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
3409
|
)
|
|
3498
3410
|
|
|
3499
3411
|
if self.config_param.get(cfg.CHECK_PROJECT_INTEGRITY, True):
|
|
@@ -4707,7 +4619,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4707
4619
|
self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False)
|
|
4708
4620
|
and util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx]) != -1
|
|
4709
4621
|
):
|
|
4710
|
-
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4622
|
+
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4623
|
+
# check if first value must be substracted
|
|
4624
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.SUBSTRACT_FIRST_EXIF_DATE, True):
|
|
4625
|
+
time_ -= self.image_time_ref
|
|
4711
4626
|
|
|
4712
4627
|
elif self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0):
|
|
4713
4628
|
time_ = (self.image_idx + 1) * self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0)
|
|
@@ -4765,7 +4680,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
4765
4680
|
self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False)
|
|
4766
4681
|
and util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx]) != -1
|
|
4767
4682
|
):
|
|
4768
|
-
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4683
|
+
time_ = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
4684
|
+
# check if first value must be substracte
|
|
4685
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.SUBSTRACT_FIRST_EXIF_DATE, True):
|
|
4686
|
+
time_ -= self.image_time_ref
|
|
4769
4687
|
|
|
4770
4688
|
elif self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0):
|
|
4771
4689
|
time_ = (self.image_idx + 1) * self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0)
|
boris/dialog.py
CHANGED
|
@@ -20,9 +20,8 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
from decimal import Decimal as dec
|
|
24
|
-
from typing import Union
|
|
25
23
|
import datetime as dt
|
|
24
|
+
from decimal import Decimal as dec
|
|
26
25
|
import logging
|
|
27
26
|
import math
|
|
28
27
|
import pathlib as pl
|
|
@@ -30,6 +29,7 @@ import platform
|
|
|
30
29
|
import subprocess
|
|
31
30
|
import sys
|
|
32
31
|
import traceback
|
|
32
|
+
from typing import Union
|
|
33
33
|
|
|
34
34
|
from PySide6.QtCore import Qt, Signal, qVersion, QRect, QTime, QDateTime, QSize
|
|
35
35
|
from PySide6.QtWidgets import (
|
|
@@ -383,7 +383,7 @@ class get_time_widget(QWidget):
|
|
|
383
383
|
dec: time in seconds (None if no format selected)
|
|
384
384
|
"""
|
|
385
385
|
|
|
386
|
-
time_sec =
|
|
386
|
+
time_sec = dec("NaN")
|
|
387
387
|
|
|
388
388
|
if self.rb_seconds.isChecked():
|
|
389
389
|
try:
|
|
@@ -396,7 +396,7 @@ class get_time_widget(QWidget):
|
|
|
396
396
|
QMessageBox.Ok | QMessageBox.Default,
|
|
397
397
|
QMessageBox.NoButton,
|
|
398
398
|
)
|
|
399
|
-
return
|
|
399
|
+
return dec("NaN")
|
|
400
400
|
|
|
401
401
|
if self.rb_time.isChecked():
|
|
402
402
|
time_sec = self.sb_hour.value() * 3600
|
|
@@ -408,7 +408,7 @@ class get_time_widget(QWidget):
|
|
|
408
408
|
if self.pb_sign.text() == "-":
|
|
409
409
|
time_sec = -time_sec
|
|
410
410
|
|
|
411
|
-
return dec(time_sec).quantize(dec("0.001")) if time_sec is not None else None
|
|
411
|
+
return dec(time_sec).quantize(dec("0.001")) # if time_sec is not None else None
|
|
412
412
|
|
|
413
413
|
def set_time(self, new_time: dec) -> None:
|
|
414
414
|
"""
|
|
@@ -489,7 +489,7 @@ class Ask_time(QDialog):
|
|
|
489
489
|
)
|
|
490
490
|
return
|
|
491
491
|
# test time value
|
|
492
|
-
if self.time_widget.get_time()
|
|
492
|
+
if self.time_widget.get_time().is_nan():
|
|
493
493
|
return
|
|
494
494
|
|
|
495
495
|
self.accept()
|
boris/edit_event.py
CHANGED
|
@@ -21,7 +21,7 @@ This file is part of BORIS.
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
from decimal import Decimal as dec
|
|
24
|
-
import
|
|
24
|
+
import logging
|
|
25
25
|
|
|
26
26
|
from PySide6.QtWidgets import (
|
|
27
27
|
QDialog,
|
|
@@ -44,11 +44,12 @@ class DlgEditEvent(QDialog, Ui_Form):
|
|
|
44
44
|
def __init__(
|
|
45
45
|
self,
|
|
46
46
|
observation_type: str,
|
|
47
|
-
time_value: dec = dec(
|
|
47
|
+
time_value: dec = dec("NaN"),
|
|
48
48
|
image_idx=None,
|
|
49
49
|
current_time=0,
|
|
50
50
|
time_format: str = cfg.S,
|
|
51
51
|
show_set_current_time: bool = False,
|
|
52
|
+
exif_date_time: dec | None = None,
|
|
52
53
|
parent=None,
|
|
53
54
|
):
|
|
54
55
|
super().__init__(parent)
|
|
@@ -59,42 +60,49 @@ class DlgEditEvent(QDialog, Ui_Form):
|
|
|
59
60
|
|
|
60
61
|
self.pb_set_to_current_time.setVisible(show_set_current_time)
|
|
61
62
|
self.current_time = current_time
|
|
63
|
+
self.exif_date_time = exif_date_time
|
|
64
|
+
|
|
65
|
+
# hide frame index for all observations
|
|
66
|
+
# frame index is determined in base of time
|
|
67
|
+
for w in (
|
|
68
|
+
self.lb_frame_idx,
|
|
69
|
+
self.sb_frame_idx,
|
|
70
|
+
self.cb_set_frame_idx_na,
|
|
71
|
+
):
|
|
72
|
+
w.setVisible(False)
|
|
62
73
|
|
|
63
74
|
# hide image index
|
|
64
75
|
if observation_type in (cfg.LIVE, cfg.MEDIA):
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
# hide image index
|
|
77
|
+
for w in (
|
|
78
|
+
self.cb_set_time_na,
|
|
79
|
+
self.gb_image_index,
|
|
80
|
+
):
|
|
69
81
|
w.setVisible(False)
|
|
70
82
|
|
|
71
|
-
|
|
72
|
-
|
|
83
|
+
# widget for time
|
|
84
|
+
self.time_widget = dialog.get_time_widget(self.time_value)
|
|
73
85
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
if time_format == cfg.S:
|
|
87
|
+
self.time_widget.rb_seconds.setChecked(True)
|
|
88
|
+
if time_format == cfg.HHMMSS:
|
|
89
|
+
self.time_widget.rb_time.setChecked(True)
|
|
90
|
+
if not self.time_value.is_nan() and int(self.time_value) > cfg.DATE_CUTOFF:
|
|
91
|
+
self.time_widget.rb_datetime.setChecked(True)
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
self.horizontalLayout_3.insertWidget(0, self.time_widget)
|
|
82
94
|
|
|
83
95
|
if observation_type == cfg.IMAGES:
|
|
84
|
-
self.time_widget = dialog.get_time_widget(self.time_value)
|
|
85
96
|
# hide frame index widgets
|
|
86
|
-
|
|
87
|
-
w.setVisible(False)
|
|
97
|
+
self.pb_set_to_current_time.setVisible(self.exif_date_time is not None)
|
|
88
98
|
self.sb_image_idx.setValue(self.image_idx)
|
|
89
99
|
|
|
90
|
-
# self.pb_set_to_current_time.setText("Set to current image index")
|
|
91
|
-
|
|
92
100
|
self.pb_set_to_current_time.clicked.connect(self.set_to_current_time)
|
|
93
101
|
self.pb_set_to_current_image_index.clicked.connect(self.set_to_current_image_index)
|
|
94
102
|
|
|
95
103
|
self.cb_set_time_na.stateChanged.connect(self.time_na)
|
|
96
104
|
|
|
97
|
-
self.cb_set_frame_idx_na.stateChanged.connect(self.frame_idx_na)
|
|
105
|
+
# self.cb_set_frame_idx_na.stateChanged.connect(self.frame_idx_na)
|
|
98
106
|
self.pbOK.clicked.connect(self.close_widget)
|
|
99
107
|
self.pbCancel.clicked.connect(self.reject)
|
|
100
108
|
|
|
@@ -119,22 +127,34 @@ class DlgEditEvent(QDialog, Ui_Form):
|
|
|
119
127
|
"""
|
|
120
128
|
set time to current media time
|
|
121
129
|
"""
|
|
130
|
+
|
|
131
|
+
print(f"{self.current_time=}")
|
|
132
|
+
|
|
122
133
|
if self.observation_type in (cfg.LIVE, cfg.MEDIA):
|
|
123
134
|
self.time_widget.set_time(dec(float(self.current_time)))
|
|
124
135
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
if self.observation_type == cfg.IMAGES:
|
|
137
|
+
if self.exif_date_time is not None:
|
|
138
|
+
self.time_widget.set_time(dec(self.exif_date_time))
|
|
139
|
+
|
|
140
|
+
# def frame_idx_na(self):
|
|
141
|
+
# """
|
|
142
|
+
# set/unset frame index NA
|
|
143
|
+
# """
|
|
144
|
+
# self.lb_frame_idx.setEnabled(not self.cb_set_frame_idx_na.isChecked())
|
|
145
|
+
# self.sb_frame_idx.setEnabled(not self.cb_set_frame_idx_na.isChecked())
|
|
131
146
|
|
|
132
147
|
def time_na(self):
|
|
133
148
|
"""
|
|
134
149
|
set/unset time to NA
|
|
135
150
|
"""
|
|
136
151
|
|
|
152
|
+
logging.debug("time_na function")
|
|
153
|
+
|
|
154
|
+
self.time_widget.setVisible(not self.cb_set_time_na.isChecked())
|
|
137
155
|
self.time_widget.setEnabled(not self.cb_set_time_na.isChecked())
|
|
156
|
+
|
|
157
|
+
self.pb_set_to_current_time.setVisible(not self.cb_set_time_na.isChecked() and self.exif_date_time is not None)
|
|
138
158
|
self.pb_set_to_current_time.setEnabled(not self.cb_set_time_na.isChecked())
|
|
139
159
|
|
|
140
160
|
|
boris/edit_event_ui.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
################################################################################
|
|
4
4
|
## Form generated from reading UI file 'edit_event.ui'
|
|
5
5
|
##
|
|
6
|
-
## Created by: Qt User Interface Compiler version 6.
|
|
6
|
+
## Created by: Qt User Interface Compiler version 6.9.0
|
|
7
7
|
##
|
|
8
8
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
|
9
9
|
################################################################################
|
|
@@ -15,60 +15,89 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|
|
15
15
|
QFont, QFontDatabase, QGradient, QIcon,
|
|
16
16
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
|
17
17
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
|
18
|
-
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox,
|
|
19
|
-
QLabel, QPlainTextEdit, QPushButton,
|
|
20
|
-
QSpacerItem, QSpinBox, QVBoxLayout,
|
|
18
|
+
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QGroupBox,
|
|
19
|
+
QHBoxLayout, QLabel, QPlainTextEdit, QPushButton,
|
|
20
|
+
QSizePolicy, QSpacerItem, QSpinBox, QVBoxLayout,
|
|
21
|
+
QWidget)
|
|
21
22
|
|
|
22
23
|
class Ui_Form(object):
|
|
23
24
|
def setupUi(self, Form):
|
|
24
25
|
if not Form.objectName():
|
|
25
26
|
Form.setObjectName(u"Form")
|
|
26
|
-
Form.resize(
|
|
27
|
-
self.
|
|
27
|
+
Form.resize(600, 638)
|
|
28
|
+
self.verticalLayout_3 = QVBoxLayout(Form)
|
|
29
|
+
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
|
30
|
+
self.gb_time = QGroupBox(Form)
|
|
31
|
+
self.gb_time.setObjectName(u"gb_time")
|
|
32
|
+
self.verticalLayout = QVBoxLayout(self.gb_time)
|
|
28
33
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
|
34
|
+
self.horizontalLayout_8 = QHBoxLayout()
|
|
35
|
+
self.horizontalLayout_8.setObjectName(u"horizontalLayout_8")
|
|
36
|
+
self.cb_set_time_na = QCheckBox(self.gb_time)
|
|
37
|
+
self.cb_set_time_na.setObjectName(u"cb_set_time_na")
|
|
38
|
+
|
|
39
|
+
self.horizontalLayout_8.addWidget(self.cb_set_time_na)
|
|
40
|
+
|
|
41
|
+
self.horizontalSpacer_7 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
42
|
+
|
|
43
|
+
self.horizontalLayout_8.addItem(self.horizontalSpacer_7)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
self.verticalLayout.addLayout(self.horizontalLayout_8)
|
|
47
|
+
|
|
29
48
|
self.horizontalLayout_3 = QHBoxLayout()
|
|
30
49
|
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
50
|
+
self.lb_time = QLabel(self.gb_time)
|
|
51
|
+
self.lb_time.setObjectName(u"lb_time")
|
|
33
52
|
|
|
34
|
-
self.horizontalLayout_3.addWidget(self.
|
|
53
|
+
self.horizontalLayout_3.addWidget(self.lb_time)
|
|
35
54
|
|
|
36
55
|
self.horizontalLayout_2 = QHBoxLayout()
|
|
37
56
|
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
|
38
|
-
self.
|
|
57
|
+
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
58
|
+
|
|
59
|
+
self.horizontalLayout_2.addItem(self.horizontalSpacer)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
self.horizontalLayout_3.addLayout(self.horizontalLayout_2)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
self.verticalLayout.addLayout(self.horizontalLayout_3)
|
|
66
|
+
|
|
67
|
+
self.horizontalLayout_9 = QHBoxLayout()
|
|
68
|
+
self.horizontalLayout_9.setObjectName(u"horizontalLayout_9")
|
|
69
|
+
self.pb_set_to_current_time = QPushButton(self.gb_time)
|
|
39
70
|
self.pb_set_to_current_time.setObjectName(u"pb_set_to_current_time")
|
|
40
71
|
|
|
41
|
-
self.
|
|
72
|
+
self.horizontalLayout_9.addWidget(self.pb_set_to_current_time)
|
|
42
73
|
|
|
43
|
-
self.
|
|
44
|
-
self.cb_set_time_na.setObjectName(u"cb_set_time_na")
|
|
74
|
+
self.horizontalSpacer_8 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
45
75
|
|
|
46
|
-
self.
|
|
76
|
+
self.horizontalLayout_9.addItem(self.horizontalSpacer_8)
|
|
47
77
|
|
|
48
|
-
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
49
78
|
|
|
50
|
-
self.
|
|
79
|
+
self.verticalLayout.addLayout(self.horizontalLayout_9)
|
|
51
80
|
|
|
81
|
+
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
|
52
82
|
|
|
53
|
-
self.
|
|
83
|
+
self.verticalLayout.addItem(self.verticalSpacer)
|
|
54
84
|
|
|
55
85
|
|
|
56
|
-
self.
|
|
86
|
+
self.verticalLayout_3.addWidget(self.gb_time)
|
|
57
87
|
|
|
88
|
+
self.gb_image_index = QGroupBox(Form)
|
|
89
|
+
self.gb_image_index.setObjectName(u"gb_image_index")
|
|
90
|
+
self.verticalLayout_2 = QVBoxLayout(self.gb_image_index)
|
|
91
|
+
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
|
58
92
|
self.horizontalLayout_7 = QHBoxLayout()
|
|
59
93
|
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
|
|
60
|
-
self.
|
|
61
|
-
self.lb_image_idx.setObjectName(u"lb_image_idx")
|
|
62
|
-
|
|
63
|
-
self.horizontalLayout_7.addWidget(self.lb_image_idx)
|
|
64
|
-
|
|
65
|
-
self.sb_image_idx = QSpinBox(Form)
|
|
94
|
+
self.sb_image_idx = QSpinBox(self.gb_image_index)
|
|
66
95
|
self.sb_image_idx.setObjectName(u"sb_image_idx")
|
|
67
96
|
self.sb_image_idx.setMaximum(10000000)
|
|
68
97
|
|
|
69
98
|
self.horizontalLayout_7.addWidget(self.sb_image_idx)
|
|
70
99
|
|
|
71
|
-
self.pb_set_to_current_image_index = QPushButton(
|
|
100
|
+
self.pb_set_to_current_image_index = QPushButton(self.gb_image_index)
|
|
72
101
|
self.pb_set_to_current_image_index.setObjectName(u"pb_set_to_current_image_index")
|
|
73
102
|
|
|
74
103
|
self.horizontalLayout_7.addWidget(self.pb_set_to_current_image_index)
|
|
@@ -78,7 +107,10 @@ class Ui_Form(object):
|
|
|
78
107
|
self.horizontalLayout_7.addItem(self.horizontalSpacer_6)
|
|
79
108
|
|
|
80
109
|
|
|
81
|
-
self.
|
|
110
|
+
self.verticalLayout_2.addLayout(self.horizontalLayout_7)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
self.verticalLayout_3.addWidget(self.gb_image_index)
|
|
82
114
|
|
|
83
115
|
self.horizontalLayout_4 = QHBoxLayout()
|
|
84
116
|
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
|
|
@@ -97,7 +129,7 @@ class Ui_Form(object):
|
|
|
97
129
|
self.horizontalLayout_4.addItem(self.horizontalSpacer_2)
|
|
98
130
|
|
|
99
131
|
|
|
100
|
-
self.
|
|
132
|
+
self.verticalLayout_3.addLayout(self.horizontalLayout_4)
|
|
101
133
|
|
|
102
134
|
self.horizontalLayout_5 = QHBoxLayout()
|
|
103
135
|
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
|
|
@@ -116,7 +148,7 @@ class Ui_Form(object):
|
|
|
116
148
|
self.horizontalLayout_5.addItem(self.horizontalSpacer_3)
|
|
117
149
|
|
|
118
150
|
|
|
119
|
-
self.
|
|
151
|
+
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
|
|
120
152
|
|
|
121
153
|
self.horizontalLayout_6 = QHBoxLayout()
|
|
122
154
|
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
|
|
@@ -142,17 +174,17 @@ class Ui_Form(object):
|
|
|
142
174
|
self.horizontalLayout_6.addItem(self.horizontalSpacer_5)
|
|
143
175
|
|
|
144
176
|
|
|
145
|
-
self.
|
|
177
|
+
self.verticalLayout_3.addLayout(self.horizontalLayout_6)
|
|
146
178
|
|
|
147
179
|
self.label_4 = QLabel(Form)
|
|
148
180
|
self.label_4.setObjectName(u"label_4")
|
|
149
181
|
|
|
150
|
-
self.
|
|
182
|
+
self.verticalLayout_3.addWidget(self.label_4)
|
|
151
183
|
|
|
152
184
|
self.leComment = QPlainTextEdit(Form)
|
|
153
185
|
self.leComment.setObjectName(u"leComment")
|
|
154
186
|
|
|
155
|
-
self.
|
|
187
|
+
self.verticalLayout_3.addWidget(self.leComment)
|
|
156
188
|
|
|
157
189
|
self.horizontalLayout = QHBoxLayout()
|
|
158
190
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
|
@@ -171,7 +203,7 @@ class Ui_Form(object):
|
|
|
171
203
|
self.horizontalLayout.addWidget(self.pbOK)
|
|
172
204
|
|
|
173
205
|
|
|
174
|
-
self.
|
|
206
|
+
self.verticalLayout_3.addLayout(self.horizontalLayout)
|
|
175
207
|
|
|
176
208
|
|
|
177
209
|
self.retranslateUi(Form)
|
|
@@ -184,13 +216,14 @@ class Ui_Form(object):
|
|
|
184
216
|
|
|
185
217
|
def retranslateUi(self, Form):
|
|
186
218
|
Form.setWindowTitle(QCoreApplication.translate("Form", u"Edit event", None))
|
|
187
|
-
self.
|
|
219
|
+
self.gb_time.setTitle(QCoreApplication.translate("Form", u"Time", None))
|
|
220
|
+
self.cb_set_time_na.setText(QCoreApplication.translate("Form", u"Set time to NA", None))
|
|
221
|
+
self.lb_time.setText("")
|
|
188
222
|
self.pb_set_to_current_time.setText(QCoreApplication.translate("Form", u"Set to current time", None))
|
|
189
|
-
self.
|
|
190
|
-
self.lb_image_idx.setText(QCoreApplication.translate("Form", u"Image index", None))
|
|
223
|
+
self.gb_image_index.setTitle(QCoreApplication.translate("Form", u"Image index", None))
|
|
191
224
|
self.pb_set_to_current_image_index.setText(QCoreApplication.translate("Form", u"Set to current image index", None))
|
|
192
225
|
self.lbSubject.setText(QCoreApplication.translate("Form", u"Subject", None))
|
|
193
|
-
self.label_2.setText(QCoreApplication.translate("Form", u"
|
|
226
|
+
self.label_2.setText(QCoreApplication.translate("Form", u"Behavior", None))
|
|
194
227
|
self.lb_frame_idx.setText(QCoreApplication.translate("Form", u"Frame index", None))
|
|
195
228
|
self.cb_set_frame_idx_na.setText(QCoreApplication.translate("Form", u"Set NA", None))
|
|
196
229
|
self.label_4.setText(QCoreApplication.translate("Form", u"Comment", None))
|
boris/event_operations.py
CHANGED
|
@@ -70,7 +70,7 @@ def add_event(self):
|
|
|
70
70
|
|
|
71
71
|
editWindow = DlgEditEvent(
|
|
72
72
|
observation_type=self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE],
|
|
73
|
-
time_value=
|
|
73
|
+
time_value=dec("NaN"),
|
|
74
74
|
image_idx=0,
|
|
75
75
|
current_time=current_time,
|
|
76
76
|
time_format=self.timeFormat,
|
|
@@ -80,8 +80,6 @@ def add_event(self):
|
|
|
80
80
|
|
|
81
81
|
sortedSubjects = [cfg.NO_FOCAL_SUBJECT] + sorted([self.pj[cfg.SUBJECTS][x][cfg.SUBJECT_NAME] for x in self.pj[cfg.SUBJECTS]])
|
|
82
82
|
|
|
83
|
-
print(f"{sortedSubjects=}")
|
|
84
|
-
|
|
85
83
|
editWindow.cobSubject.addItems(sortedSubjects)
|
|
86
84
|
if self.currentSubject:
|
|
87
85
|
editWindow.cobSubject.setCurrentIndex(editWindow.cobSubject.findText(self.currentSubject, Qt.MatchFixedString))
|
|
@@ -173,8 +171,13 @@ def add_event(self):
|
|
|
173
171
|
time_ = dec("NaN")
|
|
174
172
|
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False):
|
|
175
173
|
if self.playerType != cfg.VIEWER_IMAGES:
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
exif_date_time = util.extract_exif_DateTimeOriginal(self.images_list[new_index])
|
|
175
|
+
if exif_date_time != -1:
|
|
176
|
+
time_ = exif_date_time
|
|
177
|
+
|
|
178
|
+
# check if first value must be substracted
|
|
179
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.SUBSTRACT_FIRST_EXIF_DATE, True):
|
|
180
|
+
time_ -= self.image_time_ref
|
|
178
181
|
|
|
179
182
|
elif self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0):
|
|
180
183
|
time_ = new_index * self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.TIME_LAPSE, 0)
|
|
@@ -576,8 +579,6 @@ def edit_event(self):
|
|
|
576
579
|
|
|
577
580
|
pj_event_idx = self.tv_idx2events_idx[tvevents_row]
|
|
578
581
|
|
|
579
|
-
print(f"{self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS]=}")
|
|
580
|
-
|
|
581
582
|
time_value = self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][
|
|
582
583
|
cfg.PJ_OBS_FIELDS[self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE]][cfg.TIME]
|
|
583
584
|
]
|
|
@@ -593,6 +594,12 @@ def edit_event(self):
|
|
|
593
594
|
else:
|
|
594
595
|
current_value = self.getLaps()
|
|
595
596
|
|
|
597
|
+
# get exif date time
|
|
598
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId].get(cfg.USE_EXIF_DATE, False):
|
|
599
|
+
exif_date_time = util.extract_exif_DateTimeOriginal(self.images_list[self.image_idx])
|
|
600
|
+
else:
|
|
601
|
+
exif_date_time = None
|
|
602
|
+
|
|
596
603
|
edit_window = DlgEditEvent(
|
|
597
604
|
observation_type=self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE],
|
|
598
605
|
time_value=time_value,
|
|
@@ -600,15 +607,15 @@ def edit_event(self):
|
|
|
600
607
|
current_time=current_value,
|
|
601
608
|
time_format=self.timeFormat,
|
|
602
609
|
show_set_current_time=True,
|
|
610
|
+
exif_date_time=exif_date_time,
|
|
603
611
|
)
|
|
604
612
|
edit_window.setWindowTitle("Edit event")
|
|
605
613
|
|
|
606
|
-
print(f"{time_value=}")
|
|
607
|
-
|
|
608
614
|
# time
|
|
609
615
|
if time_value.is_nan():
|
|
610
616
|
edit_window.cb_set_time_na.setChecked(True)
|
|
611
617
|
|
|
618
|
+
# remove visibility of 'set current time' widget if VIEWER mode
|
|
612
619
|
if self.playerType in (cfg.VIEWER_MEDIA, cfg.VIEWER_LIVE, cfg.VIEWER_IMAGES):
|
|
613
620
|
edit_window.pb_set_to_current_time.setVisible(False)
|
|
614
621
|
|
|
@@ -642,20 +649,16 @@ def edit_event(self):
|
|
|
642
649
|
sortedCodes.index(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][cfg.EVENT_BEHAVIOR_FIELD_IDX])
|
|
643
650
|
)
|
|
644
651
|
else:
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
"does not exist longer in the ethogram"
|
|
649
|
-
)
|
|
652
|
+
msg: str = (
|
|
653
|
+
f"The behaviour {self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][cfg.EVENT_BEHAVIOR_FIELD_IDX]} "
|
|
654
|
+
"does not exist longer in the ethogram"
|
|
650
655
|
)
|
|
656
|
+
logging.warning(msg)
|
|
657
|
+
|
|
651
658
|
QMessageBox.warning(
|
|
652
659
|
self,
|
|
653
660
|
cfg.programName,
|
|
654
|
-
|
|
655
|
-
"The behaviour "
|
|
656
|
-
f"<b>{self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][cfg.EVENT_BEHAVIOR_FIELD_IDX]}</b> "
|
|
657
|
-
"does not exist more in the ethogram"
|
|
658
|
-
),
|
|
661
|
+
msg,
|
|
659
662
|
)
|
|
660
663
|
edit_window.cobCode.setCurrentIndex(0)
|
|
661
664
|
|
|
@@ -663,15 +666,15 @@ def edit_event(self):
|
|
|
663
666
|
f"original modifiers: {self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][cfg.EVENT_MODIFIER_FIELD_IDX]}"
|
|
664
667
|
)
|
|
665
668
|
|
|
666
|
-
# frame index
|
|
667
|
-
frame_idx = read_event_field(
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
)
|
|
672
|
-
edit_window.sb_frame_idx.setValue(0 if frame_idx in (cfg.NA, None) else frame_idx)
|
|
673
|
-
if frame_idx in (cfg.NA, None):
|
|
674
|
-
|
|
669
|
+
# # frame index
|
|
670
|
+
# frame_idx = read_event_field(
|
|
671
|
+
# self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx],
|
|
672
|
+
# self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE],
|
|
673
|
+
# cfg.FRAME_INDEX,
|
|
674
|
+
# )
|
|
675
|
+
# edit_window.sb_frame_idx.setValue(0 if frame_idx in (cfg.NA, None) else frame_idx)
|
|
676
|
+
# if frame_idx in (cfg.NA, None):
|
|
677
|
+
# edit_window.cb_set_frame_idx_na.setChecked(True)
|
|
675
678
|
|
|
676
679
|
# comment
|
|
677
680
|
edit_window.leComment.setPlainText(self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][cfg.EVENT_COMMENT_FIELD_IDX])
|
|
@@ -681,13 +684,14 @@ def edit_event(self):
|
|
|
681
684
|
if edit_window.exec(): # button OK
|
|
682
685
|
self.project_changed()
|
|
683
686
|
|
|
684
|
-
|
|
685
|
-
if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
|
|
686
|
-
new_time = edit_window.time_widget.get_time()
|
|
687
|
+
new_time = edit_window.time_widget.get_time()
|
|
687
688
|
|
|
688
|
-
|
|
689
|
+
if edit_window.cb_set_time_na.isChecked():
|
|
690
|
+
new_time = dec("NaN")
|
|
689
691
|
|
|
690
|
-
|
|
692
|
+
# MEDIA / LIVE
|
|
693
|
+
if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] in (cfg.MEDIA, cfg.LIVE):
|
|
694
|
+
if new_time.is_nan():
|
|
691
695
|
QMessageBox.warning(
|
|
692
696
|
self,
|
|
693
697
|
cfg.programName,
|
|
@@ -715,19 +719,17 @@ def edit_event(self):
|
|
|
715
719
|
event[cfg.FRAME_INDEX] = frame_idx
|
|
716
720
|
self.seek_mediaplayer(mem_time)
|
|
717
721
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
# event[cfg.FRAME_INDEX] = edit_window.sb_frame_idx.value()
|
|
730
|
-
"""
|
|
722
|
+
# if not edit_window.sb_frame_idx.value() or edit_window.cb_set_frame_idx_na.isChecked():
|
|
723
|
+
# event[cfg.FRAME_INDEX] = cfg.NA
|
|
724
|
+
# else:
|
|
725
|
+
# if self.playerType == cfg.MEDIA:
|
|
726
|
+
# mem_time = self.getLaps()
|
|
727
|
+
# if not self.seek_mediaplayer(new_time):
|
|
728
|
+
# frame_idx = self.get_frame_index()
|
|
729
|
+
# event[cfg.FRAME_INDEX] = frame_idx
|
|
730
|
+
# self.seek_mediaplayer(mem_time)
|
|
731
|
+
#
|
|
732
|
+
# # event[cfg.FRAME_INDEX] = edit_window.sb_frame_idx.value()
|
|
731
733
|
|
|
732
734
|
event["row"] = pj_event_idx
|
|
733
735
|
event["original_modifiers"] = self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][
|
|
@@ -755,14 +757,7 @@ def edit_event(self):
|
|
|
755
757
|
for key in self.pj[cfg.ETHOGRAM]:
|
|
756
758
|
if self.pj[cfg.ETHOGRAM][key][cfg.BEHAVIOR_CODE] == edit_window.cobCode.currentText():
|
|
757
759
|
event = self.full_event(key)
|
|
758
|
-
|
|
759
|
-
edit_window.time_value == cfg.NA
|
|
760
|
-
or (isinstance(edit_window.time_value, dec) and edit_window.time_value.is_nan())
|
|
761
|
-
or (edit_window.cb_set_time_na.isChecked())
|
|
762
|
-
):
|
|
763
|
-
event[cfg.TIME] = dec("NaN")
|
|
764
|
-
else:
|
|
765
|
-
event[cfg.TIME] = edit_window.time_widget.get_time()
|
|
760
|
+
event[cfg.TIME] = new_time
|
|
766
761
|
|
|
767
762
|
event[cfg.SUBJECT] = (
|
|
768
763
|
"" if edit_window.cobSubject.currentText() == cfg.NO_FOCAL_SUBJECT else edit_window.cobSubject.currentText()
|
|
@@ -820,7 +815,7 @@ def edit_time_selected_events(self):
|
|
|
820
815
|
return
|
|
821
816
|
|
|
822
817
|
d = w.time_widget.get_time()
|
|
823
|
-
if d
|
|
818
|
+
if d.is_nan() or not d:
|
|
824
819
|
return
|
|
825
820
|
|
|
826
821
|
if ":" in util.smart_time_format(abs(d)):
|
|
@@ -894,8 +889,6 @@ def copy_selected_events(self):
|
|
|
894
889
|
else:
|
|
895
890
|
copied_events.append("\t".join([str(x) for x in event]))
|
|
896
891
|
|
|
897
|
-
print(f"{copied_events=}")
|
|
898
|
-
|
|
899
892
|
cb = QApplication.clipboard()
|
|
900
893
|
cb.clear(mode=QClipboard.Mode.Clipboard)
|
|
901
894
|
cb.setText("\n".join(copied_events), mode=QClipboard.Mode.Clipboard)
|
boris/observation.py
CHANGED
|
@@ -24,10 +24,9 @@ import logging
|
|
|
24
24
|
import os
|
|
25
25
|
import pandas as pd
|
|
26
26
|
import pathlib as pl
|
|
27
|
-
import datetime as dt
|
|
28
27
|
|
|
29
28
|
from PySide6.QtCore import Qt
|
|
30
|
-
from PySide6.QtGui import QColor
|
|
29
|
+
from PySide6.QtGui import QColor
|
|
31
30
|
from PySide6.QtWidgets import (
|
|
32
31
|
QDialog,
|
|
33
32
|
QVBoxLayout,
|
boris/observation_operations.py
CHANGED
|
@@ -1024,7 +1024,21 @@ def new_observation(self, mode: str = cfg.NEW, obsId: str = "") -> None:
|
|
|
1024
1024
|
self.pj[cfg.OBSERVATIONS][new_obs_id][cfg.DIRECTORIES_LIST] = [
|
|
1025
1025
|
observationWindow.lw_images_directory.item(i).text() for i in range(observationWindow.lw_images_directory.count())
|
|
1026
1026
|
]
|
|
1027
|
+
|
|
1028
|
+
# check if exif data must be used
|
|
1027
1029
|
self.pj[cfg.OBSERVATIONS][new_obs_id][cfg.USE_EXIF_DATE] = observationWindow.rb_use_exif.isChecked()
|
|
1030
|
+
|
|
1031
|
+
# ask if the value of the exif date time of the first picture must be substracted
|
|
1032
|
+
# TODO: improve this
|
|
1033
|
+
if self.pj[cfg.OBSERVATIONS][new_obs_id][cfg.USE_EXIF_DATE]:
|
|
1034
|
+
response = dialog.MessageDialog(
|
|
1035
|
+
cfg.programName,
|
|
1036
|
+
"You choose to use the EXIF metadata. Do you want to substract the date time value of the first picture?",
|
|
1037
|
+
(cfg.YES, cfg.NO),
|
|
1038
|
+
)
|
|
1039
|
+
self.pj[cfg.OBSERVATIONS][new_obs_id][cfg.SUBSTRACT_FIRST_EXIF_DATE] = response == cfg.YES
|
|
1040
|
+
|
|
1041
|
+
# check if time lapse
|
|
1028
1042
|
if observationWindow.rb_time_lapse.isChecked():
|
|
1029
1043
|
self.pj[cfg.OBSERVATIONS][new_obs_id][cfg.TIME_LAPSE] = observationWindow.sb_time_lapse.value()
|
|
1030
1044
|
else:
|
|
@@ -1086,7 +1100,6 @@ def new_observation(self, mode: str = cfg.NEW, obsId: str = "") -> None:
|
|
|
1086
1100
|
return "Observation not launched"
|
|
1087
1101
|
|
|
1088
1102
|
if self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE] == cfg.IMAGES:
|
|
1089
|
-
# QMessageBox.critical(self, cfg.programName, "Observation from images directory is not yet implemented")
|
|
1090
1103
|
initialize_new_images_observation(self)
|
|
1091
1104
|
|
|
1092
1105
|
self.load_tw_events(self.observationId)
|
|
@@ -1286,7 +1299,7 @@ def check_creation_date(self) -> Tuple[int, dict]:
|
|
|
1286
1299
|
def init_mpv(self):
|
|
1287
1300
|
"""Start mpv process and embed it in the PySide6 application."""
|
|
1288
1301
|
|
|
1289
|
-
|
|
1302
|
+
logging.debug("function: init_mpv")
|
|
1290
1303
|
|
|
1291
1304
|
"""
|
|
1292
1305
|
print(f"{self.winId()=}")
|
|
@@ -1313,7 +1326,7 @@ def init_mpv(self):
|
|
|
1313
1326
|
def send_command(command):
|
|
1314
1327
|
"""Send a JSON command to the mpv IPC server."""
|
|
1315
1328
|
|
|
1316
|
-
|
|
1329
|
+
logging.debug("function: send commnand {command}")
|
|
1317
1330
|
# print(f"{self.mpv_process=}")
|
|
1318
1331
|
|
|
1319
1332
|
try:
|
boris/project_functions.py
CHANGED
boris/select_observations.py
CHANGED
|
@@ -21,7 +21,9 @@ Copyright 2012-2025 Olivier Friard
|
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
+
import logging
|
|
24
25
|
from typing import Tuple
|
|
26
|
+
|
|
25
27
|
from PySide6.QtCore import Qt
|
|
26
28
|
from PySide6.QtWidgets import QAbstractItemView
|
|
27
29
|
|
|
@@ -60,7 +62,10 @@ def select_observations2(self, mode: str, windows_title: str = "") -> Tuple[str,
|
|
|
60
62
|
data: list = []
|
|
61
63
|
not_paired: list = []
|
|
62
64
|
|
|
65
|
+
# check if observations changed
|
|
63
66
|
if hash(str(self.pj[cfg.OBSERVATIONS])) != self.mem_hash_obs:
|
|
67
|
+
logging.debug("observations changed")
|
|
68
|
+
|
|
64
69
|
for obs in sorted(list(pj[cfg.OBSERVATIONS].keys())):
|
|
65
70
|
date = pj[cfg.OBSERVATIONS][obs]["date"].replace("T", " ")
|
|
66
71
|
descr = util.eol2space(pj[cfg.OBSERVATIONS][obs][cfg.DESCRIPTION])
|
|
@@ -124,11 +129,12 @@ def select_observations2(self, mode: str, windows_title: str = "") -> Tuple[str,
|
|
|
124
129
|
data, header=fields_list + indep_var_header, column_type=column_type, not_paired=not_paired
|
|
125
130
|
)
|
|
126
131
|
self.data = data
|
|
132
|
+
self.not_paired = not_paired
|
|
127
133
|
self.mem_hash_obs = hash(str(self.pj[cfg.OBSERVATIONS]))
|
|
128
134
|
|
|
129
135
|
else:
|
|
130
136
|
obsList = observations_list.observationsList_widget(
|
|
131
|
-
self.data, header=fields_list + indep_var_header, column_type=column_type, not_paired=not_paired
|
|
137
|
+
self.data, header=fields_list + indep_var_header, column_type=column_type, not_paired=self.not_paired
|
|
132
138
|
)
|
|
133
139
|
|
|
134
140
|
if windows_title:
|
boris/state_events.py
CHANGED
boris/version.py
CHANGED
|
@@ -10,22 +10,22 @@ boris/behaviors_coding_map.py,sha256=xIGJxp2eghrpiGDmYH73eJPERuyc4A_54uT-Got3zTs
|
|
|
10
10
|
boris/boris_cli.py,sha256=n0OiVvZM1gM6E7yKaff9wlgmpAGK4TK052VRi8AabJo,13196
|
|
11
11
|
boris/cmd_arguments.py,sha256=oWb-FvhKLbKJhATlTHy9muWu8XnnUfOZ-3Fmz2M8Yzc,1848
|
|
12
12
|
boris/coding_pad.py,sha256=fBKdp7DDyupySJosIYtqNd8s2E-GruzCgVhDFsoVWKE,10986
|
|
13
|
-
boris/config.py,sha256=
|
|
13
|
+
boris/config.py,sha256=PQXm3gJgdCtPOFKLH65sDGw8sk_cdMP53p6eMrlRJl0,17457
|
|
14
14
|
boris/config_file.py,sha256=1-2ZmTvKET57rwrLR1dXblt0AxMpGB1LAiHxu-Sy8es,13543
|
|
15
15
|
boris/connections.py,sha256=rVI18AuXh8cEnnoCKJk0RMWAaiNOpiaS554Okgk3SBY,19383
|
|
16
16
|
boris/converters.py,sha256=c1Jps-URoglY5ILQHz-pCCf6-4DFUHZLtqr_ofsrFg0,11722
|
|
17
17
|
boris/converters_ui.py,sha256=uu7LOBV_fKv2DBdOqsqPwjGsjgONr5ODBoscAA-EP48,9900
|
|
18
18
|
boris/cooccurence.py,sha256=tVERC-V8MWjWHlGEfDuu08iS94qjt4do-38jwI62QaY,10367
|
|
19
|
-
boris/core.py,sha256=
|
|
19
|
+
boris/core.py,sha256=kmWCzeMv-26bL3a2f_NDnfyes2jLehVwh-Hmlg7c1dI,230405
|
|
20
20
|
boris/core_qrc.py,sha256=J0kom27nrmi4-TCDLJZCZbtUAitiXQ4WEDfErawuxt8,639879
|
|
21
21
|
boris/core_ui.py,sha256=SeC26uveDCjrCBLsRPuQ6FaapKfON_HIRcQStEDLhl4,76384
|
|
22
22
|
boris/db_functions.py,sha256=Uw9wWH_Pe-qNzpV1k21YG_jKsoOmfY_iiK_7ARZHGDc,13352
|
|
23
23
|
boris/dev.py,sha256=9pUElbjl9g17rFUJXX5aVSu55_iIKIuDxNdrB0DI_d0,3671
|
|
24
|
-
boris/dialog.py,sha256=
|
|
24
|
+
boris/dialog.py,sha256=ppgaWBLCPfXHLK3_9xpu8h0Nj_m0Ls_4dsZ6lym5wjE,34972
|
|
25
25
|
boris/duration_widget.py,sha256=GjZgCAMGOcsNjoPiRImEVe6yMkH2vuNoh44ulpd5nlg,6924
|
|
26
|
-
boris/edit_event.py,sha256=
|
|
27
|
-
boris/edit_event_ui.py,sha256=
|
|
28
|
-
boris/event_operations.py,sha256=
|
|
26
|
+
boris/edit_event.py,sha256=IMZnopPALNJHgGenl_kMZjqO_Q9GKtyEcyVq5w5VGyU,8001
|
|
27
|
+
boris/edit_event_ui.py,sha256=qFgt00cejGB6UGC1mFkyZcsIAdvMeYMK0WYjZtJl1T0,9207
|
|
28
|
+
boris/event_operations.py,sha256=bqUZjgJaJ1Z8oTiidG9wfCp2LLUH1Zf4kBDeg_yjC-o,38514
|
|
29
29
|
boris/events_cursor.py,sha256=VPY_ygD0fxE5lp25mcd2l00XQXurCR6hprffF4tKRbU,2078
|
|
30
30
|
boris/events_snapshots.py,sha256=PjWzQvUGQtIcEc_7FDsRphf7fAhhTccQgYc2eQSA65g,27621
|
|
31
31
|
boris/exclusion_matrix.py,sha256=ff88xD6aqc8bpIuj9ZCz9ju_HeqqgibQwoaJrIOJ6RI,5272
|
|
@@ -46,8 +46,8 @@ boris/modifiers_coding_map.py,sha256=oT56ZY_PXhEJsMoblEsyNMAPbDpv7ZMOCnvmt7Ibx_Y
|
|
|
46
46
|
boris/mpv-1.0.3.py,sha256=EXRtzQqFjOn4wMC6482Ilq3fNQ9N1GRP1VxwLzdeaBY,88077
|
|
47
47
|
boris/mpv.py,sha256=EfzIHjPbgewG4w3smEtqEUPZoVwYmMQkL4Q8ZyW-a58,76410
|
|
48
48
|
boris/mpv2.py,sha256=IUI4t4r9GYX7G5OXTjd3RhMMOkDdfal_15buBgksLsk,92152
|
|
49
|
-
boris/observation.py,sha256=
|
|
50
|
-
boris/observation_operations.py,sha256=
|
|
49
|
+
boris/observation.py,sha256=PcyRjeWnDDniug-cJTr9tqfL0lfMQ02hMk4xY7ZxIvk,57029
|
|
50
|
+
boris/observation_operations.py,sha256=6krVy8PrxZ_EUHzUN9FA6Qy17v8QhayVt0uMOMlSDac,106603
|
|
51
51
|
boris/observation_ui.py,sha256=DAnU94QBNvkLuHT6AxTwqSk_D_n6VUhSl8PexZv_dUk,33309
|
|
52
52
|
boris/observations_list.py,sha256=NqwECGHtHYmKhSe-qCfqPmJ24SSfzlXvIXS2i3op_zE,10591
|
|
53
53
|
boris/otx_parser.py,sha256=70QvilzFHXbjAHR88YH0aEXJ3xxheLS5fZGgHFHGpNE,16367
|
|
@@ -63,22 +63,22 @@ boris/plugins.py,sha256=CCS1I44OMkGZqcfLGKNPGfEQXPgngocy5YhWveXQPKM,10029
|
|
|
63
63
|
boris/preferences.py,sha256=qPfd9Tyg7u30kXwVqMOgkdy2RXri9bItRa5U2-ZVQmg,16847
|
|
64
64
|
boris/preferences_ui.py,sha256=D2bFLb3E0m6IwSeqKoItRDiqvPmJGoeXLHF2K02n1Zo,26293
|
|
65
65
|
boris/project.py,sha256=nyXfCDY_rLP3jC1QGv-280jUKgbABqESjOm7I19rJ1U,86432
|
|
66
|
-
boris/project_functions.py,sha256=
|
|
66
|
+
boris/project_functions.py,sha256=7Pf-u7rPKQGWxO2AwLuR4WljxOpuoH6bxsAEkttPCzw,80794
|
|
67
67
|
boris/project_import_export.py,sha256=oBG1CSXfKISsb3TLNT-8BH8kZPAzxIYSNemlLVH1Lh8,38560
|
|
68
68
|
boris/project_ui.py,sha256=yB-ewhHt8S8DTTRIk-dNK2tPMNU24lNji9fDW_Xazu8,38805
|
|
69
69
|
boris/qrc_boris.py,sha256=aH-qUirYY1CGxmTK1SFCPvuZfazIHX4DdUKF1gxZeYM,675008
|
|
70
70
|
boris/qrc_boris5.py,sha256=prnOw7VGXWXRuVCYp_yIrmWhrlG1F9rx-3BQvkPenjY,161608
|
|
71
71
|
boris/select_modifiers.py,sha256=42uG9F75pfPoPJ-blp-vFgmpBpVJtL42FlIxpNpq9z4,13319
|
|
72
|
-
boris/select_observations.py,sha256=
|
|
72
|
+
boris/select_observations.py,sha256=k7c3FNVQW74YGH9oFmtHXRVCRnpKGhjCVk3cQtyLML8,8027
|
|
73
73
|
boris/select_subj_behav.py,sha256=ulXbsRY-AIyQRSwXhVlvkNRS_eqWaCvkDKTTyOLqvoE,11742
|
|
74
|
-
boris/state_events.py,sha256=
|
|
74
|
+
boris/state_events.py,sha256=Vrxn3CxC3yf8aeFE76l24n03qgaQ1QXpMtprYT0pq64,7771
|
|
75
75
|
boris/subjects_pad.py,sha256=lSRRGfLfD10_YpGua8RGVdKhoXlsXawGhNibPkRhuzM,3541
|
|
76
76
|
boris/synthetic_time_budget.py,sha256=3Eb9onMLmgqCLd50CuxV9L8RV2ESzfaMWvPK_bXUMMk,10489
|
|
77
77
|
boris/time_budget_functions.py,sha256=y5He8crz0xsTxVfz0jATwFFQVnPAIrNHja_0sF6NtRE,52551
|
|
78
78
|
boris/time_budget_widget.py,sha256=z-tyITBtIz-KH1H2OdMB5a8x9QQLK7Wu96-zkC6NVDA,43213
|
|
79
79
|
boris/transitions.py,sha256=_aZJfJWv3EBrtmQ7qsdTCayQo6uWU7BXqtQQgflEhr4,12250
|
|
80
80
|
boris/utilities.py,sha256=4b3BjO96eR54sPXIYkHwxkUnROGgY5f5vhWKIy8THUo,52646
|
|
81
|
-
boris/version.py,sha256=
|
|
81
|
+
boris/version.py,sha256=GfkFu2oCmCqcl0AuaKWJ5PwDcop2IbfKoZ_56iiDrIA,787
|
|
82
82
|
boris/video_equalizer.py,sha256=FartoGghFK-T53zklP70rPKYqTuzL8qdvfGlsOF2wwc,5854
|
|
83
83
|
boris/video_equalizer_ui.py,sha256=1CG3s79eM4JAbaCx3i1ILZXLceb41_gGXlOLNfpBgnw,10142
|
|
84
84
|
boris/video_operations.py,sha256=mh3iR__Sm2KnV44L_sW2pOo3AgLwlM7wiTnnqQiAVs4,9381
|
|
@@ -96,9 +96,9 @@ boris/portion/dict.py,sha256=SyHxc7PfDw2ufNLFQycwJtzmRfL48rDp4UrM2KN7IWc,11282
|
|
|
96
96
|
boris/portion/func.py,sha256=3TkQtFKLfsqntwd27HSGHceFhnXHmT-EbNMqktElC5Q,2174
|
|
97
97
|
boris/portion/interval.py,sha256=bAdUiJjGeUAPgsBAImwNeviiwfQq5odfhFZccAWzOTA,20299
|
|
98
98
|
boris/portion/io.py,sha256=ppNeRpiLNrocF1yzGeuEUIhYMf2LfsR-cji3d0nmvUs,6371
|
|
99
|
-
boris_behav_obs-9.3.
|
|
100
|
-
boris_behav_obs-9.3.
|
|
101
|
-
boris_behav_obs-9.3.
|
|
102
|
-
boris_behav_obs-9.3.
|
|
103
|
-
boris_behav_obs-9.3.
|
|
104
|
-
boris_behav_obs-9.3.
|
|
99
|
+
boris_behav_obs-9.3.5.dist-info/licenses/LICENSE.TXT,sha256=WJ7YI-moTFb-uVrFjnzzhGJrnL9P2iqQe8NuED3hutI,35141
|
|
100
|
+
boris_behav_obs-9.3.5.dist-info/METADATA,sha256=X_4eXVCfsqHmHTFqUlJXwAAwNbqVSwEwJTl78vDXC3o,4514
|
|
101
|
+
boris_behav_obs-9.3.5.dist-info/WHEEL,sha256=joeZ_q2kZqPjVkNy_YbjGrynLS6bxmBj74YkvIORXVI,109
|
|
102
|
+
boris_behav_obs-9.3.5.dist-info/entry_points.txt,sha256=k__8XvFi4vaA4QFvQehCZjYkKmZH34HSAJI2iYCWrMs,52
|
|
103
|
+
boris_behav_obs-9.3.5.dist-info/top_level.txt,sha256=fJSgm62S7WesiwTorGbOO4nNN0yzgZ3klgfGi3Px4qI,6
|
|
104
|
+
boris_behav_obs-9.3.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|