boris-behav-obs 9.4__py2.py3-none-any.whl → 9.5__py2.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/analysis_plugins/_latency.py +1 -1
- boris/config.py +10 -0
- boris/connections.py +2 -1
- boris/core.py +45 -54
- boris/core_ui.py +6 -2
- boris/external_processes.py +98 -73
- boris/import_observations.py +28 -19
- boris/observation.py +8 -4
- boris/observation_operations.py +17 -1
- boris/player_dock_widget.py +0 -24
- boris/plot_events.py +1 -1
- boris/plot_events_rt.py +1 -1
- boris/plot_spectrogram_rt.py +62 -13
- boris/plot_waveform_rt.py +1 -1
- boris/plugins.py +136 -25
- boris/preferences.py +182 -108
- boris/preferences_ui.py +216 -32
- boris/project_functions.py +1 -3
- boris/utilities.py +21 -14
- boris/version.py +2 -2
- boris/write_event.py +11 -2
- {boris_behav_obs-9.4.dist-info → boris_behav_obs-9.5.dist-info}/METADATA +4 -1
- {boris_behav_obs-9.4.dist-info → boris_behav_obs-9.5.dist-info}/RECORD +27 -28
- {boris_behav_obs-9.4.dist-info → boris_behav_obs-9.5.dist-info}/WHEEL +1 -1
- boris/1.py +0 -45
- {boris_behav_obs-9.4.dist-info → boris_behav_obs-9.5.dist-info}/entry_points.txt +0 -0
- {boris_behav_obs-9.4.dist-info → boris_behav_obs-9.5.dist-info}/licenses/LICENSE.TXT +0 -0
- {boris_behav_obs-9.4.dist-info → boris_behav_obs-9.5.dist-info}/top_level.txt +0 -0
boris/plugins.py
CHANGED
|
@@ -47,12 +47,12 @@ def add_plugins_to_menu(self):
|
|
|
47
47
|
self.menu_plugins.addAction(action)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def get_plugin_name(plugin_path: str):
|
|
50
|
+
def get_plugin_name(plugin_path: str) -> str | None:
|
|
51
51
|
"""
|
|
52
|
-
get name of plugin
|
|
52
|
+
get name of a Python plugin
|
|
53
53
|
"""
|
|
54
54
|
# search plugin name
|
|
55
|
-
plugin_name = None
|
|
55
|
+
plugin_name: str | None = None
|
|
56
56
|
with open(plugin_path, "r") as f_in:
|
|
57
57
|
for line in f_in:
|
|
58
58
|
if line.startswith("__plugin_name__"):
|
|
@@ -61,6 +61,48 @@ def get_plugin_name(plugin_path: str):
|
|
|
61
61
|
return plugin_name
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def get_r_plugin_name(plugin_path: str) -> str | None:
|
|
65
|
+
"""
|
|
66
|
+
get name of a R plugin
|
|
67
|
+
"""
|
|
68
|
+
# search plugin name
|
|
69
|
+
plugin_name: str | None = None
|
|
70
|
+
with open(plugin_path, "r") as f_in:
|
|
71
|
+
for line in f_in:
|
|
72
|
+
if line.startswith("plugin_name"):
|
|
73
|
+
if "=" in line:
|
|
74
|
+
plugin_name = line.split("=")[1].strip().replace('"', "").replace("'", "")
|
|
75
|
+
break
|
|
76
|
+
elif "<-" in line:
|
|
77
|
+
plugin_name = line.split("<-")[1].strip().replace('"', "").replace("'", "")
|
|
78
|
+
break
|
|
79
|
+
else:
|
|
80
|
+
plugin_name = None
|
|
81
|
+
break
|
|
82
|
+
return plugin_name
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_r_plugin_description(plugin_path: str) -> str | None:
|
|
86
|
+
"""
|
|
87
|
+
get description of a R plugin
|
|
88
|
+
"""
|
|
89
|
+
# search plugin name
|
|
90
|
+
plugin_description: str | None = None
|
|
91
|
+
with open(plugin_path, "r") as f_in:
|
|
92
|
+
for line in f_in:
|
|
93
|
+
if line.startswith("description"):
|
|
94
|
+
if "=" in line:
|
|
95
|
+
plugin_description = line.split("=")[1].strip().replace('"', "").replace("'", "")
|
|
96
|
+
break
|
|
97
|
+
elif "<-" in line:
|
|
98
|
+
plugin_description = line.split("<-")[1].strip().replace('"', "").replace("'", "")
|
|
99
|
+
break
|
|
100
|
+
else:
|
|
101
|
+
plugin_description = None
|
|
102
|
+
break
|
|
103
|
+
return plugin_description
|
|
104
|
+
|
|
105
|
+
|
|
64
106
|
def load_plugins(self):
|
|
65
107
|
"""
|
|
66
108
|
load selected plugins in analysis menu
|
|
@@ -80,8 +122,6 @@ def load_plugins(self):
|
|
|
80
122
|
|
|
81
123
|
# load BORIS plugins
|
|
82
124
|
for file_ in sorted((Path(__file__).parent / "analysis_plugins").glob("*.py")):
|
|
83
|
-
if file_.name == "__init__.py":
|
|
84
|
-
continue
|
|
85
125
|
if file_.name.startswith("_"):
|
|
86
126
|
continue
|
|
87
127
|
plugin_name = get_plugin_name(file_)
|
|
@@ -96,8 +136,6 @@ def load_plugins(self):
|
|
|
96
136
|
# load personal plugins
|
|
97
137
|
if self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""):
|
|
98
138
|
for file_ in sorted(Path(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, "")).glob("*.py")):
|
|
99
|
-
if file_.name == "__init__.py":
|
|
100
|
-
continue
|
|
101
139
|
if file_.name.startswith("_"):
|
|
102
140
|
continue
|
|
103
141
|
plugin_name = get_plugin_name(file_)
|
|
@@ -109,6 +147,20 @@ def load_plugins(self):
|
|
|
109
147
|
|
|
110
148
|
self.config_param[cfg.ANALYSIS_PLUGINS][plugin_name] = str(file_)
|
|
111
149
|
|
|
150
|
+
# load personal R plugins
|
|
151
|
+
if self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""):
|
|
152
|
+
for file_ in sorted(Path(self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, "")).glob("*.R")):
|
|
153
|
+
if file_.name.startswith("_"):
|
|
154
|
+
continue
|
|
155
|
+
plugin_name = get_r_plugin_name(file_)
|
|
156
|
+
if plugin_name is not None and plugin_name not in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
157
|
+
# check if plugin with same name already loaded
|
|
158
|
+
if plugin_name in self.config_param[cfg.ANALYSIS_PLUGINS]:
|
|
159
|
+
msg()
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
self.config_param[cfg.ANALYSIS_PLUGINS][plugin_name] = str(file_)
|
|
163
|
+
|
|
112
164
|
logging.debug(f"{self.config_param.get(cfg.ANALYSIS_PLUGINS, {})=}")
|
|
113
165
|
|
|
114
166
|
|
|
@@ -203,7 +255,7 @@ def run_plugin(self, plugin_name):
|
|
|
203
255
|
QMessageBox.critical(self, cfg.programName, f"Plugin '{plugin_name}' not found")
|
|
204
256
|
return
|
|
205
257
|
|
|
206
|
-
plugin_path = self.config_param.get(cfg.ANALYSIS_PLUGINS, {})
|
|
258
|
+
plugin_path: str = self.config_param.get(cfg.ANALYSIS_PLUGINS, {}).get(plugin_name, "")
|
|
207
259
|
|
|
208
260
|
logging.debug(f"{plugin_path=}")
|
|
209
261
|
|
|
@@ -213,24 +265,12 @@ def run_plugin(self, plugin_name):
|
|
|
213
265
|
|
|
214
266
|
logging.debug(f"run plugin from {plugin_path}")
|
|
215
267
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
spec = importlib.util.spec_from_file_location(module_name, plugin_path)
|
|
219
|
-
plugin_module = importlib.util.module_from_spec(spec)
|
|
220
|
-
|
|
221
|
-
logging.debug(f"{plugin_module=}")
|
|
222
|
-
|
|
223
|
-
spec.loader.exec_module(plugin_module)
|
|
224
|
-
|
|
225
|
-
logging.info(
|
|
226
|
-
f"{plugin_module.__plugin_name__} loaded v.{getattr(plugin_module, '__version__')} v. {getattr(plugin_module, '__version_date__')}"
|
|
227
|
-
)
|
|
228
|
-
|
|
268
|
+
# select observations to analyze
|
|
229
269
|
selected_observations, parameters = self.obs_param()
|
|
230
270
|
if not selected_observations:
|
|
231
271
|
return
|
|
232
272
|
|
|
233
|
-
logging.info("preparing
|
|
273
|
+
logging.info("preparing dataframe for plugin")
|
|
234
274
|
|
|
235
275
|
df = project_functions.project2dataframe(self.pj, selected_observations)
|
|
236
276
|
|
|
@@ -247,8 +287,79 @@ def run_plugin(self, plugin_name):
|
|
|
247
287
|
filtered_df = plugin_df_filter(df, observations_list=selected_observations, parameters=parameters)
|
|
248
288
|
logging.info("done")
|
|
249
289
|
|
|
250
|
-
|
|
251
|
-
|
|
290
|
+
if Path(plugin_path).suffix == ".py":
|
|
291
|
+
# load plugin as module
|
|
292
|
+
module_name = Path(plugin_path).stem
|
|
293
|
+
|
|
294
|
+
spec = importlib.util.spec_from_file_location(module_name, plugin_path)
|
|
295
|
+
plugin_module = importlib.util.module_from_spec(spec)
|
|
296
|
+
|
|
297
|
+
logging.debug(f"{plugin_module=}")
|
|
298
|
+
|
|
299
|
+
spec.loader.exec_module(plugin_module)
|
|
300
|
+
|
|
301
|
+
plugin_version = plugin_module.__version__
|
|
302
|
+
plugin_version_date = plugin_module.__version_date__
|
|
303
|
+
|
|
304
|
+
logging.info(
|
|
305
|
+
f"{plugin_module.__plugin_name__} loaded v.{getattr(plugin_module, '__version__')} v. {getattr(plugin_module, '__version_date__')}"
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# run plugin
|
|
309
|
+
plugin_results = plugin_module.run(filtered_df)
|
|
310
|
+
|
|
311
|
+
if Path(plugin_path).suffix in (".R", ".r"):
|
|
312
|
+
try:
|
|
313
|
+
from rpy2 import robjects
|
|
314
|
+
from rpy2.robjects import pandas2ri
|
|
315
|
+
from rpy2.robjects.packages import SignatureTranslatedAnonymousPackage
|
|
316
|
+
from rpy2.robjects.conversion import localconverter
|
|
317
|
+
except Exception:
|
|
318
|
+
QMessageBox.critical(self, cfg.programName, "The rpy2 Python module is not installed. R plugins cannot be used")
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
# Read code from file
|
|
322
|
+
try:
|
|
323
|
+
with open(plugin_path, "r") as f:
|
|
324
|
+
r_code = f.read()
|
|
325
|
+
except Exception:
|
|
326
|
+
QMessageBox.critical(self, cfg.programName, f"Error reading the plugin {plugin_path}.")
|
|
327
|
+
return
|
|
328
|
+
|
|
329
|
+
# read version
|
|
330
|
+
plugin_version = next(
|
|
331
|
+
(
|
|
332
|
+
x.split("<-")[1].replace('"', "").replace("'", "").strip()
|
|
333
|
+
for x in r_code.splitlines()
|
|
334
|
+
if x.replace(" ", "").startswith("version<-")
|
|
335
|
+
),
|
|
336
|
+
None,
|
|
337
|
+
)
|
|
338
|
+
# read version date
|
|
339
|
+
plugin_version_date = next(
|
|
340
|
+
(
|
|
341
|
+
x.split("<-")[1].replace('"', "").replace("'", "").strip()
|
|
342
|
+
for x in r_code.split("\n")
|
|
343
|
+
if x.replace(" ", "").startswith("version_date<")
|
|
344
|
+
),
|
|
345
|
+
None,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
r_plugin = SignatureTranslatedAnonymousPackage(r_code, "r_plugin")
|
|
349
|
+
|
|
350
|
+
with localconverter(robjects.default_converter + pandas2ri.converter):
|
|
351
|
+
r_df = robjects.conversion.py2rpy(filtered_df)
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
r_result = r_plugin.run(r_df)
|
|
355
|
+
except Exception as e:
|
|
356
|
+
QMessageBox.critical(self, cfg.programName, f"Error in the plugin {plugin_path}: {e}.")
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
with localconverter(robjects.default_converter + pandas2ri.converter):
|
|
360
|
+
plugin_results = robjects.conversion.rpy2py(r_result)
|
|
361
|
+
|
|
362
|
+
# test if plugin_results is a tuple: if not transform it to tuple
|
|
252
363
|
if not isinstance(plugin_results, tuple):
|
|
253
364
|
plugin_results = tuple([plugin_results])
|
|
254
365
|
|
|
@@ -261,7 +372,7 @@ def run_plugin(self, plugin_name):
|
|
|
261
372
|
self.plugin_visu[-1].ptText.appendPlainText(result)
|
|
262
373
|
self.plugin_visu[-1].show()
|
|
263
374
|
elif isinstance(result, pd.DataFrame):
|
|
264
|
-
self.plugin_visu.append(view_df.View_df(plugin_name, f"{
|
|
375
|
+
self.plugin_visu.append(view_df.View_df(plugin_name, f"{plugin_version} ({plugin_version_date})", result))
|
|
265
376
|
self.plugin_visu[-1].show()
|
|
266
377
|
else:
|
|
267
378
|
# result is not str nor dataframe
|
boris/preferences.py
CHANGED
|
@@ -35,6 +35,7 @@ from .preferences_ui import Ui_prefDialog
|
|
|
35
35
|
|
|
36
36
|
from PySide6.QtWidgets import QDialog, QFileDialog, QListWidgetItem, QMessageBox
|
|
37
37
|
from PySide6.QtCore import Qt
|
|
38
|
+
from PySide6.QtGui import QFont
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class Preferences(QDialog, Ui_prefDialog):
|
|
@@ -43,8 +44,6 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
43
44
|
self.setupUi(self)
|
|
44
45
|
|
|
45
46
|
# plugins
|
|
46
|
-
"""self.pb_add_plugin.clicked.connect(self.add_plugin)
|
|
47
|
-
self.pb_remove_plugin.clicked.connect(self.remove_plugin)"""
|
|
48
47
|
self.pb_browse_plugins_dir.clicked.connect(self.browse_plugins_dir)
|
|
49
48
|
|
|
50
49
|
self.pbBrowseFFmpegCacheDir.clicked.connect(self.browseFFmpegCacheDir)
|
|
@@ -58,6 +57,12 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
58
57
|
|
|
59
58
|
self.flag_refresh = False
|
|
60
59
|
|
|
60
|
+
# Create a monospace QFont
|
|
61
|
+
monospace_font = QFont("Courier New") # or "Monospace", "Consolas", "Liberation Mono", etc.
|
|
62
|
+
monospace_font.setStyleHint(QFont.Monospace)
|
|
63
|
+
monospace_font.setPointSize(13)
|
|
64
|
+
self.pte_plugin_code.setFont(monospace_font)
|
|
65
|
+
|
|
61
66
|
def browse_plugins_dir(self):
|
|
62
67
|
"""
|
|
63
68
|
get the personal plugins directory
|
|
@@ -69,14 +74,21 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
69
74
|
self.le_personal_plugins_dir.setText(directory)
|
|
70
75
|
self.lw_personal_plugins.clear()
|
|
71
76
|
for file_ in Path(directory).glob("*.py"):
|
|
72
|
-
|
|
77
|
+
if file_.name.startswith("_"):
|
|
78
|
+
continue
|
|
79
|
+
plugin_name = plugins.get_plugin_name(file_)
|
|
80
|
+
if plugin_name is None:
|
|
81
|
+
continue
|
|
82
|
+
# check if personal plugin name is in BORIS plugins (case sensitive)
|
|
83
|
+
if plugin_name in [self.lv_all_plugins.item(i).text() for i in range(self.lv_all_plugins.count())]:
|
|
84
|
+
continue
|
|
85
|
+
item = QListWidgetItem(plugin_name)
|
|
86
|
+
# item = QListWidgetItem(file_.stem)
|
|
73
87
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
|
74
|
-
# if plugin_name in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
75
|
-
# item.setCheckState(Qt.Unchecked)
|
|
76
|
-
# else:
|
|
77
88
|
item.setCheckState(Qt.Checked)
|
|
78
|
-
item.setData(100, file_
|
|
89
|
+
item.setData(100, str(file_))
|
|
79
90
|
self.lw_personal_plugins.addItem(item)
|
|
91
|
+
|
|
80
92
|
if self.lw_personal_plugins.count() == 0:
|
|
81
93
|
QMessageBox.warning(self, cfg.programName, f"No plugin found in {directory}")
|
|
82
94
|
|
|
@@ -87,7 +99,7 @@ class Preferences(QDialog, Ui_prefDialog):
|
|
|
87
99
|
if (
|
|
88
100
|
dialog.MessageDialog(
|
|
89
101
|
"BORIS",
|
|
90
|
-
("Refresh will re-initialize
|
|
102
|
+
("Refresh will re-initialize all your preferences and close BORIS"),
|
|
91
103
|
[cfg.CANCEL, "Refresh preferences"],
|
|
92
104
|
)
|
|
93
105
|
== "Refresh preferences"
|
|
@@ -138,21 +150,39 @@ def preferences(self):
|
|
|
138
150
|
if item.text() not in self.config_param[cfg.ANALYSIS_PLUGINS]:
|
|
139
151
|
return
|
|
140
152
|
|
|
141
|
-
import importlib
|
|
142
|
-
|
|
143
153
|
plugin_path = item.data(100)
|
|
144
154
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
if Path(plugin_path).suffix == ".py":
|
|
156
|
+
import importlib
|
|
157
|
+
|
|
158
|
+
module_name = Path(plugin_path).stem
|
|
159
|
+
spec = importlib.util.spec_from_file_location(module_name, plugin_path)
|
|
160
|
+
plugin_module = importlib.util.module_from_spec(spec)
|
|
161
|
+
spec.loader.exec_module(plugin_module)
|
|
162
|
+
|
|
163
|
+
out: list = []
|
|
164
|
+
out.append(plugin_module.__plugin_name__ + "\n")
|
|
165
|
+
out.append(plugin_module.__author__)
|
|
166
|
+
out.append(f"{plugin_module.__version__} ({plugin_module.__version_date__})\n")
|
|
167
|
+
out.append(plugin_module.run.__doc__.strip())
|
|
154
168
|
|
|
155
|
-
|
|
169
|
+
preferencesWindow.pte_plugin_description.setPlainText("\n".join(out))
|
|
170
|
+
|
|
171
|
+
if Path(plugin_path).suffix == ".R":
|
|
172
|
+
plugin_description = plugins.get_r_plugin_description(plugin_path)
|
|
173
|
+
if plugin_description is not None:
|
|
174
|
+
preferencesWindow.pte_plugin_description.setPlainText("\n".join(plugin_description.split("\\n")))
|
|
175
|
+
else:
|
|
176
|
+
preferencesWindow.pte_plugin_description.setPlainText("Plugin description not found")
|
|
177
|
+
|
|
178
|
+
# display plugin code
|
|
179
|
+
try:
|
|
180
|
+
with open(plugin_path, "r") as f_in:
|
|
181
|
+
plugin_code = f_in.read()
|
|
182
|
+
except Exception:
|
|
183
|
+
plugin_code = "Not available"
|
|
184
|
+
|
|
185
|
+
preferencesWindow.pte_plugin_code.setPlainText(plugin_code)
|
|
156
186
|
|
|
157
187
|
preferencesWindow = Preferences()
|
|
158
188
|
preferencesWindow.tabWidget.setCurrentIndex(0)
|
|
@@ -205,7 +235,7 @@ def preferences(self):
|
|
|
205
235
|
preferencesWindow.lv_all_plugins.clear()
|
|
206
236
|
|
|
207
237
|
for file_ in (Path(__file__).parent / "analysis_plugins").glob("*.py"):
|
|
208
|
-
if file_.name
|
|
238
|
+
if file_.name.startswith("_"):
|
|
209
239
|
continue
|
|
210
240
|
plugin_name = plugins.get_plugin_name(file_)
|
|
211
241
|
if plugin_name is not None:
|
|
@@ -225,7 +255,7 @@ def preferences(self):
|
|
|
225
255
|
preferencesWindow.lw_personal_plugins.clear()
|
|
226
256
|
if self.config_param.get(cfg.PERSONAL_PLUGINS_DIR, ""):
|
|
227
257
|
for file_ in Path(self.config_param[cfg.PERSONAL_PLUGINS_DIR]).glob("*.py"):
|
|
228
|
-
if file_.name
|
|
258
|
+
if file_.name.startswith("_"):
|
|
229
259
|
continue
|
|
230
260
|
plugin_name = plugins.get_plugin_name(file_)
|
|
231
261
|
if plugin_name is None:
|
|
@@ -242,6 +272,22 @@ def preferences(self):
|
|
|
242
272
|
item.setData(100, str(file_))
|
|
243
273
|
preferencesWindow.lw_personal_plugins.addItem(item)
|
|
244
274
|
|
|
275
|
+
for file_ in Path(self.config_param[cfg.PERSONAL_PLUGINS_DIR]).glob("*.R"):
|
|
276
|
+
plugin_name = plugins.get_r_plugin_name(file_)
|
|
277
|
+
if plugin_name is None:
|
|
278
|
+
continue
|
|
279
|
+
# check if personal plugin name is in BORIS plugins (case sensitive)
|
|
280
|
+
if plugin_name in [preferencesWindow.lv_all_plugins.item(i).text() for i in range(preferencesWindow.lv_all_plugins.count())]:
|
|
281
|
+
continue
|
|
282
|
+
item = QListWidgetItem(plugin_name)
|
|
283
|
+
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
|
284
|
+
if plugin_name in self.config_param.get(cfg.EXCLUDED_PLUGINS, set()):
|
|
285
|
+
item.setCheckState(Qt.Unchecked)
|
|
286
|
+
else:
|
|
287
|
+
item.setCheckState(Qt.Checked)
|
|
288
|
+
item.setData(100, str(file_))
|
|
289
|
+
preferencesWindow.lw_personal_plugins.addItem(item)
|
|
290
|
+
|
|
245
291
|
# PROJET FILE INDENTATION
|
|
246
292
|
preferencesWindow.combo_project_file_indentation.clear()
|
|
247
293
|
preferencesWindow.combo_project_file_indentation.addItems(cfg.PROJECT_FILE_INDENTATION_COMBO_OPTIONS)
|
|
@@ -272,16 +318,21 @@ def preferences(self):
|
|
|
272
318
|
preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(self.spectrogram_color_map))
|
|
273
319
|
except Exception:
|
|
274
320
|
preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(cfg.SPECTROGRAM_DEFAULT_COLOR_MAP))
|
|
275
|
-
|
|
276
|
-
try:
|
|
277
|
-
preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(self.spectrogram_color_map))
|
|
278
|
-
except Exception:
|
|
279
|
-
preferencesWindow.cbSpectrogramColorMap.setCurrentIndex(cfg.SPECTROGRAM_COLOR_MAPS.index(cfg.SPECTROGRAM_DEFAULT_COLOR_MAP))
|
|
280
|
-
|
|
321
|
+
# time interval
|
|
281
322
|
try:
|
|
282
323
|
preferencesWindow.sb_time_interval.setValue(self.spectrogram_time_interval)
|
|
283
324
|
except Exception:
|
|
284
325
|
preferencesWindow.sb_time_interval.setValue(cfg.SPECTROGRAM_DEFAULT_TIME_INTERVAL)
|
|
326
|
+
# window type
|
|
327
|
+
preferencesWindow.cb_window_type.setCurrentText(self.config_param.get(cfg.SPECTROGRAM_WINDOW_TYPE, cfg.SPECTROGRAM_DEFAULT_WINDOW_TYPE))
|
|
328
|
+
# NFFT
|
|
329
|
+
preferencesWindow.cb_NFFT.setCurrentText(self.config_param.get(cfg.SPECTROGRAM_NFFT, cfg.SPECTROGRAM_DEFAULT_NFFT))
|
|
330
|
+
# noverlap
|
|
331
|
+
preferencesWindow.sb_noverlap.setValue(self.config_param.get(cfg.SPECTROGRAM_NOVERLAP, cfg.SPECTROGRAM_DEFAULT_NOVERLAP))
|
|
332
|
+
# vmin
|
|
333
|
+
preferencesWindow.sb_vmin.setValue(self.config_param.get(cfg.SPECTROGRAM_VMIN, cfg.SPECTROGRAM_DEFAULT_VMIN))
|
|
334
|
+
# vmax
|
|
335
|
+
preferencesWindow.sb_vmax.setValue(self.config_param.get(cfg.SPECTROGRAM_VMAX, cfg.SPECTROGRAM_DEFAULT_VMAX))
|
|
285
336
|
|
|
286
337
|
# behavior colors
|
|
287
338
|
if not self.plot_colors:
|
|
@@ -298,114 +349,137 @@ def preferences(self):
|
|
|
298
349
|
|
|
299
350
|
gui_utilities.restore_geometry(preferencesWindow, "preferences", (700, 500))
|
|
300
351
|
|
|
301
|
-
|
|
302
|
-
|
|
352
|
+
while True:
|
|
353
|
+
if preferencesWindow.exec():
|
|
354
|
+
if preferencesWindow.sb_vmin.value() >= preferencesWindow.sb_vmax.value():
|
|
355
|
+
QMessageBox.warning(self, cfg.programName, "Spectrogram parameters: the vmin value must be lower than the vmax value.")
|
|
356
|
+
continue
|
|
357
|
+
|
|
358
|
+
if preferencesWindow.sb_noverlap.value() >= int(preferencesWindow.cb_NFFT.currentText()):
|
|
359
|
+
QMessageBox.warning(self, cfg.programName, "Spectrogram parameters: the noverlap value must be lower than the NFFT value.")
|
|
360
|
+
continue
|
|
303
361
|
|
|
304
|
-
|
|
305
|
-
# refresh preferences remove the config file
|
|
362
|
+
gui_utilities.save_geometry(preferencesWindow, "preferences")
|
|
306
363
|
|
|
307
|
-
|
|
364
|
+
if preferencesWindow.flag_refresh:
|
|
365
|
+
# refresh preferences remove the config file
|
|
308
366
|
|
|
309
|
-
|
|
310
|
-
self.close()
|
|
311
|
-
# check if refresh canceled for not saved project
|
|
312
|
-
if "refresh_preferences" in self.config_param:
|
|
313
|
-
if (Path.home() / ".boris").exists():
|
|
314
|
-
os.remove(Path.home() / ".boris")
|
|
315
|
-
sys.exit()
|
|
367
|
+
logging.debug("flag refresh ")
|
|
316
368
|
|
|
317
|
-
|
|
318
|
-
|
|
369
|
+
self.config_param["refresh_preferences"] = True
|
|
370
|
+
self.close()
|
|
371
|
+
# check if refresh canceled for not saved project
|
|
372
|
+
if "refresh_preferences" in self.config_param:
|
|
373
|
+
if (Path.home() / ".boris").exists():
|
|
374
|
+
os.remove(Path.home() / ".boris")
|
|
375
|
+
sys.exit()
|
|
319
376
|
|
|
320
|
-
|
|
321
|
-
|
|
377
|
+
if preferencesWindow.cbTimeFormat.currentIndex() == 0:
|
|
378
|
+
self.timeFormat = cfg.S
|
|
322
379
|
|
|
323
|
-
|
|
380
|
+
if preferencesWindow.cbTimeFormat.currentIndex() == 1:
|
|
381
|
+
self.timeFormat = cfg.HHMMSS
|
|
324
382
|
|
|
325
|
-
|
|
383
|
+
self.fast = preferencesWindow.sbffSpeed.value()
|
|
326
384
|
|
|
327
|
-
|
|
385
|
+
self.config_param[cfg.ADAPT_FAST_JUMP] = preferencesWindow.cb_adapt_fast_jump.isChecked()
|
|
328
386
|
|
|
329
|
-
|
|
387
|
+
self.repositioningTimeOffset = preferencesWindow.sbRepositionTimeOffset.value()
|
|
330
388
|
|
|
331
|
-
|
|
332
|
-
if self.automaticBackup:
|
|
333
|
-
self.automaticBackupTimer.start(self.automaticBackup * 60000)
|
|
334
|
-
else:
|
|
335
|
-
self.automaticBackupTimer.stop()
|
|
389
|
+
self.play_rate_step = preferencesWindow.sbSpeedStep.value()
|
|
336
390
|
|
|
337
|
-
|
|
391
|
+
self.automaticBackup = preferencesWindow.sbAutomaticBackup.value()
|
|
392
|
+
if self.automaticBackup:
|
|
393
|
+
self.automaticBackupTimer.start(self.automaticBackup * 60000)
|
|
394
|
+
else:
|
|
395
|
+
self.automaticBackupTimer.stop()
|
|
338
396
|
|
|
339
|
-
|
|
397
|
+
self.behav_seq_separator = preferencesWindow.leSeparator.text()
|
|
340
398
|
|
|
341
|
-
|
|
399
|
+
self.close_the_same_current_event = preferencesWindow.cbCloseSameEvent.isChecked()
|
|
342
400
|
|
|
343
|
-
|
|
401
|
+
self.confirmSound = preferencesWindow.cbConfirmSound.isChecked()
|
|
344
402
|
|
|
345
|
-
|
|
403
|
+
self.beep_every = preferencesWindow.sbBeepEvery.value()
|
|
346
404
|
|
|
347
|
-
|
|
405
|
+
self.alertNoFocalSubject = preferencesWindow.cbAlertNoFocalSubject.isChecked()
|
|
348
406
|
|
|
349
|
-
|
|
407
|
+
self.trackingCursorAboveEvent = preferencesWindow.cbTrackingCursorAboveEvent.isChecked()
|
|
350
408
|
|
|
351
|
-
|
|
409
|
+
self.checkForNewVersion = preferencesWindow.cbCheckForNewVersion.isChecked()
|
|
352
410
|
|
|
353
|
-
|
|
411
|
+
self.config_param[cfg.DISPLAY_SUBTITLES] = preferencesWindow.cb_display_subtitles.isChecked()
|
|
354
412
|
|
|
355
|
-
|
|
356
|
-
self.config_param[cfg.MPV_HWDEC] = cfg.MPV_HWDEC_OPTIONS[preferencesWindow.cb_hwdec.currentIndex()]
|
|
413
|
+
self.pause_before_addevent = preferencesWindow.cb_pause_before_addevent.isChecked()
|
|
357
414
|
|
|
358
|
-
|
|
359
|
-
|
|
415
|
+
# MPV hwdec
|
|
416
|
+
self.config_param[cfg.MPV_HWDEC] = cfg.MPV_HWDEC_OPTIONS[preferencesWindow.cb_hwdec.currentIndex()]
|
|
360
417
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
self.config_param[cfg.EXCLUDED_PLUGINS] = set()
|
|
364
|
-
for i in range(preferencesWindow.lv_all_plugins.count()):
|
|
365
|
-
if preferencesWindow.lv_all_plugins.item(i).checkState() == Qt.Checked:
|
|
366
|
-
self.config_param[cfg.ANALYSIS_PLUGINS][preferencesWindow.lv_all_plugins.item(i).text()] = (
|
|
367
|
-
preferencesWindow.lv_all_plugins.item(i).data(100)
|
|
368
|
-
)
|
|
369
|
-
else:
|
|
370
|
-
self.config_param[cfg.EXCLUDED_PLUGINS].add(preferencesWindow.lv_all_plugins.item(i).text())
|
|
371
|
-
|
|
372
|
-
# update personal plugins
|
|
373
|
-
self.config_param[cfg.PERSONAL_PLUGINS_DIR] = preferencesWindow.le_personal_plugins_dir.text()
|
|
374
|
-
for i in range(preferencesWindow.lw_personal_plugins.count()):
|
|
375
|
-
if preferencesWindow.lw_personal_plugins.item(i).checkState() == Qt.Checked:
|
|
376
|
-
self.config_param[cfg.ANALYSIS_PLUGINS][preferencesWindow.lw_personal_plugins.item(i).text()] = (
|
|
377
|
-
preferencesWindow.lw_personal_plugins.item(i).data(100)
|
|
378
|
-
)
|
|
379
|
-
else:
|
|
380
|
-
self.config_param[cfg.EXCLUDED_PLUGINS].add(preferencesWindow.lw_personal_plugins.item(i).text())
|
|
418
|
+
# check project integrity
|
|
419
|
+
self.config_param[cfg.CHECK_PROJECT_INTEGRITY] = preferencesWindow.cb_check_integrity_at_opening.isChecked()
|
|
381
420
|
|
|
382
|
-
|
|
383
|
-
|
|
421
|
+
# update BORIS analysis plugins
|
|
422
|
+
self.config_param[cfg.ANALYSIS_PLUGINS] = {}
|
|
423
|
+
self.config_param[cfg.EXCLUDED_PLUGINS] = set()
|
|
424
|
+
for i in range(preferencesWindow.lv_all_plugins.count()):
|
|
425
|
+
if preferencesWindow.lv_all_plugins.item(i).checkState() == Qt.Checked:
|
|
426
|
+
self.config_param[cfg.ANALYSIS_PLUGINS][preferencesWindow.lv_all_plugins.item(i).text()] = (
|
|
427
|
+
preferencesWindow.lv_all_plugins.item(i).data(100)
|
|
428
|
+
)
|
|
429
|
+
else:
|
|
430
|
+
self.config_param[cfg.EXCLUDED_PLUGINS].add(preferencesWindow.lv_all_plugins.item(i).text())
|
|
384
431
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
preferencesWindow.
|
|
388
|
-
|
|
432
|
+
# update personal plugins
|
|
433
|
+
self.config_param[cfg.PERSONAL_PLUGINS_DIR] = preferencesWindow.le_personal_plugins_dir.text()
|
|
434
|
+
for i in range(preferencesWindow.lw_personal_plugins.count()):
|
|
435
|
+
if preferencesWindow.lw_personal_plugins.item(i).checkState() == Qt.Checked:
|
|
436
|
+
self.config_param[cfg.ANALYSIS_PLUGINS][preferencesWindow.lw_personal_plugins.item(i).text()] = (
|
|
437
|
+
preferencesWindow.lw_personal_plugins.item(i).data(100)
|
|
438
|
+
)
|
|
439
|
+
else:
|
|
440
|
+
self.config_param[cfg.EXCLUDED_PLUGINS].add(preferencesWindow.lw_personal_plugins.item(i).text())
|
|
389
441
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
self.display_statusbar_info(self.observationId)
|
|
442
|
+
plugins.load_plugins(self)
|
|
443
|
+
plugins.add_plugins_to_menu(self)
|
|
393
444
|
|
|
394
|
-
|
|
445
|
+
# project file indentation
|
|
446
|
+
self.config_param[cfg.PROJECT_FILE_INDENTATION] = cfg.PROJECT_FILE_INDENTATION_OPTIONS[
|
|
447
|
+
preferencesWindow.combo_project_file_indentation.currentIndex()
|
|
448
|
+
]
|
|
449
|
+
|
|
450
|
+
if self.observationId:
|
|
451
|
+
self.load_tw_events(self.observationId)
|
|
452
|
+
self.display_statusbar_info(self.observationId)
|
|
453
|
+
|
|
454
|
+
self.ffmpeg_cache_dir = preferencesWindow.leFFmpegCacheDir.text()
|
|
395
455
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
456
|
+
# spectrogram
|
|
457
|
+
self.spectrogram_color_map = preferencesWindow.cbSpectrogramColorMap.currentText()
|
|
458
|
+
self.spectrogram_time_interval = preferencesWindow.sb_time_interval.value()
|
|
459
|
+
# window type
|
|
460
|
+
self.config_param[cfg.SPECTROGRAM_WINDOW_TYPE] = preferencesWindow.cb_window_type.currentText()
|
|
461
|
+
# NFFT
|
|
462
|
+
self.config_param[cfg.SPECTROGRAM_NFFT] = preferencesWindow.cb_NFFT.currentText()
|
|
463
|
+
# noverlap
|
|
464
|
+
self.config_param[cfg.SPECTROGRAM_NOVERLAP] = preferencesWindow.sb_noverlap.value()
|
|
465
|
+
# vmin
|
|
466
|
+
self.config_param[cfg.SPECTROGRAM_VMIN] = preferencesWindow.sb_vmin.value()
|
|
467
|
+
# vmax
|
|
468
|
+
self.config_param[cfg.SPECTROGRAM_VMAX] = preferencesWindow.sb_vmax.value()
|
|
400
469
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
470
|
+
# behav colors
|
|
471
|
+
self.plot_colors = preferencesWindow.te_behav_colors.toPlainText().split()
|
|
472
|
+
# category colors
|
|
473
|
+
self.behav_category_colors = preferencesWindow.te_category_colors.toPlainText().split()
|
|
405
474
|
|
|
406
|
-
|
|
407
|
-
|
|
475
|
+
# interface
|
|
476
|
+
self.config_param[cfg.TOOLBAR_ICON_SIZE] = preferencesWindow.sb_toolbar_icon_size.value()
|
|
408
477
|
|
|
409
|
-
|
|
478
|
+
menu_options.update_menu(self)
|
|
410
479
|
|
|
411
|
-
|
|
480
|
+
config_file.save(self)
|
|
481
|
+
|
|
482
|
+
break
|
|
483
|
+
|
|
484
|
+
else:
|
|
485
|
+
break
|