boris-behav-obs 8.16.5__py3-none-any.whl → 9.7.1__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 +24 -36
- boris/add_modifier.py +88 -80
- boris/add_modifier_ui.py +235 -131
- boris/advanced_event_filtering.py +23 -29
- 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 +16 -34
- boris/config.py +102 -50
- boris/config_file.py +55 -64
- boris/connections.py +105 -58
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2108 -1275
- 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 +304 -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 +493 -210
- boris/observation_operations.py +1010 -391
- 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 +18 -53
- boris/plot_events.py +56 -153
- boris/plot_events_rt.py +16 -30
- boris/plot_spectrogram_rt.py +80 -56
- boris/plot_waveform_rt.py +23 -48
- 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 +298 -123
- boris/preferences_ui.py +664 -225
- boris/project.py +293 -270
- boris/project_functions.py +610 -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 +6 -8
- 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 +562 -222
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +78 -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.1.dist-info/METADATA +140 -0
- boris_behav_obs-9.7.1.dist-info/RECORD +109 -0
- {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.1.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.1.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.1.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.16.5.dist-info → boris_behav_obs-9.7.1.dist-info}/top_level.txt +0 -0
boris/converters_ui.py
CHANGED
|
@@ -1,148 +1,225 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
################################################################################
|
|
4
|
+
## Form generated from reading UI file 'converters.ui'
|
|
5
|
+
##
|
|
6
|
+
## Created by: Qt User Interface Compiler version 6.8.0
|
|
7
|
+
##
|
|
8
|
+
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
|
9
|
+
################################################################################
|
|
8
10
|
|
|
9
|
-
from
|
|
11
|
+
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
|
12
|
+
QMetaObject, QObject, QPoint, QRect,
|
|
13
|
+
QSize, QTime, QUrl, Qt)
|
|
14
|
+
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|
15
|
+
QFont, QFontDatabase, QGradient, QIcon,
|
|
16
|
+
QImage, QKeySequence, QLinearGradient, QPainter,
|
|
17
|
+
QPalette, QPixmap, QRadialGradient, QTransform)
|
|
18
|
+
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QHBoxLayout, QHeaderView,
|
|
19
|
+
QLabel, QLineEdit, QPlainTextEdit, QPushButton,
|
|
20
|
+
QSizePolicy, QSpacerItem, QTableWidget, QTableWidgetItem,
|
|
21
|
+
QVBoxLayout, QWidget)
|
|
10
22
|
|
|
11
23
|
class Ui_converters(object):
|
|
12
24
|
def setupUi(self, converters):
|
|
13
|
-
converters.
|
|
25
|
+
if not converters.objectName():
|
|
26
|
+
converters.setObjectName(u"converters")
|
|
14
27
|
converters.resize(1029, 530)
|
|
15
|
-
self.verticalLayout =
|
|
16
|
-
self.verticalLayout.setObjectName("verticalLayout")
|
|
17
|
-
self.label_4 =
|
|
18
|
-
self.label_4.setObjectName("label_4")
|
|
28
|
+
self.verticalLayout = QVBoxLayout(converters)
|
|
29
|
+
self.verticalLayout.setObjectName(u"verticalLayout")
|
|
30
|
+
self.label_4 = QLabel(converters)
|
|
31
|
+
self.label_4.setObjectName(u"label_4")
|
|
32
|
+
|
|
19
33
|
self.verticalLayout.addWidget(self.label_4)
|
|
20
|
-
|
|
21
|
-
self.tw_converters
|
|
22
|
-
self.tw_converters.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
self.tw_converters.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
self.tw_converters.
|
|
31
|
-
|
|
32
|
-
self.tw_converters.
|
|
34
|
+
|
|
35
|
+
self.tw_converters = QTableWidget(converters)
|
|
36
|
+
if (self.tw_converters.columnCount() < 3):
|
|
37
|
+
self.tw_converters.setColumnCount(3)
|
|
38
|
+
__qtablewidgetitem = QTableWidgetItem()
|
|
39
|
+
self.tw_converters.setHorizontalHeaderItem(0, __qtablewidgetitem)
|
|
40
|
+
__qtablewidgetitem1 = QTableWidgetItem()
|
|
41
|
+
self.tw_converters.setHorizontalHeaderItem(1, __qtablewidgetitem1)
|
|
42
|
+
__qtablewidgetitem2 = QTableWidgetItem()
|
|
43
|
+
self.tw_converters.setHorizontalHeaderItem(2, __qtablewidgetitem2)
|
|
44
|
+
self.tw_converters.setObjectName(u"tw_converters")
|
|
45
|
+
self.tw_converters.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
|
46
|
+
self.tw_converters.setSelectionMode(QAbstractItemView.SingleSelection)
|
|
47
|
+
self.tw_converters.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
48
|
+
self.tw_converters.setSortingEnabled(True)
|
|
49
|
+
|
|
33
50
|
self.verticalLayout.addWidget(self.tw_converters)
|
|
34
|
-
|
|
35
|
-
self.horizontalLayout
|
|
36
|
-
self.
|
|
37
|
-
self.pb_add_converter
|
|
51
|
+
|
|
52
|
+
self.horizontalLayout = QHBoxLayout()
|
|
53
|
+
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
|
54
|
+
self.pb_add_converter = QPushButton(converters)
|
|
55
|
+
self.pb_add_converter.setObjectName(u"pb_add_converter")
|
|
56
|
+
|
|
38
57
|
self.horizontalLayout.addWidget(self.pb_add_converter)
|
|
39
|
-
|
|
40
|
-
self.pb_modify_converter
|
|
58
|
+
|
|
59
|
+
self.pb_modify_converter = QPushButton(converters)
|
|
60
|
+
self.pb_modify_converter.setObjectName(u"pb_modify_converter")
|
|
61
|
+
|
|
41
62
|
self.horizontalLayout.addWidget(self.pb_modify_converter)
|
|
42
|
-
|
|
43
|
-
self.pb_delete_converter
|
|
63
|
+
|
|
64
|
+
self.pb_delete_converter = QPushButton(converters)
|
|
65
|
+
self.pb_delete_converter.setObjectName(u"pb_delete_converter")
|
|
66
|
+
|
|
44
67
|
self.horizontalLayout.addWidget(self.pb_delete_converter)
|
|
45
|
-
|
|
46
|
-
self.pb_load_from_file
|
|
68
|
+
|
|
69
|
+
self.pb_load_from_file = QPushButton(converters)
|
|
70
|
+
self.pb_load_from_file.setObjectName(u"pb_load_from_file")
|
|
71
|
+
|
|
47
72
|
self.horizontalLayout.addWidget(self.pb_load_from_file)
|
|
48
|
-
|
|
49
|
-
self.pb_load_from_repo
|
|
73
|
+
|
|
74
|
+
self.pb_load_from_repo = QPushButton(converters)
|
|
75
|
+
self.pb_load_from_repo.setObjectName(u"pb_load_from_repo")
|
|
76
|
+
|
|
50
77
|
self.horizontalLayout.addWidget(self.pb_load_from_repo)
|
|
51
|
-
|
|
52
|
-
self.
|
|
78
|
+
|
|
79
|
+
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
80
|
+
|
|
81
|
+
self.horizontalLayout.addItem(self.horizontalSpacer)
|
|
82
|
+
|
|
83
|
+
|
|
53
84
|
self.verticalLayout.addLayout(self.horizontalLayout)
|
|
54
|
-
|
|
55
|
-
self.horizontalLayout_3
|
|
56
|
-
self.
|
|
57
|
-
self.label_2
|
|
58
|
-
self.label_2.setObjectName("label_2")
|
|
85
|
+
|
|
86
|
+
self.horizontalLayout_3 = QHBoxLayout()
|
|
87
|
+
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
|
|
88
|
+
self.label_2 = QLabel(converters)
|
|
89
|
+
self.label_2.setObjectName(u"label_2")
|
|
90
|
+
self.label_2.setMinimumSize(QSize(120, 0))
|
|
91
|
+
|
|
59
92
|
self.horizontalLayout_3.addWidget(self.label_2)
|
|
60
|
-
|
|
61
|
-
self.le_converter_name
|
|
93
|
+
|
|
94
|
+
self.le_converter_name = QLineEdit(converters)
|
|
95
|
+
self.le_converter_name.setObjectName(u"le_converter_name")
|
|
96
|
+
|
|
62
97
|
self.horizontalLayout_3.addWidget(self.le_converter_name)
|
|
63
|
-
|
|
64
|
-
self.
|
|
98
|
+
|
|
99
|
+
self.horizontalSpacer_3 = QSpacerItem(10, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
|
|
100
|
+
|
|
101
|
+
self.horizontalLayout_3.addItem(self.horizontalSpacer_3)
|
|
102
|
+
|
|
103
|
+
|
|
65
104
|
self.verticalLayout.addLayout(self.horizontalLayout_3)
|
|
66
|
-
|
|
67
|
-
self.horizontalLayout_5
|
|
68
|
-
self.
|
|
69
|
-
self.label_3
|
|
70
|
-
self.label_3.setObjectName("label_3")
|
|
105
|
+
|
|
106
|
+
self.horizontalLayout_5 = QHBoxLayout()
|
|
107
|
+
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
|
|
108
|
+
self.label_3 = QLabel(converters)
|
|
109
|
+
self.label_3.setObjectName(u"label_3")
|
|
110
|
+
self.label_3.setMinimumSize(QSize(120, 0))
|
|
111
|
+
|
|
71
112
|
self.horizontalLayout_5.addWidget(self.label_3)
|
|
72
|
-
|
|
73
|
-
self.le_converter_description
|
|
113
|
+
|
|
114
|
+
self.le_converter_description = QLineEdit(converters)
|
|
115
|
+
self.le_converter_description.setObjectName(u"le_converter_description")
|
|
116
|
+
|
|
74
117
|
self.horizontalLayout_5.addWidget(self.le_converter_description)
|
|
75
|
-
|
|
76
|
-
self.
|
|
118
|
+
|
|
119
|
+
self.horizontalSpacer_4 = QSpacerItem(10, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
|
|
120
|
+
|
|
121
|
+
self.horizontalLayout_5.addItem(self.horizontalSpacer_4)
|
|
122
|
+
|
|
123
|
+
|
|
77
124
|
self.verticalLayout.addLayout(self.horizontalLayout_5)
|
|
78
|
-
|
|
79
|
-
self.horizontalLayout_2
|
|
80
|
-
self.
|
|
81
|
-
self.verticalLayout_3
|
|
82
|
-
self.
|
|
83
|
-
self.label
|
|
125
|
+
|
|
126
|
+
self.horizontalLayout_2 = QHBoxLayout()
|
|
127
|
+
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
|
128
|
+
self.verticalLayout_3 = QVBoxLayout()
|
|
129
|
+
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
|
130
|
+
self.label = QLabel(converters)
|
|
131
|
+
self.label.setObjectName(u"label")
|
|
132
|
+
|
|
84
133
|
self.verticalLayout_3.addWidget(self.label)
|
|
85
|
-
|
|
86
|
-
self.pb_code_help
|
|
134
|
+
|
|
135
|
+
self.pb_code_help = QPushButton(converters)
|
|
136
|
+
self.pb_code_help.setObjectName(u"pb_code_help")
|
|
137
|
+
|
|
87
138
|
self.verticalLayout_3.addWidget(self.pb_code_help)
|
|
88
|
-
|
|
89
|
-
self.
|
|
139
|
+
|
|
140
|
+
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
|
141
|
+
|
|
142
|
+
self.verticalLayout_3.addItem(self.verticalSpacer_2)
|
|
143
|
+
|
|
144
|
+
|
|
90
145
|
self.horizontalLayout_2.addLayout(self.verticalLayout_3)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
146
|
+
|
|
147
|
+
self.pteCode = QPlainTextEdit(converters)
|
|
148
|
+
self.pteCode.setObjectName(u"pteCode")
|
|
149
|
+
font = QFont()
|
|
150
|
+
font.setFamilies([u"Monospace"])
|
|
94
151
|
self.pteCode.setFont(font)
|
|
95
|
-
|
|
152
|
+
|
|
96
153
|
self.horizontalLayout_2.addWidget(self.pteCode)
|
|
97
|
-
|
|
98
|
-
self.verticalLayout_2
|
|
99
|
-
self.
|
|
100
|
-
self.pb_save_converter
|
|
154
|
+
|
|
155
|
+
self.verticalLayout_2 = QVBoxLayout()
|
|
156
|
+
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
|
157
|
+
self.pb_save_converter = QPushButton(converters)
|
|
158
|
+
self.pb_save_converter.setObjectName(u"pb_save_converter")
|
|
159
|
+
|
|
101
160
|
self.verticalLayout_2.addWidget(self.pb_save_converter)
|
|
102
|
-
|
|
103
|
-
self.pb_cancel_converter
|
|
161
|
+
|
|
162
|
+
self.pb_cancel_converter = QPushButton(converters)
|
|
163
|
+
self.pb_cancel_converter.setObjectName(u"pb_cancel_converter")
|
|
164
|
+
|
|
104
165
|
self.verticalLayout_2.addWidget(self.pb_cancel_converter)
|
|
105
|
-
|
|
106
|
-
self.
|
|
166
|
+
|
|
167
|
+
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
|
168
|
+
|
|
169
|
+
self.verticalLayout_2.addItem(self.verticalSpacer)
|
|
170
|
+
|
|
171
|
+
|
|
107
172
|
self.horizontalLayout_2.addLayout(self.verticalLayout_2)
|
|
173
|
+
|
|
174
|
+
|
|
108
175
|
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
|
109
|
-
|
|
110
|
-
self.horizontalLayout_4
|
|
111
|
-
|
|
112
|
-
self.
|
|
113
|
-
|
|
114
|
-
self.
|
|
176
|
+
|
|
177
|
+
self.horizontalLayout_4 = QHBoxLayout()
|
|
178
|
+
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
|
|
179
|
+
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
|
180
|
+
|
|
181
|
+
self.horizontalLayout_4.addItem(self.horizontalSpacer_2)
|
|
182
|
+
|
|
183
|
+
self.pb_cancel_widget = QPushButton(converters)
|
|
184
|
+
self.pb_cancel_widget.setObjectName(u"pb_cancel_widget")
|
|
185
|
+
|
|
115
186
|
self.horizontalLayout_4.addWidget(self.pb_cancel_widget)
|
|
116
|
-
|
|
117
|
-
self.pbOK
|
|
187
|
+
|
|
188
|
+
self.pbOK = QPushButton(converters)
|
|
189
|
+
self.pbOK.setObjectName(u"pbOK")
|
|
190
|
+
|
|
118
191
|
self.horizontalLayout_4.addWidget(self.pbOK)
|
|
192
|
+
|
|
193
|
+
|
|
119
194
|
self.verticalLayout.addLayout(self.horizontalLayout_4)
|
|
120
195
|
|
|
196
|
+
|
|
121
197
|
self.retranslateUi(converters)
|
|
122
|
-
|
|
198
|
+
|
|
199
|
+
QMetaObject.connectSlotsByName(converters)
|
|
200
|
+
# setupUi
|
|
123
201
|
|
|
124
202
|
def retranslateUi(self, converters):
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
self.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
self.
|
|
136
|
-
self.
|
|
137
|
-
self.
|
|
138
|
-
self.
|
|
139
|
-
self.
|
|
140
|
-
self.
|
|
141
|
-
self.
|
|
142
|
-
self.
|
|
143
|
-
self.
|
|
144
|
-
self.
|
|
145
|
-
self.
|
|
146
|
-
|
|
147
|
-
self.pbOK.setText(_translate("converters", "OK"))
|
|
203
|
+
converters.setWindowTitle(QCoreApplication.translate("converters", u"Time converters", None))
|
|
204
|
+
self.label_4.setText(QCoreApplication.translate("converters", u"Converters", None))
|
|
205
|
+
___qtablewidgetitem = self.tw_converters.horizontalHeaderItem(0)
|
|
206
|
+
___qtablewidgetitem.setText(QCoreApplication.translate("converters", u"Name", None));
|
|
207
|
+
___qtablewidgetitem1 = self.tw_converters.horizontalHeaderItem(1)
|
|
208
|
+
___qtablewidgetitem1.setText(QCoreApplication.translate("converters", u"Description", None));
|
|
209
|
+
___qtablewidgetitem2 = self.tw_converters.horizontalHeaderItem(2)
|
|
210
|
+
___qtablewidgetitem2.setText(QCoreApplication.translate("converters", u"Code", None));
|
|
211
|
+
self.pb_add_converter.setText(QCoreApplication.translate("converters", u"Add new converter", None))
|
|
212
|
+
self.pb_modify_converter.setText(QCoreApplication.translate("converters", u"Modify converter", None))
|
|
213
|
+
self.pb_delete_converter.setText(QCoreApplication.translate("converters", u"Delete converter", None))
|
|
214
|
+
self.pb_load_from_file.setText(QCoreApplication.translate("converters", u"Load converters from file", None))
|
|
215
|
+
self.pb_load_from_repo.setText(QCoreApplication.translate("converters", u"Load converters from BORIS repository", None))
|
|
216
|
+
self.label_2.setText(QCoreApplication.translate("converters", u"Name", None))
|
|
217
|
+
self.label_3.setText(QCoreApplication.translate("converters", u"Description", None))
|
|
218
|
+
self.label.setText(QCoreApplication.translate("converters", u"Python code", None))
|
|
219
|
+
self.pb_code_help.setText(QCoreApplication.translate("converters", u"Help", None))
|
|
220
|
+
self.pb_save_converter.setText(QCoreApplication.translate("converters", u"Save converter", None))
|
|
221
|
+
self.pb_cancel_converter.setText(QCoreApplication.translate("converters", u"Cancel", None))
|
|
222
|
+
self.pb_cancel_widget.setText(QCoreApplication.translate("converters", u"Cancel", None))
|
|
223
|
+
self.pbOK.setText(QCoreApplication.translate("converters", u"OK", None))
|
|
224
|
+
# retranslateUi
|
|
148
225
|
|
boris/cooccurence.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS
|
|
3
|
+
Behavioral Observation Research Interactive Software
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
|
+
|
|
6
|
+
This program is free software; you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU General Public License as published by
|
|
8
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU General Public License
|
|
17
|
+
along with this program; if not, write to the Free Software
|
|
18
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
19
|
+
MA 02110-1301, USA.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Module for analyzing the co-occurence of behaviors
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from . import config as cfg
|
|
27
|
+
from . import select_subj_behav
|
|
28
|
+
from . import dialog
|
|
29
|
+
from . import utilities as util
|
|
30
|
+
from . import select_observations
|
|
31
|
+
|
|
32
|
+
from . import project_functions, observation_operations
|
|
33
|
+
|
|
34
|
+
from PySide6.QtWidgets import QMessageBox
|
|
35
|
+
from PySide6.QtGui import QFont, QTextOption
|
|
36
|
+
from . import portion as I
|
|
37
|
+
import itertools
|
|
38
|
+
import logging
|
|
39
|
+
from decimal import Decimal as dec
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_cooccurence(self):
|
|
43
|
+
"""
|
|
44
|
+
get co-occurence of selected behaviors
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
QMessageBox.warning(
|
|
48
|
+
None,
|
|
49
|
+
cfg.programName,
|
|
50
|
+
(
|
|
51
|
+
"This function is experimental. Please test it and report any bug and suggestions at <br>"
|
|
52
|
+
'<a href="https://github.com/olivierfriard/BORIS/issues">'
|
|
53
|
+
"https://github.com/olivierfriard/BORIS/issues</a><br>"
|
|
54
|
+
"Thank you for your collaboration!"
|
|
55
|
+
),
|
|
56
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
57
|
+
QMessageBox.NoButton,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def interval_len(interval: I) -> dec:
|
|
61
|
+
""" "
|
|
62
|
+
returns duration of an interval or a set of intervals
|
|
63
|
+
"""
|
|
64
|
+
if interval.empty:
|
|
65
|
+
return dec(0)
|
|
66
|
+
else:
|
|
67
|
+
return dec(sum([x.upper - x.lower for x in interval]))
|
|
68
|
+
|
|
69
|
+
_, selected_observations = select_observations.select_observations2(
|
|
70
|
+
self, cfg.MULTIPLE, windows_title="Select the observations for behaviors co-occurence analysis"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
if not selected_observations:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# check if coded behaviors are defined in ethogram
|
|
77
|
+
if project_functions.check_coded_behaviors_in_obs_list(self.pj, selected_observations):
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
# check if state events are paired
|
|
81
|
+
not_ok, selected_observations = project_functions.check_state_events(self.pj, selected_observations)
|
|
82
|
+
if not_ok or not selected_observations:
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
max_media_duration_all_obs, _ = observation_operations.media_duration(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
86
|
+
|
|
87
|
+
start_coding, end_coding, _ = observation_operations.coding_time(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
88
|
+
# exit with message if events do not have timestamp
|
|
89
|
+
if start_coding.is_nan():
|
|
90
|
+
QMessageBox.critical(
|
|
91
|
+
None,
|
|
92
|
+
cfg.programName,
|
|
93
|
+
("This function is not available for observations with events that do not have timestamp"),
|
|
94
|
+
QMessageBox.Ok | QMessageBox.Default,
|
|
95
|
+
QMessageBox.NoButton,
|
|
96
|
+
)
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
start_interval, end_interval = observation_operations.time_intervals_range(self.pj[cfg.OBSERVATIONS], selected_observations)
|
|
100
|
+
|
|
101
|
+
# loop on choose subjects /behaviors until parameters are OK
|
|
102
|
+
while True:
|
|
103
|
+
flag_ok: bool = True
|
|
104
|
+
parameters = select_subj_behav.choose_obs_subj_behav_category(
|
|
105
|
+
self,
|
|
106
|
+
selected_observations,
|
|
107
|
+
start_coding=start_coding,
|
|
108
|
+
end_coding=end_coding,
|
|
109
|
+
# start_interval=start_interval,
|
|
110
|
+
# end_interval=end_interval,
|
|
111
|
+
start_interval=None,
|
|
112
|
+
end_interval=None,
|
|
113
|
+
maxTime=max_media_duration_all_obs,
|
|
114
|
+
n_observations=len(selected_observations),
|
|
115
|
+
show_include_modifiers=False,
|
|
116
|
+
show_exclude_non_coded_behaviors=True,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if not parameters: # cancel button pressed
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
if not parameters[cfg.SELECTED_SUBJECTS]:
|
|
123
|
+
QMessageBox.warning(None, cfg.programName, "Select the subject(s) to analyze")
|
|
124
|
+
flag_ok = False
|
|
125
|
+
|
|
126
|
+
# check number of behaviors (must be <=4)
|
|
127
|
+
if flag_ok and len(parameters[cfg.SELECTED_BEHAVIORS]) > 4:
|
|
128
|
+
QMessageBox.warning(None, cfg.programName, "You cannot select more than 4 behaviors")
|
|
129
|
+
flag_ok = False
|
|
130
|
+
|
|
131
|
+
# check number of behaviors (must be > 1)
|
|
132
|
+
if flag_ok and len(parameters[cfg.SELECTED_BEHAVIORS]) < 2:
|
|
133
|
+
QMessageBox.warning(None, cfg.programName, "You must select almost 2 behaviors")
|
|
134
|
+
flag_ok = False
|
|
135
|
+
|
|
136
|
+
if flag_ok:
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
logging.debug(f"{parameters[cfg.SELECTED_BEHAVIORS]}")
|
|
140
|
+
|
|
141
|
+
state_events_list = util.state_behavior_codes(self.pj[cfg.ETHOGRAM])
|
|
142
|
+
|
|
143
|
+
events_interval: dict = {}
|
|
144
|
+
mem_events_interval: dict = {}
|
|
145
|
+
|
|
146
|
+
for obs_id in selected_observations:
|
|
147
|
+
events_interval[obs_id] = {}
|
|
148
|
+
mem_events_interval[obs_id] = {}
|
|
149
|
+
|
|
150
|
+
for event in self.pj[cfg.OBSERVATIONS][obs_id][cfg.EVENTS]:
|
|
151
|
+
if event[cfg.EVENT_SUBJECT_FIELD_IDX] not in events_interval[obs_id]:
|
|
152
|
+
events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]] = {}
|
|
153
|
+
mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]] = {}
|
|
154
|
+
|
|
155
|
+
if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] not in events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]]:
|
|
156
|
+
events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]] = I.empty()
|
|
157
|
+
mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]] = []
|
|
158
|
+
|
|
159
|
+
# state event
|
|
160
|
+
if event[cfg.EVENT_BEHAVIOR_FIELD_IDX] in state_events_list:
|
|
161
|
+
mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]].append(
|
|
162
|
+
event[cfg.EVENT_TIME_FIELD_IDX]
|
|
163
|
+
)
|
|
164
|
+
if len(mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]]) == 2:
|
|
165
|
+
events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]] |= I.closedopen(
|
|
166
|
+
mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]][0],
|
|
167
|
+
mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]][1],
|
|
168
|
+
)
|
|
169
|
+
mem_events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]] = []
|
|
170
|
+
# point event
|
|
171
|
+
else:
|
|
172
|
+
events_interval[obs_id][event[cfg.EVENT_SUBJECT_FIELD_IDX]][event[cfg.EVENT_BEHAVIOR_FIELD_IDX]] |= I.singleton(
|
|
173
|
+
event[cfg.EVENT_TIME_FIELD_IDX]
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
logging.debug(f"events_interval: {events_interval}")
|
|
177
|
+
|
|
178
|
+
cooccurence_results: dict = {}
|
|
179
|
+
|
|
180
|
+
for obs_id in selected_observations:
|
|
181
|
+
logging.debug(f"obs_id: {obs_id}")
|
|
182
|
+
|
|
183
|
+
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
184
|
+
if subject == "No focal subject":
|
|
185
|
+
subj = ""
|
|
186
|
+
else:
|
|
187
|
+
subj = subject
|
|
188
|
+
|
|
189
|
+
if subject not in cooccurence_results:
|
|
190
|
+
cooccurence_results[subject] = {}
|
|
191
|
+
|
|
192
|
+
logging.debug(f"subject {subject}")
|
|
193
|
+
|
|
194
|
+
for n_combinations in range(2, len(parameters[cfg.SELECTED_BEHAVIORS]) + 1):
|
|
195
|
+
union = I.empty()
|
|
196
|
+
|
|
197
|
+
logging.debug(f"{n_combinations=}")
|
|
198
|
+
|
|
199
|
+
for combination in itertools.combinations(parameters[cfg.SELECTED_BEHAVIORS], n_combinations):
|
|
200
|
+
logging.debug(f"{combination=}")
|
|
201
|
+
if subj in events_interval[obs_id]:
|
|
202
|
+
# init
|
|
203
|
+
if combination[0] in events_interval[obs_id][subj]:
|
|
204
|
+
union = events_interval[obs_id][subj][combination[0]]
|
|
205
|
+
else:
|
|
206
|
+
union = I.empty()
|
|
207
|
+
|
|
208
|
+
logging.debug(f"{combination[0]=} {union=}")
|
|
209
|
+
|
|
210
|
+
for combination2 in combination[1:]:
|
|
211
|
+
if combination2 in events_interval[obs_id][subj]:
|
|
212
|
+
inter2 = events_interval[obs_id][subj][combination2]
|
|
213
|
+
else:
|
|
214
|
+
inter2 = I.empty()
|
|
215
|
+
|
|
216
|
+
logging.debug(f"{combination2=} {inter2=}")
|
|
217
|
+
|
|
218
|
+
union &= inter2
|
|
219
|
+
|
|
220
|
+
if combination not in cooccurence_results[subject]:
|
|
221
|
+
cooccurence_results[subject][combination] = 0
|
|
222
|
+
|
|
223
|
+
logging.debug(f"{combination=} {union=}")
|
|
224
|
+
cooccurence_results[subject][combination] += interval_len(union)
|
|
225
|
+
else:
|
|
226
|
+
if combination not in cooccurence_results[subject]:
|
|
227
|
+
cooccurence_results[subject][combination] = 0
|
|
228
|
+
cooccurence_results[subject][combination] += 0
|
|
229
|
+
|
|
230
|
+
logging.debug(f"{cooccurence_results[subject][combination]=}")
|
|
231
|
+
|
|
232
|
+
logging.debug(cooccurence_results)
|
|
233
|
+
|
|
234
|
+
out = f"<b>Co-occurence of behaviors: {','.join(parameters[cfg.SELECTED_BEHAVIORS])}</b><br><br>"
|
|
235
|
+
for subject in parameters[cfg.SELECTED_SUBJECTS]:
|
|
236
|
+
out += f"<br>Subject <b>{subject}</b><br><br>"
|
|
237
|
+
for combination in cooccurence_results[subject]:
|
|
238
|
+
if parameters[cfg.EXCLUDE_BEHAVIORS] and not cooccurence_results[subject][combination]:
|
|
239
|
+
continue
|
|
240
|
+
duration = f"<b>{cooccurence_results[subject][combination]}</b>" if cooccurence_results[subject][combination] else "0"
|
|
241
|
+
out += f"<b>{'</b> and <b>'.join(combination)}</b>: {duration} s<br>"
|
|
242
|
+
|
|
243
|
+
self.results = dialog.Results_dialog()
|
|
244
|
+
self.results.setWindowTitle("Behaviors co-occurence")
|
|
245
|
+
self.results.ptText.setFont(QFont("Courier", 12))
|
|
246
|
+
self.results.ptText.setWordWrapMode(QTextOption.NoWrap)
|
|
247
|
+
self.results.ptText.setReadOnly(True)
|
|
248
|
+
self.results.ptText.clear()
|
|
249
|
+
self.results.ptText.appendHtml(out)
|
|
250
|
+
self.results.show()
|