boris-behav-obs 9.7.7__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 (109) hide show
  1. boris/__init__.py +26 -0
  2. boris/__main__.py +25 -0
  3. boris/about.py +143 -0
  4. boris/add_modifier.py +635 -0
  5. boris/add_modifier_ui.py +303 -0
  6. boris/advanced_event_filtering.py +455 -0
  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 +1110 -0
  18. boris/behavior_binary_table.py +305 -0
  19. boris/behaviors_coding_map.py +239 -0
  20. boris/boris_cli.py +340 -0
  21. boris/cmd_arguments.py +49 -0
  22. boris/coding_pad.py +280 -0
  23. boris/config.py +785 -0
  24. boris/config_file.py +356 -0
  25. boris/connections.py +409 -0
  26. boris/converters.py +333 -0
  27. boris/converters_ui.py +225 -0
  28. boris/cooccurence.py +250 -0
  29. boris/core.py +5901 -0
  30. boris/core_qrc.py +15958 -0
  31. boris/core_ui.py +1107 -0
  32. boris/db_functions.py +324 -0
  33. boris/dev.py +134 -0
  34. boris/dialog.py +1108 -0
  35. boris/duration_widget.py +238 -0
  36. boris/edit_event.py +245 -0
  37. boris/edit_event_ui.py +233 -0
  38. boris/event_operations.py +1040 -0
  39. boris/events_cursor.py +61 -0
  40. boris/events_snapshots.py +596 -0
  41. boris/exclusion_matrix.py +141 -0
  42. boris/export_events.py +1006 -0
  43. boris/export_observation.py +1203 -0
  44. boris/external_processes.py +332 -0
  45. boris/geometric_measurement.py +941 -0
  46. boris/gui_utilities.py +135 -0
  47. boris/image_overlay.py +72 -0
  48. boris/import_observations.py +242 -0
  49. boris/ipc_mpv.py +325 -0
  50. boris/irr.py +634 -0
  51. boris/latency.py +244 -0
  52. boris/measurement_widget.py +161 -0
  53. boris/media_file.py +115 -0
  54. boris/menu_options.py +213 -0
  55. boris/modifier_coding_map_creator.py +1013 -0
  56. boris/modifiers_coding_map.py +157 -0
  57. boris/mpv.py +2016 -0
  58. boris/mpv2.py +2193 -0
  59. boris/observation.py +1453 -0
  60. boris/observation_operations.py +2538 -0
  61. boris/observation_ui.py +679 -0
  62. boris/observations_list.py +337 -0
  63. boris/otx_parser.py +442 -0
  64. boris/param_panel.py +201 -0
  65. boris/param_panel_ui.py +305 -0
  66. boris/player_dock_widget.py +198 -0
  67. boris/plot_data_module.py +536 -0
  68. boris/plot_events.py +634 -0
  69. boris/plot_events_rt.py +237 -0
  70. boris/plot_spectrogram_rt.py +316 -0
  71. boris/plot_waveform_rt.py +230 -0
  72. boris/plugins.py +431 -0
  73. boris/portion/__init__.py +31 -0
  74. boris/portion/const.py +95 -0
  75. boris/portion/dict.py +365 -0
  76. boris/portion/func.py +52 -0
  77. boris/portion/interval.py +581 -0
  78. boris/portion/io.py +181 -0
  79. boris/preferences.py +510 -0
  80. boris/preferences_ui.py +770 -0
  81. boris/project.py +2007 -0
  82. boris/project_functions.py +2041 -0
  83. boris/project_import_export.py +1096 -0
  84. boris/project_ui.py +794 -0
  85. boris/qrc_boris.py +10389 -0
  86. boris/qrc_boris5.py +2579 -0
  87. boris/select_modifiers.py +312 -0
  88. boris/select_observations.py +210 -0
  89. boris/select_subj_behav.py +286 -0
  90. boris/state_events.py +197 -0
  91. boris/subjects_pad.py +106 -0
  92. boris/synthetic_time_budget.py +290 -0
  93. boris/time_budget_functions.py +1136 -0
  94. boris/time_budget_widget.py +1039 -0
  95. boris/transitions.py +365 -0
  96. boris/utilities.py +1810 -0
  97. boris/version.py +24 -0
  98. boris/video_equalizer.py +159 -0
  99. boris/video_equalizer_ui.py +248 -0
  100. boris/video_operations.py +310 -0
  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.7.dist-info/METADATA +139 -0
  105. boris_behav_obs-9.7.7.dist-info/RECORD +109 -0
  106. boris_behav_obs-9.7.7.dist-info/WHEEL +5 -0
  107. boris_behav_obs-9.7.7.dist-info/entry_points.txt +2 -0
  108. boris_behav_obs-9.7.7.dist-info/licenses/LICENSE.TXT +674 -0
  109. boris_behav_obs-9.7.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,237 @@
1
+ """
2
+ BORIS
3
+ Behavioral Observation Research Interactive Software
4
+ Copyright 2012-2025 Olivier Friard
5
+
6
+ This file is part of BORIS.
7
+
8
+ BORIS is free software; you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation; either version 3 of the License, or
11
+ any later version.
12
+
13
+ BORIS is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
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/>.
20
+
21
+
22
+
23
+ Plot events in real time
24
+ """
25
+
26
+ import matplotlib
27
+
28
+ matplotlib.use("QtAgg")
29
+
30
+ import numpy as np
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
+
35
+ from matplotlib.figure import Figure
36
+
37
+ from . import config as cfg
38
+
39
+ # matplotlib.pyplot.switch_backend("Qt5Agg")
40
+
41
+
42
+ class Plot_events_RT(QWidget):
43
+ # send keypress event to mainwindow
44
+ sendEvent = Signal(QEvent)
45
+
46
+ def __init__(self):
47
+ super().__init__()
48
+ self.setWindowTitle("Events plot")
49
+
50
+ self.interval = 60 # default interval of visualization (in seconds)
51
+ self.time_mem = -1
52
+
53
+ self.events_mem = {"init": 0}
54
+
55
+ self.cursor_color = cfg.REALTIME_PLOT_CURSOR_COLOR # default cursor color
56
+ self.observation_type = cfg.MEDIA
57
+ self.groupby = "behaviors" # group results by "behaviors" or "modifiers"
58
+
59
+ self.figure = Figure()
60
+ self.ax = self.figure.add_subplot(1, 1, 1)
61
+
62
+ self.canvas = FigureCanvas(self.figure)
63
+
64
+ layout = QVBoxLayout()
65
+ layout.addWidget(self.canvas)
66
+
67
+ hlayout1 = QHBoxLayout()
68
+ hlayout1.addWidget(QLabel("Time interval"))
69
+ hlayout1.addWidget(QPushButton("+", self, clicked=lambda: self.time_interval_changed(1), focusPolicy=Qt.NoFocus))
70
+ hlayout1.addWidget(QPushButton("-", self, clicked=lambda: self.time_interval_changed(-1), focusPolicy=Qt.NoFocus))
71
+ self.pb_mode = QPushButton("Include modifiers", self, clicked=self.change_mode, focusPolicy=Qt.NoFocus)
72
+ hlayout1.addWidget(self.pb_mode)
73
+ layout.addLayout(hlayout1)
74
+
75
+ self.setLayout(layout)
76
+
77
+ self.installEventFilter(self)
78
+
79
+ def eventFilter(self, receiver, event):
80
+ """
81
+ send event (if keypress) to main window
82
+ """
83
+ if event.type() == QEvent.KeyPress:
84
+ self.sendEvent.emit(event)
85
+ return True
86
+ else:
87
+ return False
88
+
89
+ def change_mode(self) -> None:
90
+ """
91
+ Change plot mode
92
+ "behaviors" -> plot behaviors without modifiers
93
+ "modifiers" -> plot behaviors and modifiers
94
+ """
95
+
96
+ if self.groupby == "behaviors":
97
+ self.groupby = "modifiers"
98
+ self.pb_mode.setText("Show behaviors w/o modifiers")
99
+ else:
100
+ self.groupby = "behaviors"
101
+ self.pb_mode.setText("Include modifiers")
102
+
103
+ def time_interval_changed(self, action: int) -> None:
104
+ """
105
+ change the time interval for events plot
106
+
107
+ Args:
108
+ action (int): -1 decrease time interval, +1 increase time interval
109
+
110
+ Returns:
111
+ None
112
+ """
113
+
114
+ if action == -1 and self.interval <= 5:
115
+ return
116
+ self.interval += 5 * action
117
+ self.plot_events(current_time=self.time_mem, force_plot=True)
118
+
119
+ def aggregate_events(self, events: list, start: float, end: float) -> dict:
120
+ """
121
+ aggregate state events
122
+ take consideration of subject and modifiers
123
+
124
+ Args:
125
+ events (list): list of events
126
+ start (float): initial value
127
+ end (float): final value
128
+
129
+ Returns:
130
+ dict
131
+
132
+ """
133
+
134
+ def group(subject: str, code: str, modifier: str) -> tuple:
135
+ if self.groupby == "behaviors":
136
+ return (subject, code)
137
+ else: # with modifiers
138
+ return (subject, code, modifier)
139
+
140
+ mem_behav = {}
141
+ intervals_behav = {}
142
+
143
+ for event in events:
144
+ intervals_behav[group(event[1], event[2], event[3])] = [(0, 0)]
145
+
146
+ for event in events:
147
+ time_, subject, code, modifier = event[:4]
148
+ key = group(subject, code, modifier)
149
+
150
+ # check if code is state
151
+ if code in self.state_events_list:
152
+ if key in mem_behav and mem_behav[key] is not None:
153
+ # stop interval
154
+
155
+ # check if event is in interval start-end
156
+ if any(
157
+ (
158
+ start <= mem_behav[key] <= end,
159
+ start <= time_ <= end,
160
+ mem_behav[key] <= start and time_ > end,
161
+ )
162
+ ):
163
+ intervals_behav[key].append((float(mem_behav[key]), float(time_)))
164
+ mem_behav[key] = None
165
+ else:
166
+ # start interval
167
+ mem_behav[key] = time_
168
+
169
+ else: # point event
170
+ if start <= time_ <= end:
171
+ intervals_behav[key].append((float(time_), float(time_) + self.point_event_plot_duration * 50)) # point event -> 1 s
172
+
173
+ # check if intervals are closed
174
+ for k in mem_behav:
175
+ if mem_behav[k] is not None: # interval open
176
+ if self.observation_type == cfg.LIVE:
177
+ intervals_behav[k].append((float(mem_behav[k]), float((end + start) / 2))) # close interval with current time
178
+
179
+ elif self.observation_type == cfg.MEDIA:
180
+ intervals_behav[k].append((float(mem_behav[k]), float(end))) # close interval with end value
181
+
182
+ return intervals_behav
183
+
184
+ def plot_events(self, current_time: float, force_plot: bool = False):
185
+ """
186
+ plot events centered on the current time
187
+
188
+ Args:
189
+ current_time (float): time for displaying events
190
+ force_plot (bool): force plot even if media paused
191
+ """
192
+
193
+ self.events = self.aggregate_events(self.events_list, current_time - self.interval / 2, current_time + self.interval / 2)
194
+
195
+ if not force_plot and current_time == self.time_mem:
196
+ return
197
+
198
+ self.time_mem = current_time
199
+
200
+ if self.events != self.events_mem:
201
+ left, duration = {}, {}
202
+ for k in self.events:
203
+ left[k] = []
204
+ duration[k] = []
205
+ for interv in self.events[k]:
206
+ left[k].append(interv[0])
207
+ duration[k].append(interv[1] - interv[0])
208
+
209
+ self.behaviors, self.durations, self.lefts, self.colors = [], [], [], []
210
+ for k in self.events:
211
+ if self.groupby == "behaviors":
212
+ subject_name, bevavior_code = k
213
+ if subject_name == "":
214
+ subject_name = "No focal"
215
+ behav_col = self.behav_color[bevavior_code]
216
+ self.behaviors.extend([f"{subject_name} - {bevavior_code}"] * len(self.events[k]))
217
+ self.colors.extend([behav_col] * len(self.events[k]))
218
+ else: # with modifiers
219
+ subject_name, bevavior_code, modifier = k
220
+ behav_col = self.behav_color[bevavior_code]
221
+ self.behaviors.extend([f"{subject_name} - {bevavior_code} ({modifier})"] * len(self.events[k]))
222
+ self.colors.extend([behav_col] * len(self.events[k]))
223
+
224
+ self.lefts.extend(left[k])
225
+ self.durations.extend(duration[k])
226
+
227
+ self.events_mem = self.events
228
+
229
+ self.ax.clear()
230
+ self.ax.set_xlim(current_time - self.interval / 2, current_time + self.interval / 2)
231
+
232
+ self.ax.axvline(x=current_time, color=self.cursor_color, linestyle="-")
233
+
234
+ self.ax.barh(y=np.array(self.behaviors), width=self.durations, left=self.lefts, color=self.colors, height=0.5)
235
+
236
+ self.canvas.draw()
237
+ self.figure.canvas.flush_events()
@@ -0,0 +1,316 @@
1
+ """
2
+ BORIS
3
+ Behavioral Observation Research Interactive Software
4
+ Copyright 2012-2025 Olivier Friard
5
+
6
+
7
+ This program is free software; you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation; either version 2 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program; if not, write to the Free Software
19
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20
+ MA 02110-1301, USA.
21
+
22
+ """
23
+
24
+ import wave
25
+ import matplotlib
26
+
27
+ matplotlib.use("QtAgg")
28
+
29
+ import numpy as np
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
42
+ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
43
+ from matplotlib.figure import Figure
44
+ import matplotlib.ticker as mticker
45
+
46
+
47
+ class Plot_spectrogram_RT(QWidget):
48
+ # send keypress event to mainwindow
49
+ sendEvent = Signal(QEvent)
50
+
51
+ def __init__(self):
52
+ super().__init__()
53
+ self.setWindowTitle("Spectrogram")
54
+
55
+ self.interval = 10 # interval of visualization (in seconds)
56
+ self.time_mem = -1
57
+
58
+ self.cursor_color = cfg.REALTIME_PLOT_CURSOR_COLOR
59
+
60
+ self.spectro_color_map = matplotlib.pyplot.get_cmap("viridis")
61
+
62
+ self.figure = Figure()
63
+ self.ax = self.figure.add_subplot(1, 1, 1)
64
+
65
+ self.canvas = FigureCanvas(self.figure)
66
+
67
+ layout = QVBoxLayout()
68
+ layout.addWidget(self.canvas)
69
+
70
+ hlayout1 = QHBoxLayout()
71
+ hlayout1.addWidget(QLabel("Time interval"))
72
+ hlayout1.addWidget(
73
+ QPushButton(
74
+ "+",
75
+ self,
76
+ clicked=lambda: self.time_interval_changed(1),
77
+ focusPolicy=Qt.NoFocus,
78
+ )
79
+ )
80
+ hlayout1.addWidget(
81
+ QPushButton(
82
+ "-",
83
+ self,
84
+ clicked=lambda: self.time_interval_changed(-1),
85
+ focusPolicy=Qt.NoFocus,
86
+ )
87
+ )
88
+ layout.addLayout(hlayout1)
89
+
90
+ hlayout2 = QHBoxLayout()
91
+ hlayout2.addWidget(QLabel("Frequency interval"))
92
+ self.sb_freq_min = QSpinBox(valueChanged=self.frequency_interval_changed, focusPolicy=Qt.NoFocus)
93
+ self.sb_freq_min.setRange(0, 200000)
94
+ self.sb_freq_min.setSingleStep(100)
95
+ self.sb_freq_max = QSpinBox(valueChanged=self.frequency_interval_changed, focusPolicy=Qt.NoFocus)
96
+ self.sb_freq_max.setRange(0, 200000)
97
+ self.sb_freq_max.setSingleStep(100)
98
+ hlayout2.addWidget(self.sb_freq_min)
99
+ hlayout2.addWidget(self.sb_freq_max)
100
+ layout.addLayout(hlayout2)
101
+
102
+ self.setLayout(layout)
103
+
104
+ self.installEventFilter(self)
105
+
106
+ def eventFilter(self, receiver, event):
107
+ """
108
+ send event (if keypress) to main window
109
+ """
110
+ if event.type() == QEvent.KeyPress:
111
+ self.sendEvent.emit(event)
112
+ return True
113
+ else:
114
+ return False
115
+
116
+ def get_wav_info(self, wav_file: str) -> tuple[np.array, int]:
117
+ """
118
+ read wav file and extract information
119
+
120
+ Args:
121
+ wav_file (str): path of wav file
122
+
123
+ Returns:
124
+ np.array: signal contained in wav file
125
+ int: frame rate of wav file
126
+
127
+ """
128
+
129
+ try:
130
+ wav = wave.open(wav_file, "r")
131
+ frames = wav.readframes(-1)
132
+ # sound_info = np.fromstring(frames, dtype=np.int16)
133
+ sound_info = np.frombuffer(frames, dtype=np.int16)
134
+ frame_rate = wav.getframerate()
135
+ wav.close()
136
+ return sound_info, frame_rate
137
+ except Exception:
138
+ return np.array([]), 0
139
+
140
+ def time_interval_changed(self, action: int):
141
+ """
142
+ change the time interval for plotting spectrogram
143
+
144
+ Args:
145
+ action (int): -1 decrease time interval, +1 increase time interval
146
+
147
+ Returns:
148
+ None
149
+ """
150
+
151
+ if action == -1 and self.interval <= 5:
152
+ return
153
+ self.interval += 5 * action
154
+ self.plot_spectro(current_time=self.time_mem, force_plot=True)
155
+
156
+ def frequency_interval_changed(self):
157
+ """
158
+ change the frequency interval for plotting spectrogram
159
+ """
160
+ self.plot_spectro(current_time=self.time_mem, force_plot=True)
161
+
162
+ def load_wav(self, wav_file_path: str) -> dict:
163
+ """
164
+ load wav file in numpy array
165
+
166
+ Args:
167
+ wav_file_path (str): path of wav file
168
+
169
+ Returns:
170
+ dict: "error" key if error, "media_length" and "frame_rate"
171
+ """
172
+
173
+ try:
174
+ self.sound_info, self.frame_rate = self.get_wav_info(wav_file_path)
175
+ if not self.frame_rate:
176
+ return {"error": f"unknown format for file {wav_file_path}"}
177
+ except FileNotFoundError:
178
+ return {"error": f"File not found: {wav_file_path}"}
179
+
180
+ self.media_length = len(self.sound_info) / self.frame_rate
181
+
182
+ self.wav_file_path = wav_file_path
183
+
184
+ return {"media_length": self.media_length, "frame_rate": self.frame_rate}
185
+
186
+ def plot_spectro(self, current_time: float, force_plot: bool = False) -> tuple[float, bool]:
187
+ """
188
+ plot sound spectrogram centered on the current time
189
+
190
+ Args:
191
+ current_time (float): time for displaying spectrogram
192
+ force_plot (bool): force plot even if media paused
193
+ """
194
+
195
+ if not force_plot and current_time == self.time_mem:
196
+ return
197
+
198
+ self.time_mem = current_time
199
+
200
+ self.ax.clear()
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
+
208
+ # start
209
+ if current_time <= self.interval / 2:
210
+ self.ax.specgram(
211
+ self.sound_info[: int(self.interval * self.frame_rate)],
212
+ mode="psd",
213
+ NFFT=nfft,
214
+ Fs=self.frame_rate,
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,
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,
232
+ )
233
+
234
+ self.ax.set_xlim(current_time - self.interval / 2, current_time + self.interval / 2)
235
+
236
+ # cursor
237
+ self.ax.axvline(x=current_time, color=self.cursor_color, linestyle="-")
238
+
239
+ elif current_time >= self.media_length - self.interval / 2:
240
+ i = int(round(len(self.sound_info) - (self.interval * self.frame_rate), 0))
241
+
242
+ self.ax.specgram(
243
+ self.sound_info[i:],
244
+ mode="psd",
245
+ NFFT=nfft,
246
+ Fs=self.frame_rate,
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,
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,
264
+ )
265
+
266
+ lim1 = current_time - (self.media_length - self.interval / 2)
267
+ lim2 = lim1 + self.interval
268
+
269
+ self.ax.set_xlim(lim1, lim2)
270
+
271
+ self.ax.xaxis.set_major_locator(mticker.FixedLocator(self.ax.get_xticks().tolist()))
272
+ self.ax.set_xticklabels([str(round(w + self.media_length - self.interval, 1)) for w in self.ax.get_xticks()])
273
+
274
+ # cursor
275
+ self.ax.axvline(x=lim1 + self.interval / 2, color=self.cursor_color, linestyle="-")
276
+
277
+ # middle
278
+ else:
279
+ self.ax.specgram(
280
+ self.sound_info[
281
+ int(round((current_time - self.interval / 2) * self.frame_rate, 0)) : int(
282
+ round((current_time + self.interval / 2) * self.frame_rate, 0)
283
+ )
284
+ ],
285
+ mode="psd",
286
+ NFFT=nfft,
287
+ Fs=self.frame_rate,
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,
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,
305
+ )
306
+
307
+ self.ax.xaxis.set_major_locator(mticker.FixedLocator(self.ax.get_xticks().tolist()))
308
+ self.ax.set_xticklabels([str(round(current_time + w - self.interval / 2, 1)) for w in self.ax.get_xticks()])
309
+
310
+ # cursor
311
+ self.ax.axvline(x=self.interval / 2, color=self.cursor_color, linestyle="-")
312
+
313
+ self.ax.set_ylim(self.sb_freq_min.value(), self.sb_freq_max.value())
314
+ """self.figure.subplots_adjust(wspace=0, hspace=0)"""
315
+
316
+ self.canvas.draw()