boris-behav-obs 8.16.6__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.
Files changed (125) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +24 -40
  4. boris/add_modifier.py +88 -80
  5. boris/add_modifier_ui.py +235 -131
  6. boris/advanced_event_filtering.py +23 -29
  7. boris/analysis_plugins/__init__.py +0 -0
  8. boris/analysis_plugins/_latency.py +59 -0
  9. boris/analysis_plugins/irr_cohen_kappa.py +109 -0
  10. boris/analysis_plugins/irr_cohen_kappa_with_modifiers.py +112 -0
  11. boris/analysis_plugins/irr_weighted_cohen_kappa.py +157 -0
  12. boris/analysis_plugins/irr_weighted_cohen_kappa_with_modifiers.py +162 -0
  13. boris/analysis_plugins/list_of_dataframe_columns.py +22 -0
  14. boris/analysis_plugins/number_of_occurences.py +22 -0
  15. boris/analysis_plugins/number_of_occurences_by_independent_variable.py +54 -0
  16. boris/analysis_plugins/time_budget.py +61 -0
  17. boris/behav_coding_map_creator.py +228 -229
  18. boris/behavior_binary_table.py +33 -50
  19. boris/behaviors_coding_map.py +17 -18
  20. boris/boris_cli.py +6 -25
  21. boris/cmd_arguments.py +12 -1
  22. boris/coding_pad.py +16 -34
  23. boris/config.py +101 -49
  24. boris/config_file.py +55 -64
  25. boris/connections.py +105 -58
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2108 -1275
  30. boris/core_qrc.py +15892 -10829
  31. boris/core_ui.py +941 -806
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +134 -0
  34. boris/dialog.py +461 -242
  35. boris/duration_widget.py +9 -14
  36. boris/edit_event.py +61 -31
  37. boris/edit_event_ui.py +208 -97
  38. boris/event_operations.py +405 -281
  39. boris/events_cursor.py +25 -17
  40. boris/events_snapshots.py +36 -82
  41. boris/exclusion_matrix.py +4 -9
  42. boris/export_events.py +180 -203
  43. boris/export_observation.py +60 -73
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +427 -218
  46. boris/gui_utilities.py +91 -14
  47. boris/image_overlay.py +4 -4
  48. boris/import_observations.py +190 -98
  49. boris/ipc_mpv.py +304 -0
  50. boris/irr.py +20 -57
  51. boris/latency.py +31 -24
  52. boris/measurement_widget.py +14 -18
  53. boris/media_file.py +17 -19
  54. boris/menu_options.py +16 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv2.py +127 -36
  58. boris/observation.py +493 -210
  59. boris/observation_operations.py +1010 -391
  60. boris/observation_ui.py +573 -363
  61. boris/observations_list.py +51 -58
  62. boris/otx_parser.py +74 -68
  63. boris/param_panel.py +45 -59
  64. boris/param_panel_ui.py +254 -138
  65. boris/player_dock_widget.py +91 -56
  66. boris/plot_data_module.py +18 -53
  67. boris/plot_events.py +56 -153
  68. boris/plot_events_rt.py +16 -30
  69. boris/plot_spectrogram_rt.py +80 -56
  70. boris/plot_waveform_rt.py +23 -48
  71. boris/plugins.py +431 -0
  72. boris/portion/__init__.py +18 -8
  73. boris/portion/const.py +35 -18
  74. boris/portion/dict.py +5 -5
  75. boris/portion/func.py +2 -2
  76. boris/portion/interval.py +21 -41
  77. boris/portion/io.py +41 -32
  78. boris/preferences.py +298 -123
  79. boris/preferences_ui.py +664 -225
  80. boris/project.py +293 -270
  81. boris/project_functions.py +610 -537
  82. boris/project_import_export.py +204 -213
  83. boris/project_ui.py +673 -441
  84. boris/qrc_boris.py +6 -3
  85. boris/qrc_boris5.py +6 -3
  86. boris/select_modifiers.py +62 -90
  87. boris/select_observations.py +19 -197
  88. boris/select_subj_behav.py +67 -39
  89. boris/state_events.py +51 -33
  90. boris/subjects_pad.py +6 -8
  91. boris/synthetic_time_budget.py +25 -17
  92. boris/time_budget_functions.py +169 -169
  93. boris/time_budget_widget.py +71 -86
  94. boris/transitions.py +41 -41
  95. boris/utilities.py +562 -222
  96. boris/version.py +3 -3
  97. boris/video_equalizer.py +16 -14
  98. boris/video_equalizer_ui.py +199 -130
  99. boris/video_operations.py +78 -28
  100. boris/view_df.py +104 -0
  101. boris/view_df_ui.py +75 -0
  102. boris/write_event.py +240 -136
  103. boris_behav_obs-9.7.1.dist-info/METADATA +140 -0
  104. boris_behav_obs-9.7.1.dist-info/RECORD +109 -0
  105. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.1.dist-info}/WHEEL +1 -1
  106. boris_behav_obs-9.7.1.dist-info/entry_points.txt +2 -0
  107. boris/README.TXT +0 -22
  108. boris/add_modifier.ui +0 -323
  109. boris/converters.ui +0 -289
  110. boris/core.qrc +0 -37
  111. boris/core.ui +0 -1571
  112. boris/edit_event.ui +0 -233
  113. boris/icons/logo_eye.ico +0 -0
  114. boris/map_creator.py +0 -982
  115. boris/observation.ui +0 -814
  116. boris/param_panel.ui +0 -379
  117. boris/preferences.ui +0 -537
  118. boris/project.ui +0 -1074
  119. boris/vlc_local.py +0 -90
  120. boris_behav_obs-8.16.6.dist-info/LICENSE.TXT +0 -674
  121. boris_behav_obs-8.16.6.dist-info/METADATA +0 -134
  122. boris_behav_obs-8.16.6.dist-info/RECORD +0 -106
  123. boris_behav_obs-8.16.6.dist-info/entry_points.txt +0 -2
  124. {boris → boris_behav_obs-9.7.1.dist-info/licenses}/LICENSE.TXT +0 -0
  125. {boris_behav_obs-8.16.6.dist-info → boris_behav_obs-9.7.1.dist-info}/top_level.txt +0 -0
boris/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
4
 
5
- Copyright 2012-2023 Olivier Friard
5
+ Copyright 2012-2025 Olivier Friard
6
6
 
7
7
  This file is part of BORIS.
8
8
 
boris/__main__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
4
+ Copyright 2012-2025 Olivier Friard
5
5
 
6
6
  This file is part of BORIS.
7
7
 
boris/about.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  BORIS
3
3
  Behavioral Observation Research Interactive Software
4
- Copyright 2012-2023 Olivier Friard
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,24 +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
- # check if MPV API v. 1
37
- # is v. 1 use the old version of mpv.py
38
- try:
39
- if "libmpv.so.1" in mpv.sofile:
40
- from . import mpv as mpv
41
- except AttributeError:
42
- if "mpv-1.dll" in mpv.dll:
43
- from . import mpv as mpv
44
-
45
- except RuntimeError: # libmpv found but version too old
46
- from . import mpv as mpv
47
-
48
- from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR
49
- from PyQt5.QtGui import QPixmap
50
- from PyQt5.QtWidgets import QMessageBox
34
+ from PySide6.QtGui import QPixmap
35
+ from PySide6.QtWidgets import QMessageBox
51
36
 
52
37
 
53
38
  def actionAbout_activated(self):
@@ -55,19 +40,16 @@ def actionAbout_activated(self):
55
40
  About dialog
56
41
  """
57
42
 
58
- programs_versions = ["MPV media player"]
59
- try:
60
- python_mpv_version = mpv.__version__
61
- except Exception:
62
- python_mpv_version = "Not found"
63
- if sys.platform.startswith("linux"):
64
- programs_versions.append(
65
- f"Library version: {'.'.join([str(x) for x in mpv._mpv_client_api_version()])} file: {mpv.sofile} python-mpv version: {python_mpv_version}"
66
- )
67
- if sys.platform.startswith("win"):
68
- programs_versions.append(
69
- f"Library version: {'.'.join([str(x) for x in mpv._mpv_client_api_version()])} file: {mpv.dll} python-mpv version: {python_mpv_version}"
43
+ programs_versions: list = ["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()}"
70
51
  )
52
+ )
71
53
 
72
54
  # ffmpeg
73
55
  if self.ffmpeg_bin == "ffmpeg" and sys.platform.startswith("linux"):
@@ -95,9 +77,7 @@ def actionAbout_activated(self):
95
77
  # graphviz
96
78
  gv_result = subprocess.getoutput("dot -V")
97
79
 
98
- programs_versions.extend(
99
- ["\nGraphViz", gv_result if "graphviz" in gv_result else "not installed", "https://www.graphviz.org/"]
100
- )
80
+ programs_versions.extend(["\nGraphViz", gv_result if "graphviz" in gv_result else "not installed", "https://www.graphviz.org/"])
101
81
 
102
82
  about_dialog = QMessageBox()
103
83
  about_dialog.setIconPixmap(QPixmap(":/boris_unito"))
@@ -110,28 +90,29 @@ def actionAbout_activated(self):
110
90
  about_dialog.setInformativeText(
111
91
  (
112
92
  f"<b>{cfg.programName}</b> v. {version.__version__} - {version.__version_date__}"
113
- "<p>Copyright &copy; 2012-2023 Olivier Friard - Marco Gamba<br>"
93
+ "<p>Copyright &copy; 2012-2025 Olivier Friard - Marco Gamba<br>"
114
94
  "Department of Life Sciences and Systems Biology<br>"
115
95
  "University of Torino - Italy<br>"
116
96
  "<br>"
117
- 'BORIS is released under the <a href="http://www.gnu.org/copyleft/gpl.html">GNU General Public License</a><br>'
118
- 'See <a href="http://www.boris.unito.it">www.boris.unito.it</a> for more details.<br>'
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>'
119
99
  "<br>"
120
100
  "The authors would like to acknowledge Valentina Matteucci for her precious help."
121
101
  "<hr>"
122
102
  "How to cite BORIS:<br>"
123
103
  "Friard, O. and Gamba, M. (2016), BORIS: a free, versatile open-source event-logging software for video/audio "
124
104
  "coding and live observations. Methods Ecol Evol, 7: 1325–1330.<br>"
125
- '<a href="http://onlinelibrary.wiley.com/doi/10.1111/2041-210X.12584/abstract">DOI:10.1111/2041-210X.12584</a>'
105
+ '<a href="https://besjournals.onlinelibrary.wiley.com/doi/full/10.1111/2041-210X.12584">DOI:10.1111/2041-210X.12584</a>'
126
106
  )
127
107
  )
108
+ """
128
109
  n = "\n"
129
110
  current_system = platform.uname()
130
111
  details = (
131
112
  f"Operating system: {current_system.system} {current_system.release} {current_system.version} \n"
132
113
  f"CPU: {current_system.machine} {current_system.processor}\n\n"
133
114
  f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})"
134
- f" - Qt {QT_VERSION_STR} - PyQt {PYQT_VERSION_STR}\n\n"
115
+ f"Qt {qVersion()} - PySide {PySide6.__version__}\n"
135
116
  )
136
117
 
137
118
  r, memory = util.mem_info()
@@ -140,8 +121,11 @@ def actionAbout_activated(self):
140
121
  f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
141
122
  f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb\n\n"
142
123
  )
124
+ """
125
+
126
+ details = util.get_systeminfo()
143
127
 
144
- details += n.join(programs_versions)
128
+ details += "\n".join(programs_versions)
145
129
  """
146
130
  memory_in_use = f"{utilities.rss_memory_used(self.pid)} Mb" if utilities.rss_memory_used(self.pid) != -1 else "Not available"
147
131
  percent_memory_in_use = (f"({utilities.rss_memory_percent_used(self.pid):.1f} % of total memory)"
@@ -156,4 +140,4 @@ def actionAbout_activated(self):
156
140
 
157
141
  about_dialog.setDetailedText(details)
158
142
 
159
- _ = about_dialog.exec_()
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-2023 Olivier Friard
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/licenses/>.
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 PyQt5.QtGui import QIcon
27
- from PyQt5.QtWidgets import QDialog, QWidget, QFileDialog, QMessageBox
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.addSet)
52
- self.pbRemoveSet.clicked.connect(self.removeSet)
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("ok"))
66
- self.pbCancel.clicked.connect(lambda: self.pb_pushed("cancel"))
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
68
  self.le_name.textChanged.connect(self.set_name_changed)
69
69
  self.le_description.textChanged.connect(self.set_description_changed)
70
70
 
71
71
  self.cbType.currentIndexChanged.connect(self.type_changed)
72
72
 
73
- dummy_dict = json.loads(modifiers_str) if modifiers_str else {}
74
- modif_values = []
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
 
@@ -104,6 +109,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
104
109
  self.pb_add_subjects,
105
110
  self.pb_load_file,
106
111
  self.pb_sort_modifiers,
112
+ self.cb_ask_at_stop,
107
113
  ):
108
114
  w.setVisible(False)
109
115
  for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
@@ -113,24 +119,19 @@ class addModifierDialog(QDialog, Ui_Dialog):
113
119
  self.tabMem = 0
114
120
 
115
121
  def pb_pushed(self, button):
116
-
117
122
  if self.leModifier.text():
118
123
  if (
119
124
  dialog.MessageDialog(
120
125
  cfg.programName,
121
- (
122
- "You are working on a behavior.<br>"
123
- "If you close the window it will be lost.<br>"
124
- "Do you want to change modifiers set"
125
- ),
126
- ["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],
127
128
  )
128
129
  == cfg.CANCEL
129
130
  ):
130
131
  return
131
- if button == "ok":
132
+ if button == cfg.OK:
132
133
  self.accept()
133
- if button == "cancel":
134
+ if button == cfg.CANCEL:
134
135
  self.reject()
135
136
 
136
137
  def add_subjects(self):
@@ -153,43 +154,38 @@ class addModifierDialog(QDialog, Ui_Dialog):
153
154
  add modifiers from file
154
155
  """
155
156
 
156
- fn = QFileDialog().getOpenFileName(self, "Load modifiers from file", "", "All files (*)")
157
- file_name = fn[0] if type(fn) is tuple else fn
158
- if file_name:
159
- try:
160
- with open(file_name) as f_in:
161
- for line in f_in:
162
- if line.strip():
163
-
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
-
178
- if line.strip() not in [
179
- self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
180
- ]:
181
-
182
- if self.itemPositionMem != -1:
183
- self.lwModifiers.insertItem(self.itemPositionMem, line.strip())
184
- else:
185
- self.lwModifiers.addItem(line.strip())
186
-
187
- self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"] = [
188
- self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())
189
- ]
190
- except Exception:
191
- QMessageBox.warning(self, cfg.programName, f"Error reading modifiers from file:<br>{file_name}")
192
- 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}")
193
189
 
194
190
  def sort_modifiers(self):
195
191
  """
@@ -222,9 +218,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
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_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
  """
@@ -257,6 +251,12 @@ class addModifierDialog(QDialog, Ui_Dialog):
257
251
  else:
258
252
  self.lb_name.setText("Set name")
259
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
260
  def moveSetLeft(self):
261
261
  """
262
262
  move selected modifiers set left
@@ -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 addSet(self):
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)}")
@@ -353,6 +353,8 @@ class addModifierDialog(QDialog, Ui_Dialog):
353
353
  self.pb_sort_modifiers,
354
354
  ):
355
355
  w.setVisible(True)
356
+ self.cb_ask_at_stop.setVisible(self.ask_at_stop_enabled)
357
+
356
358
  for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
357
359
  w.setEnabled(True)
358
360
  return
@@ -362,6 +364,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
362
364
  "name": "",
363
365
  "description": "",
364
366
  "type": cfg.SINGLE_SELECTION,
367
+ "ask at stop": False,
365
368
  "values": [],
366
369
  }
367
370
  self.tabWidgetModifiersSets.addTab(QWidget(), f"Set #{len(self.modifiers_sets_dict)}")
@@ -375,16 +378,13 @@ class addModifierDialog(QDialog, Ui_Dialog):
375
378
  "It is not possible to add a modifiers' set while the current modifiers' set is empty.",
376
379
  )
377
380
 
378
- def removeSet(self):
381
+ def remove_set_of_modifiers(self):
379
382
  """
380
383
  remove set of modifiers
381
384
  """
382
385
 
383
386
  if self.tabWidgetModifiersSets.currentIndex() != -1:
384
- if (
385
- dialog.MessageDialog(cfg.programName, "Confirm deletion of this set of modifiers?", [cfg.YES, cfg.NO])
386
- == cfg.YES
387
- ):
387
+ if dialog.MessageDialog(cfg.programName, "Confirm deletion of this set of modifiers?", [cfg.YES, cfg.NO]) == cfg.YES:
388
388
  index_to_delete = self.tabWidgetModifiersSets.currentIndex()
389
389
 
390
390
  for k in range(index_to_delete, len(self.modifiers_sets_dict) - 1):
@@ -417,14 +417,15 @@ class addModifierDialog(QDialog, Ui_Dialog):
417
417
  self.pbRemoveSet,
418
418
  self.pbMoveSetLeft,
419
419
  self.pbMoveSetRight,
420
+ self.cb_ask_at_stop,
420
421
  ):
421
422
  w.setVisible(False)
422
- for w in [self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier]:
423
+ for w in (self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier):
423
424
  w.setEnabled(False)
424
425
 
425
426
  if not len(self.modifiers_sets_dict):
426
427
  # set invisible and unavailable buttons and others elements
427
- for w in [
428
+ for w in (
428
429
  self.lb_name,
429
430
  self.le_name,
430
431
  self.lbType,
@@ -442,7 +443,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
442
443
  self.pb_add_subjects,
443
444
  self.pb_load_file,
444
445
  self.pb_sort_modifiers,
445
- ]:
446
+ ):
446
447
  w.setVisible(False)
447
448
  for w in [self.leModifier, self.leCode, self.pbAddModifier, self.pbModifyModifier]:
448
449
  w.setEnabled(False)
@@ -505,7 +506,6 @@ class addModifierDialog(QDialog, Ui_Dialog):
505
506
  return
506
507
 
507
508
  if txt:
508
-
509
509
  if txt in [self.lwModifiers.item(x).text() for x in range(self.lwModifiers.count())]:
510
510
  QMessageBox.critical(self, cfg.programName, f"The modifier <b>{txt}</b> is already in the list")
511
511
  return
@@ -515,6 +515,7 @@ class addModifierDialog(QDialog, Ui_Dialog):
515
515
  "name": "",
516
516
  "description": "",
517
517
  "type": cfg.SINGLE_SELECTION,
518
+ "ask at stop": False,
518
519
  "values": [],
519
520
  }
520
521
 
@@ -545,16 +546,14 @@ class addModifierDialog(QDialog, Ui_Dialog):
545
546
  "name": "",
546
547
  "description": "",
547
548
  "type": cfg.SINGLE_SELECTION,
549
+ "ask at stop": False,
548
550
  "values": [],
549
551
  }
550
552
 
551
553
  if "(" + self.leCode.text().strip() + ")" in " ".join(
552
554
  self.modifiers_sets_dict[str(self.tabWidgetModifiersSets.currentIndex())]["values"]
553
555
  ):
554
-
555
- QMessageBox.critical(
556
- self, cfg.programName, f"The shortcut code <b>{self.leCode.text().strip()}</b> already exists!"
557
- )
556
+ QMessageBox.critical(self, cfg.programName, f"The shortcut code <b>{self.leCode.text().strip()}</b> already exists!")
558
557
  self.leCode.setFocus()
559
558
  return
560
559
  txt += f" ({self.leCode.text().strip()})"
@@ -600,6 +599,8 @@ class addModifierDialog(QDialog, Ui_Dialog):
600
599
  self.lwModifiers.clear()
601
600
  self.leCode.clear()
602
601
  self.leModifier.clear()
602
+ # if self.ask_at_stop_enabled:
603
+ # self.cb_ask_at_stop.setChecked(False)
603
604
 
604
605
  self.tabMem = tabIndex
605
606
 
@@ -607,14 +608,21 @@ class addModifierDialog(QDialog, Ui_Dialog):
607
608
  self.le_name.setText(self.modifiers_sets_dict[str(tabIndex)]["name"])
608
609
  self.le_description.setText(self.modifiers_sets_dict[str(tabIndex)].get("description", ""))
609
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
+
610
614
  self.lwModifiers.addItems(self.modifiers_sets_dict[str(tabIndex)]["values"])
611
615
 
612
- def getModifiers(self) -> str:
616
+ def get_modifiers(self) -> str:
613
617
  """
614
618
  returns modifiers as string
615
619
  """
616
- keys_to_delete = []
620
+ keys_to_delete: list = []
617
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
618
626
  if (
619
627
  self.modifiers_sets_dict[idx]["type"] in (cfg.SINGLE_SELECTION, cfg.MULTI_SELECTION)
620
628
  and not self.modifiers_sets_dict[idx]["values"]