boris-behav-obs 8.9.16__py3-none-any.whl → 9.7.6__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.
Potentially problematic release.
This version of boris-behav-obs might be problematic. Click here for more details.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +36 -39
- boris/add_modifier.py +122 -109
- boris/add_modifier_ui.py +239 -135
- boris/advanced_event_filtering.py +81 -45
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +42 -49
- boris/config.py +161 -77
- boris/config_file.py +63 -83
- boris/connections.py +112 -57
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2511 -1824
- boris/core_qrc.py +15895 -10185
- boris/core_ui.py +946 -792
- boris/db_functions.py +21 -41
- boris/dev.py +134 -0
- boris/dialog.py +505 -244
- boris/duration_widget.py +15 -20
- boris/edit_event.py +84 -28
- boris/edit_event_ui.py +214 -78
- boris/event_operations.py +517 -415
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +213 -583
- boris/export_observation.py +98 -611
- boris/external_processes.py +156 -97
- boris/geometric_measurement.py +652 -287
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +9 -9
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +26 -63
- boris/latency.py +34 -25
- boris/measurement_widget.py +14 -18
- boris/media_file.py +52 -84
- boris/menu_options.py +17 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv.py +1 -0
- boris/mpv2.py +732 -705
- boris/observation.py +655 -310
- boris/observation_operations.py +1036 -404
- boris/observation_ui.py +584 -356
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -80
- boris/param_panel.py +31 -16
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +90 -60
- boris/plot_data_module.py +43 -46
- boris/plot_events.py +127 -90
- boris/plot_events_rt.py +17 -31
- boris/plot_spectrogram_rt.py +95 -30
- boris/plot_waveform_rt.py +32 -21
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +306 -83
- boris/preferences_ui.py +685 -228
- boris/project.py +448 -293
- boris/project_functions.py +689 -254
- boris/project_import_export.py +213 -222
- boris/project_ui.py +674 -438
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +74 -48
- boris/select_observations.py +20 -199
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +53 -37
- boris/subjects_pad.py +6 -9
- boris/synthetic_time_budget.py +45 -28
- boris/time_budget_functions.py +171 -171
- boris/time_budget_widget.py +84 -114
- boris/transitions.py +41 -47
- boris/utilities.py +766 -266
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +125 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/boris_ui.py +0 -886
- boris/converters.ui +0 -289
- boris/core.qrc +0 -35
- boris/core.ui +0 -1543
- boris/edit_event.ui +0 -175
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -773
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1069
- boris/project_server.py +0 -236
- boris/vlc.py +0 -10343
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.9.16.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.9.16.dist-info/METADATA +0 -129
- boris_behav_obs-8.9.16.dist-info/RECORD +0 -108
- boris_behav_obs-8.9.16.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.9.16.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/dialog.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
BORIS
|
|
3
3
|
Behavioral Observation Research Interactive Software
|
|
4
|
-
Copyright 2012-
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
5
|
|
|
6
6
|
This file is part of BORIS.
|
|
7
7
|
|
|
@@ -20,16 +20,18 @@ This file is part of BORIS.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
+
import datetime as dt
|
|
24
|
+
from decimal import Decimal as dec
|
|
23
25
|
import logging
|
|
24
|
-
import
|
|
26
|
+
import math
|
|
25
27
|
import pathlib as pl
|
|
26
|
-
import traceback
|
|
27
28
|
import platform
|
|
28
|
-
import
|
|
29
|
-
|
|
29
|
+
import sys
|
|
30
|
+
import traceback
|
|
31
|
+
from typing import Union
|
|
30
32
|
|
|
31
|
-
from
|
|
32
|
-
from
|
|
33
|
+
from PySide6.QtCore import Qt, Signal, qVersion, QRect, QTime, QDateTime, QSize
|
|
34
|
+
from PySide6.QtWidgets import (
|
|
33
35
|
QApplication,
|
|
34
36
|
QAbstractItemView,
|
|
35
37
|
QCheckBox,
|
|
@@ -52,15 +54,32 @@ from PyQt5.QtWidgets import (
|
|
|
52
54
|
QTableWidget,
|
|
53
55
|
QVBoxLayout,
|
|
54
56
|
QWidget,
|
|
57
|
+
QDateTimeEdit,
|
|
58
|
+
QTimeEdit,
|
|
59
|
+
QAbstractSpinBox,
|
|
60
|
+
QRadioButton,
|
|
61
|
+
QStackedWidget,
|
|
62
|
+
QFrame,
|
|
55
63
|
)
|
|
64
|
+
from PySide6.QtGui import QFont, QTextCursor
|
|
56
65
|
|
|
57
66
|
from . import config as cfg
|
|
58
|
-
from . import duration_widget
|
|
59
67
|
from . import version
|
|
68
|
+
from . import utilities as util
|
|
60
69
|
|
|
61
70
|
|
|
62
71
|
def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
72
|
+
"""
|
|
73
|
+
show a generic message dialog and returns the text of the clicked button
|
|
63
74
|
|
|
75
|
+
Args:
|
|
76
|
+
title (str): Title of the dialog box
|
|
77
|
+
text (str): text of the dialog box
|
|
78
|
+
buttons (tuple): text for buttons
|
|
79
|
+
|
|
80
|
+
Return
|
|
81
|
+
str: text of the clicked button
|
|
82
|
+
"""
|
|
64
83
|
message = QMessageBox()
|
|
65
84
|
message.setWindowTitle(title)
|
|
66
85
|
message.setText(text)
|
|
@@ -68,116 +87,64 @@ def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
|
68
87
|
for button in buttons:
|
|
69
88
|
message.addButton(button, QMessageBox.YesRole)
|
|
70
89
|
|
|
71
|
-
|
|
72
|
-
message.
|
|
90
|
+
message.setWindowFlags(message.windowFlags() | Qt.WindowStaysOnTopHint)
|
|
91
|
+
message.exec()
|
|
73
92
|
return message.clickedButton().text()
|
|
74
93
|
|
|
75
94
|
|
|
76
|
-
'''
|
|
77
|
-
def error_message_box(task, error_type, error_file_name, error_lineno):
|
|
78
|
-
# do NOT use this function directly, use error_message function
|
|
79
|
-
"""
|
|
80
|
-
show a critical dialog
|
|
81
|
-
|
|
82
|
-
"""
|
|
83
|
-
QMessageBox.critical(None, cfg.programName, (f"BORIS version: {version.__version__}<br>"
|
|
84
|
-
f"An error occured during the execution of <b>{task}</b>.<br>"
|
|
85
|
-
f"Error: {error_type}<br>"
|
|
86
|
-
f"in {error_file_name} "
|
|
87
|
-
f"at line # {error_lineno}<br><br>"
|
|
88
|
-
"to improve the software please report this problem at:<br>"
|
|
89
|
-
'<a href="https://github.com/olivierfriard/BORIS/issues">'
|
|
90
|
-
'https://github.com/olivierfriard/BORIS/issues</a><br>'
|
|
91
|
-
"or by email (See the About page on the BORIS web site.<br><br>"
|
|
92
|
-
"Thank you for your collaboration!"))
|
|
93
|
-
'''
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def error_message_box(error_traceback):
|
|
97
|
-
# do NOT use this function directly, use error_message function
|
|
98
|
-
"""
|
|
99
|
-
show a critical dialog
|
|
100
|
-
|
|
101
|
-
"""
|
|
102
|
-
QMessageBox.critical(
|
|
103
|
-
None,
|
|
104
|
-
cfg.programName,
|
|
105
|
-
(
|
|
106
|
-
f"BORIS version: {version.__version__}<br><br>"
|
|
107
|
-
f"<b>An error has occured</b>:<br>"
|
|
108
|
-
f"{error_traceback}<br><br>"
|
|
109
|
-
"to improve the software please report this problem at:<br>"
|
|
110
|
-
'<a href="https://github.com/olivierfriard/BORIS/issues">'
|
|
111
|
-
"https://github.com/olivierfriard/BORIS/issues</a><br>"
|
|
112
|
-
"or by email (See the About page on the BORIS web site.<br><br>"
|
|
113
|
-
"Thank you for your collaboration!"
|
|
114
|
-
),
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def error_message() -> None:
|
|
119
|
-
"""
|
|
120
|
-
Show details about the error in a message box
|
|
121
|
-
write entry to log as CRITICAL
|
|
122
|
-
"""
|
|
123
|
-
|
|
124
|
-
error_traceback = traceback.format_exc().replace("Traceback (most recent call last):", "").replace("\n", " ")
|
|
125
|
-
|
|
126
|
-
logging.critical(error_traceback)
|
|
127
|
-
error_message_box(error_traceback)
|
|
128
|
-
|
|
129
|
-
|
|
130
95
|
def global_error_message(exception_type, exception_value, traceback_object):
|
|
131
96
|
"""
|
|
132
|
-
|
|
133
|
-
save error using loggin.critical and
|
|
97
|
+
Global error management
|
|
98
|
+
save error using loggin.critical and stdout
|
|
134
99
|
"""
|
|
135
100
|
|
|
136
|
-
error_text
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
f"CPU: {platform.uname().machine} {platform.uname().processor}\n"
|
|
140
|
-
f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})\n"
|
|
141
|
-
f"Qt {QT_VERSION_STR} - PyQt {PYQT_VERSION_STR}\n"
|
|
142
|
-
f"{dt.datetime.now():%Y-%m-%d %H:%M}\n\n"
|
|
143
|
-
)
|
|
101
|
+
error_text = "\n\nSystem info\n===========\n\n"
|
|
102
|
+
error_text += util.get_systeminfo()
|
|
103
|
+
error_text += f"Error succeded at {dt.datetime.now():%Y-%m-%d %H:%M}\n\n"
|
|
144
104
|
error_text += "".join(traceback.format_exception(exception_type, exception_value, traceback_object))
|
|
145
105
|
|
|
106
|
+
# write to stdout
|
|
146
107
|
logging.critical(error_text)
|
|
147
108
|
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
109
|
+
# write to $HOME/boris_error.log
|
|
110
|
+
try:
|
|
111
|
+
with open(pl.Path.home() / "boris_error.log", "w") as f_error:
|
|
112
|
+
f_error.write(error_text)
|
|
113
|
+
except Exception:
|
|
114
|
+
logging.critical(f"Impossible to write to {pl.Path.home() / 'boris_error.log'}")
|
|
152
115
|
|
|
153
116
|
# copy to clipboard
|
|
154
117
|
cb = QApplication.clipboard()
|
|
155
|
-
cb.clear(
|
|
156
|
-
cb.setText(error_text
|
|
157
|
-
|
|
158
|
-
error_text: str = error_text.replace("\r\n", "\n").replace("\n", "<br>")
|
|
118
|
+
cb.clear()
|
|
119
|
+
cb.setText(error_text)
|
|
159
120
|
|
|
160
121
|
text: str = (
|
|
161
|
-
f"
|
|
162
|
-
|
|
163
|
-
"
|
|
164
|
-
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"Thank you for your collaboration!"
|
|
122
|
+
f"An error has occured!\n\n"
|
|
123
|
+
"to improve the software please report this problem at:\n"
|
|
124
|
+
"https://github.com/olivierfriard/BORIS/issues\n\n"
|
|
125
|
+
"Please no screenshot, the error message was copied to the clipboard.\n\n"
|
|
126
|
+
"Thank you for your collaboration!\n\n"
|
|
127
|
+
f"{error_text}"
|
|
168
128
|
)
|
|
169
129
|
|
|
170
|
-
errorbox =
|
|
171
|
-
|
|
172
|
-
errorbox.
|
|
173
|
-
errorbox.
|
|
174
|
-
errorbox.
|
|
130
|
+
errorbox = Results_dialog()
|
|
131
|
+
|
|
132
|
+
errorbox.setWindowTitle("BORIS - An error occured")
|
|
133
|
+
errorbox.pbOK.setText("Abort")
|
|
134
|
+
errorbox.pbCancel.setVisible(True)
|
|
135
|
+
errorbox.pbCancel.setText("Ignore and try to continue")
|
|
175
136
|
|
|
176
|
-
|
|
137
|
+
font = QFont()
|
|
138
|
+
font.setFamily("monospace")
|
|
139
|
+
errorbox.ptText.setFont(font)
|
|
140
|
+
errorbox.ptText.clear()
|
|
141
|
+
errorbox.ptText.appendPlainText(text)
|
|
142
|
+
|
|
143
|
+
errorbox.ptText.moveCursor(QTextCursor.Start)
|
|
177
144
|
|
|
178
145
|
ret = errorbox.exec_()
|
|
179
146
|
|
|
180
|
-
if ret ==
|
|
147
|
+
if ret == 1: # Abort
|
|
181
148
|
sys.exit(1)
|
|
182
149
|
|
|
183
150
|
|
|
@@ -194,6 +161,319 @@ class Info_widget(QWidget):
|
|
|
194
161
|
self.setLayout(layout)
|
|
195
162
|
|
|
196
163
|
|
|
164
|
+
class get_time_widget(QWidget):
|
|
165
|
+
"""
|
|
166
|
+
widget for selecting a time in various formats: seconds, HH:MM:SS:ZZZ or YYYY-mm-DD HH:MM:SS:ZZZ
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(self, time_value=dec(0), parent=None):
|
|
170
|
+
super().__init__(parent)
|
|
171
|
+
|
|
172
|
+
self.setWindowTitle("BORIS")
|
|
173
|
+
|
|
174
|
+
self.widget = QWidget()
|
|
175
|
+
self.widget.setObjectName("widget")
|
|
176
|
+
self.widget.setGeometry(QRect(130, 220, 302, 63))
|
|
177
|
+
self.verticalLayout_3 = QVBoxLayout(self.widget)
|
|
178
|
+
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
|
179
|
+
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
|
|
180
|
+
self.horizontalLayout_3 = QHBoxLayout()
|
|
181
|
+
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
|
182
|
+
self.verticalLayout_2 = QVBoxLayout()
|
|
183
|
+
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
|
184
|
+
self.label = QLabel(self.widget)
|
|
185
|
+
self.label.setObjectName("label")
|
|
186
|
+
|
|
187
|
+
self.verticalLayout_2.addWidget(self.label)
|
|
188
|
+
|
|
189
|
+
self.pb_sign = QPushButton(self.widget)
|
|
190
|
+
self.pb_sign.setObjectName("pb_sign")
|
|
191
|
+
self.pb_sign.setMaximumSize(QSize(40, 16777215))
|
|
192
|
+
|
|
193
|
+
self.verticalLayout_2.addWidget(self.pb_sign)
|
|
194
|
+
|
|
195
|
+
self.horizontalLayout_3.addLayout(self.verticalLayout_2)
|
|
196
|
+
|
|
197
|
+
self.verticalLayout = QVBoxLayout()
|
|
198
|
+
self.verticalLayout.setObjectName("verticalLayout")
|
|
199
|
+
self.horizontalLayout = QHBoxLayout()
|
|
200
|
+
self.horizontalLayout.setObjectName("horizontalLayout")
|
|
201
|
+
self.rb_seconds = QRadioButton(self.widget)
|
|
202
|
+
self.rb_seconds.setObjectName("rb_seconds")
|
|
203
|
+
|
|
204
|
+
self.horizontalLayout.addWidget(self.rb_seconds)
|
|
205
|
+
|
|
206
|
+
self.rb_time = QRadioButton(self.widget)
|
|
207
|
+
self.rb_time.setObjectName("rb_time")
|
|
208
|
+
|
|
209
|
+
self.horizontalLayout.addWidget(self.rb_time)
|
|
210
|
+
|
|
211
|
+
self.rb_datetime = QRadioButton(self.widget)
|
|
212
|
+
self.rb_datetime.setObjectName("rb_datetime")
|
|
213
|
+
|
|
214
|
+
self.horizontalLayout.addWidget(self.rb_datetime)
|
|
215
|
+
|
|
216
|
+
self.horizontalLayout.addStretch()
|
|
217
|
+
|
|
218
|
+
self.verticalLayout.addLayout(self.horizontalLayout)
|
|
219
|
+
|
|
220
|
+
self.stackedWidget = QStackedWidget(self.widget)
|
|
221
|
+
self.stackedWidget.setObjectName("stackedWidget")
|
|
222
|
+
self.stackedWidget.setFrameShape(QFrame.NoFrame)
|
|
223
|
+
self.seconds = QWidget()
|
|
224
|
+
self.seconds.setObjectName("seconds")
|
|
225
|
+
self.widget1 = QWidget(self.seconds)
|
|
226
|
+
self.widget1.setObjectName("widget1")
|
|
227
|
+
# self.widget1.setGeometry(QRect(10, 0, 163, 27))
|
|
228
|
+
self.horizontalLayout_4 = QHBoxLayout(self.widget1)
|
|
229
|
+
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
|
230
|
+
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
|
|
231
|
+
self.le_seconds = QLineEdit(self.widget1)
|
|
232
|
+
self.le_seconds.setObjectName("le_seconds")
|
|
233
|
+
|
|
234
|
+
self.horizontalLayout_4.addWidget(self.le_seconds)
|
|
235
|
+
|
|
236
|
+
self.lb_seconds = QLabel(self.widget1)
|
|
237
|
+
self.lb_seconds.setObjectName("lb_seconds")
|
|
238
|
+
|
|
239
|
+
self.horizontalLayout_4.addWidget(self.lb_seconds)
|
|
240
|
+
|
|
241
|
+
self.horizontalLayout_4.addStretch()
|
|
242
|
+
|
|
243
|
+
self.stackedWidget.addWidget(self.seconds)
|
|
244
|
+
self.hhmmss = QWidget()
|
|
245
|
+
self.hhmmss.setObjectName("hhmmss")
|
|
246
|
+
self.widget2 = QWidget(self.hhmmss)
|
|
247
|
+
self.widget2.setMinimumWidth(500)
|
|
248
|
+
self.widget2.setObjectName("widget2")
|
|
249
|
+
self.widget2.setGeometry(QRect(0, 0, 213, 28))
|
|
250
|
+
self.horizontalLayout_2 = QHBoxLayout(self.widget2)
|
|
251
|
+
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
|
252
|
+
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
|
|
253
|
+
self.lb_hour = QLabel(self.widget2)
|
|
254
|
+
self.lb_hour.setObjectName("lb_hour")
|
|
255
|
+
|
|
256
|
+
self.horizontalLayout_2.addWidget(self.lb_hour)
|
|
257
|
+
|
|
258
|
+
self.sb_hour = QSpinBox(self.widget2)
|
|
259
|
+
self.sb_hour.setObjectName("sb_hour")
|
|
260
|
+
self.sb_hour.setButtonSymbols(QAbstractSpinBox.NoButtons)
|
|
261
|
+
|
|
262
|
+
self.horizontalLayout_2.addWidget(self.sb_hour)
|
|
263
|
+
|
|
264
|
+
self.lb_hhmmss = QLabel(self.widget2)
|
|
265
|
+
self.lb_hhmmss.setObjectName("lb_hhmmss")
|
|
266
|
+
|
|
267
|
+
self.horizontalLayout_2.addWidget(self.lb_hhmmss)
|
|
268
|
+
|
|
269
|
+
self.te_time = QTimeEdit(self.widget2)
|
|
270
|
+
self.te_time.setObjectName("te_time")
|
|
271
|
+
self.te_time.adjustSize()
|
|
272
|
+
# self.te_time.setMinimumWidth(200)
|
|
273
|
+
# self.widget2.adjustSize()
|
|
274
|
+
|
|
275
|
+
self.horizontalLayout_2.addWidget(self.te_time)
|
|
276
|
+
|
|
277
|
+
self.horizontalLayout_2.addStretch()
|
|
278
|
+
|
|
279
|
+
self.stackedWidget.addWidget(self.hhmmss)
|
|
280
|
+
self.page_2 = QWidget()
|
|
281
|
+
self.page_2.setObjectName("page_2")
|
|
282
|
+
self.dte = QDateTimeEdit(self.page_2)
|
|
283
|
+
|
|
284
|
+
self.dte.setObjectName("dte")
|
|
285
|
+
# self.dte.setGeometry(QRect(10, 0, 164, 26))
|
|
286
|
+
self.stackedWidget.addWidget(self.page_2)
|
|
287
|
+
|
|
288
|
+
self.verticalLayout.addWidget(self.stackedWidget)
|
|
289
|
+
|
|
290
|
+
self.horizontalLayout_3.addLayout(self.verticalLayout)
|
|
291
|
+
|
|
292
|
+
self.verticalLayout_3.addLayout(self.horizontalLayout_3)
|
|
293
|
+
|
|
294
|
+
self.line = QFrame(self.widget)
|
|
295
|
+
self.line.setObjectName("line")
|
|
296
|
+
self.line.setFrameShape(QFrame.HLine)
|
|
297
|
+
self.line.setFrameShadow(QFrame.Sunken)
|
|
298
|
+
|
|
299
|
+
self.verticalLayout_3.addWidget(self.line)
|
|
300
|
+
|
|
301
|
+
self.setLayout(self.verticalLayout_3)
|
|
302
|
+
|
|
303
|
+
self.stackedWidget.setCurrentIndex(0)
|
|
304
|
+
|
|
305
|
+
self.label.setText("")
|
|
306
|
+
self.pb_sign.setText("+")
|
|
307
|
+
self.rb_seconds.setText("Seconds")
|
|
308
|
+
self.rb_time.setText("hh:mm:ss")
|
|
309
|
+
self.rb_datetime.setText("Date time")
|
|
310
|
+
self.le_seconds.setText("")
|
|
311
|
+
self.lb_seconds.setText("seconds")
|
|
312
|
+
self.lb_hour.setText("hour")
|
|
313
|
+
self.lb_hhmmss.setText("mm:ss.ms")
|
|
314
|
+
self.te_time.setDisplayFormat("mm:ss.zzz")
|
|
315
|
+
self.dte.setDisplayFormat("yyyy-MM-dd hh:mm:ss:zzz")
|
|
316
|
+
self.dte.adjustSize()
|
|
317
|
+
font = QFont()
|
|
318
|
+
font.setPointSize(14)
|
|
319
|
+
self.pb_sign.setFont(font)
|
|
320
|
+
|
|
321
|
+
self.rb_seconds.toggled.connect(self.format_changed)
|
|
322
|
+
self.rb_time.toggled.connect(self.format_changed)
|
|
323
|
+
self.rb_datetime.toggled.connect(self.format_changed)
|
|
324
|
+
self.sb_hour.setMaximum(cfg.HOUR_CUTOFF)
|
|
325
|
+
self.pb_sign.clicked.connect(self.pb_sign_clicked)
|
|
326
|
+
|
|
327
|
+
if time_value:
|
|
328
|
+
self.set_time(time_value)
|
|
329
|
+
|
|
330
|
+
self.format_changed()
|
|
331
|
+
|
|
332
|
+
self.adjustSize()
|
|
333
|
+
|
|
334
|
+
def format_changed(self):
|
|
335
|
+
if self.rb_seconds.isChecked():
|
|
336
|
+
self.stackedWidget.setCurrentIndex(0)
|
|
337
|
+
if self.rb_time.isChecked():
|
|
338
|
+
self.stackedWidget.setCurrentIndex(1)
|
|
339
|
+
if self.rb_datetime.isChecked():
|
|
340
|
+
self.stackedWidget.setCurrentIndex(2)
|
|
341
|
+
|
|
342
|
+
self.le_seconds.setEnabled(self.rb_seconds.isChecked())
|
|
343
|
+
self.le_seconds.adjustSize()
|
|
344
|
+
self.lb_seconds.setEnabled(self.rb_seconds.isChecked())
|
|
345
|
+
self.sb_hour.setEnabled(self.rb_time.isChecked())
|
|
346
|
+
self.te_time.setEnabled(self.rb_time.isChecked())
|
|
347
|
+
self.lb_hour.setEnabled(self.rb_time.isChecked())
|
|
348
|
+
self.lb_hhmmss.setEnabled(self.rb_time.isChecked())
|
|
349
|
+
self.dte.setEnabled(self.rb_datetime.isChecked())
|
|
350
|
+
|
|
351
|
+
def pb_sign_clicked(self):
|
|
352
|
+
if self.pb_sign.text() == "+":
|
|
353
|
+
self.pb_sign.setText("-")
|
|
354
|
+
else:
|
|
355
|
+
self.pb_sign.setText("+")
|
|
356
|
+
|
|
357
|
+
def get_time(self) -> Union[dec, None]:
|
|
358
|
+
"""
|
|
359
|
+
Get time from the selected format in the time widget
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
dec: time in seconds (None if no format selected)
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
time_sec = dec("NaN")
|
|
366
|
+
|
|
367
|
+
if self.rb_seconds.isChecked():
|
|
368
|
+
try:
|
|
369
|
+
time_sec = float(self.le_seconds.text())
|
|
370
|
+
except Exception:
|
|
371
|
+
QMessageBox.warning(
|
|
372
|
+
None,
|
|
373
|
+
cfg.programName,
|
|
374
|
+
f"The value of seconds ({self.le_seconds.text()}) is not a decimal number",
|
|
375
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
376
|
+
QMessageBox.NoButton,
|
|
377
|
+
)
|
|
378
|
+
return dec("NaN")
|
|
379
|
+
|
|
380
|
+
if self.rb_time.isChecked():
|
|
381
|
+
time_sec = self.sb_hour.value() * 3600
|
|
382
|
+
time_sec += self.te_time.time().msecsSinceStartOfDay() / 1000
|
|
383
|
+
|
|
384
|
+
if self.rb_datetime.isChecked():
|
|
385
|
+
time_sec = self.dte.dateTime().toMSecsSinceEpoch() / 1000
|
|
386
|
+
|
|
387
|
+
if self.pb_sign.text() == "-":
|
|
388
|
+
time_sec = -time_sec
|
|
389
|
+
|
|
390
|
+
return dec(time_sec).quantize(dec("0.001")) # if time_sec is not None else None
|
|
391
|
+
|
|
392
|
+
def set_time(self, new_time: dec) -> None:
|
|
393
|
+
"""
|
|
394
|
+
set time on time widget
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
if math.isnan(new_time):
|
|
398
|
+
return
|
|
399
|
+
|
|
400
|
+
self.pb_sign.setText("-" if new_time < 0 else "+")
|
|
401
|
+
|
|
402
|
+
# seconds
|
|
403
|
+
self.le_seconds.setText(f"{new_time:0.3f}")
|
|
404
|
+
|
|
405
|
+
if new_time <= cfg.DATE_CUTOFF: # hh:mm:ss.zzz
|
|
406
|
+
h = int(abs(new_time) // 3600)
|
|
407
|
+
m = int((abs(new_time) - h * 3600) // 60)
|
|
408
|
+
s = int((abs(new_time) - h * 3600 - m * 60))
|
|
409
|
+
ms = round((abs(new_time) - h * 3600 - m * 60 - s) * 1000)
|
|
410
|
+
|
|
411
|
+
self.sb_hour.setValue(h)
|
|
412
|
+
self.te_time.setTime(QTime(0, m, s, ms))
|
|
413
|
+
else:
|
|
414
|
+
self.sb_hour.setValue(0)
|
|
415
|
+
self.te_time.setTime(QTime(0, 0, 0, 0))
|
|
416
|
+
|
|
417
|
+
self.dte.setDateTime(QDateTime().fromMSecsSinceEpoch(int(new_time * 1000)))
|
|
418
|
+
self.rb_datetime.setChecked(True)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class Ask_time(QDialog):
|
|
422
|
+
"""
|
|
423
|
+
Qdialog class for asking time to user
|
|
424
|
+
User can select a time format between seconds, HHMMSS.zzz or YYY-mm-DD HH:MM:SS.zzz
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
def __init__(self, time_value=0):
|
|
428
|
+
super().__init__()
|
|
429
|
+
self.setWindowTitle("")
|
|
430
|
+
|
|
431
|
+
hbox = QVBoxLayout(self)
|
|
432
|
+
self.label = QLabel()
|
|
433
|
+
self.label.setText("")
|
|
434
|
+
hbox.addWidget(self.label)
|
|
435
|
+
|
|
436
|
+
self.time_widget = get_time_widget(time_value)
|
|
437
|
+
|
|
438
|
+
hbox.addWidget(self.time_widget)
|
|
439
|
+
|
|
440
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.pb_ok_clicked)
|
|
441
|
+
self.pbOK.setDefault(True)
|
|
442
|
+
|
|
443
|
+
self.pbCancel = QPushButton(cfg.CANCEL)
|
|
444
|
+
self.pbCancel.clicked.connect(self.reject)
|
|
445
|
+
|
|
446
|
+
self.hbox2 = QHBoxLayout(self)
|
|
447
|
+
self.hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
448
|
+
self.hbox2.addWidget(self.pbCancel)
|
|
449
|
+
self.hbox2.addWidget(self.pbOK)
|
|
450
|
+
hbox.addLayout(self.hbox2)
|
|
451
|
+
self.setLayout(hbox)
|
|
452
|
+
|
|
453
|
+
# if time_value:
|
|
454
|
+
# self.time_widget.set_time(time_value)
|
|
455
|
+
|
|
456
|
+
def pb_ok_clicked(self):
|
|
457
|
+
if (
|
|
458
|
+
not self.time_widget.rb_seconds.isChecked()
|
|
459
|
+
and not self.time_widget.rb_time.isChecked()
|
|
460
|
+
and not self.time_widget.rb_datetime.isChecked()
|
|
461
|
+
):
|
|
462
|
+
QMessageBox.warning(
|
|
463
|
+
None,
|
|
464
|
+
cfg.programName,
|
|
465
|
+
"Select an option",
|
|
466
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
467
|
+
QMessageBox.NoButton,
|
|
468
|
+
)
|
|
469
|
+
return
|
|
470
|
+
# test time value
|
|
471
|
+
if self.time_widget.get_time().is_nan():
|
|
472
|
+
return
|
|
473
|
+
|
|
474
|
+
self.accept()
|
|
475
|
+
|
|
476
|
+
|
|
197
477
|
class Video_overlay_dialog(QDialog):
|
|
198
478
|
"""
|
|
199
479
|
dialog to ask image file and position for video overlay
|
|
@@ -203,28 +483,37 @@ class Video_overlay_dialog(QDialog):
|
|
|
203
483
|
super().__init__()
|
|
204
484
|
|
|
205
485
|
vlayout = QVBoxLayout()
|
|
206
|
-
|
|
486
|
+
self.cb_player = QComboBox()
|
|
487
|
+
vlayout.addWidget(self.cb_player)
|
|
488
|
+
|
|
207
489
|
hbox = QHBoxLayout()
|
|
490
|
+
hbox.addWidget(QLabel("Image file"))
|
|
208
491
|
self.le_file_path = QLineEdit()
|
|
209
492
|
hbox.addWidget(self.le_file_path)
|
|
493
|
+
vlayout.addLayout(hbox)
|
|
494
|
+
|
|
495
|
+
hbox = QHBoxLayout()
|
|
210
496
|
self.pb_browse = QPushButton("Browse", self, clicked=self.browse)
|
|
497
|
+
hbox.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
211
498
|
hbox.addWidget(self.pb_browse)
|
|
212
499
|
vlayout.addLayout(hbox)
|
|
213
500
|
|
|
214
|
-
|
|
501
|
+
hbox = QHBoxLayout()
|
|
502
|
+
hbox.addWidget(QLabel("Position: x,y"))
|
|
215
503
|
self.le_overlay_position = QLineEdit()
|
|
216
|
-
|
|
504
|
+
hbox.addWidget(self.le_overlay_position)
|
|
505
|
+
vlayout.addLayout(hbox)
|
|
217
506
|
|
|
218
|
-
|
|
507
|
+
hbox = QHBoxLayout()
|
|
508
|
+
hbox.addWidget(QLabel("Transparency %"))
|
|
219
509
|
self.sb_overlay_transparency = QSpinBox()
|
|
220
510
|
self.sb_overlay_transparency.setRange(0, 100)
|
|
221
511
|
self.sb_overlay_transparency.setSingleStep(1)
|
|
222
|
-
self.sb_overlay_transparency.setValue(
|
|
223
|
-
|
|
224
|
-
|
|
512
|
+
self.sb_overlay_transparency.setValue(90)
|
|
513
|
+
hbox.addWidget(self.sb_overlay_transparency)
|
|
514
|
+
vlayout.addLayout(hbox)
|
|
225
515
|
|
|
226
|
-
self.
|
|
227
|
-
vlayout.addWidget(self.cb_player)
|
|
516
|
+
# self.sb_overlay_transparency.setEnabled(False)
|
|
228
517
|
|
|
229
518
|
hbox = QHBoxLayout()
|
|
230
519
|
self.pb_cancel = QPushButton("Cancel", self, clicked=self.reject)
|
|
@@ -239,8 +528,7 @@ class Video_overlay_dialog(QDialog):
|
|
|
239
528
|
self.setLayout(vlayout)
|
|
240
529
|
|
|
241
530
|
def browse(self):
|
|
242
|
-
|
|
243
|
-
file_name = fn[0] if type(fn) is tuple else fn
|
|
531
|
+
file_name, _ = QFileDialog.getOpenFileName(self, "Choose an image file", "", "PNG files (*.png);;All files (*)")
|
|
244
532
|
if file_name:
|
|
245
533
|
self.le_file_path.setText(file_name)
|
|
246
534
|
|
|
@@ -284,7 +572,8 @@ class Input_dialog(QDialog):
|
|
|
284
572
|
dialog for user input. Elements can be:
|
|
285
573
|
checkbox (cb): Tuple(str, str, bool)
|
|
286
574
|
lineedit (le): Tuple(str, str)
|
|
287
|
-
spinbox (
|
|
575
|
+
spinbox (sb)
|
|
576
|
+
doubleSpinbox (dsb)
|
|
288
577
|
items list (il)
|
|
289
578
|
|
|
290
579
|
"""
|
|
@@ -301,7 +590,6 @@ class Input_dialog(QDialog):
|
|
|
301
590
|
|
|
302
591
|
self.elements: dict = {}
|
|
303
592
|
for element in elements_list:
|
|
304
|
-
|
|
305
593
|
if element[0] == "cb": # checkbox
|
|
306
594
|
self.elements[element[1]] = QCheckBox(element[1])
|
|
307
595
|
self.elements[element[1]].setChecked(element[2])
|
|
@@ -334,7 +622,7 @@ class Input_dialog(QDialog):
|
|
|
334
622
|
# 3 - maximum value
|
|
335
623
|
# 4 - step
|
|
336
624
|
# 5 - initial value
|
|
337
|
-
# 6 - number of
|
|
625
|
+
# 6 - number of decimals
|
|
338
626
|
|
|
339
627
|
lb = QLabel(element[1])
|
|
340
628
|
hbox.addWidget(lb)
|
|
@@ -353,15 +641,15 @@ class Input_dialog(QDialog):
|
|
|
353
641
|
self.elements[element[1]] = QComboBox()
|
|
354
642
|
self.elements[element[1]].addItems([x[0] for x in element[2]]) # take first element of tuple
|
|
355
643
|
try:
|
|
356
|
-
self.elements[element[1]].setCurrentIndex(
|
|
357
|
-
|
|
358
|
-
)
|
|
359
|
-
except:
|
|
644
|
+
self.elements[element[1]].setCurrentIndex([idx for idx, x in enumerate(element[2]) if x[1] == "selected"][0])
|
|
645
|
+
except Exception:
|
|
360
646
|
self.elements[element[1]].setCurrentIndex(0)
|
|
361
647
|
hbox.addWidget(self.elements[element[1]])
|
|
362
648
|
|
|
363
649
|
hbox2 = QHBoxLayout()
|
|
364
650
|
|
|
651
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
652
|
+
|
|
365
653
|
self.pbCancel = QPushButton(cfg.CANCEL)
|
|
366
654
|
self.pbCancel.clicked.connect(self.reject)
|
|
367
655
|
hbox2.addWidget(self.pbCancel)
|
|
@@ -375,14 +663,13 @@ class Input_dialog(QDialog):
|
|
|
375
663
|
self.setLayout(hbox)
|
|
376
664
|
|
|
377
665
|
|
|
378
|
-
class
|
|
666
|
+
class Duplicate_items(QDialog):
|
|
379
667
|
"""
|
|
380
|
-
let user show between behaviors that are coded by same key
|
|
668
|
+
let user show between behaviors/subjects that are coded by same key
|
|
381
669
|
"""
|
|
382
670
|
|
|
383
671
|
def __init__(self, text, codes_list):
|
|
384
|
-
|
|
385
|
-
super(DuplicateBehaviorCode, self).__init__()
|
|
672
|
+
super(Duplicate_items, self).__init__()
|
|
386
673
|
|
|
387
674
|
self.setWindowTitle(cfg.programName)
|
|
388
675
|
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
@@ -406,13 +693,17 @@ class DuplicateBehaviorCode(QDialog):
|
|
|
406
693
|
|
|
407
694
|
Vlayout.addWidget(self.lw)
|
|
408
695
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
696
|
+
hlayout = QHBoxLayout()
|
|
697
|
+
hlayout.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
698
|
+
|
|
699
|
+
pbCancel = QPushButton("Cancel", clicked=self.reject)
|
|
700
|
+
hlayout.addWidget(pbCancel)
|
|
701
|
+
|
|
702
|
+
pbOK = QPushButton("OK", clicked=self.accept)
|
|
413
703
|
pbOK.setDefault(True)
|
|
414
|
-
|
|
415
|
-
|
|
704
|
+
hlayout.addWidget(pbOK)
|
|
705
|
+
|
|
706
|
+
Vlayout.addLayout(hlayout)
|
|
416
707
|
|
|
417
708
|
self.setLayout(Vlayout)
|
|
418
709
|
|
|
@@ -426,9 +717,8 @@ class DuplicateBehaviorCode(QDialog):
|
|
|
426
717
|
"""
|
|
427
718
|
if self.lw.selectedItems():
|
|
428
719
|
return self.lw.selectedItems()[0].text()
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
self.accept()
|
|
720
|
+
else:
|
|
721
|
+
return None
|
|
432
722
|
|
|
433
723
|
|
|
434
724
|
class ChooseObservationsToImport(QDialog):
|
|
@@ -437,7 +727,6 @@ class ChooseObservationsToImport(QDialog):
|
|
|
437
727
|
"""
|
|
438
728
|
|
|
439
729
|
def __init__(self, text, observations_list):
|
|
440
|
-
|
|
441
730
|
super(ChooseObservationsToImport, self).__init__()
|
|
442
731
|
|
|
443
732
|
self.setWindowTitle(cfg.programName)
|
|
@@ -485,50 +774,13 @@ class ChooseObservationsToImport(QDialog):
|
|
|
485
774
|
return [item.text() for item in self.lw.selectedItems()]
|
|
486
775
|
|
|
487
776
|
|
|
488
|
-
class Ask_time(QDialog):
|
|
489
|
-
"""
|
|
490
|
-
"Ask time" dialog box using duration widget in duration_widget module
|
|
491
|
-
"""
|
|
492
|
-
|
|
493
|
-
def __init__(self, time_format):
|
|
494
|
-
super().__init__()
|
|
495
|
-
hbox = QVBoxLayout(self)
|
|
496
|
-
self.label = QLabel()
|
|
497
|
-
self.label.setText("Go to time")
|
|
498
|
-
hbox.addWidget(self.label)
|
|
499
|
-
|
|
500
|
-
self.time_widget = duration_widget.Duration_widget()
|
|
501
|
-
if time_format == cfg.HHMMSS:
|
|
502
|
-
self.time_widget.set_format_hhmmss()
|
|
503
|
-
if time_format == cfg.S:
|
|
504
|
-
self.time_widget.set_format_s()
|
|
505
|
-
|
|
506
|
-
hbox.addWidget(self.time_widget)
|
|
507
|
-
|
|
508
|
-
self.pbOK = QPushButton("OK")
|
|
509
|
-
self.pbOK.clicked.connect(self.accept)
|
|
510
|
-
self.pbOK.setDefault(True)
|
|
511
|
-
|
|
512
|
-
self.pbCancel = QPushButton("Cancel")
|
|
513
|
-
self.pbCancel.clicked.connect(self.reject)
|
|
514
|
-
|
|
515
|
-
self.hbox2 = QHBoxLayout(self)
|
|
516
|
-
self.hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
517
|
-
self.hbox2.addWidget(self.pbCancel)
|
|
518
|
-
self.hbox2.addWidget(self.pbOK)
|
|
519
|
-
hbox.addLayout(self.hbox2)
|
|
520
|
-
self.setLayout(hbox)
|
|
521
|
-
self.setWindowTitle("Time")
|
|
522
|
-
|
|
523
|
-
|
|
524
777
|
class FindInEvents(QWidget):
|
|
525
778
|
"""
|
|
526
779
|
"find in events" dialog box
|
|
527
780
|
"""
|
|
528
781
|
|
|
529
|
-
clickSignal =
|
|
530
|
-
|
|
531
|
-
currentIdx = -1
|
|
782
|
+
clickSignal = Signal(str)
|
|
783
|
+
currentIdx: int = -1
|
|
532
784
|
|
|
533
785
|
def __init__(self):
|
|
534
786
|
super().__init__()
|
|
@@ -571,9 +823,10 @@ class FindInEvents(QWidget):
|
|
|
571
823
|
hbox.addWidget(self.lb_message)
|
|
572
824
|
|
|
573
825
|
hbox2 = QHBoxLayout()
|
|
826
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
574
827
|
self.pbOK = QPushButton("Find")
|
|
575
828
|
self.pbOK.clicked.connect(lambda: self.click("FIND"))
|
|
576
|
-
self.pbCancel = QPushButton(
|
|
829
|
+
self.pbCancel = QPushButton(cfg.CLOSE)
|
|
577
830
|
self.pbCancel.clicked.connect(lambda: self.click("CLOSE"))
|
|
578
831
|
hbox2.addWidget(self.pbCancel)
|
|
579
832
|
hbox2.addWidget(self.pbOK)
|
|
@@ -590,10 +843,7 @@ class FindReplaceEvents(QWidget):
|
|
|
590
843
|
"find replace events" dialog box
|
|
591
844
|
"""
|
|
592
845
|
|
|
593
|
-
clickSignal =
|
|
594
|
-
"""
|
|
595
|
-
sendEventSignal = pyqtSignal(QEvent)
|
|
596
|
-
"""
|
|
846
|
+
clickSignal = Signal(str)
|
|
597
847
|
|
|
598
848
|
def __init__(self):
|
|
599
849
|
super().__init__()
|
|
@@ -602,21 +852,9 @@ class FindReplaceEvents(QWidget):
|
|
|
602
852
|
|
|
603
853
|
hbox = QVBoxLayout()
|
|
604
854
|
|
|
605
|
-
self.
|
|
606
|
-
self.
|
|
607
|
-
hbox.addWidget(self.
|
|
608
|
-
|
|
609
|
-
self.cbBehavior = QCheckBox("Behavior")
|
|
610
|
-
self.cbBehavior.setChecked(False)
|
|
611
|
-
hbox.addWidget(self.cbBehavior)
|
|
612
|
-
|
|
613
|
-
self.cbModifier = QCheckBox("Modifiers")
|
|
614
|
-
self.cbModifier.setChecked(False)
|
|
615
|
-
hbox.addWidget(self.cbModifier)
|
|
616
|
-
|
|
617
|
-
self.cbComment = QCheckBox("Comment")
|
|
618
|
-
self.cbComment.setChecked(False)
|
|
619
|
-
hbox.addWidget(self.cbComment)
|
|
855
|
+
self.combo_fields = QComboBox()
|
|
856
|
+
self.combo_fields.addItems(("Choose a field", "Subject", "Behavior", "Modifiers", "Comment"))
|
|
857
|
+
hbox.addWidget(self.combo_fields)
|
|
620
858
|
|
|
621
859
|
self.lbFind = QLabel("Find")
|
|
622
860
|
hbox.addWidget(self.lbFind)
|
|
@@ -630,7 +868,7 @@ class FindReplaceEvents(QWidget):
|
|
|
630
868
|
self.replaceText = QLineEdit()
|
|
631
869
|
hbox.addWidget(self.replaceText)
|
|
632
870
|
|
|
633
|
-
self.cbFindInSelectedEvents = QCheckBox("Find/Replace in selected events")
|
|
871
|
+
self.cbFindInSelectedEvents = QCheckBox("Find/Replace only in selected events")
|
|
634
872
|
self.cbFindInSelectedEvents.setChecked(False)
|
|
635
873
|
hbox.addWidget(self.cbFindInSelectedEvents)
|
|
636
874
|
|
|
@@ -640,6 +878,8 @@ class FindReplaceEvents(QWidget):
|
|
|
640
878
|
|
|
641
879
|
hbox2 = QHBoxLayout()
|
|
642
880
|
|
|
881
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
882
|
+
|
|
643
883
|
self.pbCancel = QPushButton(cfg.CANCEL, clicked=lambda: self.click("CANCEL"))
|
|
644
884
|
hbox2.addWidget(self.pbCancel)
|
|
645
885
|
|
|
@@ -650,68 +890,76 @@ class FindReplaceEvents(QWidget):
|
|
|
650
890
|
hbox2.addWidget(self.pbFindReplaceAll)
|
|
651
891
|
|
|
652
892
|
hbox.addLayout(hbox2)
|
|
893
|
+
|
|
894
|
+
self.lb_results = QLabel(" ")
|
|
895
|
+
hbox.addWidget(self.lb_results)
|
|
896
|
+
|
|
653
897
|
self.setLayout(hbox)
|
|
654
898
|
|
|
655
899
|
def click(self, msg):
|
|
656
900
|
self.clickSignal.emit(msg)
|
|
657
901
|
|
|
658
902
|
|
|
659
|
-
|
|
660
|
-
class explore_project_dialog(QDialog):
|
|
903
|
+
class Results_dialog(QDialog):
|
|
661
904
|
"""
|
|
662
|
-
|
|
905
|
+
widget for visualizing text output in HTML
|
|
663
906
|
"""
|
|
664
907
|
|
|
665
908
|
def __init__(self):
|
|
666
909
|
super().__init__()
|
|
667
910
|
|
|
668
|
-
self.
|
|
669
|
-
|
|
670
|
-
hbox = QVBoxLayout()
|
|
671
|
-
|
|
672
|
-
hbox.addWidget(QLabel("Search in all observations"))
|
|
911
|
+
self.dataset = False
|
|
673
912
|
|
|
674
|
-
self.
|
|
675
|
-
hbox.addWidget(self.lb_subject)
|
|
676
|
-
self.find_subject = QLineEdit()
|
|
677
|
-
hbox.addWidget(self.find_subject)
|
|
913
|
+
self.setWindowTitle("")
|
|
678
914
|
|
|
679
|
-
|
|
680
|
-
hbox.addWidget(self.lb_behav)
|
|
681
|
-
self.find_behavior = QLineEdit()
|
|
682
|
-
hbox.addWidget(self.find_behavior)
|
|
915
|
+
hbox = QVBoxLayout()
|
|
683
916
|
|
|
684
|
-
self.
|
|
685
|
-
hbox.addWidget(self.
|
|
686
|
-
self.find_modifier = QLineEdit()
|
|
687
|
-
hbox.addWidget(self.find_modifier)
|
|
917
|
+
self.lb = QLabel("")
|
|
918
|
+
hbox.addWidget(self.lb)
|
|
688
919
|
|
|
689
|
-
self.
|
|
690
|
-
|
|
691
|
-
self.
|
|
692
|
-
hbox.addWidget(self.find_comment)
|
|
920
|
+
self.ptText = QPlainTextEdit()
|
|
921
|
+
self.ptText.setReadOnly(True)
|
|
922
|
+
hbox.addWidget(self.ptText)
|
|
693
923
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
hbox.addWidget(self.cb_case_sensitive)
|
|
924
|
+
hbox2 = QHBoxLayout()
|
|
925
|
+
hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
697
926
|
|
|
698
|
-
self.
|
|
699
|
-
|
|
927
|
+
self.pbSave = QPushButton("Save results", clicked=self.save_results)
|
|
928
|
+
hbox2.addWidget(self.pbSave)
|
|
700
929
|
|
|
701
|
-
|
|
702
|
-
self.pbOK = QPushButton("Find")
|
|
703
|
-
self.pbOK.clicked.connect(self.accept)
|
|
704
|
-
self.pbCancel = QPushButton("Cancel")
|
|
705
|
-
self.pbCancel.clicked.connect(self.reject)
|
|
930
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.reject)
|
|
706
931
|
hbox2.addWidget(self.pbCancel)
|
|
932
|
+
self.pbCancel.setVisible(False)
|
|
933
|
+
|
|
934
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.accept)
|
|
707
935
|
hbox2.addWidget(self.pbOK)
|
|
708
|
-
hbox.addLayout(hbox2)
|
|
709
936
|
|
|
937
|
+
hbox.addLayout(hbox2)
|
|
710
938
|
self.setLayout(hbox)
|
|
711
|
-
'''
|
|
712
939
|
|
|
940
|
+
self.resize(800, 640)
|
|
713
941
|
|
|
714
|
-
|
|
942
|
+
def save_results(self):
|
|
943
|
+
"""
|
|
944
|
+
save content of self.ptText
|
|
945
|
+
"""
|
|
946
|
+
|
|
947
|
+
if not self.dataset:
|
|
948
|
+
file_name, _ = QFileDialog().getSaveFileName(self, "Save results", "", "Text files (*.txt *.tsv);;All files (*)")
|
|
949
|
+
|
|
950
|
+
if not file_name:
|
|
951
|
+
return
|
|
952
|
+
try:
|
|
953
|
+
with open(file_name, "w") as f:
|
|
954
|
+
f.write(self.ptText.toPlainText())
|
|
955
|
+
except Exception:
|
|
956
|
+
QMessageBox.critical(self, cfg.programName, f"The file {file_name} can not be saved")
|
|
957
|
+
|
|
958
|
+
else:
|
|
959
|
+
self.done(cfg.SAVE_DATASET)
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
class Results_dialog_exit_code(QDialog):
|
|
715
963
|
"""
|
|
716
964
|
widget for visualizing text output
|
|
717
965
|
"""
|
|
@@ -733,32 +981,35 @@ class Results_dialog(QDialog):
|
|
|
733
981
|
hbox.addWidget(self.ptText)
|
|
734
982
|
|
|
735
983
|
hbox2 = QHBoxLayout()
|
|
984
|
+
hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
985
|
+
|
|
736
986
|
self.pbSave = QPushButton("Save results", clicked=self.save_results)
|
|
737
987
|
hbox2.addWidget(self.pbSave)
|
|
738
988
|
|
|
739
|
-
|
|
989
|
+
self.pb1 = QPushButton("1", clicked=lambda: self.done_(1))
|
|
990
|
+
hbox2.addWidget(self.pb1)
|
|
740
991
|
|
|
741
|
-
self.
|
|
742
|
-
hbox2.addWidget(self.
|
|
743
|
-
self.pbCancel.setVisible(False)
|
|
992
|
+
self.pb2 = QPushButton("2", clicked=lambda: self.done_(2))
|
|
993
|
+
hbox2.addWidget(self.pb2)
|
|
744
994
|
|
|
745
|
-
self.
|
|
746
|
-
|
|
747
|
-
hbox2.addWidget(self.pbOK)
|
|
995
|
+
self.pb3 = QPushButton("3", clicked=lambda: self.done_(3))
|
|
996
|
+
hbox2.addWidget(self.pb3)
|
|
748
997
|
|
|
749
998
|
hbox.addLayout(hbox2)
|
|
750
999
|
self.setLayout(hbox)
|
|
751
1000
|
|
|
752
1001
|
self.resize(800, 640)
|
|
753
1002
|
|
|
1003
|
+
def done_(self, status):
|
|
1004
|
+
self.done(status)
|
|
1005
|
+
|
|
754
1006
|
def save_results(self):
|
|
755
1007
|
"""
|
|
756
1008
|
save content of self.ptText
|
|
757
1009
|
"""
|
|
758
1010
|
|
|
759
1011
|
if not self.dataset:
|
|
760
|
-
|
|
761
|
-
file_name = fn[0] if type(fn) is tuple else fn
|
|
1012
|
+
file_name, _ = QFileDialog().getSaveFileName(self, "Save results", "", "Text files (*.txt *.tsv);;All files (*)")
|
|
762
1013
|
|
|
763
1014
|
if not file_name:
|
|
764
1015
|
return
|
|
@@ -772,9 +1023,9 @@ class Results_dialog(QDialog):
|
|
|
772
1023
|
self.done(cfg.SAVE_DATASET)
|
|
773
1024
|
|
|
774
1025
|
|
|
775
|
-
class
|
|
1026
|
+
class View_data(QDialog):
|
|
776
1027
|
"""
|
|
777
|
-
widget for visualizing
|
|
1028
|
+
widget for visualizing rows of data file
|
|
778
1029
|
"""
|
|
779
1030
|
|
|
780
1031
|
def __init__(self):
|
|
@@ -788,8 +1039,18 @@ class View_data_head(QDialog):
|
|
|
788
1039
|
vbox.addWidget(self.lb)
|
|
789
1040
|
|
|
790
1041
|
self.tw = QTableWidget()
|
|
1042
|
+
self.tw.verticalHeader().hide()
|
|
791
1043
|
vbox.addWidget(self.tw)
|
|
792
1044
|
|
|
1045
|
+
vbox.addWidget(QLabel("Descriptive statistics"))
|
|
1046
|
+
|
|
1047
|
+
self.stats = QPlainTextEdit()
|
|
1048
|
+
font = QFont()
|
|
1049
|
+
font.setFamily("Monospace")
|
|
1050
|
+
self.stats.setFont(font)
|
|
1051
|
+
|
|
1052
|
+
vbox.addWidget(self.stats)
|
|
1053
|
+
|
|
793
1054
|
self.label = QLabel("Enter the column indices to plot (time, value) separated by comma (,)")
|
|
794
1055
|
vbox.addWidget(self.label)
|
|
795
1056
|
|
|
@@ -798,17 +1059,19 @@ class View_data_head(QDialog):
|
|
|
798
1059
|
|
|
799
1060
|
hbox2 = QHBoxLayout()
|
|
800
1061
|
|
|
801
|
-
|
|
1062
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
1063
|
+
|
|
1064
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.reject)
|
|
802
1065
|
hbox2.addWidget(self.pbCancel)
|
|
803
1066
|
|
|
804
|
-
self.pbOK = QPushButton(
|
|
1067
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.accept)
|
|
805
1068
|
hbox2.addWidget(self.pbOK)
|
|
806
1069
|
|
|
807
1070
|
vbox.addLayout(hbox2)
|
|
808
1071
|
|
|
809
1072
|
self.setLayout(vbox)
|
|
810
1073
|
|
|
811
|
-
self.resize(
|
|
1074
|
+
self.resize(800, 640)
|
|
812
1075
|
|
|
813
1076
|
|
|
814
1077
|
class View_explore_project_results(QWidget):
|
|
@@ -816,7 +1079,7 @@ class View_explore_project_results(QWidget):
|
|
|
816
1079
|
widget for visualizing results of explore project
|
|
817
1080
|
"""
|
|
818
1081
|
|
|
819
|
-
double_click_signal =
|
|
1082
|
+
double_click_signal = Signal(str, int)
|
|
820
1083
|
|
|
821
1084
|
def __init__(self):
|
|
822
1085
|
super().__init__()
|
|
@@ -834,14 +1097,12 @@ class View_explore_project_results(QWidget):
|
|
|
834
1097
|
vbox.addWidget(self.tw)
|
|
835
1098
|
|
|
836
1099
|
hbox2 = QHBoxLayout()
|
|
837
|
-
hbox2.
|
|
1100
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
1101
|
+
hbox2.addWidget(QPushButton(cfg.CLOSE, clicked=self.close))
|
|
838
1102
|
|
|
839
1103
|
vbox.addLayout(hbox2)
|
|
840
1104
|
|
|
841
1105
|
self.setLayout(vbox)
|
|
842
1106
|
|
|
843
|
-
# self.resize(540, 640)
|
|
844
|
-
|
|
845
1107
|
def tw_cellDoubleClicked(self, r, c):
|
|
846
|
-
|
|
847
1108
|
self.double_click_signal.emit(self.tw.item(r, 0).text(), int(self.tw.item(r, 1).text()))
|