Skip to content

Commit c86b697

Browse files
committed
Add 1D Gaussian data generation and curve update test
1 parent fb4a2b2 commit c86b697

File tree

2 files changed

+134
-1
lines changed

2 files changed

+134
-1
lines changed

plotpy/tests/data.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,19 @@ def gen_2d_gaussian(size, dtype, x0=0, y0=0, mu=0.0, sigma=2.0, amp=None) -> np.
166166
return np.array(amp * np.exp(-t / (2.0 * sigma**2)), dtype=dtype)
167167

168168

169-
def gen_xyz_data():
169+
def gen_1d_gaussian(
170+
size, x0=0, mu=0.0, sigma=2.0, amp=None
171+
) -> tuple[np.ndarray, np.ndarray]:
172+
"""Creating 1D Gaussian (-10 <= x <= 10)"""
173+
x = np.linspace(-10, 10, size)
174+
if amp is None:
175+
amp = 1.0
176+
t = (np.abs(x - x0) - mu) ** 2
177+
y = np.array(amp * np.exp(-t / (2.0 * sigma**2)), dtype=float)
178+
return x, y
179+
180+
181+
def gen_xyz_data() -> tuple[np.ndarray, np.ndarray, np.ndarray]:
170182
"""Create a X, Y, Z data set for contour detection features"""
171183
delta = 0.025
172184
x, y = np.arange(-3.0, 3.0, delta), np.arange(-2.0, 2.0, delta)
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Licensed under the terms of the BSD 3-Clause
4+
# (see plotpy/LICENSE for details)
5+
6+
"""
7+
Testing curve plot with data update
8+
====================================
9+
10+
The purpose of this test is to implement a simple use case where a curve is
11+
updated in a loop. The curve is created with random data and the values are
12+
updated in a loop. The test is successful if the curve is updated in the
13+
plot window.
14+
"""
15+
16+
# guitest: show
17+
18+
from __future__ import annotations
19+
20+
import numpy as np
21+
import pytest
22+
from guidata.qthelpers import exec_dialog, qt_app_context
23+
from qtpy import QtCore as QC
24+
from qtpy import QtGui as QG
25+
from qtpy import QtWidgets as QW
26+
27+
from plotpy.builder import make
28+
from plotpy.plot import PlotDialog, PlotOptions
29+
from plotpy.tests import data as ptd
30+
31+
32+
def get_data(variable_size: bool) -> tuple[np.ndarray, np.ndarray]:
33+
"""Compute 1D Gaussian data and add a narrower Gaussian on top with a random
34+
position and amplitude."""
35+
size = np.random.randint(10, 200) if variable_size else 100
36+
amp = 0.3
37+
x, y = ptd.gen_1d_gaussian(size, sigma=10.0, x0=0.0, amp=amp)
38+
# Choose a random position: x0 has to be in the range [-10.0, 10.0]
39+
x0 = np.random.uniform(-10.0, 10.0)
40+
# Choose a random amplitude: a has to be in the range [0.1, 0.5]
41+
a = np.random.uniform(0.1, 0.7)
42+
# Add the narrower Gaussian on top
43+
y += ptd.gen_1d_gaussian(size, sigma=4.0, x0=x0, amp=a)[1]
44+
return x, y
45+
46+
47+
class CurveUpdateDialog(PlotDialog):
48+
"""Dialog box for curve update"""
49+
50+
def __init__(self, title: str, variable_size: bool = False) -> None:
51+
self.variable_size = variable_size
52+
options = PlotOptions(title="-", show_contrast=True, type="curve")
53+
super().__init__(title=title, toolbar=True, edit=False, options=options)
54+
self.resize(600, 600)
55+
self.timer = QC.QTimer()
56+
self.item = make.curve(
57+
*get_data(variable_size),
58+
color="blue",
59+
marker="o",
60+
markersize=5,
61+
markerfacecolor="cyan",
62+
markeredgecolor="blue",
63+
)
64+
plot = self.get_plot()
65+
plot.add_item(self.item)
66+
plot.set_active_item(self.item, select=False)
67+
self.counter = 0
68+
69+
def populate_plot_layout(self) -> None:
70+
"""Populate the plot layout with the item"""
71+
start_btn = QW.QPushButton("Start curve update")
72+
start_btn.clicked.connect(self.start_curve_update)
73+
self.add_widget(start_btn, 0, 0)
74+
stop_btn = QW.QPushButton("Stop curve update")
75+
stop_btn.clicked.connect(self.stop_curve_update)
76+
self.add_widget(stop_btn, 0, 1)
77+
variable_size_cb = QW.QCheckBox("Variable size")
78+
variable_size_cb.setChecked(self.variable_size)
79+
variable_size_cb.stateChanged.connect(self.toggle_variable_size)
80+
self.add_widget(variable_size_cb, 0, 2)
81+
self.add_widget(self.plot_widget, 1, 0, 1, 0)
82+
83+
def toggle_variable_size(self, state: int) -> None:
84+
"""Toggle variable size"""
85+
self.variable_size = state == QC.Qt.Checked
86+
87+
def start_curve_update(self) -> None:
88+
"""Start curve update"""
89+
self.timer.timeout.connect(self.update_curve)
90+
self.timer.start(100)
91+
92+
def stop_curve_update(self) -> None:
93+
"""Stop curve update"""
94+
self.timer.stop()
95+
96+
def update_curve(self) -> None:
97+
"""Update curve data"""
98+
data = get_data(self.variable_size)
99+
self.counter += 1
100+
plot = self.get_plot()
101+
self.item.set_data(data[0], data[1])
102+
plot.set_title(f"Curve update {self.counter:03d}")
103+
plot.replot()
104+
105+
def closeEvent(self, event: QG.QCloseEvent) -> None:
106+
"""Close the dialog and stop the timer"""
107+
self.timer.stop()
108+
super().closeEvent(event)
109+
110+
111+
@pytest.mark.skip(reason="Not relevant in automated test suite")
112+
def test_curve_update() -> None:
113+
"""Test curve update"""
114+
title = test_curve_update.__doc__
115+
with qt_app_context(exec_loop=False):
116+
dlg = CurveUpdateDialog(title)
117+
exec_dialog(dlg)
118+
119+
120+
if __name__ == "__main__":
121+
test_curve_update()

0 commit comments

Comments
 (0)