├── .gitignore
├── LICENSE.md
├── README.md
├── overlay.py
├── phaseplot.py
├── realtime.gif
├── realtime_plot
└── __init__.py
├── serialplot.py
├── setup.py
└── slowfast.py
/.gitignore:
--------------------------------------------------------------------------------
1 | RealtimePlotter.egg-info/
2 | build/
3 | dist/
4 | *.pyc
5 | *.swp
6 | *~
7 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # RealtimePlotter
4 | Real-time scrolling multi-plot
5 |
6 | Requires: matplotlib
7 | numpy
8 |
--------------------------------------------------------------------------------
/overlay.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | '''
3 | Real-time plot demo using overlain sine waves.
4 |
5 | Copyright (C) 2016 Simon D. Levy
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU Lesser General Public License as
9 | published by the Free Software Foundation, either version 3 of the
10 | License, or (at your option) any later version.
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 | '''
16 |
17 | from realtime_plot import RealtimePlotter
18 | import numpy as np
19 |
20 | # Simple example with threading
21 |
22 | class _SinePlotter(RealtimePlotter):
23 |
24 | def __init__(self):
25 |
26 | RealtimePlotter.__init__(self, [(-1,+1)],
27 | window_name='Sine and Cosine',
28 | yticks = [(-1,0,+1)],
29 | styles = [('r--', 'b-')],
30 | legends = [('sin', 'cos')])
31 |
32 | self.xcurr = 0
33 |
34 | def getValues(self):
35 |
36 | return self._getWave(np.sin), self._getWave(np.cos)
37 |
38 | def _getWave(self, fun):
39 |
40 | size = len(self.x)
41 |
42 | return fun(2*np.pi*(float(self.xcurr)%size)/size)
43 |
44 |
45 | def _update(plotter):
46 |
47 | from time import sleep
48 |
49 | while True:
50 |
51 | plotter.xcurr += 1
52 | sleep(.002)
53 |
54 | if __name__ == '__main__':
55 |
56 | import threading
57 |
58 | plotter = _SinePlotter()
59 |
60 | thread = threading.Thread(target=_update, args = (plotter,))
61 | thread.daemon = True
62 | thread.start()
63 |
64 | plotter.start()
65 |
66 |
--------------------------------------------------------------------------------
/phaseplot.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | '''
3 | Real-time plot demo using sine waves.
4 |
5 | Copyright (C) 2015 Simon D. Levy
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU Lesser General Public License as
9 | published by the Free Software Foundation, either version 3 of the
10 | License, or (at your option) any later version.
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 | '''
16 |
17 | from realtime_plot import RealtimePlotter
18 | import numpy as np
19 |
20 | # Simple example with threading
21 |
22 | class _SinePlotter(RealtimePlotter):
23 |
24 | def __init__(self):
25 |
26 | RealtimePlotter.__init__(self, [(-1,+1), (-1,+1)],
27 | phaselims=((-1,+1), (-1,+1)),
28 | window_name='Sinewave demo',
29 | yticks = [(-1,0,+1),(-1,0,+1)],
30 | styles = ['r--', 'b-'],
31 | ylabels=['Sin', 'Cos'])
32 |
33 | self.xcurr = 0
34 |
35 | def getValues(self):
36 |
37 | s = self._getRow(1)
38 | c = self._getRow(2)
39 |
40 | return s,c, s, c
41 |
42 | def _getRow(self, row):
43 |
44 | size = len(self.x)
45 |
46 | angle = 2*np.pi*(float(self.xcurr)%size)/size
47 |
48 | return np.sin(angle) if row == 2 else np.cos(angle)
49 |
50 |
51 | def _update(plotter):
52 |
53 | from time import sleep
54 |
55 | while True:
56 |
57 | plotter.xcurr += 1
58 | sleep(.002)
59 |
60 | if __name__ == '__main__':
61 |
62 | import threading
63 |
64 | plotter = _SinePlotter()
65 |
66 | thread = threading.Thread(target=_update, args = (plotter,))
67 | thread.daemon = True
68 | thread.start()
69 |
70 | plotter.start()
71 |
72 |
--------------------------------------------------------------------------------
/realtime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondlevy/RealtimePlotter/dc280b4ea030227745d9e74b6f3ce0007f5d7bf9/realtime.gif
--------------------------------------------------------------------------------
/realtime_plot/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | Real-time scrolling multi-plot over time.
3 |
4 | Copyright (C) 2015 Simon D. Levy
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU Lesser General Public License as
8 | published by the Free Software Foundation, either version 3 of the
9 | License, or (at your option) any later version.
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 | '''
15 |
16 | import matplotlib.pyplot as plt
17 | import matplotlib.animation as animation
18 | import numpy as np
19 |
20 |
21 | class RealtimePlotter(object):
22 | '''
23 | Real-time scrolling multi-plot over time. Your data-acquisition code
24 | should run on its own thread, to prevent blocking / slowdown.
25 | '''
26 |
27 | def _check_param(self, nrows, propvals, propname, dflt):
28 | retval = [dflt]*nrows
29 | if propvals:
30 | if len(propvals) != nrows:
31 | raise Exception('Provided %d ylims but %d %s' %
32 | (nrows, len(propvals), propname))
33 | else:
34 | retval = propvals
35 | return retval
36 |
37 | def handleClose(self, event):
38 | '''
39 | Automatically called when user closes plot window.
40 | Override to do you own shutdown.
41 | '''
42 |
43 | self.is_open = False
44 |
45 | def __init__(self, ylims, size=100, phaselims=None, show_yvals=False,
46 | window_name=None, styles=None, ylabels=None, yticks=[],
47 | legends=[], interval_msec=20):
48 | '''
49 | Initializes a multi-plot with specified Y-axis limits as a list of
50 | pairs; e.g., [(-1,+1), (0.,5)]. Optional parameters are:
51 |
52 | size size of display (X axis) in arbitrary time steps
53 | phaselims xlim,ylim for phase plot
54 | show_yvals display Y values in plot if True
55 | window_name name to display at the top of the figure
56 | styles plot styles (e.g., 'b-', 'r.'; default='b-')
57 | yticks Y-axis tick / grid positions
58 | legends list of legends for each subplot
59 | interval_msec animation update in milliseconds
60 |
61 | For overlaying plots, use a tuple for styles; e.g.,
62 | styles=[('r','g'), 'b']
63 | '''
64 |
65 | # Row count is provided by Y-axis limits
66 | nrows = len(ylims)
67 |
68 | # Bozo filters
69 | styles = self._check_param(nrows, styles, 'styles', 'b-')
70 | ylabels = self._check_param(nrows, ylabels, 'ylabels', '')
71 | yticks = self._check_param(nrows, yticks, 'yticks', [])
72 | self.legends = self._check_param(nrows, legends, 'legends', [])
73 |
74 | self.fig = plt.gcf()
75 |
76 | # X values are arbitrary ascending; Y is initially zero
77 | self.x = np.arange(0, size)
78 | y = np.zeros(size)
79 |
80 | # Set up subplots
81 | self.axes = [None]*nrows
82 | ncols = 2 if phaselims else 1
83 | self.sideline = None
84 | if phaselims:
85 | side = plt.subplot(1, 2, 1)
86 | side.set_aspect('equal')
87 | self.sideline = side.plot(y, y, 'o', animated=True)
88 | side.set_xlim(phaselims[0])
89 | side.set_ylim(phaselims[1])
90 | for k in range(nrows):
91 | self.axes[k] = plt.subplot(nrows, ncols, ncols*(k+1))
92 | self.window_name = ('RealtimePlotter' if window_name is None
93 | else window_name)
94 |
95 | # Set up handler for window-close events
96 | self.fig.canvas.mpl_connect('close_event', self.handleClose)
97 | self.is_open = True
98 |
99 | # Create lines
100 | self.lines = []
101 | for j in range(len(styles)):
102 | style = styles[j]
103 | ax = self.axes[j]
104 | legend = self.legends[j] if len(self.legends) > 0 else None
105 | stylesForRow = style if isinstance(style, tuple) else [style]
106 | for k in range(len(stylesForRow)):
107 | label = legend[k] if legend and len(legend) > 0 else ''
108 | self.lines.append(ax.plot(self.x, y, stylesForRow[k],
109 | animated=True, label=label)[0])
110 | if legend is not None and len(legend) > 0:
111 | ax.legend()
112 |
113 | # Create baselines, initially hidden
114 | self.baselines = [axis.plot(self.x, y, 'k', animated=True)[0]
115 | for axis in self.axes]
116 | self.baseflags = [False]*nrows
117 |
118 | # Add properties as specified
119 | [axis.set_ylabel(ylabel) for axis, ylabel in zip(self.axes, ylabels)]
120 |
121 | # Set axis limits
122 | [axis.set_xlim((0, size)) for axis in self.axes]
123 | [axis.set_ylim(ylim) for axis, ylim in zip(self.axes, ylims)]
124 |
125 | # Set ticks and gridlines
126 | [axis.yaxis.set_ticks(ytick) for axis, ytick in zip(self.axes, yticks)]
127 | [axis.yaxis.grid(True if yticks else False) for axis in self.axes]
128 |
129 | # Hide X axis ticks and labels for now
130 | [axis.xaxis.set_visible(False) for axis in self.axes]
131 |
132 | # Allow interval specification
133 | self.interval_msec = interval_msec
134 |
135 | # Add axis text if indicated
136 | self.axis_texts = ([axis.text(0.8, ylim[1] - .1 * (ylim[1] - ylim[0]),
137 | '')
138 | for axis, ylim in zip(self.axes, ylims)]
139 | if show_yvals else [])
140 |
141 | def start(self):
142 | '''
143 | Starts the realtime plotter.
144 | '''
145 |
146 | # If we don't assign the result of the function, we won't see anything!
147 | ani = animation.FuncAnimation(self.fig, self._animate,
148 | interval=self.interval_msec, blit=True,
149 | cache_frame_data=False)
150 |
151 | try:
152 | plt.show()
153 | except Exception:
154 | pass
155 |
156 | def getValues(self):
157 | '''
158 | Override this method to return actual Y values at current time.
159 | '''
160 |
161 | return None
162 |
163 | def showBaseline(self, axid, value):
164 | '''
165 | Shows a baseline of specified value for specified row of this
166 | multi-plot.
167 | '''
168 |
169 | self._axis_check(axid)
170 |
171 | self.baselines[axid].set_ydata(value * np.ones(self.x.shape))
172 | self.baseflags[axid] = True
173 |
174 | def hideBaseline(self, axid):
175 | '''
176 | Hides the baseline for the specified row of this multi-plot.
177 | '''
178 |
179 | self._axis_check(axid)
180 |
181 | self.baseflags[axid] = False
182 |
183 | def _axis_check(self, axid):
184 |
185 | nrows = len(self.lines)
186 |
187 | if axid < 0 or axid >= nrows:
188 |
189 | raise Exception('Axis index must be in [0,%d)' % nrows)
190 |
191 | @classmethod
192 | def roll(cls, getter, setter, line, newval):
193 | data = getter(line)
194 | data = np.roll(data, -1)
195 | data[-1] = newval
196 | setter(data)
197 |
198 | @classmethod
199 | def rollx(cls, line, newval):
200 | RealtimePlotter.roll(line.get_xdata, line.set_xdata, line, newval)
201 |
202 | @classmethod
203 | def rolly(cls, line, newval):
204 | RealtimePlotter.roll(line.get_ydata, line.set_ydata, line, newval)
205 |
206 | def _animate(self, t):
207 |
208 | values = self.getValues()
209 |
210 | if values is None:
211 |
212 | self.fig.canvas.manager.set_window_title('Waiting for data ...')
213 |
214 | else:
215 |
216 | self.fig.canvas.manager.set_window_title(self.window_name)
217 |
218 | yvals = values[2:] if self.sideline else values
219 |
220 | for k, text in enumerate(self.axis_texts):
221 | text.set_text('%+f' % yvals[k])
222 |
223 | for row, line in enumerate(self.lines, start=1):
224 | RealtimePlotter.rolly(line, yvals[row-1])
225 |
226 | if self.sideline:
227 | sideline = self.sideline[0]
228 | RealtimePlotter.rollx(sideline, values[0])
229 | RealtimePlotter.rolly(sideline, values[1])
230 |
231 | # Animation function must return everything we want to animate
232 | return ((self.sideline if self.sideline is not None else []) +
233 | self.lines + [baseline for baseline, flag in
234 | zip(self.baselines, self.baseflags) if flag] +
235 | self.axis_texts)
236 |
--------------------------------------------------------------------------------
/serialplot.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | '''
3 | Real-time plot demo using serial input
4 |
5 | Copyright (C) 2015 Simon D. Levy
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU Lesser General Public License as
9 | published by the Free Software Foundation, either version 3 of the
10 | License, or (at your option) any later version.
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 | '''
16 |
17 |
18 | import serial
19 | from realtime_plot import RealtimePlotter
20 | from threading import Thread
21 |
22 | # Change these to suit your needs
23 | PORT = '/dev/ttyACM0'
24 | BAUD = 115200
25 | RANGE = (-1,+1)
26 |
27 | class SerialPlotter(RealtimePlotter):
28 |
29 | def __init__(self):
30 |
31 | RealtimePlotter.__init__(self, [RANGE],
32 | window_name='Serial input',
33 | yticks = [RANGE],
34 | styles = ['b-'])
35 |
36 | self.xcurr = 0
37 | self.ycurr = 0
38 |
39 | def getValues(self):
40 |
41 | return (self.ycurr,)
42 |
43 | def _update(port, plotter):
44 |
45 | msg = ''
46 |
47 | while True:
48 |
49 | c = port.read().decode()
50 |
51 | if c == '\n':
52 | try:
53 | plotter.ycurr = float(msg)
54 | except:
55 | pass
56 | msg = ''
57 | else:
58 | msg += c
59 |
60 | plotter.xcurr += 1
61 |
62 | if __name__ == '__main__':
63 |
64 | try:
65 | port = serial.Serial(PORT, BAUD)
66 | except serial.SerialException:
67 | print('Unable to access device on port %s' % PORT)
68 | exit(1)
69 |
70 | plotter = SerialPlotter()
71 |
72 | thread = Thread(target=_update, args = (port, plotter))
73 | thread.daemon = True
74 | thread.start()
75 |
76 | plotter.start()
77 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | '''
4 | setup.py - Python distutils setup file for RealtimePlotter module.
5 |
6 | Copyright (C) 2015 Simon D. Levy
7 |
8 | This code is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU Lesser General Public License as
10 | published by the Free Software Foundation, either version 3 of the
11 | License, or (at your option) any later version.
12 |
13 | This code 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 Lesser General Public License
19 | along with this code. If not, see .
20 | '''
21 |
22 | #from distutils.core import setup
23 | from setuptools import setup
24 |
25 | setup (name = 'RealtimePlotter',
26 | version = '0.1',
27 | install_requires = ['numpy', 'matplotlib'],
28 | description = 'Real-time plotting in Python',
29 | packages = ['realtime_plot'],
30 | author='Simon D. Levy',
31 | author_email='simon.d.levy@gmail.com',
32 | url='https://github.com/simondlevy/RealtimePlotter',
33 | license='LGPL',
34 | platforms='Linux; Windows; OS X'
35 | )
36 |
--------------------------------------------------------------------------------
/slowfast.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | '''
3 | Real-time plot demo using sine waves.
4 |
5 | Copyright (C) 2015 Simon D. Levy
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU Lesser General Public License as
9 | published by the Free Software Foundation, either version 3 of the
10 | License, or (at your option) any later version.
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 | '''
16 |
17 | from realtime_plot import RealtimePlotter
18 | import numpy as np
19 |
20 | # Simple example with threading
21 |
22 | class _SinePlotter(RealtimePlotter):
23 |
24 | def __init__(self):
25 |
26 | RealtimePlotter.__init__(self, [(-1,+1), (-1,+1)],
27 | show_yvals=True,
28 | window_name='Sinewave demo',
29 | yticks = [(-1,0,+1),(-1,0,+1)],
30 | styles = ['r--', 'b-'],
31 | ylabels=['Slow', 'Fast'])
32 |
33 | self.xcurr = 0
34 |
35 | def getValues(self):
36 |
37 | return self._getWave(1), self._getWave(2)
38 |
39 | def _getWave(self, k):
40 |
41 | size = len(self.x)
42 |
43 | return np.sin(2*k*np.pi*(float(self.xcurr)%size)/size)
44 |
45 |
46 | def _update(plotter):
47 |
48 | from time import sleep
49 |
50 | while True:
51 |
52 | plotter.xcurr += 1
53 | sleep(.002)
54 |
55 | if __name__ == '__main__':
56 |
57 | import threading
58 |
59 | plotter = _SinePlotter()
60 |
61 | thread = threading.Thread(target=_update, args = (plotter,))
62 | thread.daemon = True
63 | thread.start()
64 |
65 | plotter.start()
66 |
67 |
--------------------------------------------------------------------------------