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.

Files changed (128) hide show
  1. boris/__init__.py +1 -1
  2. boris/__main__.py +1 -1
  3. boris/about.py +28 -39
  4. boris/add_modifier.py +122 -109
  5. boris/add_modifier_ui.py +239 -135
  6. boris/advanced_event_filtering.py +81 -45
  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 +42 -49
  23. boris/config.py +141 -65
  24. boris/config_file.py +58 -67
  25. boris/connections.py +107 -61
  26. boris/converters.py +13 -37
  27. boris/converters_ui.py +187 -110
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +2373 -1786
  30. boris/core_qrc.py +15895 -10743
  31. boris/core_ui.py +943 -798
  32. boris/db_functions.py +17 -42
  33. boris/dev.py +109 -8
  34. boris/dialog.py +482 -236
  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 +408 -293
  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 +184 -223
  43. boris/export_observation.py +74 -100
  44. boris/external_processes.py +123 -98
  45. boris/geometric_measurement.py +644 -290
  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 +325 -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 +17 -6
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +7 -9
  57. boris/mpv.py +1 -0
  58. boris/mpv2.py +732 -705
  59. boris/observation.py +533 -221
  60. boris/observation_operations.py +1025 -390
  61. boris/observation_ui.py +572 -362
  62. boris/observations_list.py +71 -53
  63. boris/otx_parser.py +74 -68
  64. boris/param_panel.py +31 -16
  65. boris/param_panel_ui.py +254 -138
  66. boris/player_dock_widget.py +90 -60
  67. boris/plot_data_module.py +25 -33
  68. boris/plot_events.py +127 -90
  69. boris/plot_events_rt.py +17 -31
  70. boris/plot_spectrogram_rt.py +95 -30
  71. boris/plot_waveform_rt.py +32 -21
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +18 -8
  74. boris/portion/const.py +35 -18
  75. boris/portion/dict.py +5 -5
  76. boris/portion/func.py +2 -2
  77. boris/portion/interval.py +21 -41
  78. boris/portion/io.py +41 -32
  79. boris/preferences.py +306 -83
  80. boris/preferences_ui.py +684 -227
  81. boris/project.py +448 -293
  82. boris/project_functions.py +671 -238
  83. boris/project_import_export.py +213 -222
  84. boris/project_ui.py +674 -438
  85. boris/qrc_boris.py +6 -3
  86. boris/qrc_boris5.py +6 -3
  87. boris/select_modifiers.py +74 -48
  88. boris/select_observations.py +20 -198
  89. boris/select_subj_behav.py +67 -39
  90. boris/state_events.py +52 -35
  91. boris/subjects_pad.py +6 -9
  92. boris/synthetic_time_budget.py +45 -28
  93. boris/time_budget_functions.py +171 -171
  94. boris/time_budget_widget.py +84 -114
  95. boris/transitions.py +41 -47
  96. boris/utilities.py +627 -236
  97. boris/version.py +3 -3
  98. boris/video_equalizer.py +16 -14
  99. boris/video_equalizer_ui.py +199 -130
  100. boris/video_operations.py +95 -29
  101. boris/view_df.py +104 -0
  102. boris/view_df_ui.py +75 -0
  103. boris/write_event.py +538 -0
  104. boris_behav_obs-9.7.6.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.6.dist-info/RECORD +109 -0
  106. {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/WHEEL +1 -1
  107. boris_behav_obs-9.7.6.dist-info/entry_points.txt +2 -0
  108. boris/README.TXT +0 -22
  109. boris/add_modifier.ui +0 -323
  110. boris/converters.ui +0 -289
  111. boris/core.qrc +0 -36
  112. boris/core.ui +0 -1556
  113. boris/edit_event.ui +0 -233
  114. boris/icons/logo_eye.ico +0 -0
  115. boris/map_creator.py +0 -850
  116. boris/observation.ui +0 -814
  117. boris/param_panel.ui +0 -379
  118. boris/preferences.ui +0 -537
  119. boris/project.ui +0 -1069
  120. boris/project_server.py +0 -236
  121. boris/vlc.py +0 -10343
  122. boris/vlc_local.py +0 -90
  123. boris_behav_obs-8.12.dist-info/LICENSE.TXT +0 -674
  124. boris_behav_obs-8.12.dist-info/METADATA +0 -128
  125. boris_behav_obs-8.12.dist-info/RECORD +0 -108
  126. boris_behav_obs-8.12.dist-info/entry_points.txt +0 -3
  127. {boris → boris_behav_obs-9.7.6.dist-info/licenses}/LICENSE.TXT +0 -0
  128. {boris_behav_obs-8.12.dist-info → boris_behav_obs-9.7.6.dist-info}/top_level.txt +0 -0
@@ -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
 
7
7
  This program is free software; you can redistribute it and/or modify
@@ -22,32 +22,40 @@ Copyright 2012-2023 Olivier Friard
22
22
  """
23
23
 
24
24
  import wave
25
-
26
25
  import matplotlib
27
26
 
28
- matplotlib.use("Qt5Agg")
27
+ matplotlib.use("QtAgg")
28
+
29
29
  import numpy as np
30
- from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QSpinBox
31
- from PyQt5.QtCore import pyqtSignal, QEvent
32
- from PyQt5 import Qt
30
+ from scipy import signal
31
+ from . import config as cfg
32
+
33
+ from PySide6.QtWidgets import (
34
+ QWidget,
35
+ QVBoxLayout,
36
+ QHBoxLayout,
37
+ QPushButton,
38
+ QLabel,
39
+ QSpinBox,
40
+ )
41
+ from PySide6.QtCore import Signal, QEvent, Qt
33
42
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
34
43
  from matplotlib.figure import Figure
35
44
  import matplotlib.ticker as mticker
36
45
 
37
46
 
38
47
  class Plot_spectrogram_RT(QWidget):
39
-
40
48
  # send keypress event to mainwindow
41
- sendEvent = pyqtSignal(QEvent)
49
+ sendEvent = Signal(QEvent)
42
50
 
43
51
  def __init__(self):
44
52
  super().__init__()
45
- self.setWindowTitle(f"Spectrogram")
53
+ self.setWindowTitle("Spectrogram")
46
54
 
47
55
  self.interval = 10 # interval of visualization (in seconds)
48
56
  self.time_mem = -1
49
57
 
50
- self.cursor_color = "red"
58
+ self.cursor_color = cfg.REALTIME_PLOT_CURSOR_COLOR
51
59
 
52
60
  self.spectro_color_map = matplotlib.pyplot.get_cmap("viridis")
53
61
 
@@ -62,19 +70,29 @@ class Plot_spectrogram_RT(QWidget):
62
70
  hlayout1 = QHBoxLayout()
63
71
  hlayout1.addWidget(QLabel("Time interval"))
64
72
  hlayout1.addWidget(
65
- QPushButton("+", self, clicked=lambda: self.time_interval_changed(1), focusPolicy=Qt.Qt.NoFocus)
73
+ QPushButton(
74
+ "+",
75
+ self,
76
+ clicked=lambda: self.time_interval_changed(1),
77
+ focusPolicy=Qt.NoFocus,
78
+ )
66
79
  )
67
80
  hlayout1.addWidget(
68
- QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.Qt.NoFocus)
81
+ QPushButton(
82
+ "-",
83
+ self,
84
+ clicked=lambda: self.time_interval_changed(-1),
85
+ focusPolicy=Qt.NoFocus,
86
+ )
69
87
  )
70
88
  layout.addLayout(hlayout1)
71
89
 
72
90
  hlayout2 = QHBoxLayout()
73
91
  hlayout2.addWidget(QLabel("Frequency interval"))
74
- self.sb_freq_min = QSpinBox(valueChanged=self.frequency_interval_changed, focusPolicy=Qt.Qt.NoFocus)
92
+ self.sb_freq_min = QSpinBox(valueChanged=self.frequency_interval_changed, focusPolicy=Qt.NoFocus)
75
93
  self.sb_freq_min.setRange(0, 200000)
76
94
  self.sb_freq_min.setSingleStep(100)
77
- self.sb_freq_max = QSpinBox(valueChanged=self.frequency_interval_changed, focusPolicy=Qt.Qt.NoFocus)
95
+ self.sb_freq_max = QSpinBox(valueChanged=self.frequency_interval_changed, focusPolicy=Qt.NoFocus)
78
96
  self.sb_freq_max.setRange(0, 200000)
79
97
  self.sb_freq_max.setSingleStep(100)
80
98
  hlayout2.addWidget(self.sb_freq_min)
@@ -95,7 +113,7 @@ class Plot_spectrogram_RT(QWidget):
95
113
  else:
96
114
  return False
97
115
 
98
- def get_wav_info(self, wav_file: str):
116
+ def get_wav_info(self, wav_file: str) -> tuple[np.array, int]:
99
117
  """
100
118
  read wav file and extract information
101
119
 
@@ -111,7 +129,8 @@ class Plot_spectrogram_RT(QWidget):
111
129
  try:
112
130
  wav = wave.open(wav_file, "r")
113
131
  frames = wav.readframes(-1)
114
- sound_info = np.fromstring(frames, dtype=np.int16)
132
+ # sound_info = np.fromstring(frames, dtype=np.int16)
133
+ sound_info = np.frombuffer(frames, dtype=np.int16)
115
134
  frame_rate = wav.getframerate()
116
135
  wav.close()
117
136
  return sound_info, frame_rate
@@ -164,7 +183,7 @@ class Plot_spectrogram_RT(QWidget):
164
183
 
165
184
  return {"media_length": self.media_length, "frame_rate": self.frame_rate}
166
185
 
167
- def plot_spectro(self, current_time: float, force_plot: bool = False):
186
+ def plot_spectro(self, current_time: float, force_plot: bool = False) -> tuple[float, bool]:
168
187
  """
169
188
  plot sound spectrogram centered on the current time
170
189
 
@@ -180,16 +199,36 @@ class Plot_spectrogram_RT(QWidget):
180
199
 
181
200
  self.ax.clear()
182
201
 
202
+ window_type = "blackmanharris" # self.config_param.get(cfg.SPECTROGRAM_WINDOW_TYPE, cfg.SPECTROGRAM_DEFAULT_WINDOW_TYPE)
203
+ nfft = int(self.config_param.get(cfg.SPECTROGRAM_NFFT, cfg.SPECTROGRAM_DEFAULT_NFFT))
204
+ noverlap = self.config_param.get(cfg.SPECTROGRAM_NOVERLAP, cfg.SPECTROGRAM_DEFAULT_NOVERLAP)
205
+ vmin = self.config_param.get(cfg.SPECTROGRAM_VMIN, cfg.SPECTROGRAM_DEFAULT_VMIN)
206
+ vmax = self.config_param.get(cfg.SPECTROGRAM_VMAX, cfg.SPECTROGRAM_DEFAULT_VMAX)
207
+
183
208
  # start
184
209
  if current_time <= self.interval / 2:
185
-
186
210
  self.ax.specgram(
187
- self.sound_info[: int((self.interval) * self.frame_rate)],
211
+ self.sound_info[: int(self.interval * self.frame_rate)],
188
212
  mode="psd",
189
- # NFFT=1024,
213
+ NFFT=nfft,
190
214
  Fs=self.frame_rate,
191
- # noverlap=900,
215
+ noverlap=noverlap,
216
+ window=signal.get_window(window_type, nfft),
217
+ # matplotlib.mlab.window_hanning
218
+ # if window_type == "hanning"
219
+ # else matplotlib.mlab.window_hamming
220
+ # if window_type == "hamming"
221
+ # else matplotlib.mlab.window_blackmanharris
222
+ # if window_type == "blackmanharris"
223
+ # else matplotlib.mlab.window_hanning,
192
224
  cmap=self.spectro_color_map,
225
+ vmin=vmin,
226
+ vmax=vmax,
227
+ # mode="psd",
228
+ ## NFFT=1024,
229
+ # Fs=self.frame_rate,
230
+ ## noverlap=900,
231
+ # cmap=self.spectro_color_map,
193
232
  )
194
233
 
195
234
  self.ax.set_xlim(current_time - self.interval / 2, current_time + self.interval / 2)
@@ -198,16 +237,30 @@ class Plot_spectrogram_RT(QWidget):
198
237
  self.ax.axvline(x=current_time, color=self.cursor_color, linestyle="-")
199
238
 
200
239
  elif current_time >= self.media_length - self.interval / 2:
201
-
202
240
  i = int(round(len(self.sound_info) - (self.interval * self.frame_rate), 0))
203
241
 
204
242
  self.ax.specgram(
205
243
  self.sound_info[i:],
206
244
  mode="psd",
207
- # NFFT=1024,
245
+ NFFT=nfft,
208
246
  Fs=self.frame_rate,
209
- # noverlap=900,
247
+ noverlap=noverlap,
248
+ window=signal.get_window(window_type, nfft),
249
+ # matplotlib.mlab.window_hanning
250
+ # if window_type == "hanning"
251
+ # else matplotlib.mlab.window_hamming
252
+ # if window_type == "hamming"
253
+ # else matplotlib.mlab.window_blackmanharris
254
+ # if window_type == "blackmanharris"
255
+ # else matplotlib.mlab.window_hanning,
210
256
  cmap=self.spectro_color_map,
257
+ vmin=vmin,
258
+ vmax=vmax,
259
+ # mode="psd",
260
+ ## NFFT=1024,
261
+ # Fs=self.frame_rate,
262
+ ## noverlap=900,
263
+ # cmap=self.spectro_color_map,
211
264
  )
212
265
 
213
266
  lim1 = current_time - (self.media_length - self.interval / 2)
@@ -216,16 +269,13 @@ class Plot_spectrogram_RT(QWidget):
216
269
  self.ax.set_xlim(lim1, lim2)
217
270
 
218
271
  self.ax.xaxis.set_major_locator(mticker.FixedLocator(self.ax.get_xticks().tolist()))
219
- self.ax.set_xticklabels(
220
- [str(round(w + self.media_length - self.interval, 1)) for w in self.ax.get_xticks()]
221
- )
272
+ self.ax.set_xticklabels([str(round(w + self.media_length - self.interval, 1)) for w in self.ax.get_xticks()])
222
273
 
223
274
  # cursor
224
275
  self.ax.axvline(x=lim1 + self.interval / 2, color=self.cursor_color, linestyle="-")
225
276
 
226
277
  # middle
227
278
  else:
228
-
229
279
  self.ax.specgram(
230
280
  self.sound_info[
231
281
  int(round((current_time - self.interval / 2) * self.frame_rate, 0)) : int(
@@ -233,10 +283,25 @@ class Plot_spectrogram_RT(QWidget):
233
283
  )
234
284
  ],
235
285
  mode="psd",
236
- # NFFT=1024,
286
+ NFFT=nfft,
237
287
  Fs=self.frame_rate,
238
- # noverlap=900,
288
+ noverlap=noverlap,
289
+ window=signal.get_window(window_type, nfft),
290
+ # matplotlib.mlab.window_hanning
291
+ # if window_type == "hanning"
292
+ # else matplotlib.mlab.window_hamming
293
+ # if window_type == "hamming"
294
+ # else matplotlib.mlab.window_blackmanharris
295
+ # if window_type == "blackmanharris"
296
+ # else matplotlib.mlab.window_hanning,
239
297
  cmap=self.spectro_color_map,
298
+ vmin=vmin,
299
+ vmax=vmax,
300
+ # mode="psd",
301
+ ## NFFT=1024,
302
+ # Fs=self.frame_rate,
303
+ ## noverlap=900,
304
+ # cmap=self.spectro_color_map,
240
305
  )
241
306
 
242
307
  self.ax.xaxis.set_major_locator(mticker.FixedLocator(self.ax.get_xticks().tolist()))
boris/plot_waveform_rt.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
 
7
7
  This program is free software; you can redistribute it and/or modify
@@ -22,32 +22,33 @@ Copyright 2012-2023 Olivier Friard
22
22
  """
23
23
 
24
24
  import wave
25
-
25
+ from . import config as cfg
26
26
  import matplotlib
27
27
 
28
- matplotlib.use("Qt5Agg")
28
+ matplotlib.use("QtAgg")
29
+
29
30
  import numpy as np
30
- from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
31
- from PyQt5.QtCore import pyqtSignal, QEvent
32
- from PyQt5 import Qt
33
- from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
31
+ from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel
32
+ from PySide6.QtCore import Signal, QEvent, Qt
33
+ from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
34
34
  from matplotlib.figure import Figure
35
35
  import matplotlib.ticker as mticker
36
36
 
37
+ # matplotlib.pyplot.switch_backend("Qt5Agg")
37
38
 
38
- class Plot_waveform_RT(QWidget):
39
39
 
40
+ class Plot_waveform_RT(QWidget):
40
41
  # send keypress event to mainwindow
41
- sendEvent = pyqtSignal(QEvent)
42
+ sendEvent = Signal(QEvent)
42
43
 
43
44
  def __init__(self):
44
45
  super().__init__()
45
- self.setWindowTitle(f"Waveform")
46
+ self.setWindowTitle("Waveform")
46
47
 
47
48
  self.interval = 60 # interval of visualization (in seconds)
48
49
  self.time_mem = -1
49
50
 
50
- self.cursor_color = "red"
51
+ self.cursor_color: str = cfg.REALTIME_PLOT_CURSOR_COLOR
51
52
 
52
53
  self.spectro_color_map = matplotlib.pyplot.get_cmap("viridis")
53
54
 
@@ -62,10 +63,20 @@ class Plot_waveform_RT(QWidget):
62
63
  hlayout1 = QHBoxLayout()
63
64
  hlayout1.addWidget(QLabel("Time interval"))
64
65
  hlayout1.addWidget(
65
- QPushButton("+", self, clicked=lambda: self.time_interval_changed(1), focusPolicy=Qt.Qt.NoFocus)
66
+ QPushButton(
67
+ "+",
68
+ self,
69
+ clicked=lambda: self.time_interval_changed(1),
70
+ focusPolicy=Qt.NoFocus,
71
+ )
66
72
  )
67
73
  hlayout1.addWidget(
68
- QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.Qt.NoFocus)
74
+ QPushButton(
75
+ "-",
76
+ self,
77
+ clicked=lambda: self.time_interval_changed(-1),
78
+ focusPolicy=Qt.NoFocus,
79
+ )
69
80
  )
70
81
  layout.addLayout(hlayout1)
71
82
 
@@ -98,7 +109,8 @@ class Plot_waveform_RT(QWidget):
98
109
  try:
99
110
  wav = wave.open(wav_file, "r")
100
111
  frames = wav.readframes(-1)
101
- signal = np.fromstring(frames, dtype=np.int16)
112
+ # signal = np.fromstring(frames, dtype=np.int16)
113
+ signal = np.frombuffer(frames, dtype=np.int16)
102
114
  frame_rate = wav.getframerate()
103
115
  wav.close()
104
116
  return signal, frame_rate
@@ -162,7 +174,6 @@ class Plot_waveform_RT(QWidget):
162
174
 
163
175
  # start
164
176
  if current_time <= self.interval / 2:
165
-
166
177
  time_ = np.linspace(
167
178
  0,
168
179
  len(self.sound_info[: int((self.interval) * self.frame_rate)]) / self.frame_rate,
@@ -176,10 +187,13 @@ class Plot_waveform_RT(QWidget):
176
187
  self.ax.axvline(x=current_time, color=self.cursor_color, linestyle="-")
177
188
 
178
189
  elif current_time >= self.media_length - self.interval / 2:
179
-
180
190
  i = int(round(len(self.sound_info) - (self.interval * self.frame_rate), 0))
181
191
 
182
- time_ = np.linspace(0, len(self.sound_info[i:]) / self.frame_rate, num=len(self.sound_info[i:]))
192
+ time_ = np.linspace(
193
+ 0,
194
+ len(self.sound_info[i:]) / self.frame_rate,
195
+ num=len(self.sound_info[i:]),
196
+ )
183
197
  self.ax.plot(time_, self.sound_info[i:])
184
198
 
185
199
  lim1 = current_time - (self.media_length - self.interval / 2)
@@ -188,16 +202,13 @@ class Plot_waveform_RT(QWidget):
188
202
  self.ax.set_xlim(lim1, lim2)
189
203
 
190
204
  self.ax.xaxis.set_major_locator(mticker.FixedLocator(self.ax.get_xticks().tolist()))
191
- self.ax.set_xticklabels(
192
- [str(round(w + self.media_length - self.interval, 1)) for w in self.ax.get_xticks()]
193
- )
205
+ self.ax.set_xticklabels([str(round(w + self.media_length - self.interval, 1)) for w in self.ax.get_xticks()])
194
206
 
195
207
  # cursor
196
208
  self.ax.axvline(x=lim1 + self.interval / 2, color=self.cursor_color, linestyle="-")
197
209
 
198
210
  # middle
199
211
  else:
200
-
201
212
  start = (current_time - self.interval / 2) * self.frame_rate
202
213
  end = (current_time + self.interval / 2) * self.frame_rate
203
214