-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscope.py
More file actions
152 lines (97 loc) · 3.66 KB
/
scope.py
File metadata and controls
152 lines (97 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# -*- coding: utf-8 -*-
# Pyblab
# Copyright (C) 2021 Marco Pizzocaro <m.pizzocaro@inrim.it>
#
# This file is part of Pyblab.
#
# Pyblab is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyblab is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Pyblab. If not, see <https://www.gnu.org/licenses/>.
from collections import deque
import numpy
import threading
import matplotlib.pyplot as plt
#from matplotlib.figure import Figure
#from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
#from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
class MultiScope(object):
""" A real time 'Scope' view of data based on matplotlib subplots and deque."""
def __init__(self, cycle, title="", n=1, maxn=19, xlabel=None, ylabels=[None], yslices=[slice(1,None)], size=(800,400), dpi=80, **kwargs):
# set up the plot
self.size = size
x, y = size
figsize = x/dpi, y*n/dpi
self.fig, self.axes = plt.subplots(nrows=n, sharex=True, figsize=figsize, dpi=dpi)
self.fig.canvas.set_window_title(title)
# axes is a list except for n=1
if n==1:
self.axes =[self.axes]
self.set_up_axes(xlabel,ylabels,yslices)
# set up the acquisition on a separate thread
self.cycle = cycle
self.event = self.cycle.newdata_event
# set up the buffer of data
self.maxn = maxn
self.buffer = deque([], self.maxn)
self.lines = []
self.doclear = False
self.start()
def set_up_axes(self, xlabel, ylabels, yslices):
for ax, ylabel in zip(self.axes, ylabels):
ax.set_ylabel(ylabel)
# fix the label position
ax.yaxis.set_label_coords(-0.14, 0.5)
self.axes[-1].set_xlabel(xlabel)
self.fig.tight_layout()
self.fig.subplots_adjust(left=0.16)
self.yslices = yslices
def start(self):
self.thread = threading.Thread(target=self.update)
self.thread.start()
def close(self):
plt.close(self.fig)
def clear(self):
#self.buffer.clear()
self.doclear = True #quick workaround, should be an event
def update(self):
# not necessary. the glib.idle_add will take care of that withouth blocking the main program
#plt.show(self.fig)
while True:
#wait for new data
self.event.wait()
self.event.clear()
if self.doclear:
self.buffer.clear()
self.doclear = False
self.buffer.append(self.cycle.newdata)
# extract all data in buffer in a fast way (with a temprary conversion in list)
# and with a proper shape (see later)
data = list(self.buffer)
data = numpy.array(data).T
x = data[0] # first row it is x
#yy = data[1:-1] x other are y
#labels = self.cycle.what[1:-1]
for ax, sli in zip(self.axes, self.yslices):
yy = data[sli]
if ax.lines:
for l, y in zip(ax.lines, yy):
l.set_xdata(x)
l.set_ydata(y)
if ax.lines:
# set autoscale from new data!!!
ax.relim()
ax.autoscale_view(tight=True, scalex=False, scaley=True)
else:
ax.plot(x, yy.T, '.', markersize=15.)
# update plot
self.axes[-1].autoscale_view(tight=True, scalex=True, scaley=False)
self.fig.canvas.draw()