boris-behav-obs 8.16.5__py3-none-any.whl → 9.7.12__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/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +28 -40
- boris/add_modifier.py +88 -80
- boris/add_modifier_ui.py +266 -144
- boris/advanced_event_filtering.py +23 -29
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_export_to_feral.py +225 -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 +235 -236
- 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 +19 -36
- boris/config.py +109 -50
- boris/config_file.py +58 -67
- boris/connections.py +105 -58
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2174 -1303
- boris/core_qrc.py +15892 -10829
- boris/core_ui.py +941 -806
- boris/db_functions.py +17 -42
- boris/dev.py +27 -7
- boris/dialog.py +461 -242
- boris/duration_widget.py +9 -14
- boris/edit_event.py +61 -31
- boris/edit_event_ui.py +208 -97
- boris/event_operations.py +405 -281
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +180 -203
- boris/export_observation.py +60 -73
- boris/external_processes.py +123 -98
- boris/geometric_measurement.py +427 -218
- 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 +16 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv2.py +128 -35
- boris/observation.py +501 -211
- boris/observation_operations.py +1037 -393
- boris/observation_ui.py +573 -363
- boris/observations_list.py +51 -58
- boris/otx_parser.py +74 -68
- boris/param_panel.py +45 -59
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +91 -56
- boris/plot_data_module.py +20 -53
- boris/plot_events.py +56 -153
- boris/plot_events_rt.py +16 -30
- boris/plot_spectrogram_rt.py +83 -56
- boris/plot_waveform_rt.py +27 -49
- boris/plugins.py +468 -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 +307 -123
- boris/preferences_ui.py +686 -227
- boris/project.py +294 -271
- boris/project_functions.py +626 -537
- boris/project_import_export.py +204 -213
- boris/project_ui.py +673 -441
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +62 -90
- boris/select_observations.py +19 -197
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +51 -33
- boris/subjects_pad.py +7 -9
- boris/synthetic_time_budget.py +42 -26
- boris/time_budget_functions.py +169 -169
- boris/time_budget_widget.py +77 -89
- boris/transitions.py +41 -41
- boris/utilities.py +594 -226
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +86 -28
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +240 -136
- boris_behav_obs-9.7.12.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.12.dist-info/RECORD +110 -0
- {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.12.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.12.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 -37
- boris/core.ui +0 -1571
- boris/edit_event.ui +0 -233
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -982
- boris/observation.ui +0 -814
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1074
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.16.5.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.16.5.dist-info/METADATA +0 -134
- boris_behav_obs-8.16.5.dist-info/RECORD +0 -107
- boris_behav_obs-8.16.5.dist-info/entry_points.txt +0 -2
- {boris → boris_behav_obs-9.7.12.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.12.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,17 +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
|
-
import
|
|
30
|
-
from
|
|
29
|
+
import sys
|
|
30
|
+
import traceback
|
|
31
|
+
from typing import Union
|
|
31
32
|
|
|
32
|
-
from
|
|
33
|
-
from
|
|
33
|
+
from PySide6.QtCore import Qt, Signal, qVersion, QRect, QTime, QDateTime, QSize
|
|
34
|
+
from PySide6.QtWidgets import (
|
|
34
35
|
QApplication,
|
|
35
36
|
QAbstractItemView,
|
|
36
37
|
QCheckBox,
|
|
@@ -53,16 +54,32 @@ from PyQt5.QtWidgets import (
|
|
|
53
54
|
QTableWidget,
|
|
54
55
|
QVBoxLayout,
|
|
55
56
|
QWidget,
|
|
57
|
+
QDateTimeEdit,
|
|
58
|
+
QTimeEdit,
|
|
59
|
+
QAbstractSpinBox,
|
|
60
|
+
QRadioButton,
|
|
61
|
+
QStackedWidget,
|
|
62
|
+
QFrame,
|
|
56
63
|
)
|
|
57
|
-
from
|
|
64
|
+
from PySide6.QtGui import QFont, QTextCursor
|
|
58
65
|
|
|
59
66
|
from . import config as cfg
|
|
60
|
-
from . import duration_widget
|
|
61
67
|
from . import version
|
|
68
|
+
from . import utilities as util
|
|
62
69
|
|
|
63
70
|
|
|
64
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
|
|
65
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
|
+
"""
|
|
66
83
|
message = QMessageBox()
|
|
67
84
|
message.setWindowTitle(title)
|
|
68
85
|
message.setText(text)
|
|
@@ -70,133 +87,61 @@ def MessageDialog(title: str, text: str, buttons: tuple) -> str:
|
|
|
70
87
|
for button in buttons:
|
|
71
88
|
message.addButton(button, QMessageBox.YesRole)
|
|
72
89
|
|
|
73
|
-
|
|
74
|
-
message.
|
|
90
|
+
message.setWindowFlags(message.windowFlags() | Qt.WindowStaysOnTopHint)
|
|
91
|
+
message.exec()
|
|
75
92
|
return message.clickedButton().text()
|
|
76
93
|
|
|
77
94
|
|
|
78
|
-
'''
|
|
79
|
-
def error_message_box(task, error_type, error_file_name, error_lineno):
|
|
80
|
-
# do NOT use this function directly, use error_message function
|
|
81
|
-
"""
|
|
82
|
-
show a critical dialog
|
|
83
|
-
|
|
84
|
-
"""
|
|
85
|
-
QMessageBox.critical(None, cfg.programName, (f"BORIS version: {version.__version__}<br>"
|
|
86
|
-
f"An error occured during the execution of <b>{task}</b>.<br>"
|
|
87
|
-
f"Error: {error_type}<br>"
|
|
88
|
-
f"in {error_file_name} "
|
|
89
|
-
f"at line # {error_lineno}<br><br>"
|
|
90
|
-
"to improve the software please report this problem at:<br>"
|
|
91
|
-
'<a href="https://github.com/olivierfriard/BORIS/issues">'
|
|
92
|
-
'https://github.com/olivierfriard/BORIS/issues</a><br>'
|
|
93
|
-
"or by email (See the About page on the BORIS web site.<br><br>"
|
|
94
|
-
"Thank you for your collaboration!"))
|
|
95
|
-
'''
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def error_message_box(error_traceback):
|
|
99
|
-
# do NOT use this function directly, use error_message function
|
|
100
|
-
"""
|
|
101
|
-
show a critical dialog
|
|
102
|
-
|
|
103
|
-
"""
|
|
104
|
-
QMessageBox.critical(
|
|
105
|
-
None,
|
|
106
|
-
cfg.programName,
|
|
107
|
-
(
|
|
108
|
-
f"BORIS version: {version.__version__}<br><br>"
|
|
109
|
-
f"<b>An error has occured</b>:<br>"
|
|
110
|
-
f"{error_traceback}<br><br>"
|
|
111
|
-
"to improve the software please report this problem at:<br>"
|
|
112
|
-
'<a href="https://github.com/olivierfriard/BORIS/issues">'
|
|
113
|
-
"https://github.com/olivierfriard/BORIS/issues</a><br>"
|
|
114
|
-
"or by email (See the About page on the BORIS web site.<br><br>"
|
|
115
|
-
"Thank you for your collaboration!"
|
|
116
|
-
),
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def error_message() -> None:
|
|
121
|
-
"""
|
|
122
|
-
Show details about the error in a message box
|
|
123
|
-
write entry to log as CRITICAL
|
|
124
|
-
"""
|
|
125
|
-
|
|
126
|
-
error_traceback = traceback.format_exc().replace("Traceback (most recent call last):", "").replace("\n", " ")
|
|
127
|
-
|
|
128
|
-
logging.critical(error_traceback)
|
|
129
|
-
error_message_box(error_traceback)
|
|
130
|
-
|
|
131
|
-
|
|
132
95
|
def global_error_message(exception_type, exception_value, traceback_object):
|
|
133
96
|
"""
|
|
134
|
-
|
|
97
|
+
Global error management
|
|
135
98
|
save error using loggin.critical and stdout
|
|
136
99
|
"""
|
|
137
100
|
|
|
138
|
-
error_text
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
f"CPU: {platform.uname().machine} {platform.uname().processor}\n"
|
|
142
|
-
f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})\n"
|
|
143
|
-
f"Qt {QT_VERSION_STR} - PyQt {PYQT_VERSION_STR}\n"
|
|
144
|
-
f"{dt.datetime.now():%Y-%m-%d %H:%M}\n\n"
|
|
145
|
-
)
|
|
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"
|
|
146
104
|
error_text += "".join(traceback.format_exception(exception_type, exception_value, traceback_object))
|
|
147
105
|
|
|
148
|
-
#
|
|
149
|
-
if sys.platform.startswith("win"):
|
|
150
|
-
systeminfo = subprocess.getoutput("systeminfo")
|
|
151
|
-
if sys.platform.startswith("linux"):
|
|
152
|
-
systeminfo = subprocess.getoutput("cat /etc/*rel*; uname -a")
|
|
153
|
-
|
|
106
|
+
# write to stdout
|
|
154
107
|
logging.critical(error_text)
|
|
155
|
-
|
|
108
|
+
|
|
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'}")
|
|
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
|
-
"to improve the software please report this problem at
|
|
167
|
-
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
"Thank you for your collaboration!"
|
|
171
|
-
"<br><br>"
|
|
172
|
-
"<pre>"
|
|
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"
|
|
173
127
|
f"{error_text}"
|
|
174
|
-
"<hr>"
|
|
175
|
-
"<b>System info</b><br>"
|
|
176
|
-
f"{systeminfo}"
|
|
177
|
-
"</pre>"
|
|
178
128
|
)
|
|
179
129
|
|
|
180
130
|
errorbox = Results_dialog()
|
|
131
|
+
|
|
181
132
|
errorbox.setWindowTitle("BORIS - An error occured")
|
|
182
133
|
errorbox.pbOK.setText("Abort")
|
|
183
134
|
errorbox.pbCancel.setVisible(True)
|
|
184
135
|
errorbox.pbCancel.setText("Ignore and try to continue")
|
|
185
136
|
|
|
137
|
+
font = QFont()
|
|
138
|
+
font.setFamily("monospace")
|
|
139
|
+
errorbox.ptText.setFont(font)
|
|
186
140
|
errorbox.ptText.clear()
|
|
187
|
-
errorbox.ptText.
|
|
141
|
+
errorbox.ptText.appendPlainText(text)
|
|
188
142
|
|
|
189
143
|
errorbox.ptText.moveCursor(QTextCursor.Start)
|
|
190
144
|
|
|
191
|
-
"""
|
|
192
|
-
errorbox = QMessageBox()
|
|
193
|
-
errorbox.setWindowTitle("BORIS error occured")
|
|
194
|
-
errorbox.setText(text)
|
|
195
|
-
errorbox.setTextFormat(Qt.RichText)
|
|
196
|
-
errorbox.setStandardButtons(QMessageBox.Abort)
|
|
197
|
-
_ = errorbox.addButton("Ignore and try to continue", QMessageBox.RejectRole)
|
|
198
|
-
"""
|
|
199
|
-
|
|
200
145
|
ret = errorbox.exec_()
|
|
201
146
|
|
|
202
147
|
if ret == 1: # Abort
|
|
@@ -216,6 +161,319 @@ class Info_widget(QWidget):
|
|
|
216
161
|
self.setLayout(layout)
|
|
217
162
|
|
|
218
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
|
+
|
|
219
477
|
class Video_overlay_dialog(QDialog):
|
|
220
478
|
"""
|
|
221
479
|
dialog to ask image file and position for video overlay
|
|
@@ -270,8 +528,7 @@ class Video_overlay_dialog(QDialog):
|
|
|
270
528
|
self.setLayout(vlayout)
|
|
271
529
|
|
|
272
530
|
def browse(self):
|
|
273
|
-
|
|
274
|
-
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 (*)")
|
|
275
532
|
if file_name:
|
|
276
533
|
self.le_file_path.setText(file_name)
|
|
277
534
|
|
|
@@ -315,7 +572,8 @@ class Input_dialog(QDialog):
|
|
|
315
572
|
dialog for user input. Elements can be:
|
|
316
573
|
checkbox (cb): Tuple(str, str, bool)
|
|
317
574
|
lineedit (le): Tuple(str, str)
|
|
318
|
-
spinbox (
|
|
575
|
+
spinbox (sb)
|
|
576
|
+
doubleSpinbox (dsb)
|
|
319
577
|
items list (il)
|
|
320
578
|
|
|
321
579
|
"""
|
|
@@ -332,7 +590,6 @@ class Input_dialog(QDialog):
|
|
|
332
590
|
|
|
333
591
|
self.elements: dict = {}
|
|
334
592
|
for element in elements_list:
|
|
335
|
-
|
|
336
593
|
if element[0] == "cb": # checkbox
|
|
337
594
|
self.elements[element[1]] = QCheckBox(element[1])
|
|
338
595
|
self.elements[element[1]].setChecked(element[2])
|
|
@@ -365,7 +622,7 @@ class Input_dialog(QDialog):
|
|
|
365
622
|
# 3 - maximum value
|
|
366
623
|
# 4 - step
|
|
367
624
|
# 5 - initial value
|
|
368
|
-
# 6 - number of
|
|
625
|
+
# 6 - number of decimals
|
|
369
626
|
|
|
370
627
|
lb = QLabel(element[1])
|
|
371
628
|
hbox.addWidget(lb)
|
|
@@ -384,15 +641,15 @@ class Input_dialog(QDialog):
|
|
|
384
641
|
self.elements[element[1]] = QComboBox()
|
|
385
642
|
self.elements[element[1]].addItems([x[0] for x in element[2]]) # take first element of tuple
|
|
386
643
|
try:
|
|
387
|
-
self.elements[element[1]].setCurrentIndex(
|
|
388
|
-
|
|
389
|
-
)
|
|
390
|
-
except:
|
|
644
|
+
self.elements[element[1]].setCurrentIndex([idx for idx, x in enumerate(element[2]) if x[1] == "selected"][0])
|
|
645
|
+
except Exception:
|
|
391
646
|
self.elements[element[1]].setCurrentIndex(0)
|
|
392
647
|
hbox.addWidget(self.elements[element[1]])
|
|
393
648
|
|
|
394
649
|
hbox2 = QHBoxLayout()
|
|
395
650
|
|
|
651
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
652
|
+
|
|
396
653
|
self.pbCancel = QPushButton(cfg.CANCEL)
|
|
397
654
|
self.pbCancel.clicked.connect(self.reject)
|
|
398
655
|
hbox2.addWidget(self.pbCancel)
|
|
@@ -406,14 +663,13 @@ class Input_dialog(QDialog):
|
|
|
406
663
|
self.setLayout(hbox)
|
|
407
664
|
|
|
408
665
|
|
|
409
|
-
class
|
|
666
|
+
class Duplicate_items(QDialog):
|
|
410
667
|
"""
|
|
411
|
-
let user show between behaviors that are coded by same key
|
|
668
|
+
let user show between behaviors/subjects that are coded by same key
|
|
412
669
|
"""
|
|
413
670
|
|
|
414
671
|
def __init__(self, text, codes_list):
|
|
415
|
-
|
|
416
|
-
super(DuplicateBehaviorCode, self).__init__()
|
|
672
|
+
super(Duplicate_items, self).__init__()
|
|
417
673
|
|
|
418
674
|
self.setWindowTitle(cfg.programName)
|
|
419
675
|
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
@@ -437,13 +693,17 @@ class DuplicateBehaviorCode(QDialog):
|
|
|
437
693
|
|
|
438
694
|
Vlayout.addWidget(self.lw)
|
|
439
695
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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)
|
|
444
703
|
pbOK.setDefault(True)
|
|
445
|
-
|
|
446
|
-
|
|
704
|
+
hlayout.addWidget(pbOK)
|
|
705
|
+
|
|
706
|
+
Vlayout.addLayout(hlayout)
|
|
447
707
|
|
|
448
708
|
self.setLayout(Vlayout)
|
|
449
709
|
|
|
@@ -457,9 +717,8 @@ class DuplicateBehaviorCode(QDialog):
|
|
|
457
717
|
"""
|
|
458
718
|
if self.lw.selectedItems():
|
|
459
719
|
return self.lw.selectedItems()[0].text()
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
self.accept()
|
|
720
|
+
else:
|
|
721
|
+
return None
|
|
463
722
|
|
|
464
723
|
|
|
465
724
|
class ChooseObservationsToImport(QDialog):
|
|
@@ -468,7 +727,6 @@ class ChooseObservationsToImport(QDialog):
|
|
|
468
727
|
"""
|
|
469
728
|
|
|
470
729
|
def __init__(self, text, observations_list):
|
|
471
|
-
|
|
472
730
|
super(ChooseObservationsToImport, self).__init__()
|
|
473
731
|
|
|
474
732
|
self.setWindowTitle(cfg.programName)
|
|
@@ -516,50 +774,13 @@ class ChooseObservationsToImport(QDialog):
|
|
|
516
774
|
return [item.text() for item in self.lw.selectedItems()]
|
|
517
775
|
|
|
518
776
|
|
|
519
|
-
class Ask_time(QDialog):
|
|
520
|
-
"""
|
|
521
|
-
"Ask time" dialog box using duration widget in duration_widget module
|
|
522
|
-
"""
|
|
523
|
-
|
|
524
|
-
def __init__(self, time_format):
|
|
525
|
-
super().__init__()
|
|
526
|
-
hbox = QVBoxLayout(self)
|
|
527
|
-
self.label = QLabel()
|
|
528
|
-
self.label.setText("Go to time")
|
|
529
|
-
hbox.addWidget(self.label)
|
|
530
|
-
|
|
531
|
-
self.time_widget = duration_widget.Duration_widget()
|
|
532
|
-
if time_format == cfg.HHMMSS:
|
|
533
|
-
self.time_widget.set_format_hhmmss()
|
|
534
|
-
if time_format == cfg.S:
|
|
535
|
-
self.time_widget.set_format_s()
|
|
536
|
-
|
|
537
|
-
hbox.addWidget(self.time_widget)
|
|
538
|
-
|
|
539
|
-
self.pbOK = QPushButton("OK")
|
|
540
|
-
self.pbOK.clicked.connect(self.accept)
|
|
541
|
-
self.pbOK.setDefault(True)
|
|
542
|
-
|
|
543
|
-
self.pbCancel = QPushButton("Cancel")
|
|
544
|
-
self.pbCancel.clicked.connect(self.reject)
|
|
545
|
-
|
|
546
|
-
self.hbox2 = QHBoxLayout(self)
|
|
547
|
-
self.hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
548
|
-
self.hbox2.addWidget(self.pbCancel)
|
|
549
|
-
self.hbox2.addWidget(self.pbOK)
|
|
550
|
-
hbox.addLayout(self.hbox2)
|
|
551
|
-
self.setLayout(hbox)
|
|
552
|
-
self.setWindowTitle("Time")
|
|
553
|
-
|
|
554
|
-
|
|
555
777
|
class FindInEvents(QWidget):
|
|
556
778
|
"""
|
|
557
779
|
"find in events" dialog box
|
|
558
780
|
"""
|
|
559
781
|
|
|
560
|
-
clickSignal =
|
|
561
|
-
|
|
562
|
-
currentIdx = -1
|
|
782
|
+
clickSignal = Signal(str)
|
|
783
|
+
currentIdx: int = -1
|
|
563
784
|
|
|
564
785
|
def __init__(self):
|
|
565
786
|
super().__init__()
|
|
@@ -602,9 +823,10 @@ class FindInEvents(QWidget):
|
|
|
602
823
|
hbox.addWidget(self.lb_message)
|
|
603
824
|
|
|
604
825
|
hbox2 = QHBoxLayout()
|
|
826
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
605
827
|
self.pbOK = QPushButton("Find")
|
|
606
828
|
self.pbOK.clicked.connect(lambda: self.click("FIND"))
|
|
607
|
-
self.pbCancel = QPushButton(
|
|
829
|
+
self.pbCancel = QPushButton(cfg.CLOSE)
|
|
608
830
|
self.pbCancel.clicked.connect(lambda: self.click("CLOSE"))
|
|
609
831
|
hbox2.addWidget(self.pbCancel)
|
|
610
832
|
hbox2.addWidget(self.pbOK)
|
|
@@ -621,10 +843,7 @@ class FindReplaceEvents(QWidget):
|
|
|
621
843
|
"find replace events" dialog box
|
|
622
844
|
"""
|
|
623
845
|
|
|
624
|
-
clickSignal =
|
|
625
|
-
"""
|
|
626
|
-
sendEventSignal = pyqtSignal(QEvent)
|
|
627
|
-
"""
|
|
846
|
+
clickSignal = Signal(str)
|
|
628
847
|
|
|
629
848
|
def __init__(self):
|
|
630
849
|
super().__init__()
|
|
@@ -633,21 +852,9 @@ class FindReplaceEvents(QWidget):
|
|
|
633
852
|
|
|
634
853
|
hbox = QVBoxLayout()
|
|
635
854
|
|
|
636
|
-
self.
|
|
637
|
-
self.
|
|
638
|
-
hbox.addWidget(self.
|
|
639
|
-
|
|
640
|
-
self.cbBehavior = QCheckBox("Behavior")
|
|
641
|
-
self.cbBehavior.setChecked(False)
|
|
642
|
-
hbox.addWidget(self.cbBehavior)
|
|
643
|
-
|
|
644
|
-
self.cbModifier = QCheckBox("Modifiers")
|
|
645
|
-
self.cbModifier.setChecked(False)
|
|
646
|
-
hbox.addWidget(self.cbModifier)
|
|
647
|
-
|
|
648
|
-
self.cbComment = QCheckBox("Comment")
|
|
649
|
-
self.cbComment.setChecked(False)
|
|
650
|
-
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)
|
|
651
858
|
|
|
652
859
|
self.lbFind = QLabel("Find")
|
|
653
860
|
hbox.addWidget(self.lbFind)
|
|
@@ -661,7 +868,7 @@ class FindReplaceEvents(QWidget):
|
|
|
661
868
|
self.replaceText = QLineEdit()
|
|
662
869
|
hbox.addWidget(self.replaceText)
|
|
663
870
|
|
|
664
|
-
self.cbFindInSelectedEvents = QCheckBox("Find/Replace in selected events")
|
|
871
|
+
self.cbFindInSelectedEvents = QCheckBox("Find/Replace only in selected events")
|
|
665
872
|
self.cbFindInSelectedEvents.setChecked(False)
|
|
666
873
|
hbox.addWidget(self.cbFindInSelectedEvents)
|
|
667
874
|
|
|
@@ -671,6 +878,8 @@ class FindReplaceEvents(QWidget):
|
|
|
671
878
|
|
|
672
879
|
hbox2 = QHBoxLayout()
|
|
673
880
|
|
|
881
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
882
|
+
|
|
674
883
|
self.pbCancel = QPushButton(cfg.CANCEL, clicked=lambda: self.click("CANCEL"))
|
|
675
884
|
hbox2.addWidget(self.pbCancel)
|
|
676
885
|
|
|
@@ -681,68 +890,76 @@ class FindReplaceEvents(QWidget):
|
|
|
681
890
|
hbox2.addWidget(self.pbFindReplaceAll)
|
|
682
891
|
|
|
683
892
|
hbox.addLayout(hbox2)
|
|
893
|
+
|
|
894
|
+
self.lb_results = QLabel(" ")
|
|
895
|
+
hbox.addWidget(self.lb_results)
|
|
896
|
+
|
|
684
897
|
self.setLayout(hbox)
|
|
685
898
|
|
|
686
899
|
def click(self, msg):
|
|
687
900
|
self.clickSignal.emit(msg)
|
|
688
901
|
|
|
689
902
|
|
|
690
|
-
|
|
691
|
-
class explore_project_dialog(QDialog):
|
|
903
|
+
class Results_dialog(QDialog):
|
|
692
904
|
"""
|
|
693
|
-
|
|
905
|
+
widget for visualizing text output in HTML
|
|
694
906
|
"""
|
|
695
907
|
|
|
696
908
|
def __init__(self):
|
|
697
909
|
super().__init__()
|
|
698
910
|
|
|
699
|
-
self.
|
|
700
|
-
|
|
701
|
-
hbox = QVBoxLayout()
|
|
702
|
-
|
|
703
|
-
hbox.addWidget(QLabel("Search in all observations"))
|
|
911
|
+
self.dataset = False
|
|
704
912
|
|
|
705
|
-
self.
|
|
706
|
-
hbox.addWidget(self.lb_subject)
|
|
707
|
-
self.find_subject = QLineEdit()
|
|
708
|
-
hbox.addWidget(self.find_subject)
|
|
913
|
+
self.setWindowTitle("")
|
|
709
914
|
|
|
710
|
-
|
|
711
|
-
hbox.addWidget(self.lb_behav)
|
|
712
|
-
self.find_behavior = QLineEdit()
|
|
713
|
-
hbox.addWidget(self.find_behavior)
|
|
915
|
+
hbox = QVBoxLayout()
|
|
714
916
|
|
|
715
|
-
self.
|
|
716
|
-
hbox.addWidget(self.
|
|
717
|
-
self.find_modifier = QLineEdit()
|
|
718
|
-
hbox.addWidget(self.find_modifier)
|
|
917
|
+
self.lb = QLabel("")
|
|
918
|
+
hbox.addWidget(self.lb)
|
|
719
919
|
|
|
720
|
-
self.
|
|
721
|
-
|
|
722
|
-
self.
|
|
723
|
-
hbox.addWidget(self.find_comment)
|
|
920
|
+
self.ptText = QPlainTextEdit()
|
|
921
|
+
self.ptText.setReadOnly(True)
|
|
922
|
+
hbox.addWidget(self.ptText)
|
|
724
923
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
hbox.addWidget(self.cb_case_sensitive)
|
|
924
|
+
hbox2 = QHBoxLayout()
|
|
925
|
+
hbox2.addItem(QSpacerItem(241, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
728
926
|
|
|
729
|
-
self.
|
|
730
|
-
|
|
927
|
+
self.pbSave = QPushButton("Save results", clicked=self.save_results)
|
|
928
|
+
hbox2.addWidget(self.pbSave)
|
|
731
929
|
|
|
732
|
-
|
|
733
|
-
self.pbOK = QPushButton("Find")
|
|
734
|
-
self.pbOK.clicked.connect(self.accept)
|
|
735
|
-
self.pbCancel = QPushButton("Cancel")
|
|
736
|
-
self.pbCancel.clicked.connect(self.reject)
|
|
930
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.reject)
|
|
737
931
|
hbox2.addWidget(self.pbCancel)
|
|
932
|
+
self.pbCancel.setVisible(False)
|
|
933
|
+
|
|
934
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.accept)
|
|
738
935
|
hbox2.addWidget(self.pbOK)
|
|
739
|
-
hbox.addLayout(hbox2)
|
|
740
936
|
|
|
937
|
+
hbox.addLayout(hbox2)
|
|
741
938
|
self.setLayout(hbox)
|
|
742
|
-
'''
|
|
743
939
|
|
|
940
|
+
self.resize(800, 640)
|
|
744
941
|
|
|
745
|
-
|
|
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):
|
|
746
963
|
"""
|
|
747
964
|
widget for visualizing text output
|
|
748
965
|
"""
|
|
@@ -769,26 +986,30 @@ class Results_dialog(QDialog):
|
|
|
769
986
|
self.pbSave = QPushButton("Save results", clicked=self.save_results)
|
|
770
987
|
hbox2.addWidget(self.pbSave)
|
|
771
988
|
|
|
772
|
-
self.
|
|
773
|
-
hbox2.addWidget(self.
|
|
774
|
-
self.pbCancel.setVisible(False)
|
|
989
|
+
self.pb1 = QPushButton("1", clicked=lambda: self.done_(1))
|
|
990
|
+
hbox2.addWidget(self.pb1)
|
|
775
991
|
|
|
776
|
-
self.
|
|
777
|
-
hbox2.addWidget(self.
|
|
992
|
+
self.pb2 = QPushButton("2", clicked=lambda: self.done_(2))
|
|
993
|
+
hbox2.addWidget(self.pb2)
|
|
994
|
+
|
|
995
|
+
self.pb3 = QPushButton("3", clicked=lambda: self.done_(3))
|
|
996
|
+
hbox2.addWidget(self.pb3)
|
|
778
997
|
|
|
779
998
|
hbox.addLayout(hbox2)
|
|
780
999
|
self.setLayout(hbox)
|
|
781
1000
|
|
|
782
1001
|
self.resize(800, 640)
|
|
783
1002
|
|
|
1003
|
+
def done_(self, status):
|
|
1004
|
+
self.done(status)
|
|
1005
|
+
|
|
784
1006
|
def save_results(self):
|
|
785
1007
|
"""
|
|
786
1008
|
save content of self.ptText
|
|
787
1009
|
"""
|
|
788
1010
|
|
|
789
1011
|
if not self.dataset:
|
|
790
|
-
|
|
791
|
-
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 (*)")
|
|
792
1013
|
|
|
793
1014
|
if not file_name:
|
|
794
1015
|
return
|
|
@@ -840,10 +1061,10 @@ class View_data(QDialog):
|
|
|
840
1061
|
|
|
841
1062
|
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
842
1063
|
|
|
843
|
-
self.pbCancel = QPushButton(
|
|
1064
|
+
self.pbCancel = QPushButton(cfg.CANCEL, clicked=self.reject)
|
|
844
1065
|
hbox2.addWidget(self.pbCancel)
|
|
845
1066
|
|
|
846
|
-
self.pbOK = QPushButton(
|
|
1067
|
+
self.pbOK = QPushButton(cfg.OK, clicked=self.accept)
|
|
847
1068
|
hbox2.addWidget(self.pbOK)
|
|
848
1069
|
|
|
849
1070
|
vbox.addLayout(hbox2)
|
|
@@ -858,7 +1079,7 @@ class View_explore_project_results(QWidget):
|
|
|
858
1079
|
widget for visualizing results of explore project
|
|
859
1080
|
"""
|
|
860
1081
|
|
|
861
|
-
double_click_signal =
|
|
1082
|
+
double_click_signal = Signal(str, int)
|
|
862
1083
|
|
|
863
1084
|
def __init__(self):
|
|
864
1085
|
super().__init__()
|
|
@@ -876,14 +1097,12 @@ class View_explore_project_results(QWidget):
|
|
|
876
1097
|
vbox.addWidget(self.tw)
|
|
877
1098
|
|
|
878
1099
|
hbox2 = QHBoxLayout()
|
|
879
|
-
hbox2.
|
|
1100
|
+
hbox2.addItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
|
|
1101
|
+
hbox2.addWidget(QPushButton(cfg.CLOSE, clicked=self.close))
|
|
880
1102
|
|
|
881
1103
|
vbox.addLayout(hbox2)
|
|
882
1104
|
|
|
883
1105
|
self.setLayout(vbox)
|
|
884
1106
|
|
|
885
|
-
# self.resize(540, 640)
|
|
886
|
-
|
|
887
1107
|
def tw_cellDoubleClicked(self, r, c):
|
|
888
|
-
|
|
889
1108
|
self.double_click_signal.emit(self.tw.item(r, 0).text(), int(self.tw.item(r, 1).text()))
|