viewephys 1.1.2__tar.gz → 1.2.0__tar.gz

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 (25) hide show
  1. {viewephys-1.1.2 → viewephys-1.2.0}/PKG-INFO +1 -1
  2. {viewephys-1.1.2 → viewephys-1.2.0}/pyproject.toml +1 -1
  3. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/gui.py +83 -9
  4. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/nav_file.ui +64 -0
  5. viewephys-1.2.0/src/viewephys/tests/test_viewer_gui.py +283 -0
  6. viewephys-1.1.2/src/viewephys/tests/test_viewer_gui.py +0 -64
  7. {viewephys-1.1.2 → viewephys-1.2.0}/LICENSE +0 -0
  8. {viewephys-1.1.2 → viewephys-1.2.0}/README.md +0 -0
  9. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/__init__.py +0 -0
  10. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/command_line.py +0 -0
  11. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/raster.py +0 -0
  12. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/raster.ui +0 -0
  13. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/tests/__init__.py +0 -0
  14. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/tests/test_model.py +0 -0
  15. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/tests/test_pick.py +0 -0
  16. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/tests/test_viewer_helpers.py +0 -0
  17. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/tests/test_viewer_model.py +0 -0
  18. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/tests/test_viewer_pgtools.py +0 -0
  19. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewephys.svg +0 -0
  20. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewer/__init__.py +0 -0
  21. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewer/gui.py +0 -0
  22. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewer/pgtools.py +0 -0
  23. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewer/qt.py +0 -0
  24. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewer/spectrogram.ui +0 -0
  25. {viewephys-1.1.2 → viewephys-1.2.0}/src/viewephys/viewer/view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: viewephys
3
- Version: 1.1.2
3
+ Version: 1.2.0
4
4
  Summary: Neuropixel raw data viewer
5
5
  Author: Olivier Winter
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "viewephys"
3
- version = "1.1.2"
3
+ version = "1.2.0"
4
4
  description = "Neuropixel raw data viewer"
5
5
  authors = [
6
6
  { name = "Olivier Winter" },
@@ -67,8 +67,16 @@ class EphysBinViewer(QtWidgets.QMainWindow):
67
67
  self.horizontalSlider.setMinimum(0)
68
68
  self.horizontalSlider.setSingleStep(1)
69
69
  self.horizontalSlider.setTickInterval(10)
70
+ self._first_sample = 0
70
71
  self.horizontalSlider.sliderReleased.connect(self.on_horizontalSliderReleased)
71
72
  self.horizontalSlider.valueChanged.connect(self.on_horizontalSliderValueChanged)
73
+ validator = QtGui.QDoubleValidator(0.0, 1e12, 6, self.lineEdit_jumpTime)
74
+ validator.setNotation(QtGui.QDoubleValidator.StandardNotation)
75
+ # To handle dot as decimal separator regardless of user locale
76
+ validator.setLocale(QtCore.QLocale.c())
77
+ self.lineEdit_jumpTime.setValidator(validator)
78
+ self.lineEdit_jumpTime.returnPressed.connect(self.on_jumpToTimeRequested)
79
+ self.pushButton_jumpTime.clicked.connect(self.on_jumpToTimeRequested)
72
80
  self.label_smin.setText("0")
73
81
  self.show()
74
82
 
@@ -135,32 +143,79 @@ class EphysBinViewer(QtWidgets.QMainWindow):
135
143
  )
136
144
  self.label.setText(tlabel)
137
145
  self.horizontalSlider.setValue(0)
146
+ self._first_sample = 0
138
147
  self.horizontalSlider.setEnabled(True)
148
+ self.lineEdit_jumpTime.setEnabled(True)
149
+ self.pushButton_jumpTime.setEnabled(True)
139
150
  self.on_horizontalSliderReleased()
140
151
 
141
152
  def on_horizontalSliderValueChanged(self) -> None:
142
- tcur = self.horizontalSlider.value() * NSAMP_CHUNK / self.sr.fs
143
- self.label_sval.setText(f"{tcur:0.2f}s")
153
+ self._first_sample = int(self.horizontalSlider.value()) * NSAMP_CHUNK
154
+ self._update_time_label()
144
155
 
145
- def on_horizontalSliderReleased(self) -> None:
156
+ def _update_time_label(self) -> None:
157
+ tcur = self._first_sample / self.sr.fs
158
+ self.label_sval.setText(f"{tcur:0.6f}s")
159
+
160
+ def on_jumpToTimeRequested(self) -> None:
161
+ """Jump to the user-typed time, centering the loaded window on it.
162
+
163
+ The slider thumb is moved to the nearest chunk for visual feedback only.
164
+ """
165
+ text = self.lineEdit_jumpTime.text().strip()
166
+ if text == "" or not hasattr(self, "sr"):
167
+ return
168
+ try:
169
+ t = float(text)
170
+ except ValueError:
171
+ return
172
+ requested_sample = int(round(t * self.sr.fs))
173
+ requested_sample = max(0, min(requested_sample, int(self.sr.ns) - 1))
174
+ max_first = max(0, int(self.sr.ns) - NSAMP_CHUNK)
175
+ first_sample = requested_sample - NSAMP_CHUNK // 2
176
+ first_sample = max(0, min(first_sample, max_first))
177
+ center_time = requested_sample / self.sr.fs
178
+ self._first_sample = first_sample
179
+ slider_value = int(round(first_sample / NSAMP_CHUNK))
180
+ slider_value = max(0, min(slider_value, self.horizontalSlider.maximum()))
181
+ # Move slider for visual feedback without letting valueChanged
182
+ # overwrite the exact first_sample we just set.
183
+ self.horizontalSlider.blockSignals(True)
184
+ self.horizontalSlider.setValue(slider_value)
185
+ self.horizontalSlider.blockSignals(False)
186
+ self._update_time_label()
187
+ self.on_horizontalSliderReleased(center_time=center_time)
188
+
189
+ def on_horizontalSliderReleased( # noqa: C901
190
+ self, center_time: float | None = None
191
+ ) -> None:
146
192
  """
147
193
  Open EphysViewer windows at the selected timepoint
148
194
  for the selected preprocessing steps.
149
195
 
150
- The horizontal slider allows the user to select the timepoint
151
- at which they would like to see the data. This will open EphysVeiewer
152
- windows at a window starting at that time point.
196
+ The horizontal slider opens EphysViewer windows starting at the
197
+ selected timepoint. Jump requests may pass ``center_time`` so existing
198
+ viewer zoom is preserved around the requested absolute time.
153
199
 
154
200
  Depending on the selected preprocessing steps, open a number of
155
201
  viewers (one for each selected preprocessing step) to display the
156
202
  preprocessed data at the selected timepoint.
157
203
  """
158
- first = int(float(self.horizontalSlider.value()) * NSAMP_CHUNK)
204
+ # Capture current zoom per visible viewer so we can restore it after the reload.
205
+ prev_ranges: dict[str, tuple[list[float], list[float]] | None] = {}
206
+ for k, ev in self.viewers.items():
207
+ if ev is not None and ev.isVisible():
208
+ xr, yr = ev.viewBox_seismic.viewRange()
209
+ prev_ranges[k] = (list(xr), list(yr))
210
+ else:
211
+ prev_ranges[k] = None
212
+
213
+ first = int(self._first_sample)
159
214
  last = first + int(NSAMP_CHUNK)
160
215
  raw = self.sr[first:last, : self.sr.nc - self.sr.nsync].T
161
216
 
162
217
  # get parameters for both AP and LFP band
163
- t0 = first / self.sr.fs * 0
218
+ t0 = first / self.sr.fs
164
219
  if self.sr.type == "lf":
165
220
  butter_kwargs = {"N": 3, "Wn": 3 / self.sr.fs * 2, "btype": "highpass"}
166
221
  fcn_destripe = voltage.destripe_lfp
@@ -197,7 +252,7 @@ class EphysBinViewer(QtWidgets.QMainWindow):
197
252
  sos = scipy.signal.butter(**butter_kwargs, output="sos")
198
253
  data = scipy.signal.sosfiltfilt(sos, raw)
199
254
 
200
- self.viewers[k] = viewephys(
255
+ viewer = viewephys(
201
256
  data,
202
257
  self.sr.fs,
203
258
  channels=self.sr.geometry,
@@ -206,6 +261,25 @@ class EphysBinViewer(QtWidgets.QMainWindow):
206
261
  t_scalar=T_SCALAR,
207
262
  a_scalar=A_SCALAR,
208
263
  )
264
+ self.viewers[k] = viewer
265
+ prev = prev_ranges.get(k)
266
+ if prev is not None:
267
+ xr_prev, yr_prev = prev
268
+ width = xr_prev[1] - xr_prev[0]
269
+ xmin, xmax = viewer.ctrl.limits()[0]
270
+ if center_time is None:
271
+ new_x0 = t0 * T_SCALAR
272
+ else:
273
+ new_x0 = center_time * T_SCALAR - width / 2
274
+
275
+ # Preserve the previous zoom width without panning past the data.
276
+ if width >= xmax - xmin:
277
+ new_x0, new_x1 = xmin, xmax
278
+ else:
279
+ new_x0 = max(xmin, min(new_x0, xmax - width))
280
+ new_x1 = new_x0 + width
281
+ viewer.viewBox_seismic.setXRange(new_x0, new_x1, padding=0)
282
+ viewer.viewBox_seismic.setYRange(yr_prev[0], yr_prev[1], padding=0)
209
283
 
210
284
  def closeEvent(self, event: QtGui.QCloseEvent | None) -> None:
211
285
  """
@@ -135,6 +135,70 @@
135
135
  </layout>
136
136
  </widget>
137
137
  </item>
138
+ <item row="6" column="0">
139
+ <widget class="QFrame" name="frame_3">
140
+ <property name="maximumSize">
141
+ <size>
142
+ <width>16777215</width>
143
+ <height>40</height>
144
+ </size>
145
+ </property>
146
+ <property name="frameShape">
147
+ <enum>QFrame::StyledPanel</enum>
148
+ </property>
149
+ <property name="frameShadow">
150
+ <enum>QFrame::Raised</enum>
151
+ </property>
152
+ <layout class="QHBoxLayout" name="horizontalLayout_jumpTime">
153
+ <item>
154
+ <widget class="QLabel" name="label_jumpTime">
155
+ <property name="text">
156
+ <string>Jump to:</string>
157
+ </property>
158
+ </widget>
159
+ </item>
160
+ <item>
161
+ <widget class="QLineEdit" name="lineEdit_jumpTime">
162
+ <property name="enabled">
163
+ <bool>false</bool>
164
+ </property>
165
+ <property name="maximumSize">
166
+ <size>
167
+ <width>120</width>
168
+ <height>16777215</height>
169
+ </size>
170
+ </property>
171
+ <property name="placeholderText">
172
+ <string>seconds</string>
173
+ </property>
174
+ </widget>
175
+ </item>
176
+ <item>
177
+ <widget class="QPushButton" name="pushButton_jumpTime">
178
+ <property name="enabled">
179
+ <bool>false</bool>
180
+ </property>
181
+ <property name="text">
182
+ <string>Go</string>
183
+ </property>
184
+ </widget>
185
+ </item>
186
+ <item>
187
+ <spacer name="horizontalSpacer_jumpTime">
188
+ <property name="orientation">
189
+ <enum>Qt::Horizontal</enum>
190
+ </property>
191
+ <property name="sizeHint" stdset="0">
192
+ <size>
193
+ <width>40</width>
194
+ <height>20</height>
195
+ </size>
196
+ </property>
197
+ </spacer>
198
+ </item>
199
+ </layout>
200
+ </widget>
201
+ </item>
138
202
  </layout>
139
203
  </widget>
140
204
  <widget class="QMenuBar" name="menubar">
@@ -0,0 +1,283 @@
1
+ import numpy as np
2
+ import pytest
3
+ from qtpy import QtCore
4
+
5
+ from viewephys.gui import NSAMP_CHUNK, EphysBinViewer
6
+ from viewephys.tests.test_viewer_helpers import synthetic_seismic_data
7
+ from viewephys.viewer.gui import EasyQC, viewseis
8
+
9
+
10
+ @pytest.fixture
11
+ def synthetic_seis():
12
+ return synthetic_seismic_data()
13
+
14
+
15
+ @pytest.fixture
16
+ def easyqc_window(qtbot):
17
+ window = EasyQC()
18
+ qtbot.addWidget(window)
19
+ window.show()
20
+ qtbot.wait(50)
21
+ yield window
22
+ window.close()
23
+ window.deleteLater()
24
+ qtbot.wait(50)
25
+
26
+
27
+ @pytest.fixture
28
+ def view_with_data(qtbot, synthetic_seis):
29
+ data, header = synthetic_seis
30
+ window = viewseis(data, si=0.002, h=header, title="test")
31
+ qtbot.addWidget(window)
32
+ window.show()
33
+ qtbot.wait(50)
34
+ yield window
35
+ window.close()
36
+ window.deleteLater()
37
+ qtbot.wait(50)
38
+
39
+
40
+ def test_viewseis_shows(view_with_data):
41
+ assert view_with_data.isVisible()
42
+ assert hasattr(view_with_data, "plotItem_seismic")
43
+
44
+
45
+ def test_window_builds(easyqc_window):
46
+ assert easyqc_window.isVisible()
47
+ assert hasattr(easyqc_window, "plotItem_seismic")
48
+
49
+
50
+ def test_gain_edit_updates(view_with_data, qtbot):
51
+ window = view_with_data
52
+ window.lineEdit_gain.setText("6")
53
+ qtbot.keyPress(window.lineEdit_gain, QtCore.Qt.Key_Return)
54
+ qtbot.mouseClick(window.radio_wiggle, QtCore.Qt.LeftButton)
55
+ qtbot.keyPress(window.lineEdit_gain, QtCore.Qt.Key_Return)
56
+ assert float(window.lineEdit_gain.text()) == 6.0
57
+
58
+
59
+ def test_toggle_density_wiggle(view_with_data, qtbot):
60
+ window = view_with_data
61
+ assert window._display_mode == "density"
62
+ qtbot.mouseClick(window.radio_wiggle, QtCore.Qt.LeftButton)
63
+ assert window._display_mode == "wiggle"
64
+ assert window.imageItem_seismic.image is None
65
+ qtbot.mouseClick(window.radio_density, QtCore.Qt.LeftButton)
66
+ assert window._display_mode == "density"
67
+
68
+
69
+ class _FakeSR:
70
+ """Minimal stand-in for spikeglx.Reader, exposing only what the jump logic reads."""
71
+
72
+ fs = 30000.0
73
+ ns = 30000 * 2200 # 2200 s recording, like the screenshot in the request
74
+
75
+
76
+ class _FakeArraySR(_FakeSR):
77
+ """Small array reader used to test jump reloads without a real binary file."""
78
+
79
+ nc = 4
80
+ nsync = 0
81
+ type = "ap"
82
+ geometry = {"trace": np.arange(nc)}
83
+
84
+ def __getitem__(self, key):
85
+ sample_slice, channel_slice = key
86
+ first = 0 if sample_slice.start is None else sample_slice.start
87
+ last = self.ns if sample_slice.stop is None else sample_slice.stop
88
+ first_channel = 0 if channel_slice.start is None else channel_slice.start
89
+ last_channel = self.nc if channel_slice.stop is None else channel_slice.stop
90
+ shape = (last - first, last_channel - first_channel)
91
+ return np.zeros(shape, dtype=np.float32)
92
+
93
+
94
+ class _FakeViewBox:
95
+ """Minimal view box that records range changes made by the jump code."""
96
+
97
+ def __init__(self, view_range=None):
98
+ self._view_range = view_range or ([0.0, 1.0], [0.0, 1.0])
99
+ self.xrange = None
100
+ self.yrange = None
101
+
102
+ def viewRange(self):
103
+ return self._view_range
104
+
105
+ def setXRange(self, x0, x1, padding=0):
106
+ self.xrange = (x0, x1, padding)
107
+
108
+ def setYRange(self, y0, y1, padding=0):
109
+ self.yrange = (y0, y1, padding)
110
+
111
+
112
+ class _FakeCtrl:
113
+ """Minimal controller exposing data limits for range clamping."""
114
+
115
+ def __init__(self, xlim):
116
+ self._xlim = xlim
117
+
118
+ def limits(self):
119
+ return self._xlim, [0.0, 1.0]
120
+
121
+
122
+ class _FakeViewer:
123
+ """Small viewer object used to test range preservation."""
124
+
125
+ def __init__(self, xlim=None, view_range=None):
126
+ self.viewBox_seismic = _FakeViewBox(view_range=view_range)
127
+ self.ctrl = _FakeCtrl(xlim or [0.0, 1.0])
128
+
129
+ def isVisible(self):
130
+ return True
131
+
132
+ def close(self):
133
+ return None
134
+
135
+
136
+ def _centered_first_sample(typed_seconds, fs, ns):
137
+ """Return the expected first sample after centering a jump request."""
138
+ requested_sample = int(round(typed_seconds * fs))
139
+ requested_sample = max(0, min(requested_sample, int(ns) - 1))
140
+ max_first = max(0, int(ns) - NSAMP_CHUNK)
141
+ first_sample = requested_sample - NSAMP_CHUNK // 2
142
+ return max(0, min(first_sample, max_first))
143
+
144
+
145
+ @pytest.fixture
146
+ def jump_window(qtbot, monkeypatch):
147
+ window = EphysBinViewer()
148
+ qtbot.addWidget(window)
149
+ window.sr = _FakeSR()
150
+ slider_max = int(np.floor(window.sr.ns / NSAMP_CHUNK))
151
+ window.horizontalSlider.setMaximum(slider_max)
152
+ window.horizontalSlider.setEnabled(True)
153
+ window.lineEdit_jumpTime.setEnabled(True)
154
+ window.pushButton_jumpTime.setEnabled(True)
155
+ monkeypatch.setattr(
156
+ window, "on_horizontalSliderReleased", lambda center_time=None: None
157
+ )
158
+ window.show()
159
+ qtbot.wait(50)
160
+ yield window
161
+ window.close()
162
+ window.deleteLater()
163
+ qtbot.wait(50)
164
+
165
+
166
+ @pytest.mark.parametrize(
167
+ "typed_seconds",
168
+ [0.0, 0.4, 0.5, 100.0, 500.0, 500.150, 1173.67, 2199.99],
169
+ )
170
+ def test_jump_to_time_loads_exact_sample(jump_window, qtbot, typed_seconds):
171
+ """Jump-to should center the window on the requested sample, while
172
+ parking the slider near the loaded window for visual feedback only."""
173
+ window = jump_window
174
+ fs = window.sr.fs
175
+ expected_first = _centered_first_sample(typed_seconds, fs, window.sr.ns)
176
+ expected_slider = max(
177
+ 0,
178
+ min(
179
+ int(round(expected_first / NSAMP_CHUNK)), window.horizontalSlider.maximum()
180
+ ),
181
+ )
182
+ expected_t = expected_first / fs
183
+
184
+ window.lineEdit_jumpTime.setText(str(typed_seconds))
185
+ qtbot.keyPress(window.lineEdit_jumpTime, QtCore.Qt.Key_Return)
186
+
187
+ assert window._first_sample == expected_first
188
+ assert window.horizontalSlider.value() == expected_slider
189
+ assert window.label_sval.text() == f"{expected_t:0.6f}s"
190
+
191
+
192
+ def test_jump_to_time_non_chunk_aligned(jump_window, qtbot):
193
+ """Sanity check that 500.150 s lands at the center of the loaded window."""
194
+ window = jump_window
195
+ window.lineEdit_jumpTime.setText("500.150")
196
+ window.on_jumpToTimeRequested()
197
+
198
+ requested_sample = int(round(500.150 * window.sr.fs))
199
+ assert window._first_sample + NSAMP_CHUNK // 2 == requested_sample
200
+ assert window._first_sample == 14_999_500
201
+ assert window.horizontalSlider.value() == 1500
202
+ assert window.label_sval.text() == "499.983333s"
203
+
204
+
205
+ def test_slider_drag_resets_first_sample_to_chunk(jump_window, qtbot):
206
+ """After a non-chunk-aligned jump, dragging the slider must snap
207
+ `_first_sample` back to the chunk boundary so subsequent loads use the
208
+ slider position."""
209
+ window = jump_window
210
+ window.lineEdit_jumpTime.setText("500.150")
211
+ qtbot.keyPress(window.lineEdit_jumpTime, QtCore.Qt.Key_Return)
212
+ assert window._first_sample == 14_999_500 # not chunk-aligned
213
+
214
+ window.horizontalSlider.setValue(1501)
215
+ assert window._first_sample == 1501 * NSAMP_CHUNK
216
+ assert window.label_sval.text() == f"{1501 * NSAMP_CHUNK / window.sr.fs:0.6f}s"
217
+
218
+
219
+ def test_jump_to_time_clamps_out_of_range(jump_window, qtbot):
220
+ window = jump_window
221
+ max_first = max(0, int(window.sr.ns) - NSAMP_CHUNK)
222
+ expected_slider_high = max(
223
+ 0, min(int(round(max_first / NSAMP_CHUNK)), window.horizontalSlider.maximum())
224
+ )
225
+
226
+ window.lineEdit_jumpTime.setText("-50")
227
+ qtbot.keyPress(window.lineEdit_jumpTime, QtCore.Qt.Key_Return)
228
+ assert window._first_sample == 0
229
+ assert window.horizontalSlider.value() == 0
230
+
231
+ window.lineEdit_jumpTime.setText("99999")
232
+ qtbot.keyPress(window.lineEdit_jumpTime, QtCore.Qt.Key_Return)
233
+ assert window._first_sample == max_first
234
+ assert window.horizontalSlider.value() == expected_slider_high
235
+
236
+
237
+ def test_jump_to_time_ignores_garbage(jump_window, qtbot):
238
+ window = jump_window
239
+ window.horizontalSlider.setValue(42)
240
+ window.lineEdit_jumpTime.setText("abc")
241
+ qtbot.keyPress(window.lineEdit_jumpTime, QtCore.Qt.Key_Return)
242
+ assert window.horizontalSlider.value() == 42
243
+
244
+ window.lineEdit_jumpTime.setText("")
245
+ qtbot.keyPress(window.lineEdit_jumpTime, QtCore.Qt.Key_Return)
246
+ assert window.horizontalSlider.value() == 42
247
+
248
+
249
+ def test_jump_to_time_recenters_existing_zoom(qtbot, monkeypatch):
250
+ """Reloaded viewers should keep zoom width and recenter on the jump time."""
251
+ window = EphysBinViewer()
252
+ qtbot.addWidget(window)
253
+ window.sr = _FakeArraySR()
254
+ window.horizontalSlider.setMaximum(int(np.floor(window.sr.ns / NSAMP_CHUNK)))
255
+ for checkbox in window.cbs.values():
256
+ checkbox.setChecked(False)
257
+ window.cbs["raw"].setChecked(True)
258
+ window.viewers["raw"] = _FakeViewer(view_range=([400.0, 400.1], [10.0, 20.0]))
259
+
260
+ captured = {}
261
+
262
+ def fake_viewephys(data, fs, channels=None, title="ephys", t0=0.0, **kwargs):
263
+ """Return a fake viewer with the same x-limits as the displayed chunk."""
264
+ viewer = _FakeViewer(xlim=[t0, t0 + data.shape[1] / fs])
265
+ captured["t0"] = t0
266
+ captured["viewer"] = viewer
267
+ return viewer
268
+
269
+ monkeypatch.setattr("viewephys.gui.viewephys", fake_viewephys)
270
+
271
+ window.lineEdit_jumpTime.setText("500.150")
272
+ window.on_jumpToTimeRequested()
273
+
274
+ x0, x1, padding = captured["viewer"].viewBox_seismic.xrange
275
+ y0, y1, y_padding = captured["viewer"].viewBox_seismic.yrange
276
+ assert captured["t0"] == pytest.approx(window._first_sample / window.sr.fs)
277
+ assert (x0 + x1) / 2 == pytest.approx(500.150)
278
+ assert x1 - x0 == pytest.approx(0.1)
279
+ assert padding == 0
280
+ assert (y0, y1, y_padding) == (10.0, 20.0, 0)
281
+
282
+ window.close()
283
+ window.deleteLater()
@@ -1,64 +0,0 @@
1
- import pytest
2
- from qtpy import QtCore
3
-
4
- from viewephys.tests.test_viewer_helpers import synthetic_seismic_data
5
- from viewephys.viewer.gui import EasyQC, viewseis
6
-
7
-
8
- @pytest.fixture
9
- def synthetic_seis():
10
- return synthetic_seismic_data()
11
-
12
-
13
- @pytest.fixture
14
- def easyqc_window(qtbot):
15
- window = EasyQC()
16
- qtbot.addWidget(window)
17
- window.show()
18
- qtbot.wait(50)
19
- yield window
20
- window.close()
21
- window.deleteLater()
22
- qtbot.wait(50)
23
-
24
-
25
- @pytest.fixture
26
- def view_with_data(qtbot, synthetic_seis):
27
- data, header = synthetic_seis
28
- window = viewseis(data, si=0.002, h=header, title="test")
29
- qtbot.addWidget(window)
30
- window.show()
31
- qtbot.wait(50)
32
- yield window
33
- window.close()
34
- window.deleteLater()
35
- qtbot.wait(50)
36
-
37
-
38
- def test_viewseis_shows(view_with_data):
39
- assert view_with_data.isVisible()
40
- assert hasattr(view_with_data, "plotItem_seismic")
41
-
42
-
43
- def test_window_builds(easyqc_window):
44
- assert easyqc_window.isVisible()
45
- assert hasattr(easyqc_window, "plotItem_seismic")
46
-
47
-
48
- def test_gain_edit_updates(view_with_data, qtbot):
49
- window = view_with_data
50
- window.lineEdit_gain.setText("6")
51
- qtbot.keyPress(window.lineEdit_gain, QtCore.Qt.Key_Return)
52
- qtbot.mouseClick(window.radio_wiggle, QtCore.Qt.LeftButton)
53
- qtbot.keyPress(window.lineEdit_gain, QtCore.Qt.Key_Return)
54
- assert float(window.lineEdit_gain.text()) == 6.0
55
-
56
-
57
- def test_toggle_density_wiggle(view_with_data, qtbot):
58
- window = view_with_data
59
- assert window._display_mode == "density"
60
- qtbot.mouseClick(window.radio_wiggle, QtCore.Qt.LeftButton)
61
- assert window._display_mode == "wiggle"
62
- assert window.imageItem_seismic.image is None
63
- qtbot.mouseClick(window.radio_density, QtCore.Qt.LeftButton)
64
- assert window._display_mode == "density"
File without changes
File without changes