├── .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 | --------------------------------------------------------------------------------