boris-behav-obs 8.12__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 +28 -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 +141 -65
- boris/config_file.py +58 -67
- boris/connections.py +107 -61
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2373 -1786
- boris/core_qrc.py +15895 -10743
- boris/core_ui.py +943 -798
- boris/db_functions.py +17 -42
- boris/dev.py +109 -8
- boris/dialog.py +482 -236
- boris/duration_widget.py +9 -14
- boris/edit_event.py +61 -31
- boris/edit_event_ui.py +208 -97
- boris/event_operations.py +408 -293
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +184 -223
- boris/export_observation.py +74 -100
- boris/external_processes.py +123 -98
- boris/geometric_measurement.py +644 -290
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +4 -4
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +20 -57
- boris/latency.py +31 -24
- boris/measurement_widget.py +14 -18
- boris/media_file.py +17 -19
- 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 +533 -221
- boris/observation_operations.py +1025 -390
- boris/observation_ui.py +572 -362
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -68
- 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 +25 -33
- 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 +684 -227
- boris/project.py +448 -293
- boris/project_functions.py +671 -238
- 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 -198
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +52 -35
- 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 +627 -236
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +95 -29
- 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.12.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/converters.ui +0 -289
- boris/core.qrc +0 -36
- boris/core.ui +0 -1556
- boris/edit_event.ui +0 -233
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -814
- 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.12.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.12.dist-info/METADATA +0 -128
- boris_behav_obs-8.12.dist-info/RECORD +0 -108
- boris_behav_obs-8.12.dist-info/entry_points.txt +0 -3
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.12.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,120 +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
|
-
#
|
|
109
|
+
# write to $HOME/boris_error.log
|
|
149
110
|
try:
|
|
150
|
-
with open(pl.Path.home() / "
|
|
151
|
-
|
|
152
|
-
f_out.write("-" * 80 + "\n")
|
|
111
|
+
with open(pl.Path.home() / "boris_error.log", "w") as f_error:
|
|
112
|
+
f_error.write(error_text)
|
|
153
113
|
except Exception:
|
|
154
|
-
|
|
155
|
-
logging.critical(f"Cannot write to {pl.Path.home() / 'boris.log'} file")
|
|
114
|
+
logging.critical(f"Impossible to write to {pl.Path.home() / 'boris_error.log'}")
|
|
156
115
|
|
|
157
116
|
# copy to clipboard
|
|
158
117
|
cb = QApplication.clipboard()
|
|
159
|
-
cb.clear(
|
|
160
|
-
cb.setText(error_text
|
|
161
|
-
|
|
162
|
-
error_text: str = error_text.replace("\r\n", "\n").replace("\n", "<br>")
|
|
118
|
+
cb.clear()
|
|
119
|
+
cb.setText(error_text)
|
|
163
120
|
|
|
164
121
|
text: str = (
|
|
165
|
-
f"
|
|
166
|
-
|
|
167
|
-
"
|
|
168
|
-
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
"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}"
|
|
172
128
|
)
|
|
173
129
|
|
|
174
|
-
errorbox =
|
|
175
|
-
|
|
176
|
-
errorbox.
|
|
177
|
-
errorbox.
|
|
178
|
-
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")
|
|
179
136
|
|
|
180
|
-
|
|
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)
|
|
181
144
|
|
|
182
145
|
ret = errorbox.exec_()
|
|
183
146
|
|
|
184
|
-
if ret ==
|
|
147
|
+
if ret == 1: # Abort
|
|
185
148
|
sys.exit(1)
|
|
186
149
|
|
|
187
150
|
|
|
@@ -198,6 +161,319 @@ class Info_widget(QWidget):
|
|
|
198
161
|
self.setLayout(layout)
|
|
199
162
|
|
|
200
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
|
+
|
|
201
477
|
class Video_overlay_dialog(QDialog):
|
|
202
478
|
"""
|
|
203
479
|
dialog to ask image file and position for video overlay
|
|
@@ -252,8 +528,7 @@ class Video_overlay_dialog(QDialog):
|
|
|
252
528
|
self.setLayout(vlayout)
|
|
253
529
|
|
|
254
530
|
def browse(self):
|
|
255
|
-
|
|
256
|
-
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 (*)")
|
|
257
532
|
if file_name:
|
|
258
533
|
self.le_file_path.setText(file_name)
|
|
259
534
|
|
|
@@ -297,7 +572,8 @@ class Input_dialog(QDialog):
|
|
|
297
572
|
dialog for user input. Elements can be:
|
|
298
573
|
checkbox (cb): Tuple(str, str, bool)
|
|
299
574
|
lineedit (le): Tuple(str, str)
|
|
300
|
-
spinbox (
|
|
575
|
+
spinbox (sb)
|
|
576
|
+
doubleSpinbox (dsb)
|
|
301
577
|
items list (il)
|
|
302
578
|
|
|
303
579
|
"""
|
|
@@ -314,7 +590,6 @@ class Input_dialog(QDialog):
|
|
|
314
590
|
|
|
315
591
|
self.elements: dict = {}
|
|
316
592
|
for element in elements_list:
|
|
317
|
-
|
|
318
593
|
if element[0] == "cb": # checkbox
|
|
319
594
|
self.elements[element[1]] = QCheckBox(element[1])
|
|
320
595
|
self.elements[element[1]].setChecked(element[2])
|
|
@@ -347,7 +622,7 @@ class Input_dialog(QDialog):
|
|
|
347
622
|
# 3 - maximum value
|
|
348
623
|
# 4 - step
|
|
349
624
|
# 5 - initial value
|
|
350
|
-
# 6 - number of
|
|
625
|
+
# 6 - number of decimals
|
|
351
626
|
|
|
352
627
|
lb = QLabel(element[1])
|
|
353
628
|
hbox.addWidget(lb)
|
|
@@ -366,15 +641,15 @@ class Input_dialog(QDialog):
|
|
|
366
641
|
self.elements[element[1]] = QComboBox()
|
|
367
642
|
self.elements[element[1]].addItems([x[0] for x in element[2]]) # take first element of tuple
|
|
368
643
|
try:
|
|
369
|
-
self.elements[element[1]].setCurrentIndex(
|
|
370
|
-
|
|
371
|
-
)
|
|
372
|
-
except:
|
|
644
|
+
self.elements[element[1]].setCurrentIndex([idx for idx, x in enumerate(element[2]) if x[1] == "selected"][0])
|
|
645
|
+
except Exception:
|
|
373
646
|
self.elements[element[1]].setCurrentIndex(0)
|
|
374
647
|
hbox.addWidget(self.elements[element[1]])
|
|
375
648
|
|
|
376
649
|
hbox2 = QHBoxLayout()
|
|
377
650
|
|
|
651
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
652
|
+
|
|
378
653
|
self.pbCancel = QPushButton(cfg.CANCEL)
|
|
379
654
|
self.pbCancel.clicked.connect(self.reject)
|
|
380
655
|
hbox2.addWidget(self.pbCancel)
|
|
@@ -388,14 +663,13 @@ class Input_dialog(QDialog):
|
|
|
388
663
|
self.setLayout(hbox)
|
|
389
664
|
|
|
390
665
|
|
|
391
|
-
class
|
|
666
|
+
class Duplicate_items(QDialog):
|
|
392
667
|
"""
|
|
393
|
-
let user show between behaviors that are coded by same key
|
|
668
|
+
let user show between behaviors/subjects that are coded by same key
|
|
394
669
|
"""
|
|
395
670
|
|
|
396
671
|
def __init__(self, text, codes_list):
|
|
397
|
-
|
|
398
|
-
super(DuplicateBehaviorCode, self).__init__()
|
|
672
|
+
super(Duplicate_items, self).__init__()
|
|
399
673
|
|
|
400
674
|
self.setWindowTitle(cfg.programName)
|
|
401
675
|
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
@@ -419,13 +693,17 @@ class DuplicateBehaviorCode(QDialog):
|
|
|
419
693
|
|
|
420
694
|
Vlayout.addWidget(self.lw)
|
|
421
695
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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)
|
|
426
703
|
pbOK.setDefault(True)
|
|
427
|
-
|
|
428
|
-
|
|
704
|
+
hlayout.addWidget(pbOK)
|
|
705
|
+
|
|
706
|
+
Vlayout.addLayout(hlayout)
|
|
429
707
|
|
|
430
708
|
self.setLayout(Vlayout)
|
|
431
709
|
|
|
@@ -439,9 +717,8 @@ class DuplicateBehaviorCode(QDialog):
|
|
|
439
717
|
"""
|
|
440
718
|
if self.lw.selectedItems():
|
|
441
719
|
return self.lw.selectedItems()[0].text()
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
self.accept()
|
|
720
|
+
else:
|
|
721
|
+
return None
|
|
445
722
|
|
|
446
723
|
|
|
447
724
|
class ChooseObservationsToImport(QDialog):
|
|
@@ -450,7 +727,6 @@ class ChooseObservationsToImport(QDialog):
|
|
|
450
727
|
"""
|
|
451
728
|
|
|
452
729
|
def __init__(self, text, observations_list):
|
|
453
|
-
|
|
454
730
|
super(ChooseObservationsToImport, self).__init__()
|
|
455
731
|
|
|
456
732
|
self.setWindowTitle(cfg.programName)
|
|
@@ -498,50 +774,13 @@ class ChooseObservationsToImport(QDialog):
|
|
|
498
774
|
return [item.text() for item in self.lw.selectedItems()]
|
|
499
775
|
|
|
500
776
|
|
|
501
|
-
class Ask_time(QDialog):
|
|
502
|
-
"""
|
|
503
|
-
"Ask time" dialog box using duration widget in duration_widget module
|
|
504
|
-
"""
|
|
505
|
-
|
|
506
|
-
def __init__(self, time_format):
|
|
507
|
-
super().__init__()
|
|
508
|
-
hbox = QVBoxLayout(self)
|
|
509
|
-
self.label = QLabel()
|
|
510
|
-
self.label.setText("Go to time")
|
|
511
|
-
hbox.addWidget(self.label)
|
|
512
|
-
|
|
513
|
-
self.time_widget = duration_widget.Duration_widget()
|
|
514
|
-
if time_format == cfg.HHMMSS:
|
|
515
|
-
self.time_widget.set_format_hhmmss()
|
|
516
|
-
if time_format == cfg.S:
|
|
517
|
-
self.time_widget.set_format_s()
|
|
518
|
-
|
|
519
|
-
hbox.addWidget(self.time_widget)
|
|
520
|
-
|
|
521
|
-
self.pbOK = QPushButton("OK")
|
|
522
|
-
self.pbOK.clicked.connect(self.accept)
|
|
523
|
-
self.pbOK.setDefault(True)
|
|
524
|
-
|
|
525
|
-
self.pbCancel = QPushButton("Cancel")
|
|
526
|
-
self.pbCancel.clicked.connect(self.reject)
|
|
527
|
-
|
|
528
|
-
self.hbox2 = QHBoxLayout(self)
|
|
529
|
-
self.hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
530
|
-
self.hbox2.addWidget(self.pbCancel)
|
|
531
|
-
self.hbox2.addWidget(self.pbOK)
|
|
532
|
-
hbox.addLayout(self.hbox2)
|
|
533
|
-
self.setLayout(hbox)
|
|
534
|
-
self.setWindowTitle("Time")
|
|
535
|
-
|
|
536
|
-
|
|
537
777
|
class FindInEvents(QWidget):
|
|
538
778
|
"""
|
|
539
779
|
"find in events" dialog box
|
|
540
780
|
"""
|
|
541
781
|
|
|
542
|
-
clickSignal =
|
|
543
|
-
|
|
544
|
-
currentIdx = -1
|
|
782
|
+
clickSignal = Signal(str)
|
|
783
|
+
currentIdx: int = -1
|
|
545
784
|
|
|
546
785
|
def __init__(self):
|
|
547
786
|
super().__init__()
|
|
@@ -584,9 +823,10 @@ class FindInEvents(QWidget):
|
|
|
584
823
|
hbox.addWidget(self.lb_message)
|
|
585
824
|
|
|
586
825
|
hbox2 = QHBoxLayout()
|
|
826
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
587
827
|
self.pbOK = QPushButton("Find")
|
|
588
828
|
self.pbOK.clicked.connect(lambda: self.click("FIND"))
|
|
589
|
-
self.pbCancel = QPushButton(
|
|
829
|
+
self.pbCancel = QPushButton(cfg.CLOSE)
|
|
590
830
|
self.pbCancel.clicked.connect(lambda: self.click("CLOSE"))
|
|
591
831
|
hbox2.addWidget(self.pbCancel)
|
|
592
832
|
hbox2.addWidget(self.pbOK)
|
|
@@ -603,10 +843,7 @@ class FindReplaceEvents(QWidget):
|
|
|
603
843
|
"find replace events" dialog box
|
|
604
844
|
"""
|
|
605
845
|
|
|
606
|
-
clickSignal =
|
|
607
|
-
"""
|
|
608
|
-
sendEventSignal = pyqtSignal(QEvent)
|
|
609
|
-
"""
|
|
846
|
+
clickSignal = Signal(str)
|
|
610
847
|
|
|
611
848
|
def __init__(self):
|
|
612
849
|
super().__init__()
|
|
@@ -615,21 +852,9 @@ class FindReplaceEvents(QWidget):
|
|
|
615
852
|
|
|
616
853
|
hbox = QVBoxLayout()
|
|
617
854
|
|
|
618
|
-
self.
|
|
619
|
-
self.
|
|
620
|
-
hbox.addWidget(self.
|
|
621
|
-
|
|
622
|
-
self.cbBehavior = QCheckBox("Behavior")
|
|
623
|
-
self.cbBehavior.setChecked(False)
|
|
624
|
-
hbox.addWidget(self.cbBehavior)
|
|
625
|
-
|
|
626
|
-
self.cbModifier = QCheckBox("Modifiers")
|
|
627
|
-
self.cbModifier.setChecked(False)
|
|
628
|
-
hbox.addWidget(self.cbModifier)
|
|
629
|
-
|
|
630
|
-
self.cbComment = QCheckBox("Comment")
|
|
631
|
-
self.cbComment.setChecked(False)
|
|
632
|
-
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)
|
|
633
858
|
|
|
634
859
|
self.lbFind = QLabel("Find")
|
|
635
860
|
hbox.addWidget(self.lbFind)
|
|
@@ -643,7 +868,7 @@ class FindReplaceEvents(QWidget):
|
|
|
643
868
|
self.replaceText = QLineEdit()
|
|
644
869
|
hbox.addWidget(self.replaceText)
|
|
645
870
|
|
|
646
|
-
self.cbFindInSelectedEvents = QCheckBox("Find/Replace in selected events")
|
|
871
|
+
self.cbFindInSelectedEvents = QCheckBox("Find/Replace only in selected events")
|
|
647
872
|
self.cbFindInSelectedEvents.setChecked(False)
|
|
648
873
|
hbox.addWidget(self.cbFindInSelectedEvents)
|
|
649
874
|
|
|
@@ -653,6 +878,8 @@ class FindReplaceEvents(QWidget):
|
|
|
653
878
|
|
|
654
879
|
hbox2 = QHBoxLayout()
|
|
655
880
|
|
|
881
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
882
|
+
|
|
656
883
|
self.pbCancel = QPushButton(cfg.CANCEL, clicked=lambda: self.click("CANCEL"))
|
|
657
884
|
hbox2.addWidget(self.pbCancel)
|
|
658
885
|
|
|
@@ -663,68 +890,76 @@ class FindReplaceEvents(QWidget):
|
|
|
663
890
|
hbox2.addWidget(self.pbFindReplaceAll)
|
|
664
891
|
|
|
665
892
|
hbox.addLayout(hbox2)
|
|
893
|
+
|
|
894
|
+
self.lb_results = QLabel(" ")
|
|
895
|
+
hbox.addWidget(self.lb_results)
|
|
896
|
+
|
|
666
897
|
self.setLayout(hbox)
|
|
667
898
|
|
|
668
899
|
def click(self, msg):
|
|
669
900
|
self.clickSignal.emit(msg)
|
|
670
901
|
|
|
671
902
|
|
|
672
|
-
|
|
673
|
-
class explore_project_dialog(QDialog):
|
|
903
|
+
class Results_dialog(QDialog):
|
|
674
904
|
"""
|
|
675
|
-
|
|
905
|
+
widget for visualizing text output in HTML
|
|
676
906
|
"""
|
|
677
907
|
|
|
678
908
|
def __init__(self):
|
|
679
909
|
super().__init__()
|
|
680
910
|
|
|
681
|
-
self.
|
|
682
|
-
|
|
683
|
-
hbox = QVBoxLayout()
|
|
684
|
-
|
|
685
|
-
hbox.addWidget(QLabel("Search in all observations"))
|
|
911
|
+
self.dataset = False
|
|
686
912
|
|
|
687
|
-
self.
|
|
688
|
-
hbox.addWidget(self.lb_subject)
|
|
689
|
-
self.find_subject = QLineEdit()
|
|
690
|
-
hbox.addWidget(self.find_subject)
|
|
913
|
+
self.setWindowTitle("")
|
|
691
914
|
|
|
692
|
-
|
|
693
|
-
hbox.addWidget(self.lb_behav)
|
|
694
|
-
self.find_behavior = QLineEdit()
|
|
695
|
-
hbox.addWidget(self.find_behavior)
|
|
915
|
+
hbox = QVBoxLayout()
|
|
696
916
|
|
|
697
|
-
self.
|
|
698
|
-
hbox.addWidget(self.
|
|
699
|
-
self.find_modifier = QLineEdit()
|
|
700
|
-
hbox.addWidget(self.find_modifier)
|
|
917
|
+
self.lb = QLabel("")
|
|
918
|
+
hbox.addWidget(self.lb)
|
|
701
919
|
|
|
702
|
-
self.
|
|
703
|
-
|
|
704
|
-
self.
|
|
705
|
-
hbox.addWidget(self.find_comment)
|
|
920
|
+
self.ptText = QPlainTextEdit()
|
|
921
|
+
self.ptText.setReadOnly(True)
|
|
922
|
+
hbox.addWidget(self.ptText)
|
|
706
923
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
hbox.addWidget(self.cb_case_sensitive)
|
|
924
|
+
hbox2 = QHBoxLayout()
|
|
925
|
+
hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
710
926
|
|
|
711
|
-
self.
|
|
712
|
-
|
|
927
|
+
self.pbSave = QPushButton("Save results", clicked=self.save_results)
|
|
928
|
+
hbox2.addWidget(self.pbSave)
|
|
713
929
|
|
|
714
|
-
|
|
715
|
-
self.pbOK = QPushButton("Find")
|
|
716
|
-
self.pbOK.clicked.connect(self.accept)
|
|
717
|
-
self.pbCancel = QPushButton("Cancel")
|
|
718
|
-
self.pbCancel.clicked.connect(self.reject)
|
|
930
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.reject)
|
|
719
931
|
hbox2.addWidget(self.pbCancel)
|
|
932
|
+
self.pbCancel.setVisible(False)
|
|
933
|
+
|
|
934
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.accept)
|
|
720
935
|
hbox2.addWidget(self.pbOK)
|
|
721
|
-
hbox.addLayout(hbox2)
|
|
722
936
|
|
|
937
|
+
hbox.addLayout(hbox2)
|
|
723
938
|
self.setLayout(hbox)
|
|
724
|
-
'''
|
|
725
939
|
|
|
940
|
+
self.resize(800, 640)
|
|
726
941
|
|
|
727
|
-
|
|
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):
|
|
728
963
|
"""
|
|
729
964
|
widget for visualizing text output
|
|
730
965
|
"""
|
|
@@ -746,32 +981,35 @@ class Results_dialog(QDialog):
|
|
|
746
981
|
hbox.addWidget(self.ptText)
|
|
747
982
|
|
|
748
983
|
hbox2 = QHBoxLayout()
|
|
984
|
+
hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
985
|
+
|
|
749
986
|
self.pbSave = QPushButton("Save results", clicked=self.save_results)
|
|
750
987
|
hbox2.addWidget(self.pbSave)
|
|
751
988
|
|
|
752
|
-
|
|
989
|
+
self.pb1 = QPushButton("1", clicked=lambda: self.done_(1))
|
|
990
|
+
hbox2.addWidget(self.pb1)
|
|
753
991
|
|
|
754
|
-
self.
|
|
755
|
-
hbox2.addWidget(self.
|
|
756
|
-
self.pbCancel.setVisible(False)
|
|
992
|
+
self.pb2 = QPushButton("2", clicked=lambda: self.done_(2))
|
|
993
|
+
hbox2.addWidget(self.pb2)
|
|
757
994
|
|
|
758
|
-
self.
|
|
759
|
-
|
|
760
|
-
hbox2.addWidget(self.pbOK)
|
|
995
|
+
self.pb3 = QPushButton("3", clicked=lambda: self.done_(3))
|
|
996
|
+
hbox2.addWidget(self.pb3)
|
|
761
997
|
|
|
762
998
|
hbox.addLayout(hbox2)
|
|
763
999
|
self.setLayout(hbox)
|
|
764
1000
|
|
|
765
1001
|
self.resize(800, 640)
|
|
766
1002
|
|
|
1003
|
+
def done_(self, status):
|
|
1004
|
+
self.done(status)
|
|
1005
|
+
|
|
767
1006
|
def save_results(self):
|
|
768
1007
|
"""
|
|
769
1008
|
save content of self.ptText
|
|
770
1009
|
"""
|
|
771
1010
|
|
|
772
1011
|
if not self.dataset:
|
|
773
|
-
|
|
774
|
-
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 (*)")
|
|
775
1013
|
|
|
776
1014
|
if not file_name:
|
|
777
1015
|
return
|
|
@@ -785,7 +1023,7 @@ class Results_dialog(QDialog):
|
|
|
785
1023
|
self.done(cfg.SAVE_DATASET)
|
|
786
1024
|
|
|
787
1025
|
|
|
788
|
-
class
|
|
1026
|
+
class View_data(QDialog):
|
|
789
1027
|
"""
|
|
790
1028
|
widget for visualizing rows of data file
|
|
791
1029
|
"""
|
|
@@ -801,8 +1039,18 @@ class View_data_head(QDialog):
|
|
|
801
1039
|
vbox.addWidget(self.lb)
|
|
802
1040
|
|
|
803
1041
|
self.tw = QTableWidget()
|
|
1042
|
+
self.tw.verticalHeader().hide()
|
|
804
1043
|
vbox.addWidget(self.tw)
|
|
805
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
|
+
|
|
806
1054
|
self.label = QLabel("Enter the column indices to plot (time, value) separated by comma (,)")
|
|
807
1055
|
vbox.addWidget(self.label)
|
|
808
1056
|
|
|
@@ -813,17 +1061,17 @@ class View_data_head(QDialog):
|
|
|
813
1061
|
|
|
814
1062
|
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
815
1063
|
|
|
816
|
-
self.pbCancel = QPushButton(
|
|
1064
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.reject)
|
|
817
1065
|
hbox2.addWidget(self.pbCancel)
|
|
818
1066
|
|
|
819
|
-
self.pbOK = QPushButton(
|
|
1067
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.accept)
|
|
820
1068
|
hbox2.addWidget(self.pbOK)
|
|
821
1069
|
|
|
822
1070
|
vbox.addLayout(hbox2)
|
|
823
1071
|
|
|
824
1072
|
self.setLayout(vbox)
|
|
825
1073
|
|
|
826
|
-
self.resize(
|
|
1074
|
+
self.resize(800, 640)
|
|
827
1075
|
|
|
828
1076
|
|
|
829
1077
|
class View_explore_project_results(QWidget):
|
|
@@ -831,7 +1079,7 @@ class View_explore_project_results(QWidget):
|
|
|
831
1079
|
widget for visualizing results of explore project
|
|
832
1080
|
"""
|
|
833
1081
|
|
|
834
|
-
double_click_signal =
|
|
1082
|
+
double_click_signal = Signal(str, int)
|
|
835
1083
|
|
|
836
1084
|
def __init__(self):
|
|
837
1085
|
super().__init__()
|
|
@@ -849,14 +1097,12 @@ class View_explore_project_results(QWidget):
|
|
|
849
1097
|
vbox.addWidget(self.tw)
|
|
850
1098
|
|
|
851
1099
|
hbox2 = QHBoxLayout()
|
|
852
|
-
hbox2.
|
|
1100
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
1101
|
+
hbox2.addWidget(QPushButton(cfg.CLOSE, clicked=self.close))
|
|
853
1102
|
|
|
854
1103
|
vbox.addLayout(hbox2)
|
|
855
1104
|
|
|
856
1105
|
self.setLayout(vbox)
|
|
857
1106
|
|
|
858
|
-
# self.resize(540, 640)
|
|
859
|
-
|
|
860
1107
|
def tw_cellDoubleClicked(self, r, c):
|
|
861
|
-
|
|
862
1108
|
self.double_click_signal.emit(self.tw.item(r, 0).text(), int(self.tw.item(r, 1).text()))
|