boris-behav-obs 8.12__py3-none-any.whl → 9.7.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of boris-behav-obs might be problematic. Click here for more details.
- boris/__init__.py +1 -1
- boris/__main__.py +1 -1
- boris/about.py +28 -39
- boris/add_modifier.py +122 -109
- boris/add_modifier_ui.py +239 -135
- boris/advanced_event_filtering.py +81 -45
- boris/analysis_plugins/__init__.py +0 -0
- boris/analysis_plugins/_latency.py +59 -0
- boris/analysis_plugins/irr_cohen_kappa.py +109 -0
- boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
- boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
- boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
- boris/analysis_plugins/number_of_occurences.py +22 -0
- boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
- boris/analysis_plugins/time_budget.py +61 -0
- boris/behav_coding_map_creator.py +228 -229
- boris/behavior_binary_table.py +33 -50
- boris/behaviors_coding_map.py +17 -18
- boris/boris_cli.py +6 -25
- boris/cmd_arguments.py +12 -1
- boris/coding_pad.py +42 -49
- boris/config.py +141 -65
- boris/config_file.py +58 -67
- boris/connections.py +107 -61
- boris/converters.py +13 -37
- boris/converters_ui.py +187 -110
- boris/cooccurence.py +250 -0
- boris/core.py +2373 -1786
- boris/core_qrc.py +15895 -10743
- boris/core_ui.py +943 -798
- boris/db_functions.py +17 -42
- boris/dev.py +109 -8
- boris/dialog.py +482 -236
- boris/duration_widget.py +9 -14
- boris/edit_event.py +61 -31
- boris/edit_event_ui.py +208 -97
- boris/event_operations.py +408 -293
- boris/events_cursor.py +25 -17
- boris/events_snapshots.py +36 -82
- boris/exclusion_matrix.py +4 -9
- boris/export_events.py +184 -223
- boris/export_observation.py +74 -100
- boris/external_processes.py +123 -98
- boris/geometric_measurement.py +644 -290
- boris/gui_utilities.py +91 -14
- boris/image_overlay.py +4 -4
- boris/import_observations.py +190 -98
- boris/ipc_mpv.py +325 -0
- boris/irr.py +20 -57
- boris/latency.py +31 -24
- boris/measurement_widget.py +14 -18
- boris/media_file.py +17 -19
- boris/menu_options.py +17 -6
- boris/modifier_coding_map_creator.py +1013 -0
- boris/modifiers_coding_map.py +7 -9
- boris/mpv.py +1 -0
- boris/mpv2.py +732 -705
- boris/observation.py +533 -221
- boris/observation_operations.py +1025 -390
- boris/observation_ui.py +572 -362
- boris/observations_list.py +71 -53
- boris/otx_parser.py +74 -68
- boris/param_panel.py +31 -16
- boris/param_panel_ui.py +254 -138
- boris/player_dock_widget.py +90 -60
- boris/plot_data_module.py +25 -33
- boris/plot_events.py +127 -90
- boris/plot_events_rt.py +17 -31
- boris/plot_spectrogram_rt.py +95 -30
- boris/plot_waveform_rt.py +32 -21
- boris/plugins.py +431 -0
- boris/portion/__init__.py +18 -8
- boris/portion/const.py +35 -18
- boris/portion/dict.py +5 -5
- boris/portion/func.py +2 -2
- boris/portion/interval.py +21 -41
- boris/portion/io.py +41 -32
- boris/preferences.py +306 -83
- boris/preferences_ui.py +684 -227
- boris/project.py +448 -293
- boris/project_functions.py +671 -238
- boris/project_import_export.py +213 -222
- boris/project_ui.py +674 -438
- boris/qrc_boris.py +6 -3
- boris/qrc_boris5.py +6 -3
- boris/select_modifiers.py +74 -48
- boris/select_observations.py +20 -198
- boris/select_subj_behav.py +67 -39
- boris/state_events.py +52 -35
- boris/subjects_pad.py +6 -9
- boris/synthetic_time_budget.py +45 -28
- boris/time_budget_functions.py +171 -171
- boris/time_budget_widget.py +84 -114
- boris/transitions.py +41 -47
- boris/utilities.py +627 -236
- boris/version.py +3 -3
- boris/video_equalizer.py +16 -14
- boris/video_equalizer_ui.py +199 -130
- boris/video_operations.py +95 -29
- boris/view_df.py +104 -0
- boris/view_df_ui.py +75 -0
- boris/write_event.py +538 -0
- boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
- boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
- {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
- boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
- boris/README.TXT +0 -22
- boris/add_modifier.ui +0 -323
- boris/converters.ui +0 -289
- boris/core.qrc +0 -36
- boris/core.ui +0 -1556
- boris/edit_event.ui +0 -233
- boris/icons/logo_eye.ico +0 -0
- boris/map_creator.py +0 -850
- boris/observation.ui +0 -814
- boris/param_panel.ui +0 -379
- boris/preferences.ui +0 -537
- boris/project.ui +0 -1069
- boris/project_server.py +0 -236
- boris/vlc.py +0 -10343
- boris/vlc_local.py +0 -90
- boris_behav_obs-8.12.dist-info/LICENSE.TXT +0 -674
- boris_behav_obs-8.12.dist-info/METADATA +0 -128
- boris_behav_obs-8.12.dist-info/RECORD +0 -108
- boris_behav_obs-8.12.dist-info/entry_points.txt +0 -3
- {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
- {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
boris/__init__.py
CHANGED
boris/__main__.py
CHANGED
boris/about.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 program is free software; you can redistribute it and/or modify
|
|
7
7
|
it under the terms of the GNU General Public License as published by
|
|
@@ -30,23 +30,9 @@ from . import version
|
|
|
30
30
|
from . import config as cfg
|
|
31
31
|
from . import utilities as util
|
|
32
32
|
|
|
33
|
-
try:
|
|
34
|
-
from . import mpv2 as mpv
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if "libmpv.so.1" in mpv.sofile:
|
|
39
|
-
from . import mpv as mpv
|
|
40
|
-
except AttributeError:
|
|
41
|
-
if "mpv-1.dll" in mpv.dll:
|
|
42
|
-
from . import mpv as mpv
|
|
43
|
-
|
|
44
|
-
except RuntimeError: # libmpv found but version too old
|
|
45
|
-
from . import mpv as mpv
|
|
46
|
-
|
|
47
|
-
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR
|
|
48
|
-
from PyQt5.QtGui import QPixmap
|
|
49
|
-
from PyQt5.QtWidgets import QMessageBox
|
|
34
|
+
from PySide6.QtGui import QPixmap
|
|
35
|
+
from PySide6.QtWidgets import QMessageBox
|
|
50
36
|
|
|
51
37
|
|
|
52
38
|
def actionAbout_activated(self):
|
|
@@ -54,25 +40,26 @@ def actionAbout_activated(self):
|
|
|
54
40
|
About dialog
|
|
55
41
|
"""
|
|
56
42
|
|
|
57
|
-
programs_versions = ["MPV media player"]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
f"
|
|
43
|
+
programs_versions: list[str] = ["MPV media player"]
|
|
44
|
+
|
|
45
|
+
mpv_lib_version, mpv_lib_file_path, mpv_api_version = util.mpv_lib_version()
|
|
46
|
+
programs_versions.append(
|
|
47
|
+
(
|
|
48
|
+
f"Library version: {mpv_lib_version} file: {mpv_lib_file_path}\n"
|
|
49
|
+
f"MPV API version: {mpv_api_version}\n"
|
|
50
|
+
f"python-mpv version: {util.python_mpv_script_version()}"
|
|
65
51
|
)
|
|
52
|
+
)
|
|
66
53
|
|
|
67
54
|
# ffmpeg
|
|
68
55
|
if self.ffmpeg_bin == "ffmpeg" and sys.platform.startswith("linux"):
|
|
69
|
-
ffmpeg_true_path = subprocess.getoutput("which ffmpeg")
|
|
56
|
+
ffmpeg_true_path: str = subprocess.getoutput("which ffmpeg")
|
|
70
57
|
else:
|
|
71
58
|
ffmpeg_true_path = self.ffmpeg_bin
|
|
72
59
|
programs_versions.extend(
|
|
73
60
|
[
|
|
74
61
|
"\nFFmpeg",
|
|
75
|
-
subprocess.getoutput(f'"{self.ffmpeg_bin}" -version').split("\n")[0],
|
|
62
|
+
subprocess.getoutput(cmd=f'"{self.ffmpeg_bin}" -version').split(sep="\n")[0],
|
|
76
63
|
f"Path: {ffmpeg_true_path}",
|
|
77
64
|
"https://www.ffmpeg.org",
|
|
78
65
|
]
|
|
@@ -88,13 +75,11 @@ def actionAbout_activated(self):
|
|
|
88
75
|
programs_versions.extend(["\nPandas", f"version {pd.__version__}", "https://pandas.pydata.org"])
|
|
89
76
|
|
|
90
77
|
# graphviz
|
|
91
|
-
gv_result = subprocess.getoutput("dot -V")
|
|
78
|
+
gv_result = subprocess.getoutput(cmd="dot -V")
|
|
92
79
|
|
|
93
|
-
programs_versions.extend(
|
|
94
|
-
["\nGraphViz", gv_result if "graphviz" in gv_result else "not installed", "https://www.graphviz.org/"]
|
|
95
|
-
)
|
|
80
|
+
programs_versions.extend(["\nGraphViz", gv_result if "graphviz" in gv_result else "not installed", "https://www.graphviz.org/"])
|
|
96
81
|
|
|
97
|
-
about_dialog = QMessageBox()
|
|
82
|
+
about_dialog: QMessageBox = QMessageBox()
|
|
98
83
|
about_dialog.setIconPixmap(QPixmap(":/boris_unito"))
|
|
99
84
|
|
|
100
85
|
about_dialog.setWindowTitle(f"About {cfg.programName}")
|
|
@@ -105,28 +90,29 @@ def actionAbout_activated(self):
|
|
|
105
90
|
about_dialog.setInformativeText(
|
|
106
91
|
(
|
|
107
92
|
f"<b>{cfg.programName}</b> v. {version.__version__} - {version.__version_date__}"
|
|
108
|
-
"<p>Copyright © 2012-
|
|
93
|
+
"<p>Copyright © 2012-2025 Olivier Friard - Marco Gamba<br>"
|
|
109
94
|
"Department of Life Sciences and Systems Biology<br>"
|
|
110
95
|
"University of Torino - Italy<br>"
|
|
111
96
|
"<br>"
|
|
112
|
-
'BORIS is released under the <a href="
|
|
113
|
-
'See <a href="
|
|
97
|
+
'BORIS is released under the <a href="https://www.gnu.org/copyleft/gpl.html">GNU General Public License</a><br>'
|
|
98
|
+
'See <a href="https://www.boris.unito.it">www.boris.unito.it</a> for more details.<br>'
|
|
114
99
|
"<br>"
|
|
115
100
|
"The authors would like to acknowledge Valentina Matteucci for her precious help."
|
|
116
101
|
"<hr>"
|
|
117
102
|
"How to cite BORIS:<br>"
|
|
118
103
|
"Friard, O. and Gamba, M. (2016), BORIS: a free, versatile open-source event-logging software for video/audio "
|
|
119
104
|
"coding and live observations. Methods Ecol Evol, 7: 1325–1330.<br>"
|
|
120
|
-
'<a href="
|
|
105
|
+
'<a href="https://besjournals.onlinelibrary.wiley.com/doi/full/10.1111/2041-210X.12584">DOI:10.1111/2041-210X.12584</a>'
|
|
121
106
|
)
|
|
122
107
|
)
|
|
108
|
+
"""
|
|
123
109
|
n = "\n"
|
|
124
110
|
current_system = platform.uname()
|
|
125
111
|
details = (
|
|
126
112
|
f"Operating system: {current_system.system} {current_system.release} {current_system.version} \n"
|
|
127
113
|
f"CPU: {current_system.machine} {current_system.processor}\n\n"
|
|
128
114
|
f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})"
|
|
129
|
-
f"
|
|
115
|
+
f"Qt {qVersion()} - PySide {PySide6.__version__}\n"
|
|
130
116
|
)
|
|
131
117
|
|
|
132
118
|
r, memory = util.mem_info()
|
|
@@ -135,8 +121,11 @@ def actionAbout_activated(self):
|
|
|
135
121
|
f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
|
|
136
122
|
f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb\n\n"
|
|
137
123
|
)
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
details = util.get_systeminfo()
|
|
138
127
|
|
|
139
|
-
details += n.join(programs_versions)
|
|
128
|
+
details += "\n".join(programs_versions)
|
|
140
129
|
"""
|
|
141
130
|
memory_in_use = f"{utilities.rss_memory_used(self.pid)} Mb" if utilities.rss_memory_used(self.pid) != -1 else "Not available"
|
|
142
131
|
percent_memory_in_use = (f"({utilities.rss_memory_percent_used(self.pid):.1f} % of total memory)"
|
|
@@ -151,4 +140,4 @@ def actionAbout_activated(self):
|
|
|
151
140
|
|
|
152
141
|
about_dialog.setDetailedText(details)
|
|
153
142
|
|
|
154
|
-
_ = about_dialog.
|
|
143
|
+
_ = about_dialog.exec()
|
boris/add_modifier.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
|
|
|
@@ -16,15 +16,15 @@ This file is part of BORIS.
|
|
|
16
16
|
GNU General Public License for more details.
|
|
17
17
|
|
|
18
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/
|
|
19
|
+
along with this program; if not see <http://www.gnu.org/licPbehav_enses/>.
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
import logging
|
|
24
24
|
import json
|
|
25
25
|
|
|
26
|
-
from
|
|
27
|
-
from
|
|
26
|
+
from PySide6.QtGui import QIcon
|
|
27
|
+
from PySide6.QtWidgets import QDialog, QWidget, QFileDialog, QMessageBox
|
|
28
28
|
|
|
29
29
|
from . import dialog
|
|
30
30
|
from .add_modifier_ui import Ui_Dialog
|
|
@@ -33,12 +33,10 @@ from .utilities import sorted_keys
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class addModifierDialog(QDialog, Ui_Dialog):
|
|
36
|
-
|
|
37
36
|
tabMem = -1
|
|
38
37
|
itemPositionMem = -1
|
|
39
38
|
|
|
40
|
-
def __init__(self, modifiers_str, subjects=[], parent=None):
|
|
41
|
-
|
|
39
|
+
def __init__(self, modifiers_str: str, subjects: list = [], ask_at_stop_enabled: bool = False, parent=None):
|
|
42
40
|
super().__init__()
|
|
43
41
|
self.setupUi(self)
|
|
44
42
|
|
|
@@ -46,10 +44,12 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
46
44
|
if not self.subjects:
|
|
47
45
|
self.pb_add_subjects.setEnabled(False)
|
|
48
46
|
|
|
47
|
+
self.ask_at_stop_enabled = ask_at_stop_enabled
|
|
48
|
+
|
|
49
49
|
self.pbAddModifier.clicked.connect(self.addModifier)
|
|
50
50
|
self.pbAddModifier.setIcon(QIcon(":/frame_forward"))
|
|
51
|
-
self.pbAddSet.clicked.connect(self.
|
|
52
|
-
self.pbRemoveSet.clicked.connect(self.
|
|
51
|
+
self.pbAddSet.clicked.connect(self.add_set_of_modifiers)
|
|
52
|
+
self.pbRemoveSet.clicked.connect(self.remove_set_of_modifiers)
|
|
53
53
|
self.pbModifyModifier.clicked.connect(self.modifyModifier)
|
|
54
54
|
self.pbModifyModifier.setIcon(QIcon(":/frame_backward"))
|
|
55
55
|
|
|
@@ -62,22 +62,27 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
62
62
|
self.pb_add_subjects.clicked.connect(self.add_subjects)
|
|
63
63
|
self.pb_load_file.clicked.connect(self.add_modifiers_from_file)
|
|
64
64
|
|
|
65
|
-
self.pbOK.clicked.connect(lambda: self.pb_pushed(
|
|
66
|
-
self.pbCancel.clicked.connect(lambda: self.pb_pushed(
|
|
65
|
+
self.pbOK.clicked.connect(lambda: self.pb_pushed(cfg.OK))
|
|
66
|
+
self.pbCancel.clicked.connect(lambda: self.pb_pushed(cfg.CANCEL))
|
|
67
67
|
|
|
68
|
-
self.
|
|
69
|
-
self.
|
|
68
|
+
self.le_name.textChanged.connect(self.set_name_changed)
|
|
69
|
+
self.le_description.textChanged.connect(self.set_description_changed)
|
|
70
70
|
|
|
71
71
|
self.cbType.currentIndexChanged.connect(self.type_changed)
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
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 = []
|
|
75
77
|
for idx in sorted_keys(dummy_dict):
|
|
76
78
|
modif_values.append(dummy_dict[idx])
|
|
77
79
|
|
|
78
|
-
self.modifiers_sets_dict = {}
|
|
80
|
+
self.modifiers_sets_dict: dict = {}
|
|
79
81
|
for modif in modif_values:
|
|
80
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)
|
|
81
86
|
|
|
82
87
|
self.tabWidgetModifiersSets.currentChanged.connect(self.tabWidgetModifiersSets_changed)
|
|
83
88
|
|
|
@@ -86,11 +91,13 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
86
91
|
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{int(idx) + 1}")
|
|
87
92
|
|
|
88
93
|
if self.tabWidgetModifiersSets.currentIndex() == -1:
|
|
89
|
-
for w in
|
|
90
|
-
self.
|
|
94
|
+
for w in (
|
|
95
|
+
self.lb_name,
|
|
96
|
+
self.le_name,
|
|
91
97
|
self.lbType,
|
|
92
98
|
self.lbValues,
|
|
93
|
-
self.
|
|
99
|
+
self.lb_description,
|
|
100
|
+
self.le_description,
|
|
94
101
|
self.cbType,
|
|
95
102
|
self.lwModifiers,
|
|
96
103
|
self.pbMoveUp,
|
|
@@ -102,33 +109,29 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
102
109
|
self.pb_add_subjects,
|
|
103
110
|
self.pb_load_file,
|
|
104
111
|
self.pb_sort_modifiers,
|
|
105
|
-
|
|
112
|
+
self.cb_ask_at_stop,
|
|
113
|
+
):
|
|
106
114
|
w.setVisible(False)
|
|
107
|
-
for w in
|
|
115
|
+
for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
|
|
108
116
|
w.setEnabled(False)
|
|
109
117
|
|
|
110
118
|
# set first tab as active
|
|
111
119
|
self.tabMem = 0
|
|
112
120
|
|
|
113
121
|
def pb_pushed(self, button):
|
|
114
|
-
|
|
115
122
|
if self.leModifier.text():
|
|
116
123
|
if (
|
|
117
124
|
dialog.MessageDialog(
|
|
118
125
|
cfg.programName,
|
|
119
|
-
(
|
|
120
|
-
|
|
121
|
-
"If you close the window it will be lost.<br>"
|
|
122
|
-
"Do you want to change modifiers set"
|
|
123
|
-
),
|
|
124
|
-
["Close", cfg.CANCEL],
|
|
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],
|
|
125
128
|
)
|
|
126
129
|
== cfg.CANCEL
|
|
127
130
|
):
|
|
128
131
|
return
|
|
129
|
-
if button ==
|
|
132
|
+
if button == cfg.OK:
|
|
130
133
|
self.accept()
|
|
131
|
-
if button ==
|
|
134
|
+
if button == cfg.CANCEL:
|
|
132
135
|
self.reject()
|
|
133
136
|
|
|
134
137
|
def add_subjects(self):
|
|
@@ -151,43 +154,38 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
151
154
|
add modifiers from file
|
|
152
155
|
"""
|
|
153
156
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
187
|
-
]
|
|
188
|
-
except Exception:
|
|
189
|
-
QMessageBox.warning(self, cfg.programName, f"Error reading modifiers from file:<br>{file_name}")
|
|
190
|
-
logging.warning(f"Error reading modifiers from file<br>{file_name}")
|
|
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}")
|
|
191
189
|
|
|
192
190
|
def sort_modifiers(self):
|
|
193
191
|
"""
|
|
@@ -212,19 +210,15 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
212
210
|
"""
|
|
213
211
|
if not self.modifiers_sets_dict:
|
|
214
212
|
self.modifiers_sets_dict["0"] = {"name": "", "description": "", "type": cfg.SINGLE_SELECTION, "values": []}
|
|
215
|
-
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())][
|
|
216
|
-
"name"
|
|
217
|
-
] = self.leSetName.text().strip()
|
|
213
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["name"] = self.le_name.text().strip()
|
|
218
214
|
|
|
219
215
|
def set_description_changed(self):
|
|
220
216
|
"""
|
|
221
|
-
set
|
|
217
|
+
set description changed
|
|
222
218
|
"""
|
|
223
219
|
if not self.modifiers_sets_dict:
|
|
224
220
|
self.modifiers_sets_dict["0"] = {"name": "", "description": "", "type": cfg.SINGLE_SELECTION, "values": []}
|
|
225
|
-
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())][
|
|
226
|
-
"description"
|
|
227
|
-
] = self.le_set_description.text().strip()
|
|
221
|
+
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["description"] = self.le_description.text().strip()
|
|
228
222
|
|
|
229
223
|
def type_changed(self):
|
|
230
224
|
"""
|
|
@@ -234,7 +228,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
234
228
|
self.modifiers_sets_dict["0"] = {"name": "", "description": "", "type": cfg.SINGLE_SELECTION, "values": []}
|
|
235
229
|
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["type"] = self.cbType.currentIndex()
|
|
236
230
|
# disable if modifier numeric or value from external data file
|
|
237
|
-
for obj in
|
|
231
|
+
for obj in (
|
|
238
232
|
self.lbValues,
|
|
239
233
|
self.lwModifiers,
|
|
240
234
|
self.leModifier,
|
|
@@ -250,12 +244,18 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
250
244
|
self.pbModifyModifier,
|
|
251
245
|
self.pb_load_file,
|
|
252
246
|
self.pb_sort_modifiers,
|
|
253
|
-
|
|
247
|
+
):
|
|
254
248
|
obj.setEnabled(self.cbType.currentIndex() not in [cfg.NUMERIC_MODIFIER, cfg.EXTERNAL_DATA_MODIFIER])
|
|
255
249
|
if self.cbType.currentIndex() == cfg.EXTERNAL_DATA_MODIFIER:
|
|
256
|
-
self.
|
|
250
|
+
self.lb_name.setText("Variable name")
|
|
257
251
|
else:
|
|
258
|
-
self.
|
|
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
259
|
|
|
260
260
|
def moveSetLeft(self):
|
|
261
261
|
"""
|
|
@@ -277,7 +277,6 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
277
277
|
move selected modifiers set right
|
|
278
278
|
"""
|
|
279
279
|
if self.tabWidgetModifiersSets.currentIndex() < self.tabWidgetModifiersSets.count() - 1:
|
|
280
|
-
|
|
281
280
|
(
|
|
282
281
|
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex() + 1)],
|
|
283
282
|
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())],
|
|
@@ -315,7 +314,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
315
314
|
self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
|
|
316
315
|
]
|
|
317
316
|
|
|
318
|
-
def
|
|
317
|
+
def add_set_of_modifiers(self):
|
|
319
318
|
"""
|
|
320
319
|
Add a set of modifiers
|
|
321
320
|
"""
|
|
@@ -326,6 +325,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
326
325
|
"name": "",
|
|
327
326
|
"description": "",
|
|
328
327
|
"type": cfg.SINGLE_SELECTION,
|
|
328
|
+
"ask at stop": False,
|
|
329
329
|
"values": [],
|
|
330
330
|
}
|
|
331
331
|
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{len(self.modifiers_sets_dict)}")
|
|
@@ -333,12 +333,13 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
333
333
|
self.tabMem = self.tabWidgetModifiersSets.currentIndex()
|
|
334
334
|
|
|
335
335
|
# set visible and available buttons and others elements
|
|
336
|
-
for w in
|
|
337
|
-
self.
|
|
336
|
+
for w in (
|
|
337
|
+
self.lb_name,
|
|
338
338
|
self.lbType,
|
|
339
339
|
self.lbValues,
|
|
340
|
-
self.
|
|
341
|
-
self.
|
|
340
|
+
self.le_name,
|
|
341
|
+
self.lb_description,
|
|
342
|
+
self.le_description,
|
|
342
343
|
self.cbType,
|
|
343
344
|
self.lwModifiers,
|
|
344
345
|
self.pbMoveUp,
|
|
@@ -350,9 +351,11 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
350
351
|
self.pb_add_subjects,
|
|
351
352
|
self.pb_load_file,
|
|
352
353
|
self.pb_sort_modifiers,
|
|
353
|
-
|
|
354
|
+
):
|
|
354
355
|
w.setVisible(True)
|
|
355
|
-
|
|
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):
|
|
356
359
|
w.setEnabled(True)
|
|
357
360
|
return
|
|
358
361
|
|
|
@@ -361,6 +364,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
361
364
|
"name": "",
|
|
362
365
|
"description": "",
|
|
363
366
|
"type": cfg.SINGLE_SELECTION,
|
|
367
|
+
"ask at stop": False,
|
|
364
368
|
"values": [],
|
|
365
369
|
}
|
|
366
370
|
self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{len(self.modifiers_sets_dict)}")
|
|
@@ -374,16 +378,13 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
374
378
|
"It is not possible to add a modifiers' set while the current modifiers' set is empty.",
|
|
375
379
|
)
|
|
376
380
|
|
|
377
|
-
def
|
|
381
|
+
def remove_set_of_modifiers(self):
|
|
378
382
|
"""
|
|
379
383
|
remove set of modifiers
|
|
380
384
|
"""
|
|
381
385
|
|
|
382
386
|
if self.tabWidgetModifiersSets.currentIndex() != -1:
|
|
383
|
-
if (
|
|
384
|
-
dialog.MessageDialog(cfg.programName, "Confirm deletion of this set of modifiers?", [cfg.YES, cfg.NO])
|
|
385
|
-
== cfg.YES
|
|
386
|
-
):
|
|
387
|
+
if dialog.MessageDialog(cfg.programName, "Confirm deletion of this set of modifiers?", [cfg.YES, cfg.NO]) == cfg.YES:
|
|
387
388
|
index_to_delete = self.tabWidgetModifiersSets.currentIndex()
|
|
388
389
|
|
|
389
390
|
for k in range(index_to_delete, len(self.modifiers_sets_dict) - 1):
|
|
@@ -401,11 +402,13 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
401
402
|
|
|
402
403
|
# set not visible and not available buttons and others elements
|
|
403
404
|
if self.tabWidgetModifiersSets.currentIndex() == -1:
|
|
404
|
-
for w in
|
|
405
|
-
self.
|
|
405
|
+
for w in (
|
|
406
|
+
self.lb_name,
|
|
407
|
+
self.le_name,
|
|
406
408
|
self.lbType,
|
|
407
409
|
self.lbValues,
|
|
408
|
-
self.
|
|
410
|
+
self.lb_description,
|
|
411
|
+
self.le_description,
|
|
409
412
|
self.cbType,
|
|
410
413
|
self.lwModifiers,
|
|
411
414
|
self.pbMoveUp,
|
|
@@ -414,18 +417,21 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
414
417
|
self.pbRemoveSet,
|
|
415
418
|
self.pbMoveSetLeft,
|
|
416
419
|
self.pbMoveSetRight,
|
|
417
|
-
|
|
420
|
+
self.cb_ask_at_stop,
|
|
421
|
+
):
|
|
418
422
|
w.setVisible(False)
|
|
419
|
-
for w in
|
|
423
|
+
for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
|
|
420
424
|
w.setEnabled(False)
|
|
421
425
|
|
|
422
426
|
if not len(self.modifiers_sets_dict):
|
|
423
427
|
# set invisible and unavailable buttons and others elements
|
|
424
|
-
for w in
|
|
425
|
-
self.
|
|
428
|
+
for w in (
|
|
429
|
+
self.lb_name,
|
|
430
|
+
self.le_name,
|
|
426
431
|
self.lbType,
|
|
427
432
|
self.lbValues,
|
|
428
|
-
self.
|
|
433
|
+
self.lb_description,
|
|
434
|
+
self.le_description,
|
|
429
435
|
self.cbType,
|
|
430
436
|
self.lwModifiers,
|
|
431
437
|
self.pbMoveUp,
|
|
@@ -437,7 +443,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
437
443
|
self.pb_add_subjects,
|
|
438
444
|
self.pb_load_file,
|
|
439
445
|
self.pb_sort_modifiers,
|
|
440
|
-
|
|
446
|
+
):
|
|
441
447
|
w.setVisible(False)
|
|
442
448
|
for w in [self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier]:
|
|
443
449
|
w.setEnabled(False)
|
|
@@ -500,7 +506,6 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
500
506
|
return
|
|
501
507
|
|
|
502
508
|
if txt:
|
|
503
|
-
|
|
504
509
|
if txt in [self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())]:
|
|
505
510
|
QMessageBox.critical(self, cfg.programName, f"The modifier <b>{txt}</b> is already in the list")
|
|
506
511
|
return
|
|
@@ -510,6 +515,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
510
515
|
"name": "",
|
|
511
516
|
"description": "",
|
|
512
517
|
"type": cfg.SINGLE_SELECTION,
|
|
518
|
+
"ask at stop": False,
|
|
513
519
|
"values": [],
|
|
514
520
|
}
|
|
515
521
|
|
|
@@ -540,16 +546,14 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
540
546
|
"name": "",
|
|
541
547
|
"description": "",
|
|
542
548
|
"type": cfg.SINGLE_SELECTION,
|
|
549
|
+
"ask at stop": False,
|
|
543
550
|
"values": [],
|
|
544
551
|
}
|
|
545
552
|
|
|
546
553
|
if "(" + self.leCode.text().strip() + ")" in " ".join(
|
|
547
554
|
self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"]
|
|
548
555
|
):
|
|
549
|
-
|
|
550
|
-
QMessageBox.critical(
|
|
551
|
-
self, cfg.programName, f"The shortcut code <b>{self.leCode.text().strip()}</b> already exists!"
|
|
552
|
-
)
|
|
556
|
+
QMessageBox.critical(self, cfg.programName, f"The shortcut code <b>{self.leCode.text().strip()}</b> already exists!")
|
|
553
557
|
self.leCode.setFocus()
|
|
554
558
|
return
|
|
555
559
|
txt += f" ({self.leCode.text().strip()})"
|
|
@@ -595,21 +599,30 @@ class addModifierDialog(QDialog, Ui_Dialog):
|
|
|
595
599
|
self.lwModifiers.clear()
|
|
596
600
|
self.leCode.clear()
|
|
597
601
|
self.leModifier.clear()
|
|
602
|
+
# if self.ask_at_stop_enabled:
|
|
603
|
+
# self.cb_ask_at_stop.setChecked(False)
|
|
598
604
|
|
|
599
605
|
self.tabMem = tabIndex
|
|
600
606
|
|
|
601
607
|
if tabIndex != -1:
|
|
602
|
-
self.
|
|
603
|
-
self.
|
|
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", ""))
|
|
604
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
|
+
|
|
605
614
|
self.lwModifiers.addItems(self.modifiers_sets_dict[str(tabIndex)]["values"])
|
|
606
615
|
|
|
607
|
-
def
|
|
616
|
+
def get_modifiers(self) -> str:
|
|
608
617
|
"""
|
|
609
618
|
returns modifiers as string
|
|
610
619
|
"""
|
|
611
|
-
keys_to_delete = []
|
|
620
|
+
keys_to_delete: list = []
|
|
612
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
|
|
613
626
|
if (
|
|
614
627
|
self.modifiers_sets_dict[idx]["type"] in (cfg.SINGLE_SELECTION, cfg.MULTI_SELECTION)
|
|
615
628
|
and not self.modifiers_sets_dict[idx]["values"]
|