boris-behav-obs 9.3.4__py2.py3-none-any.whl → 9.4__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/1.py +45 -0
- boris/about.py +5 -4
- boris/config.py +2 -0
- boris/config_file.py +1 -1
- boris/converters.py +1 -3
- boris/core.py +107 -168
- boris/dialog.py +10 -31
- 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/utilities.py +170 -75
- boris/version.py +2 -2
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.4.dist-info}/METADATA +1 -1
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.4.dist-info}/RECORD +23 -22
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.4.dist-info}/WHEEL +1 -1
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.4.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.4.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.3.4.dist-info → boris_behav_obs-9.4.dist-info}/top_level.txt +0 -0
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