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/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 math
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(0),
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
- for w in (self.lb_image_idx, self.sb_image_idx, self.cb_set_time_na, self.pb_set_to_current_image_index):
66
- w.setVisible(False)
67
- # hide frame index because frame index is automatically extracted
68
- for w in (self.lb_frame_idx, self.sb_frame_idx, self.cb_set_frame_idx_na):
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
- if (observation_type in (cfg.LIVE, cfg.MEDIA)) or (observation_type == cfg.IMAGES and not math.isnan(self.time_value)):
72
- self.time_widget = dialog.get_time_widget(self.time_value)
83
+ # widget for time
84
+ self.time_widget = dialog.get_time_widget(self.time_value)
73
85
 
74
- if time_format == cfg.S:
75
- self.time_widget.rb_seconds.setChecked(True)
76
- if time_format == cfg.HHMMSS:
77
- self.time_widget.rb_time.setChecked(True)
78
- if self.time_value > cfg.DATE_CUTOFF:
79
- self.time_widget.rb_datetime.setChecked(True)
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
- self.horizontalLayout_2.insertWidget(0, self.time_widget)
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
- for w in (self.lb_frame_idx, self.sb_frame_idx, self.cb_set_frame_idx_na, self.pb_set_to_current_time):
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
- def frame_idx_na(self):
126
- """
127
- set/unset frame index NA
128
- """
129
- self.lb_frame_idx.setEnabled(not self.cb_set_frame_idx_na.isChecked())
130
- self.sb_frame_idx.setEnabled(not self.cb_set_frame_idx_na.isChecked())
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.8.0
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, QHBoxLayout,
19
- QLabel, QPlainTextEdit, QPushButton, QSizePolicy,
20
- QSpacerItem, QSpinBox, QVBoxLayout, QWidget)
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(413, 488)
27
- self.verticalLayout = QVBoxLayout(Form)
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.label = QLabel(Form)
32
- self.label.setObjectName(u"label")
50
+ self.lb_time = QLabel(self.gb_time)
51
+ self.lb_time.setObjectName(u"lb_time")
33
52
 
34
- self.horizontalLayout_3.addWidget(self.label)
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.pb_set_to_current_time = QPushButton(Form)
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.horizontalLayout_2.addWidget(self.pb_set_to_current_time)
72
+ self.horizontalLayout_9.addWidget(self.pb_set_to_current_time)
42
73
 
43
- self.cb_set_time_na = QCheckBox(Form)
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.horizontalLayout_2.addWidget(self.cb_set_time_na)
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.horizontalLayout_2.addItem(self.horizontalSpacer)
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.horizontalLayout_3.addLayout(self.horizontalLayout_2)
83
+ self.verticalLayout.addItem(self.verticalSpacer)
54
84
 
55
85
 
56
- self.verticalLayout.addLayout(self.horizontalLayout_3)
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.lb_image_idx = QLabel(Form)
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(Form)
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.verticalLayout.addLayout(self.horizontalLayout_7)
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.verticalLayout.addLayout(self.horizontalLayout_4)
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.verticalLayout.addLayout(self.horizontalLayout_5)
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.verticalLayout.addLayout(self.horizontalLayout_6)
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.verticalLayout.addWidget(self.label_4)
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.verticalLayout.addWidget(self.leComment)
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.verticalLayout.addLayout(self.horizontalLayout)
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.label.setText(QCoreApplication.translate("Form", u"Time", None))
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.cb_set_time_na.setText(QCoreApplication.translate("Form", u"Set NA", None))
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"Code", None))
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=0,
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
- if self.extract_exif_DateTimeOriginal(self.images_list[new_index]) != -1:
177
- time_ = self.extract_exif_DateTimeOriginal(self.images_list[new_index]) - self.image_time_ref
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
- logging.warning(
646
- (
647
- f"The behaviour {self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx][cfg.EVENT_BEHAVIOR_FIELD_IDX]} "
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
- self.pj[cfg.OBSERVATIONS][self.observationId][cfg.EVENTS][pj_event_idx],
669
- self.pj[cfg.OBSERVATIONS][self.observationId][cfg.TYPE],
670
- cfg.FRAME_INDEX,
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
- edit_window.cb_set_frame_idx_na.setChecked(True)
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
- # MEDIA / LIVE
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
- print(f"{new_time=}")
689
+ if edit_window.cb_set_time_na.isChecked():
690
+ new_time = dec("NaN")
689
691
 
690
- if new_time is None:
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
- if not edit_window.sb_frame_idx.value() or edit_window.cb_set_frame_idx_na.isChecked():
720
- event[cfg.FRAME_INDEX] = cfg.NA
721
- else:
722
- if self.playerType == cfg.MEDIA:
723
- mem_time = self.getLaps()
724
- if not self.seek_mediaplayer(new_time):
725
- frame_idx = self.get_frame_index()
726
- event[cfg.FRAME_INDEX] = frame_idx
727
- self.seek_mediaplayer(mem_time)
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
- if (
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 is None or not 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, QTextCursor, QAction
29
+ from PySide6.QtGui import QColor
31
30
  from PySide6.QtWidgets import (
32
31
  QDialog,
33
32
  QVBoxLayout,
@@ -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
- print("start MPV process")
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
- print(f"send commnand {command}")
1329
+ logging.debug("function: send commnand {command}")
1317
1330
  # print(f"{self.mpv_process=}")
1318
1331
 
1319
1332
  try:
@@ -1984,6 +1984,6 @@ def project2dataframe(pj: dict, observations_list: list = []) -> pd.DataFrame:
1984
1984
 
1985
1985
  pd.DataFrame(data).info()
1986
1986
 
1987
- print(pd.DataFrame(data))
1987
+ # print(pd.DataFrame(data))
1988
1988
 
1989
1989
  return pd.DataFrame(data)
@@ -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
@@ -109,7 +109,7 @@ def fix_unpaired_events(self, silent_mode: bool = False):
109
109
  return
110
110
 
111
111
  fix_at_time = w.time_widget.get_time()
112
- if fix_at_time is None:
112
+ if fix_at_time.is_nan():
113
113
  QMessageBox.warning(
114
114
  self,
115
115
  cfg.programName,