boris-behav-obs 9.7.7__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 +26 -0
- boris/__main__.py +25 -0
- boris/about.py +143 -0
- boris/add_modifier.py +635 -0
- boris/add_modifier_ui.py +303 -0
- boris/advanced_event_filtering.py +455 -0
- 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 +1110 -0
- boris/behavior_binary_table.py +305 -0
- boris/behaviors_coding_map.py +239 -0
- boris/boris_cli.py +340 -0
- boris/cmd_arguments.py +49 -0
- boris/coding_pad.py +280 -0
- boris/config.py +785 -0
- boris/config_file.py +356 -0
- boris/connections.py +409 -0
- boris/converters.py +333 -0
- boris/converters_ui.py +225 -0
- boris/cooccurence.py +250 -0
- boris/core.py +5901 -0
- boris/core_qrc.py +15958 -0
- boris/core_ui.py +1107 -0
- boris/db_functions.py +324 -0
- boris/dev.py +134 -0
- boris/dialog.py +1108 -0
- boris/duration_widget.py +238 -0
- boris/edit_event.py +245 -0
- boris/edit_event_ui.py +233 -0
- boris/event_operations.py +1040 -0
- boris/events_cursor.py +61 -0
- boris/events_snapshots.py +596 -0
- boris/exclusion_matrix.py +141 -0
- boris/export_events.py +1006 -0
- boris/export_observation.py +1203 -0
- boris/external_processes.py +332 -0
- boris/geometric_measurement.py +941 -0
- boris/gui_utilities.py +135 -0
- boris/image_overlay.py +72 -0
- boris/import_observations.py +242 -0
- boris/ipc_mpv.py +325 -0
- boris/irr.py +634 -0
- boris/latency.py +244 -0
- boris/measurement_widget.py +161 -0
- boris/media_file.py +115 -0
- boris/menu_options.py +213 -0
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +157 -0
- boris/mpv.py +2016 -0
- boris/mpv2.py +2193 -0
- boris/observation.py +1453 -0
- boris/observation_operations.py +2538 -0
- boris/observation_ui.py +679 -0
- boris/observations_list.py +337 -0
- boris/otx_parser.py +442 -0
- boris/param_panel.py +201 -0
- boris/param_panel_ui.py +305 -0
- boris/player_dock_widget.py +198 -0
- boris/plot_data_module.py +536 -0
- boris/plot_events.py +634 -0
- boris/plot_events_rt.py +237 -0
- boris/plot_spectrogram_rt.py +316 -0
- boris/plot_waveform_rt.py +230 -0
- boris/plugins.py +431 -0
- boris/portion/__init__.py +31 -0
- boris/portion/const.py +95 -0
- boris/portion/dict.py +365 -0
- boris/portion/func.py +52 -0
- boris/portion/interval.py +581 -0
- boris/portion/io.py +181 -0
- boris/preferences.py +510 -0
- boris/preferences_ui.py +770 -0
- boris/project.py +2007 -0
- boris/project_functions.py +2041 -0
- boris/project_import_export.py +1096 -0
- boris/project_ui.py +794 -0
- boris/qrc_boris.py +10389 -0
- boris/qrc_boris5.py +2579 -0
- boris/select_modifiers.py +312 -0
- boris/select_observations.py +210 -0
- boris/select_subj_behav.py +286 -0
- boris/state_events.py +197 -0
- boris/subjects_pad.py +106 -0
- boris/synthetic_time_budget.py +290 -0
- boris/time_budget_functions.py +1136 -0
- boris/time_budget_widget.py +1039 -0
- boris/transitions.py +365 -0
- boris/utilities.py +1810 -0
- boris/version.py +24 -0
- boris/video_equalizer.py +159 -0
- boris/video_equalizer_ui.py +248 -0
- boris/video_operations.py +310 -0
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.7.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
- boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
- boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
- boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
- boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
boris/add_modifier.py
ADDED
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BORIS
|
|
3
|
+
Behavioral Observation Research Interactive Software
|
|
4
|
+
Copyright 2012-2025 Olivier Friard
|
|
5
|
+
|
|
6
|
+
This file is part of BORIS.
|
|
7
|
+
|
|
8
|
+
BORIS is free software; you can redistribute it and/or modify
|
|
9
|
+
it under the terms of the GNU General Public License as published by
|
|
10
|
+
the Free Software Foundation; either version 3 of the License, or
|
|
11
|
+
any later version.
|
|
12
|
+
|
|
13
|
+
BORIS is distributed in the hope that it will be useful,
|
|
14
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
GNU General Public License for more details.
|
|
17
|
+
|
|
18
|
+
You should have received a copy of the GNU General Public License
|
|
19
|
+
along with this program; if not see <http://www.gnu.org/licPbehav_enses/>.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import json
|
|
25
|
+
|
|
26
|
+
from PySide6.QtGui import QIcon
|
|
27
|
+
from PySide6.QtWidgets import QDialog, QWidget, QFileDialog, QMessageBox
|
|
28
|
+
|
|
29
|
+
from . import dialog
|
|
30
|
+
from .add_modifier_ui import Ui_Dialog
|
|
31
|
+
from . import config as cfg
|
|
32
|
+
from .utilities import sorted_keys
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class addModifierDialog(QDialog, Ui_Dialog):
|
|
36
|
+
tabMem = -1
|
|
37
|
+
itemPositionMem = -1
|
|
38
|
+
|
|
39
|
+
def __init__(self, modifiers_str: str, subjects: list = [], ask_at_stop_enabled: bool = False, parent=None):
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.setupUi(self)
|
|
42
|
+
|
|
43
|
+
self.subjects = subjects
|
|
44
|
+
if not self.subjects:
|
|
45
|
+
self.pb_add_subjects.setEnabled(False)
|
|
46
|
+
|
|
47
|
+
self.ask_at_stop_enabled = ask_at_stop_enabled
|
|
48
|
+
|
|
49
|
+
self.pbAddModifier.clicked.connect(self.addModifier)
|
|
50
|
+
self.pbAddModifier.setIcon(QIcon(":/frame_forward"))
|
|
51
|
+
self.pbAddSet.clicked.connect(self.add_set_of_modifiers)
|
|
52
|
+
self.pbRemoveSet.clicked.connect(self.remove_set_of_modifiers)
|
|
53
|
+
self.pbModifyModifier.clicked.connect(self.modifyModifier)
|
|
54
|
+
self.pbModifyModifier.setIcon(QIcon(":/frame_backward"))
|
|
55
|
+
|
|
56
|
+
self.pbMoveUp.clicked.connect(self.moveModifierUp)
|
|
57
|
+
self.pbMoveDown.clicked.connect(self.moveModifierDown)
|
|
58
|
+
self.pbMoveSetLeft.clicked.connect(self.moveSetLeft)
|
|
59
|
+
self.pbMoveSetRight.clicked.connect(self.moveSetRight)
|
|
60
|
+
self.pbRemoveModifier.clicked.connect(self.removeModifier)
|
|
61
|
+
self.pb_sort_modifiers.clicked.connect(self.sort_modifiers)
|
|
62
|
+
self.pb_add_subjects.clicked.connect(self.add_subjects)
|
|
63
|
+
self.pb_load_file.clicked.connect(self.add_modifiers_from_file)
|
|
64
|
+
|
|
65
|
+
self.pbOK.clicked.connect(lambda: self.pb_pushed(cfg.OK))
|
|
66
|
+
self.pbCancel.clicked.connect(lambda: self.pb_pushed(cfg.CANCEL))
|
|
67
|
+
|
|
68
|
+
self.le_name.textChanged.connect(self.set_name_changed)
|
|
69
|
+
self.le_description.textChanged.connect(self.set_description_changed)
|
|
70
|
+
|
|
71
|
+
self.cbType.currentIndexChanged.connect(self.type_changed)
|
|
72
|
+
|
|
73
|
+
# self.cb_ask_at_stop.clicked.connect(self.ask_at_stop_changed)
|
|
74
|
+
|
|
75
|
+
dummy_dict: dict = json.loads(modifiers_str) if modifiers_str else {}
|
|
76
|
+
modif_values: list = []
|
|
77
|
+
for idx in sorted_keys(dummy_dict):
|
|
78
|
+
modif_values.append(dummy_dict[idx])
|
|
79
|
+
|
|
80
|
+
self.modifiers_sets_dict: dict = {}
|
|
81
|
+
for modif in modif_values:
|
|
82
|
+
self.modifiers_sets_dict[str(len(self.modifiers_sets_dict))] = dict(modif)
|
|
83
|
+
if self.ask_at_stop_enabled:
|
|
84
|
+
if dict(modif).get("ask at stop", False):
|
|
85
|
+
self.cb_ask_at_stop.setChecked(True)
|
|
86
|
+
|
|
87
|
+
self.tabWidgetModifiersSets.currentChanged.connect(self.tabWidgetModifiersSets_changed)
|
|
88
|
+
|
|
89
|
+
# create tab
|
|
90
|
+
for idx in sorted_keys(self.modifiers_sets_dict):
|
|
91
|
+
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{int(idx) + 1}")
|
|
92
|
+
|
|
93
|
+
if self.tabWidgetModifiersSets.currentIndex() == -1:
|
|
94
|
+
for w in (
|
|
95
|
+
self.lb_name,
|
|
96
|
+
self.le_name,
|
|
97
|
+
self.lbType,
|
|
98
|
+
self.lbValues,
|
|
99
|
+
self.lb_description,
|
|
100
|
+
self.le_description,
|
|
101
|
+
self.cbType,
|
|
102
|
+
self.lwModifiers,
|
|
103
|
+
self.pbMoveUp,
|
|
104
|
+
self.pbMoveDown,
|
|
105
|
+
self.pbRemoveModifier,
|
|
106
|
+
self.pbRemoveSet,
|
|
107
|
+
self.pbMoveSetLeft,
|
|
108
|
+
self.pbMoveSetRight,
|
|
109
|
+
self.pb_add_subjects,
|
|
110
|
+
self.pb_load_file,
|
|
111
|
+
self.pb_sort_modifiers,
|
|
112
|
+
self.cb_ask_at_stop,
|
|
113
|
+
):
|
|
114
|
+
w.setVisible(False)
|
|
115
|
+
for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
|
|
116
|
+
w.setEnabled(False)
|
|
117
|
+
|
|
118
|
+
# set first tab as active
|
|
119
|
+
self.tabMem = 0
|
|
120
|
+
|
|
121
|
+
def pb_pushed(self, button):
|
|
122
|
+
if self.leModifier.text():
|
|
123
|
+
if (
|
|
124
|
+
dialog.MessageDialog(
|
|
125
|
+
cfg.programName,
|
|
126
|
+
("You are working on a behavior.<br>If you close the window it will be lost.<br>Do you want to change modifiers set"),
|
|
127
|
+
[cfg.CLOSE, cfg.CANCEL],
|
|
128
|
+
)
|
|
129
|
+
== cfg.CANCEL
|
|
130
|
+
):
|
|
131
|
+
return
|
|
132
|
+
if button == cfg.OK:
|
|
133
|
+
self.accept()
|
|
134
|
+
if button == cfg.CANCEL:
|
|
135
|
+
self.reject()
|
|
136
|
+
|
|
137
|
+
def add_subjects(self):
|
|
138
|
+
"""
|
|
139
|
+
add subjects as modifiers
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
for subject, key in self.subjects:
|
|
143
|
+
if self.itemPositionMem != -1:
|
|
144
|
+
self.lwModifiers.insertItem(self.itemPositionMem, f"{subject} ({key})" if key else subject)
|
|
145
|
+
else:
|
|
146
|
+
self.lwModifiers.addItem(f"{subject} ({key})" if key else subject)
|
|
147
|
+
|
|
148
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
149
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
def add_modifiers_from_file(self):
|
|
153
|
+
"""
|
|
154
|
+
add modifiers from file
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
file_name, _ = QFileDialog.getOpenFileName(self, "Load modifiers from file", "", "All files (*)")
|
|
158
|
+
if not file_name:
|
|
159
|
+
return
|
|
160
|
+
try:
|
|
161
|
+
with open(file_name) as f_in:
|
|
162
|
+
for line in f_in:
|
|
163
|
+
if line.strip():
|
|
164
|
+
for c in cfg.CHAR_FORBIDDEN_IN_MODIFIERS:
|
|
165
|
+
if c in line.strip():
|
|
166
|
+
QMessageBox.critical(
|
|
167
|
+
self,
|
|
168
|
+
cfg.programName,
|
|
169
|
+
(
|
|
170
|
+
f"The character <b>{c}</b> is not allowed.<br>"
|
|
171
|
+
"The following characters are not allowed in modifiers:<br>"
|
|
172
|
+
f"<b>{cfg.CHAR_FORBIDDEN_IN_MODIFIERS}</b>"
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
break
|
|
176
|
+
else:
|
|
177
|
+
if line.strip() not in [self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())]:
|
|
178
|
+
if self.itemPositionMem != -1:
|
|
179
|
+
self.lwModifiers.insertItem(self.itemPositionMem, line.strip())
|
|
180
|
+
else:
|
|
181
|
+
self.lwModifiers.addItem(line.strip())
|
|
182
|
+
|
|
183
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
184
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
185
|
+
]
|
|
186
|
+
except Exception:
|
|
187
|
+
QMessageBox.warning(self, cfg.programName, f"Error reading modifiers from file:<br>{file_name}")
|
|
188
|
+
logging.warning(f"Error reading modifiers from file<br>{file_name}")
|
|
189
|
+
|
|
190
|
+
def sort_modifiers(self):
|
|
191
|
+
"""
|
|
192
|
+
sort modifiers
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
modifiers = sorted([self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())])
|
|
196
|
+
self.lwModifiers.clear()
|
|
197
|
+
for modifier in modifiers:
|
|
198
|
+
if self.itemPositionMem != -1:
|
|
199
|
+
self.lwModifiers.insertItem(self.itemPositionMem, modifier)
|
|
200
|
+
else:
|
|
201
|
+
self.lwModifiers.addItem(modifier)
|
|
202
|
+
|
|
203
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
204
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
def set_name_changed(self):
|
|
208
|
+
"""
|
|
209
|
+
set name changed
|
|
210
|
+
"""
|
|
211
|
+
if not self.modifiers_sets_dict:
|
|
212
|
+
self.modifiers_sets_dict["0"] = {"name": "", "description": "", "type": cfg.SINGLE_SELECTION, "values": []}
|
|
213
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["name"] = self.le_name.text().strip()
|
|
214
|
+
|
|
215
|
+
def set_description_changed(self):
|
|
216
|
+
"""
|
|
217
|
+
set description changed
|
|
218
|
+
"""
|
|
219
|
+
if not self.modifiers_sets_dict:
|
|
220
|
+
self.modifiers_sets_dict["0"] = {"name": "", "description": "", "type": cfg.SINGLE_SELECTION, "values": []}
|
|
221
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["description"] = self.le_description.text().strip()
|
|
222
|
+
|
|
223
|
+
def type_changed(self):
|
|
224
|
+
"""
|
|
225
|
+
type changed
|
|
226
|
+
"""
|
|
227
|
+
if not self.modifiers_sets_dict:
|
|
228
|
+
self.modifiers_sets_dict["0"] = {"name": "", "description": "", "type": cfg.SINGLE_SELECTION, "values": []}
|
|
229
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["type"] = self.cbType.currentIndex()
|
|
230
|
+
# disable if modifier numeric or value from external data file
|
|
231
|
+
for obj in (
|
|
232
|
+
self.lbValues,
|
|
233
|
+
self.lwModifiers,
|
|
234
|
+
self.leModifier,
|
|
235
|
+
self.leCode,
|
|
236
|
+
self.lbModifier,
|
|
237
|
+
self.lbCode,
|
|
238
|
+
self.lbCodeHelp,
|
|
239
|
+
self.pbMoveUp,
|
|
240
|
+
self.pbMoveDown,
|
|
241
|
+
self.pbRemoveModifier,
|
|
242
|
+
self.pb_add_subjects,
|
|
243
|
+
self.pbAddModifier,
|
|
244
|
+
self.pbModifyModifier,
|
|
245
|
+
self.pb_load_file,
|
|
246
|
+
self.pb_sort_modifiers,
|
|
247
|
+
):
|
|
248
|
+
obj.setEnabled(self.cbType.currentIndex() not in [cfg.NUMERIC_MODIFIER, cfg.EXTERNAL_DATA_MODIFIER])
|
|
249
|
+
if self.cbType.currentIndex() == cfg.EXTERNAL_DATA_MODIFIER:
|
|
250
|
+
self.lb_name.setText("Variable name")
|
|
251
|
+
else:
|
|
252
|
+
self.lb_name.setText("Set name")
|
|
253
|
+
|
|
254
|
+
# def ask_at_stop_changed(self):
|
|
255
|
+
# """
|
|
256
|
+
# value changed
|
|
257
|
+
# """
|
|
258
|
+
# self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["ask at stop"] = self.cb_ask_at_stop.isChecked()
|
|
259
|
+
|
|
260
|
+
def moveSetLeft(self):
|
|
261
|
+
"""
|
|
262
|
+
move selected modifiers set left
|
|
263
|
+
"""
|
|
264
|
+
if self.tabWidgetModifiersSets.currentIndex():
|
|
265
|
+
(
|
|
266
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex() - 1)],
|
|
267
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())],
|
|
268
|
+
) = (
|
|
269
|
+
dict(self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]),
|
|
270
|
+
dict(self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex() - 1)]),
|
|
271
|
+
)
|
|
272
|
+
self.tabWidgetModifiersSets.setCurrentIndex(self.tabWidgetModifiersSets.currentIndex() - 1)
|
|
273
|
+
self.tabMem = self.tabWidgetModifiersSets.currentIndex()
|
|
274
|
+
|
|
275
|
+
def moveSetRight(self):
|
|
276
|
+
"""
|
|
277
|
+
move selected modifiers set right
|
|
278
|
+
"""
|
|
279
|
+
if self.tabWidgetModifiersSets.currentIndex() < self.tabWidgetModifiersSets.count() - 1:
|
|
280
|
+
(
|
|
281
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex() + 1)],
|
|
282
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())],
|
|
283
|
+
) = (
|
|
284
|
+
dict(self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]),
|
|
285
|
+
dict(self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex() + 1)]),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
self.tabWidgetModifiersSets.setCurrentIndex(self.tabWidgetModifiersSets.currentIndex() + 1)
|
|
289
|
+
self.tabMem = self.tabWidgetModifiersSets.currentIndex()
|
|
290
|
+
|
|
291
|
+
def moveModifierUp(self):
|
|
292
|
+
"""
|
|
293
|
+
move up the selected modifier
|
|
294
|
+
"""
|
|
295
|
+
if self.lwModifiers.currentRow() >= 0:
|
|
296
|
+
currentRow = self.lwModifiers.currentRow()
|
|
297
|
+
currentItem = self.lwModifiers.takeItem(currentRow)
|
|
298
|
+
self.lwModifiers.insertItem(currentRow - 1, currentItem)
|
|
299
|
+
self.lwModifiers.setCurrentItem(currentItem)
|
|
300
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
301
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
def moveModifierDown(self):
|
|
305
|
+
"""
|
|
306
|
+
move down the selected modifier
|
|
307
|
+
"""
|
|
308
|
+
if self.lwModifiers.currentRow() >= 0:
|
|
309
|
+
currentRow = self.lwModifiers.currentRow()
|
|
310
|
+
currentItem = self.lwModifiers.takeItem(currentRow)
|
|
311
|
+
self.lwModifiers.insertItem(currentRow + 1, currentItem)
|
|
312
|
+
self.lwModifiers.setCurrentItem(currentItem)
|
|
313
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
314
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
def add_set_of_modifiers(self):
|
|
318
|
+
"""
|
|
319
|
+
Add a set of modifiers
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
# no modifiers set
|
|
323
|
+
if self.tabWidgetModifiersSets.currentIndex() == -1:
|
|
324
|
+
self.modifiers_sets_dict[str(len(self.modifiers_sets_dict))] = {
|
|
325
|
+
"name": "",
|
|
326
|
+
"description": "",
|
|
327
|
+
"type": cfg.SINGLE_SELECTION,
|
|
328
|
+
"ask at stop": False,
|
|
329
|
+
"values": [],
|
|
330
|
+
}
|
|
331
|
+
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{len(self.modifiers_sets_dict)}")
|
|
332
|
+
self.tabWidgetModifiersSets.setCurrentIndex(self.tabWidgetModifiersSets.count() - 1)
|
|
333
|
+
self.tabMem = self.tabWidgetModifiersSets.currentIndex()
|
|
334
|
+
|
|
335
|
+
# set visible and available buttons and others elements
|
|
336
|
+
for w in (
|
|
337
|
+
self.lb_name,
|
|
338
|
+
self.lbType,
|
|
339
|
+
self.lbValues,
|
|
340
|
+
self.le_name,
|
|
341
|
+
self.lb_description,
|
|
342
|
+
self.le_description,
|
|
343
|
+
self.cbType,
|
|
344
|
+
self.lwModifiers,
|
|
345
|
+
self.pbMoveUp,
|
|
346
|
+
self.pbMoveDown,
|
|
347
|
+
self.pbRemoveModifier,
|
|
348
|
+
self.pbRemoveSet,
|
|
349
|
+
self.pbMoveSetLeft,
|
|
350
|
+
self.pbMoveSetRight,
|
|
351
|
+
self.pb_add_subjects,
|
|
352
|
+
self.pb_load_file,
|
|
353
|
+
self.pb_sort_modifiers,
|
|
354
|
+
):
|
|
355
|
+
w.setVisible(True)
|
|
356
|
+
self.cb_ask_at_stop.setVisible(self.ask_at_stop_enabled)
|
|
357
|
+
|
|
358
|
+
for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
|
|
359
|
+
w.setEnabled(True)
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
if len(self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]):
|
|
363
|
+
self.modifiers_sets_dict[str(len(self.modifiers_sets_dict))] = {
|
|
364
|
+
"name": "",
|
|
365
|
+
"description": "",
|
|
366
|
+
"type": cfg.SINGLE_SELECTION,
|
|
367
|
+
"ask at stop": False,
|
|
368
|
+
"values": [],
|
|
369
|
+
}
|
|
370
|
+
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{len(self.modifiers_sets_dict)}")
|
|
371
|
+
self.tabWidgetModifiersSets.setCurrentIndex(self.tabWidgetModifiersSets.count() - 1)
|
|
372
|
+
self.tabMem = self.tabWidgetModifiersSets.currentIndex()
|
|
373
|
+
|
|
374
|
+
else:
|
|
375
|
+
QMessageBox.information(
|
|
376
|
+
self,
|
|
377
|
+
cfg.programName,
|
|
378
|
+
"It is not possible to add a modifiers' set while the current modifiers' set is empty.",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def remove_set_of_modifiers(self):
|
|
382
|
+
"""
|
|
383
|
+
remove set of modifiers
|
|
384
|
+
"""
|
|
385
|
+
|
|
386
|
+
if self.tabWidgetModifiersSets.currentIndex() != -1:
|
|
387
|
+
if dialog.MessageDialog(cfg.programName, "Confirm deletion of this set of modifiers?", [cfg.YES, cfg.NO]) == cfg.YES:
|
|
388
|
+
index_to_delete = self.tabWidgetModifiersSets.currentIndex()
|
|
389
|
+
|
|
390
|
+
for k in range(index_to_delete, len(self.modifiers_sets_dict) - 1):
|
|
391
|
+
self.modifiers_sets_dict[str(k)] = self.modifiers_sets_dict[str(k + 1)]
|
|
392
|
+
# del last key
|
|
393
|
+
del self.modifiers_sets_dict[str(len(self.modifiers_sets_dict) - 1)]
|
|
394
|
+
|
|
395
|
+
# remove all tabs
|
|
396
|
+
while self.tabWidgetModifiersSets.count():
|
|
397
|
+
self.tabWidgetModifiersSets.removeTab(0)
|
|
398
|
+
|
|
399
|
+
# recreate tabs
|
|
400
|
+
for idx in sorted_keys(self.modifiers_sets_dict):
|
|
401
|
+
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{int(idx) + 1}")
|
|
402
|
+
|
|
403
|
+
# set not visible and not available buttons and others elements
|
|
404
|
+
if self.tabWidgetModifiersSets.currentIndex() == -1:
|
|
405
|
+
for w in (
|
|
406
|
+
self.lb_name,
|
|
407
|
+
self.le_name,
|
|
408
|
+
self.lbType,
|
|
409
|
+
self.lbValues,
|
|
410
|
+
self.lb_description,
|
|
411
|
+
self.le_description,
|
|
412
|
+
self.cbType,
|
|
413
|
+
self.lwModifiers,
|
|
414
|
+
self.pbMoveUp,
|
|
415
|
+
self.pbMoveDown,
|
|
416
|
+
self.pbRemoveModifier,
|
|
417
|
+
self.pbRemoveSet,
|
|
418
|
+
self.pbMoveSetLeft,
|
|
419
|
+
self.pbMoveSetRight,
|
|
420
|
+
self.cb_ask_at_stop,
|
|
421
|
+
):
|
|
422
|
+
w.setVisible(False)
|
|
423
|
+
for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
|
|
424
|
+
w.setEnabled(False)
|
|
425
|
+
|
|
426
|
+
if not len(self.modifiers_sets_dict):
|
|
427
|
+
# set invisible and unavailable buttons and others elements
|
|
428
|
+
for w in (
|
|
429
|
+
self.lb_name,
|
|
430
|
+
self.le_name,
|
|
431
|
+
self.lbType,
|
|
432
|
+
self.lbValues,
|
|
433
|
+
self.lb_description,
|
|
434
|
+
self.le_description,
|
|
435
|
+
self.cbType,
|
|
436
|
+
self.lwModifiers,
|
|
437
|
+
self.pbMoveUp,
|
|
438
|
+
self.pbMoveDown,
|
|
439
|
+
self.pbRemoveModifier,
|
|
440
|
+
self.pbRemoveSet,
|
|
441
|
+
self.pbMoveSetLeft,
|
|
442
|
+
self.pbMoveSetRight,
|
|
443
|
+
self.pb_add_subjects,
|
|
444
|
+
self.pb_load_file,
|
|
445
|
+
self.pb_sort_modifiers,
|
|
446
|
+
):
|
|
447
|
+
w.setVisible(False)
|
|
448
|
+
for w in [self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier]:
|
|
449
|
+
w.setEnabled(False)
|
|
450
|
+
return
|
|
451
|
+
|
|
452
|
+
else:
|
|
453
|
+
QMessageBox.information(self, cfg.programName, "It is not possible to remove the last modifiers' set.")
|
|
454
|
+
|
|
455
|
+
def modifyModifier(self):
|
|
456
|
+
"""
|
|
457
|
+
modify modifier <- arrow
|
|
458
|
+
"""
|
|
459
|
+
|
|
460
|
+
if self.lwModifiers.currentRow() >= 0:
|
|
461
|
+
txt = self.lwModifiers.currentItem().text()
|
|
462
|
+
code = ""
|
|
463
|
+
if "(" in txt and ")" in txt:
|
|
464
|
+
code = txt.split("(")[1].split(")")[0]
|
|
465
|
+
|
|
466
|
+
self.leModifier.setText(txt.split("(")[0].strip())
|
|
467
|
+
self.leCode.setText(code)
|
|
468
|
+
|
|
469
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"].remove(
|
|
470
|
+
self.lwModifiers.currentItem().text()
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
self.itemPositionMem = self.lwModifiers.currentRow()
|
|
474
|
+
self.lwModifiers.takeItem(self.lwModifiers.currentRow())
|
|
475
|
+
else:
|
|
476
|
+
QMessageBox.information(self, cfg.programName, "Select a modifier to modify from the modifiers set")
|
|
477
|
+
|
|
478
|
+
def removeModifier(self):
|
|
479
|
+
"""
|
|
480
|
+
remove modifier from set
|
|
481
|
+
"""
|
|
482
|
+
if self.lwModifiers.currentIndex().row() >= 0:
|
|
483
|
+
self.lwModifiers.takeItem(self.lwModifiers.currentIndex().row())
|
|
484
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
485
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
486
|
+
]
|
|
487
|
+
|
|
488
|
+
def addModifier(self):
|
|
489
|
+
"""
|
|
490
|
+
add a modifier to set
|
|
491
|
+
"""
|
|
492
|
+
|
|
493
|
+
txt = self.leModifier.text().strip()
|
|
494
|
+
for c in cfg.CHAR_FORBIDDEN_IN_MODIFIERS:
|
|
495
|
+
if c in txt:
|
|
496
|
+
QMessageBox.critical(
|
|
497
|
+
self,
|
|
498
|
+
cfg.programName,
|
|
499
|
+
(
|
|
500
|
+
f"The character <b>{c}</b> is not allowed.<br>"
|
|
501
|
+
"The following characters are not allowed in modifiers:<br>"
|
|
502
|
+
f"<b>{cfg.CHAR_FORBIDDEN_IN_MODIFIERS}</b>"
|
|
503
|
+
),
|
|
504
|
+
)
|
|
505
|
+
self.leModifier.setFocus()
|
|
506
|
+
return
|
|
507
|
+
|
|
508
|
+
if txt:
|
|
509
|
+
if txt in [self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())]:
|
|
510
|
+
QMessageBox.critical(self, cfg.programName, f"The modifier <b>{txt}</b> is already in the list")
|
|
511
|
+
return
|
|
512
|
+
|
|
513
|
+
if not self.modifiers_sets_dict:
|
|
514
|
+
self.modifiers_sets_dict["0"] = {
|
|
515
|
+
"name": "",
|
|
516
|
+
"description": "",
|
|
517
|
+
"type": cfg.SINGLE_SELECTION,
|
|
518
|
+
"ask at stop": False,
|
|
519
|
+
"values": [],
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if len(self.leCode.text().strip()) > 1:
|
|
523
|
+
if self.leCode.text().strip().upper() not in cfg.function_keys.values():
|
|
524
|
+
QMessageBox.critical(
|
|
525
|
+
self,
|
|
526
|
+
cfg.programName,
|
|
527
|
+
"The modifier key code can not exceed one key\nSelect one key or a function key (F1, F2 ... F12)",
|
|
528
|
+
)
|
|
529
|
+
self.leCode.setFocus()
|
|
530
|
+
return
|
|
531
|
+
|
|
532
|
+
if self.leCode.text().strip():
|
|
533
|
+
for c in cfg.CHAR_FORBIDDEN_IN_MODIFIERS:
|
|
534
|
+
if c in self.leCode.text().strip():
|
|
535
|
+
QMessageBox.critical(
|
|
536
|
+
self,
|
|
537
|
+
cfg.programName,
|
|
538
|
+
f"The modifier key code is not allowed {cfg.CHAR_FORBIDDEN_IN_MODIFIERS}!",
|
|
539
|
+
)
|
|
540
|
+
self.leCode.setFocus()
|
|
541
|
+
return
|
|
542
|
+
|
|
543
|
+
# check if code already exists
|
|
544
|
+
if not self.modifiers_sets_dict:
|
|
545
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())] = {
|
|
546
|
+
"name": "",
|
|
547
|
+
"description": "",
|
|
548
|
+
"type": cfg.SINGLE_SELECTION,
|
|
549
|
+
"ask at stop": False,
|
|
550
|
+
"values": [],
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if "(" + self.leCode.text().strip() + ")" in " ".join(
|
|
554
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"]
|
|
555
|
+
):
|
|
556
|
+
QMessageBox.critical(self, cfg.programName, f"The shortcut code <b>{self.leCode.text().strip()}</b> already exists!")
|
|
557
|
+
self.leCode.setFocus()
|
|
558
|
+
return
|
|
559
|
+
txt += f" ({self.leCode.text().strip()})"
|
|
560
|
+
|
|
561
|
+
if self.itemPositionMem != -1:
|
|
562
|
+
self.lwModifiers.insertItem(self.itemPositionMem, txt)
|
|
563
|
+
else:
|
|
564
|
+
self.lwModifiers.addItem(txt)
|
|
565
|
+
|
|
566
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
|
|
567
|
+
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
568
|
+
]
|
|
569
|
+
self.leModifier.setText("")
|
|
570
|
+
self.leCode.setText("")
|
|
571
|
+
|
|
572
|
+
else:
|
|
573
|
+
QMessageBox.critical(self, cfg.programName, "No modifier to add!")
|
|
574
|
+
self.leModifier.setFocus()
|
|
575
|
+
|
|
576
|
+
def tabWidgetModifiersSets_changed(self, tabIndex):
|
|
577
|
+
"""
|
|
578
|
+
user changed the tab widget
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
# check if modifier field empty
|
|
582
|
+
if self.leModifier.text() and tabIndex != self.tabMem:
|
|
583
|
+
if (
|
|
584
|
+
dialog.MessageDialog(
|
|
585
|
+
cfg.programName,
|
|
586
|
+
(
|
|
587
|
+
"You are working on a behavior.<br>"
|
|
588
|
+
"If you change the modifier's set it will be lost.<br>"
|
|
589
|
+
"Do you want to change modifiers set"
|
|
590
|
+
),
|
|
591
|
+
[cfg.YES, cfg.NO],
|
|
592
|
+
)
|
|
593
|
+
== cfg.NO
|
|
594
|
+
):
|
|
595
|
+
self.tabWidgetModifiersSets.setCurrentIndex(self.tabMem)
|
|
596
|
+
return
|
|
597
|
+
|
|
598
|
+
if tabIndex != self.tabMem:
|
|
599
|
+
self.lwModifiers.clear()
|
|
600
|
+
self.leCode.clear()
|
|
601
|
+
self.leModifier.clear()
|
|
602
|
+
# if self.ask_at_stop_enabled:
|
|
603
|
+
# self.cb_ask_at_stop.setChecked(False)
|
|
604
|
+
|
|
605
|
+
self.tabMem = tabIndex
|
|
606
|
+
|
|
607
|
+
if tabIndex != -1:
|
|
608
|
+
self.le_name.setText(self.modifiers_sets_dict[str(tabIndex)]["name"])
|
|
609
|
+
self.le_description.setText(self.modifiers_sets_dict[str(tabIndex)].get("description", ""))
|
|
610
|
+
self.cbType.setCurrentIndex(self.modifiers_sets_dict[str(tabIndex)]["type"])
|
|
611
|
+
# if self.ask_at_stop_enabled:
|
|
612
|
+
# self.cb_ask_at_stop.setChecked(self.modifiers_sets_dict[str(tabIndex)].get("ask at stop", False))
|
|
613
|
+
|
|
614
|
+
self.lwModifiers.addItems(self.modifiers_sets_dict[str(tabIndex)]["values"])
|
|
615
|
+
|
|
616
|
+
def get_modifiers(self) -> str:
|
|
617
|
+
"""
|
|
618
|
+
returns modifiers as string
|
|
619
|
+
"""
|
|
620
|
+
keys_to_delete: list = []
|
|
621
|
+
for idx in self.modifiers_sets_dict:
|
|
622
|
+
# add ask_at_stop value (boolean) to each set of modifiers
|
|
623
|
+
if self.ask_at_stop_enabled:
|
|
624
|
+
self.modifiers_sets_dict[idx]["ask at stop"] = self.cb_ask_at_stop.isChecked()
|
|
625
|
+
# delete modifiers without values for selection
|
|
626
|
+
if (
|
|
627
|
+
self.modifiers_sets_dict[idx]["type"] in (cfg.SINGLE_SELECTION, cfg.MULTI_SELECTION)
|
|
628
|
+
and not self.modifiers_sets_dict[idx]["values"]
|
|
629
|
+
):
|
|
630
|
+
keys_to_delete.append(idx)
|
|
631
|
+
|
|
632
|
+
for idx in keys_to_delete:
|
|
633
|
+
del self.modifiers_sets_dict[idx]
|
|
634
|
+
|
|
635
|
+
return json.dumps(self.modifiers_sets_dict) if self.modifiers_sets_dict else ""
|