boris-behav-obs 9.7.1__py3-none-any.whl → 9.7.12__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.
@@ -26,7 +26,7 @@ from decimal import Decimal as dec
26
26
  import json
27
27
  from math import log2, floor
28
28
  import os
29
- import pathlib as pl
29
+ from pathlib import Path
30
30
  import socket
31
31
  import subprocess
32
32
  import sys
@@ -89,10 +89,10 @@ def export_observations_list_clicked(self):
89
89
  return
90
90
 
91
91
  output_format = cfg.FILE_NAME_SUFFIX[filter_]
92
- if pl.Path(file_name).suffix != "." + output_format:
93
- file_name = str(pl.Path(file_name)) + "." + output_format
92
+ if Path(file_name).suffix != "." + output_format:
93
+ file_name = str(Path(file_name)) + "." + output_format
94
94
  # check if file name with extension already exists
95
- if pl.Path(file_name).is_file():
95
+ if Path(file_name).is_file():
96
96
  if dialog.MessageDialog(cfg.programName, f"The file {file_name} already exists.", [cfg.CANCEL, cfg.OVERWRITE]) == cfg.CANCEL:
97
97
  return
98
98
 
@@ -113,6 +113,8 @@ def observations_list(self):
113
113
  result, selected_obs = select_observations.select_observations2(self, cfg.SINGLE)
114
114
 
115
115
  if not selected_obs:
116
+ # activate main window
117
+ self.activateWindow()
116
118
  return
117
119
 
118
120
  if self.observationId:
@@ -122,6 +124,9 @@ def observations_list(self):
122
124
  )
123
125
  if response == cfg.NO:
124
126
  self.show_data_files()
127
+ # activate main window
128
+ self.activateWindow()
129
+
125
130
  return ""
126
131
  else:
127
132
  close_observation(self)
@@ -145,6 +150,8 @@ def observations_list(self):
145
150
  )
146
151
 
147
152
  logging.debug("end observations list")
153
+ # activate main window
154
+ self.activateWindow()
148
155
 
149
156
 
150
157
  def open_observation(self, mode: str) -> str:
@@ -594,7 +601,7 @@ def new_observation(self, mode: str = cfg.NEW, obsId: str = "") -> None:
594
601
  close_observation(self)
595
602
 
596
603
  observationWindow = observation.Observation(
597
- tmp_dir=self.ffmpeg_cache_dir if (self.ffmpeg_cache_dir and pl.Path(self.ffmpeg_cache_dir).is_dir()) else tempfile.gettempdir(),
604
+ tmp_dir=self.ffmpeg_cache_dir if (self.ffmpeg_cache_dir and Path(self.ffmpeg_cache_dir).is_dir()) else tempfile.gettempdir(),
598
605
  project_path=self.projectFileName,
599
606
  converters=self.pj.get(cfg.CONVERTERS, {}),
600
607
  time_format=self.timeFormat,
@@ -1151,6 +1158,9 @@ def close_observation(self):
1151
1158
  logging.info("Stop plot timer")
1152
1159
  self.plot_timer.stop()
1153
1160
 
1161
+ if self.MPV_IPC_MODE:
1162
+ self.main_window_activation_timer.stop()
1163
+
1154
1164
  for i, player in enumerate(self.dw_player):
1155
1165
  if (
1156
1166
  str(i + 1) in self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE]
@@ -1304,7 +1314,7 @@ def check_creation_date(self) -> Tuple[int, dict]:
1304
1314
 
1305
1315
  if ret == 1: # use file creation time
1306
1316
  for media in not_tagged_media_list:
1307
- media_creation_time[media] = pl.Path(media).stat().st_ctime
1317
+ media_creation_time[media] = Path(media).stat().st_ctime
1308
1318
  return (0, media_creation_time) # OK use media file creation date/time
1309
1319
  else:
1310
1320
  return (1, {})
@@ -1340,8 +1350,6 @@ def initialize_new_media_observation(self) -> bool:
1340
1350
  self.playerType = cfg.VIEWER_MEDIA
1341
1351
  return True
1342
1352
 
1343
- # print(f"{self.process=}")
1344
-
1345
1353
  self.playerType = cfg.MEDIA
1346
1354
  self.fps = 0
1347
1355
 
@@ -1365,7 +1373,7 @@ def initialize_new_media_observation(self) -> bool:
1365
1373
 
1366
1374
  # add all media files to media lists
1367
1375
  self.setDockOptions(QMainWindow.AnimatedDocks | QMainWindow.AllowNestedDocks)
1368
- self.dw_player: list = []
1376
+ self.dw_player = []
1369
1377
 
1370
1378
  # check if media creation time used as offset
1371
1379
  # TODO check if cfg.MEDIA_CREATION_TIME dict is present
@@ -1831,8 +1839,8 @@ def initialize_new_media_observation(self) -> bool:
1831
1839
  self.dw_player[i].resize_signal.connect(self.resize_dw)
1832
1840
 
1833
1841
  # add durations list
1834
- self.dw_player[i].media_durations: list = []
1835
- self.dw_player[i].cumul_media_durations: List[int] = [0] # [idx for idx,x in enumerate(l) if l[idx-1]<pos<=x]
1842
+ self.dw_player[i].media_durations = []
1843
+ self.dw_player[i].cumul_media_durations = [0] # [idx for idx,x in enumerate(l) if l[idx-1]<pos<=x]
1836
1844
 
1837
1845
  # add fps list
1838
1846
  self.dw_player[i].fps = {}
@@ -1844,6 +1852,13 @@ def initialize_new_media_observation(self) -> bool:
1844
1852
  if r:
1845
1853
  break
1846
1854
 
1855
+ # start timer for activating the main window
1856
+ self.main_window_activation_timer = QTimer()
1857
+ self.main_window_activation_timer.setInterval(500)
1858
+ # self.main_window_activation_timer.timeout.connect(self.activateWindow)
1859
+ self.main_window_activation_timer.timeout.connect(self.activate_main_window)
1860
+ self.main_window_activation_timer.start()
1861
+
1847
1862
  for mediaFile in self.pj[cfg.OBSERVATIONS][self.observationId][cfg.FILE][n_player]:
1848
1863
  logging.debug(f"media file: {mediaFile}")
1849
1864
 
@@ -1887,7 +1902,7 @@ def initialize_new_media_observation(self) -> bool:
1887
1902
  self.dw_player[i].player.playlist_append(media_full_path)
1888
1903
 
1889
1904
  # add media file name to player window title
1890
- self.dw_player[i].setWindowTitle(f"Player #{i + 1} ({pl.Path(media_full_path).name})")
1905
+ self.dw_player[i].setWindowTitle(f"Player #{i + 1} ({Path(media_full_path).name})")
1891
1906
 
1892
1907
  # media duration cumuled in seconds
1893
1908
  self.dw_player[i].cumul_media_durations_sec = [round(dec(x / 1000), 3) for x in self.dw_player[i].cumul_media_durations]
@@ -1964,7 +1979,7 @@ def initialize_new_media_observation(self) -> bool:
1964
1979
 
1965
1980
  menu_options.update_menu(self)
1966
1981
 
1967
- if not self.MPV_IPC_MODE:
1982
+ if self.MPV_IPC_MODE:
1968
1983
  # activate timer
1969
1984
  self.ipc_mpv_timer = QTimer()
1970
1985
  self.ipc_mpv_timer.setInterval(500)
@@ -1996,7 +2011,7 @@ def initialize_new_media_observation(self) -> bool:
1996
2011
  tmp_dir = self.ffmpeg_cache_dir if self.ffmpeg_cache_dir and os.path.isdir(self.ffmpeg_cache_dir) else tempfile.gettempdir()
1997
2012
 
1998
2013
  wav_file_path = (
1999
- pl.Path(tmp_dir) / pl.Path(self.dw_player[0].player.playlist[self.dw_player[0].player.playlist_pos]["filename"] + ".wav").name
2014
+ Path(tmp_dir) / Path(self.dw_player[0].player.playlist[self.dw_player[0].player.playlist_pos]["filename"] + ".wav").name
2000
2015
  )
2001
2016
 
2002
2017
  if not wav_file_path.is_file():
@@ -2012,7 +2027,7 @@ def initialize_new_media_observation(self) -> bool:
2012
2027
  tmp_dir = self.ffmpeg_cache_dir if self.ffmpeg_cache_dir and os.path.isdir(self.ffmpeg_cache_dir) else tempfile.gettempdir()
2013
2028
 
2014
2029
  wav_file_path = (
2015
- pl.Path(tmp_dir) / pl.Path(self.dw_player[0].player.playlist[self.dw_player[0].player.playlist_pos]["filename"] + ".wav").name
2030
+ Path(tmp_dir) / Path(self.dw_player[0].player.playlist[self.dw_player[0].player.playlist_pos]["filename"] + ".wav").name
2016
2031
  )
2017
2032
 
2018
2033
  if not wav_file_path.is_file():
@@ -2296,8 +2311,8 @@ def initialize_new_images_observation(self):
2296
2311
  sorted(
2297
2312
  list(
2298
2313
  set(
2299
- [str(x) for x in pl.Path(full_dir_path).glob(pattern)]
2300
- + [str(x) for x in pl.Path(full_dir_path).glob(pattern.upper())]
2314
+ [str(x) for x in Path(full_dir_path).glob(pattern)]
2315
+ + [str(x) for x in Path(full_dir_path).glob(pattern.upper())]
2301
2316
  )
2302
2317
  )
2303
2318
  )
@@ -2422,26 +2437,31 @@ def event2media_file_name(observation: dict, timestamp: dec) -> Optional[str]:
2422
2437
 
2423
2438
  def create_observations(self):
2424
2439
  """
2425
- Create observations from a media file directory
2440
+ Create observations from a directory of media files
2426
2441
  """
2427
- # print(self.pj[cfg.OBSERVATIONS])
2428
2442
 
2429
- dir_path = QFileDialog.getExistingDirectory(None, "Select directory", os.getenv("HOME"))
2430
- if not dir_path:
2443
+ if not (dir_path := QFileDialog.getExistingDirectory(None, "Select directory", os.getenv("HOME"))):
2431
2444
  return
2432
2445
 
2433
- dlg = dialog.Input_dialog(
2434
- label_caption="Set the following observation parameters",
2435
- elements_list=[
2446
+ elements_list: list = []
2447
+ if util.is_subdir(Path(dir_path), Path(self.projectFileName).parent):
2448
+ elements_list.append(("cb", "Use relative paths", False))
2449
+
2450
+ elements_list.extend(
2451
+ [
2436
2452
  ("cb", "Recurse the subdirectories", False),
2437
- ("cb", "Save the absolute media file path", True),
2438
2453
  ("cb", "Visualize spectrogram", False),
2439
2454
  ("cb", "Visualize waveform", False),
2440
2455
  ("cb", "Media creation date as offset", False),
2441
2456
  ("cb", "Close behaviors between videos", False),
2442
2457
  ("dsb", "Time offset (in seconds)", 0.0, 86400, 1, 0, 3),
2443
2458
  ("dsb", "Media scan sampling duration (in seconds)", 0.0, 86400, 1, 0, 3),
2444
- ],
2459
+ ]
2460
+ )
2461
+
2462
+ dlg = dialog.Input_dialog(
2463
+ label_caption="Set the following observation parameters",
2464
+ elements_list=elements_list,
2445
2465
  title="Observation parameters",
2446
2466
  )
2447
2467
  if not dlg.exec_():
@@ -2450,9 +2470,9 @@ def create_observations(self):
2450
2470
  file_count: int = 0
2451
2471
 
2452
2472
  if dlg.elements["Recurse the subdirectories"].isChecked():
2453
- files_list = pl.Path(dir_path).rglob("*")
2473
+ files_list = Path(dir_path).rglob("*")
2454
2474
  else:
2455
- files_list = pl.Path(dir_path).glob("*")
2475
+ files_list = Path(dir_path).glob("*")
2456
2476
 
2457
2477
  for file in files_list:
2458
2478
  if not file.is_file():
@@ -2462,22 +2482,25 @@ def create_observations(self):
2462
2482
  if not r.get("frames_number", 0):
2463
2483
  continue
2464
2484
 
2465
- if dlg.elements["Save the absolute media file path"].isChecked():
2466
- media_file = str(file)
2485
+ if "Use relative paths" in dlg.elements and dlg.elements["Use relative paths"].isChecked():
2486
+ media_file = str(file.relative_to(Path(self.projectFileName).parent))
2467
2487
  else:
2468
- try:
2469
- media_file = str(file.relative_to(pl.Path(self.projectFileName).parent))
2470
- except ValueError:
2471
- QMessageBox.critical(
2472
- self,
2473
- cfg.programName,
2474
- (
2475
- f"the media file <b>{file}</b> can not be relative to the project directory "
2476
- f"(<b>{pl.Path(self.projectFileName).parent}</b>)"
2477
- "<br><br>Aborting the creation of observations"
2478
- ),
2479
- )
2480
- return
2488
+ media_file = str(file)
2489
+
2490
+ # else:
2491
+ # try:
2492
+ # media_file = str(file.relative_to(Path(self.projectFileName).parent))
2493
+ # except ValueError:
2494
+ # QMessageBox.critical(
2495
+ # self,
2496
+ # cfg.programName,
2497
+ # (
2498
+ # f"the media file <b>{file}</b> can not be relative to the project directory "
2499
+ # f"(<b>{Path(self.projectFileName).parent}</b>)"
2500
+ # "<br><br>Aborting the creation of observations"
2501
+ # ),
2502
+ # )
2503
+ # return
2481
2504
 
2482
2505
  if media_file in self.pj[cfg.OBSERVATIONS]:
2483
2506
  QMessageBox.critical(
@@ -2518,4 +2541,6 @@ def create_observations(self):
2518
2541
  else:
2519
2542
  message: str = f"No media file were found in {dir_path}"
2520
2543
 
2544
+ menu_options.update_menu(self)
2545
+
2521
2546
  QMessageBox.information(self, cfg.programName, message)
boris/plot_data_module.py CHANGED
@@ -134,6 +134,8 @@ class Plot_data(QWidget):
134
134
  column_converter=column_converter,
135
135
  )
136
136
 
137
+ print(f"{error_msg=}")
138
+
137
139
  if not result:
138
140
  self.error_msg = error_msg
139
141
  return
@@ -205,6 +205,9 @@ class Plot_spectrogram_RT(QWidget):
205
205
  vmin = self.config_param.get(cfg.SPECTROGRAM_VMIN, cfg.SPECTROGRAM_DEFAULT_VMIN)
206
206
  vmax = self.config_param.get(cfg.SPECTROGRAM_VMAX, cfg.SPECTROGRAM_DEFAULT_VMAX)
207
207
 
208
+ if current_time is None:
209
+ return
210
+
208
211
  # start
209
212
  if current_time <= self.interval / 2:
210
213
  self.ax.specgram(
boris/plot_waveform_rt.py CHANGED
@@ -156,7 +156,7 @@ class Plot_waveform_RT(QWidget):
156
156
  self.interval += 5 * action
157
157
  self.plot_waveform(current_time=self.time_mem, force_plot=True)
158
158
 
159
- def plot_waveform(self, current_time: float, force_plot: bool = False):
159
+ def plot_waveform(self, current_time: float, force_plot: bool = False) -> None:
160
160
  """
161
161
  plot sound waveform centered on the current time
162
162
 
@@ -172,6 +172,9 @@ class Plot_waveform_RT(QWidget):
172
172
 
173
173
  self.ax.clear()
174
174
 
175
+ if current_time is None:
176
+ return
177
+
175
178
  # start
176
179
  if current_time <= self.interval / 2:
177
180
  time_ = np.linspace(
boris/plugins.py CHANGED
@@ -24,6 +24,8 @@ import logging
24
24
  import numpy as np
25
25
  import pandas as pd
26
26
  from pathlib import Path
27
+ import copy
28
+ import inspect
27
29
 
28
30
  from PySide6.QtGui import QAction
29
31
  from PySide6.QtWidgets import QMessageBox
@@ -297,6 +299,7 @@ def run_plugin(self, plugin_name):
297
299
 
298
300
  logging.debug(f"{plugin_path=}")
299
301
 
302
+ # check if plugin file exists
300
303
  if not Path(plugin_path).is_file():
301
304
  QMessageBox.critical(self, cfg.programName, f"The plugin {plugin_path} was not found.")
302
305
  return
@@ -308,27 +311,7 @@ def run_plugin(self, plugin_name):
308
311
  if not selected_observations:
309
312
  return
310
313
 
311
- logging.info("preparing dataframe for plugin")
312
-
313
- message, df = project_functions.project2dataframe(self.pj, selected_observations)
314
- if message:
315
- logging.critical(message)
316
- QMessageBox.critical(self, cfg.programName, message)
317
- return
318
-
319
- logging.info("done")
320
-
321
- """
322
- logging.debug("dataframe info")
323
- logging.debug(f"{df.info()}")
324
- logging.debug(f"{df.head()}")
325
- """
326
-
327
- # filter the dataframe with parameters
328
- logging.info("filtering dataframe for plugin")
329
- filtered_df = plugin_df_filter(df, observations_list=selected_observations, parameters=parameters)
330
- logging.info("done")
331
-
314
+ # Python plugin
332
315
  if Path(plugin_path).suffix == ".py":
333
316
  # load plugin as module
334
317
  module_name = Path(plugin_path).stem
@@ -347,9 +330,48 @@ def run_plugin(self, plugin_name):
347
330
  f"{plugin_module.__plugin_name__} loaded v.{getattr(plugin_module, '__version__')} v. {getattr(plugin_module, '__version_date__')}"
348
331
  )
349
332
 
350
- # run plugin
351
- plugin_results = plugin_module.run(filtered_df)
333
+ # check arguments required by the run function of the plugin
334
+ dataframe_required = False
335
+ project_required = False
336
+ # for param in inspect.signature(plugin_module.run).parameters.values():
337
+ for name, annotation in inspect.getfullargspec(plugin_module.run).annotations.items():
338
+ if name == "df" and annotation is pd.DataFrame:
339
+ dataframe_required = True
340
+ if name == "project" and annotation is dict:
341
+ project_required = True
342
+
343
+ # create arguments for the plugin run function
344
+ plugin_kwargs: dict = {}
345
+
346
+ if dataframe_required:
347
+ logging.info("preparing dataframe for plugin")
348
+ message, df = project_functions.project2dataframe(self.pj, selected_observations)
349
+ if message:
350
+ logging.critical(message)
351
+ QMessageBox.critical(self, cfg.programName, message)
352
+ return
353
+ logging.info("done")
354
+
355
+ # filter the dataframe with parameters
356
+ logging.info("filtering dataframe for plugin")
357
+ filtered_df = plugin_df_filter(df, observations_list=selected_observations, parameters=parameters)
358
+ logging.info("done")
352
359
 
360
+ plugin_kwargs["df"] = filtered_df
361
+
362
+ if project_required:
363
+ pj_copy = copy.deepcopy(self.pj)
364
+
365
+ # remove unselected observations from project
366
+ for obs_id in self.pj[cfg.OBSERVATIONS]:
367
+ if obs_id not in selected_observations:
368
+ del pj_copy[cfg.OBSERVATIONS][obs_id]
369
+
370
+ plugin_kwargs["project"] = pj_copy
371
+
372
+ plugin_results = plugin_module.run(**plugin_kwargs)
373
+
374
+ # R plugin
353
375
  if Path(plugin_path).suffix in (".R", ".r"):
354
376
  try:
355
377
  from rpy2 import robjects
@@ -360,6 +382,21 @@ def run_plugin(self, plugin_name):
360
382
  QMessageBox.critical(self, cfg.programName, "The rpy2 Python module is not installed. R plugins cannot be used")
361
383
  return
362
384
 
385
+ logging.info("preparing dataframe for plugin")
386
+
387
+ message, df = project_functions.project2dataframe(self.pj, selected_observations)
388
+ if message:
389
+ logging.critical(message)
390
+ QMessageBox.critical(self, cfg.programName, message)
391
+ return
392
+
393
+ logging.info("done")
394
+
395
+ # filter the dataframe with parameters
396
+ logging.info("filtering dataframe for plugin")
397
+ filtered_df = plugin_df_filter(df, observations_list=selected_observations, parameters=parameters)
398
+ logging.info("done")
399
+
363
400
  # Read code from file
364
401
  try:
365
402
  with open(plugin_path, "r") as f:
boris/preferences.py CHANGED
@@ -221,6 +221,9 @@ def preferences(self):
221
221
  preferencesWindow.cbConfirmSound.setChecked(self.confirmSound)
222
222
  # beep every
223
223
  preferencesWindow.sbBeepEvery.setValue(self.beep_every)
224
+ # frame step size
225
+ # preferencesWindow.sb_frame_step_size.setValue(self.config_param.get(cfg.FRAME_STEP_SIZE, cfg.FRAME_STEP_SIZE_DEFAULT_VALUE))
226
+
224
227
  # alert no focal subject
225
228
  preferencesWindow.cbAlertNoFocalSubject.setChecked(self.alertNoFocalSubject)
226
229
  # tracking cursor above event
@@ -418,6 +421,9 @@ def preferences(self):
418
421
 
419
422
  self.beep_every = preferencesWindow.sbBeepEvery.value()
420
423
 
424
+ # frame step size
425
+ # self.config_param[cfg.FRAME_STEP_SIZE] = preferencesWindow.sb_frame_step_size.value()
426
+
421
427
  self.alertNoFocalSubject = preferencesWindow.cbAlertNoFocalSubject.isChecked()
422
428
 
423
429
  self.trackingCursorAboveEvent = preferencesWindow.cbTrackingCursorAboveEvent.isChecked()
@@ -499,3 +505,6 @@ def preferences(self):
499
505
 
500
506
  else:
501
507
  break
508
+
509
+ # activate main window
510
+ self.activateWindow()
boris/preferences_ui.py CHANGED
@@ -3,7 +3,7 @@
3
3
  ################################################################################
4
4
  ## Form generated from reading UI file 'preferences.ui'
5
5
  ##
6
- ## Created by: Qt User Interface Compiler version 6.9.0
6
+ ## Created by: Qt User Interface Compiler version 6.10.0
7
7
  ##
8
8
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
9
9
  ################################################################################
@@ -145,11 +145,10 @@ class Ui_prefDialog(object):
145
145
 
146
146
  self.horizontalLayout_4.addWidget(self.label_4)
147
147
 
148
- self.sbffSpeed = QSpinBox(self.tab_observations)
148
+ self.sbffSpeed = QDoubleSpinBox(self.tab_observations)
149
149
  self.sbffSpeed.setObjectName(u"sbffSpeed")
150
- self.sbffSpeed.setMinimum(0)
151
- self.sbffSpeed.setMaximum(10000)
152
- self.sbffSpeed.setValue(10)
150
+ self.sbffSpeed.setDecimals(3)
151
+ self.sbffSpeed.setMaximum(1000000.000000000000000)
153
152
 
154
153
  self.horizontalLayout_4.addWidget(self.sbffSpeed)
155
154
 
@@ -244,6 +243,26 @@ class Ui_prefDialog(object):
244
243
 
245
244
  self.verticalLayout.addWidget(self.cb_pause_before_addevent)
246
245
 
246
+ self.horizontalLayout_23 = QHBoxLayout()
247
+ self.horizontalLayout_23.setObjectName(u"horizontalLayout_23")
248
+ self.label_24 = QLabel(self.tab_observations)
249
+ self.label_24.setObjectName(u"label_24")
250
+ self.label_24.setEnabled(False)
251
+
252
+ self.horizontalLayout_23.addWidget(self.label_24)
253
+
254
+ self.sb_frame_step_size = QSpinBox(self.tab_observations)
255
+ self.sb_frame_step_size.setObjectName(u"sb_frame_step_size")
256
+ self.sb_frame_step_size.setEnabled(False)
257
+ self.sb_frame_step_size.setMinimum(1)
258
+ self.sb_frame_step_size.setMaximum(1000)
259
+ self.sb_frame_step_size.setValue(1)
260
+
261
+ self.horizontalLayout_23.addWidget(self.sb_frame_step_size)
262
+
263
+
264
+ self.verticalLayout.addLayout(self.horizontalLayout_23)
265
+
247
266
  self.verticalSpacer_4 = QSpacerItem(20, 391, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
248
267
 
249
268
  self.verticalLayout.addItem(self.verticalSpacer_4)
@@ -256,35 +275,35 @@ class Ui_prefDialog(object):
256
275
  self.splitter_2 = QSplitter(self.tab_analysis_plugins)
257
276
  self.splitter_2.setObjectName(u"splitter_2")
258
277
  self.splitter_2.setOrientation(Qt.Orientation.Horizontal)
259
- self.widget = QWidget(self.splitter_2)
260
- self.widget.setObjectName(u"widget")
261
- self.verticalLayout_11 = QVBoxLayout(self.widget)
278
+ self.layoutWidget = QWidget(self.splitter_2)
279
+ self.layoutWidget.setObjectName(u"layoutWidget")
280
+ self.verticalLayout_11 = QVBoxLayout(self.layoutWidget)
262
281
  self.verticalLayout_11.setObjectName(u"verticalLayout_11")
263
282
  self.verticalLayout_11.setContentsMargins(0, 0, 0, 0)
264
- self.label_13 = QLabel(self.widget)
283
+ self.label_13 = QLabel(self.layoutWidget)
265
284
  self.label_13.setObjectName(u"label_13")
266
285
 
267
286
  self.verticalLayout_11.addWidget(self.label_13)
268
287
 
269
- self.lv_all_plugins = QListWidget(self.widget)
288
+ self.lv_all_plugins = QListWidget(self.layoutWidget)
270
289
  self.lv_all_plugins.setObjectName(u"lv_all_plugins")
271
290
 
272
291
  self.verticalLayout_11.addWidget(self.lv_all_plugins)
273
292
 
274
- self.label_15 = QLabel(self.widget)
293
+ self.label_15 = QLabel(self.layoutWidget)
275
294
  self.label_15.setObjectName(u"label_15")
276
295
 
277
296
  self.verticalLayout_11.addWidget(self.label_15)
278
297
 
279
298
  self.horizontalLayout_16 = QHBoxLayout()
280
299
  self.horizontalLayout_16.setObjectName(u"horizontalLayout_16")
281
- self.le_personal_plugins_dir = QLineEdit(self.widget)
300
+ self.le_personal_plugins_dir = QLineEdit(self.layoutWidget)
282
301
  self.le_personal_plugins_dir.setObjectName(u"le_personal_plugins_dir")
283
302
  self.le_personal_plugins_dir.setReadOnly(True)
284
303
 
285
304
  self.horizontalLayout_16.addWidget(self.le_personal_plugins_dir)
286
305
 
287
- self.pb_browse_plugins_dir = QPushButton(self.widget)
306
+ self.pb_browse_plugins_dir = QPushButton(self.layoutWidget)
288
307
  self.pb_browse_plugins_dir.setObjectName(u"pb_browse_plugins_dir")
289
308
 
290
309
  self.horizontalLayout_16.addWidget(self.pb_browse_plugins_dir)
@@ -292,49 +311,49 @@ class Ui_prefDialog(object):
292
311
 
293
312
  self.verticalLayout_11.addLayout(self.horizontalLayout_16)
294
313
 
295
- self.lw_personal_plugins = QListWidget(self.widget)
314
+ self.lw_personal_plugins = QListWidget(self.layoutWidget)
296
315
  self.lw_personal_plugins.setObjectName(u"lw_personal_plugins")
297
316
 
298
317
  self.verticalLayout_11.addWidget(self.lw_personal_plugins)
299
318
 
300
- self.splitter_2.addWidget(self.widget)
319
+ self.splitter_2.addWidget(self.layoutWidget)
301
320
  self.splitter = QSplitter(self.splitter_2)
302
321
  self.splitter.setObjectName(u"splitter")
303
322
  self.splitter.setOrientation(Qt.Orientation.Vertical)
304
- self.widget1 = QWidget(self.splitter)
305
- self.widget1.setObjectName(u"widget1")
306
- self.verticalLayout_12 = QVBoxLayout(self.widget1)
323
+ self.layoutWidget1 = QWidget(self.splitter)
324
+ self.layoutWidget1.setObjectName(u"layoutWidget1")
325
+ self.verticalLayout_12 = QVBoxLayout(self.layoutWidget1)
307
326
  self.verticalLayout_12.setObjectName(u"verticalLayout_12")
308
327
  self.verticalLayout_12.setContentsMargins(0, 0, 0, 0)
309
- self.label_14 = QLabel(self.widget1)
328
+ self.label_14 = QLabel(self.layoutWidget1)
310
329
  self.label_14.setObjectName(u"label_14")
311
330
 
312
331
  self.verticalLayout_12.addWidget(self.label_14)
313
332
 
314
- self.pte_plugin_description = QPlainTextEdit(self.widget1)
333
+ self.pte_plugin_description = QPlainTextEdit(self.layoutWidget1)
315
334
  self.pte_plugin_description.setObjectName(u"pte_plugin_description")
316
335
  self.pte_plugin_description.setReadOnly(True)
317
336
 
318
337
  self.verticalLayout_12.addWidget(self.pte_plugin_description)
319
338
 
320
- self.splitter.addWidget(self.widget1)
321
- self.widget2 = QWidget(self.splitter)
322
- self.widget2.setObjectName(u"widget2")
323
- self.verticalLayout_14 = QVBoxLayout(self.widget2)
339
+ self.splitter.addWidget(self.layoutWidget1)
340
+ self.layoutWidget2 = QWidget(self.splitter)
341
+ self.layoutWidget2.setObjectName(u"layoutWidget2")
342
+ self.verticalLayout_14 = QVBoxLayout(self.layoutWidget2)
324
343
  self.verticalLayout_14.setObjectName(u"verticalLayout_14")
325
344
  self.verticalLayout_14.setContentsMargins(0, 0, 0, 0)
326
- self.label_23 = QLabel(self.widget2)
345
+ self.label_23 = QLabel(self.layoutWidget2)
327
346
  self.label_23.setObjectName(u"label_23")
328
347
 
329
348
  self.verticalLayout_14.addWidget(self.label_23)
330
349
 
331
- self.pte_plugin_code = QPlainTextEdit(self.widget2)
350
+ self.pte_plugin_code = QPlainTextEdit(self.layoutWidget2)
332
351
  self.pte_plugin_code.setObjectName(u"pte_plugin_code")
333
352
  self.pte_plugin_code.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
334
353
 
335
354
  self.verticalLayout_14.addWidget(self.pte_plugin_code)
336
355
 
337
- self.splitter.addWidget(self.widget2)
356
+ self.splitter.addWidget(self.layoutWidget2)
338
357
  self.splitter_2.addWidget(self.splitter)
339
358
 
340
359
  self.verticalLayout_15.addWidget(self.splitter_2)
@@ -676,7 +695,7 @@ class Ui_prefDialog(object):
676
695
 
677
696
  self.retranslateUi(prefDialog)
678
697
 
679
- self.tabWidget.setCurrentIndex(2)
698
+ self.tabWidget.setCurrentIndex(1)
680
699
 
681
700
 
682
701
  QMetaObject.connectSlotsByName(prefDialog)
@@ -707,6 +726,7 @@ class Ui_prefDialog(object):
707
726
  self.cbTrackingCursorAboveEvent.setText(QCoreApplication.translate("prefDialog", u"Tracking cursor above current event", None))
708
727
  self.cbAlertNoFocalSubject.setText(QCoreApplication.translate("prefDialog", u"Alert if focal subject is not set", None))
709
728
  self.cb_pause_before_addevent.setText(QCoreApplication.translate("prefDialog", u"Pause media before \"Add event\" command", None))
729
+ self.label_24.setText(QCoreApplication.translate("prefDialog", u"Frame step size", None))
710
730
  self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_observations), QCoreApplication.translate("prefDialog", u"Observations", None))
711
731
  self.label_13.setText(QCoreApplication.translate("prefDialog", u"BORIS plugins", None))
712
732
  self.label_15.setText(QCoreApplication.translate("prefDialog", u"Personal plugins", None))
boris/project.py CHANGED
@@ -1977,7 +1977,7 @@ class projectDialog(QDialog, Ui_dlgProject):
1977
1977
  self.pj[cfg.INDEPENDENT_VARIABLES] = dict(self.indVar)
1978
1978
 
1979
1979
  # converters
1980
- converters = {}
1980
+ converters: dict = {}
1981
1981
  for row in range(self.tw_converters.rowCount()):
1982
1982
  converters[self.tw_converters.item(row, 0).text()] = {
1983
1983
  "name": self.tw_converters.item(row, 0).text(),