class MiniMap(QtGui.QWidget): """Shows the entire signal and allows the user to navigate through it. Provides an scrollable selector over the entire signal. Attributes: xmin: Selector lower limit (measured in h-axis units). xmax: Selector upper limit (measured in h-axis units). step: Selector length (measured in h-axis units). """ def __init__(self, parent, ax, record=None): super(MiniMap, self).__init__(parent) self.ax = ax self.xmin = 0.0 self.xmax = 0.0 self.step = 10.0 self.xrange = np.array([]) self.minimapFig = plt.figure() self.minimapFig.set_figheight(0.75) self.minimapFig.add_axes((0, 0, 1, 1)) self.minimapCanvas = FigureCanvas(self.minimapFig) self.minimapCanvas.setFixedHeight(64) self.minimapSelector = self.minimapFig.axes[0].axvspan(0, self.step, color='gray', alpha=0.5, animated=True) self.minimapSelection = self.minimapFig.axes[0].axvspan( 0, self.step, color='LightCoral', alpha=0.5, animated=True) self.minimapSelection.set_visible(False) self.minimapBackground = [] self.minimapSize = (self.minimapFig.bbox.width, self.minimapFig.bbox.height) self.press_selector = None self.playback_marker = None self.minimapCanvas.mpl_connect('button_press_event', self.onpress) self.minimapCanvas.mpl_connect('button_release_event', self.onrelease) self.minimapCanvas.mpl_connect('motion_notify_event', self.onmove) # Animation related attrs. self.background = None self.animated = False # Set the layout self.layout = QtGui.QVBoxLayout(self) self.layout.addWidget(self.minimapCanvas) # Animation related attributes self.parentViewer = parent # Set Markers dict self.markers = {} self.record = None if record is not None: self.set_record(record) def set_record(self, record, step): self.record = record self.step = step self.xrange = np.linspace(0, len(self.record.signal) / self.record.fs, num=len(self.record.signal), endpoint=False) self.xmin = self.xrange[0] self.xmax = self.xrange[-1] self.markers = {} ax = self.minimapFig.axes[0] ax.lines = [] formatter = FuncFormatter( lambda x, pos: str(datetime.timedelta(seconds=x))) ax.xaxis.set_major_formatter(formatter) ax.grid(True, which='both') # Set dataseries to plot xmin = self.xmin * self.record.fs xmax = self.xmax * self.record.fs pixel_width = np.ceil(self.minimapFig.get_figwidth() * self.minimapFig.get_dpi()) x_data, y_data = plotting.reduce_data(self.xrange, self.record.signal, pixel_width, xmin, xmax) # self._plot_data.set_xdata(x_data) # self._plot_data.set_ydata(y_data) ax.plot(x_data, y_data, color='black', rasterized=True) ax.set_xlim(self.xmin, self.xmax) plotting.adjust_axes_height(ax) # Set the playback marker self.playback_marker = PlayBackMarker(self.minimapFig, self) self.playback_marker.markers[0].set_animated(True) # Draw canvas self.minimapCanvas.draw() self.minimapBackground = self.minimapCanvas.copy_from_bbox( self.minimapFig.bbox) self.draw_animate() def onpress(self, event): self.press_selector = event xdata = round(self.get_xdata(event), 2) xmin = round(xdata - (self.step / 2.0), 2) xmax = round(xdata + (self.step / 2.0), 2) self.parentViewer._set_animated(True) self.set_selector_limits(xmin, xmax) def onrelease(self, event): self.press_selector = None # Finish parent animation self.parentViewer._set_animated(False) def onmove(self, event): if self.press_selector is not None: xdata = round(self.get_xdata(event), 2) xmin = round(xdata - (self.step / 2.0), 2) xmax = round(xdata + (self.step / 2.0), 2) self.set_selector_limits(xmin, xmax) def get_xdata(self, event): inv = self.minimapFig.axes[0].transData.inverted() xdata, _ = inv.transform((event.x, event.y)) return xdata def set_selector_limits(self, xmin, xmax): step = xmax - xmin if step >= self.xmax - self.xmin: xleft = self.xmin xright = self.xmax if xmin < self.xmin: xleft = self.xmin xright = self.step elif xmax > self.xmax: xleft = self.xmax - step xright = self.xmax else: xleft = xmin xright = xmax if (xleft, xright) != (self.minimapSelector.xy[1, 0], self.minimapSelector.xy[2, 0]): self.step = step self.minimapSelector.xy[:2, 0] = xleft self.minimapSelector.xy[2:4, 0] = xright self.ax.set_xlim(xleft, xright) self.draw_animate() else: self.parentViewer.draw() def get_selector_limits(self): return self.minimapSelector.xy[0, 0], self.minimapSelector.xy[2, 0] def draw(self): self.draw_animate() def draw_animate(self): size = self.minimapFig.bbox.width, self.minimapFig.bbox.height if size != self.minimapSize: self.minimapSize = size self.minimapCanvas.draw() self.minimapBackground = self.minimapCanvas.copy_from_bbox( self.minimapFig.bbox) self.minimapCanvas.restore_region(self.minimapBackground) self.minimapFig.draw_artist(self.minimapSelection) self.minimapFig.draw_artist(self.minimapSelector) self.minimapFig.draw_artist(self.playback_marker.markers[0]) for marker in self.markers.values(): self.minimapFig.draw_artist(marker) self.minimapCanvas.blit(self.minimapFig.bbox) def set_visible(self, value): self.minimapCanvas.setVisible(value) def get_visible(self): return self.minimapCanvas.isVisible() def set_selection_limits(self, xleft, xright): self.minimapSelection.xy[:2, 0] = xleft self.minimapSelection.xy[2:4, 0] = xright self.draw_animate() def set_selection_visible(self, value): self.minimapSelection.set_visible(value) self.draw_animate() def create_marker(self, key, position, **kwargs): if self.xmin <= position <= self.xmax: marker = self.minimapFig.axes[0].axvline(position, animated=True) self.markers[key] = marker self.markers[key].set(**kwargs) def set_marker_position(self, key, value): marker = self.markers.get(key) if marker is not None: if self.xmin <= value <= self.xmax: marker.set_xdata(value) def set_marker(self, key, **kwargs): marker = self.markers.get(key) if marker is not None: kwargs.pop( "animated", None ) # marker's animated property must be always true to be drawn properly marker.set(**kwargs) def delete_marker(self, key): marker = self.markers.get(key) if marker is not None: self.minimapFig.axes[0].lines.remove(marker) self.markers.pop(key)
class StreamViewerWidget(QtGui.QWidget): """Shows the entire signal and allows the user to navigate through it. Provides an scrollable selector over the entire signal. Attributes: xmin: Selector lower limit (measured in h-axis units). xmax: Selector upper limit (measured in h-axis units). step: Selector length (measured in h-axis units). """ trace_selected = QtCore.Signal(int) selection_made = QtCore.Signal(bool) def __init__(self, parent, stream=None): super(StreamViewerWidget, self).__init__(parent) self.fig = plt.figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Policy.Expanding, QtGui.QSizePolicy.Policy.Expanding)) self.canvas.setMinimumHeight(320) self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus) self.canvas.setFocus() self.graphArea = QtGui.QScrollArea(self) self.graphArea.setWidget(self.canvas) self.graphArea.setWidgetResizable(True) self.graphArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) # Set the layout self.layout = QtGui.QVBoxLayout(self) self.layout.addWidget(self.graphArea) # Animation related attrs. self.background = [] self.animated = False self.size = (self.fig.bbox.width, self.fig.bbox.height) # Set TracePlot list self.trace_plots = [] self.stream = None if stream is not None: self.set_stream(stream) # Event handling self.visible_axes = [] self._selected_traces = set() self.shift_pressed = False self.press_selector = None self.fig.canvas.mpl_connect('motion_notify_event', self.on_move) self.fig.canvas.mpl_connect('button_press_event', self.on_press) self.fig.canvas.mpl_connect('key_press_event', self.on_key_press) self.fig.canvas.mpl_connect('key_release_event', self.on_key_release) @property def selected_traces(self): if self.stream is not None: return [self.stream.traces[i] for i in self._selected_traces] return [] def on_move(self, event): axes_selected = False for i, axes in enumerate(self.fig.axes): if axes.get_visible(): ymin, ymax = axes.get_position().ymin, axes.get_position().ymax xmin, xmax = axes.get_position().xmin, axes.get_position().xmax xfig, yfig = self._event_to_fig_coords(event) if ymin <= yfig <= ymax and xmin <= xfig <= xmax: self.canvas.setToolTip(self.stream.traces[i].name) axes_selected = True break if not axes_selected: self.canvas.setToolTip("") def on_key_press(self, event): if event.key == 'control': self.shift_pressed = True def on_key_release(self, event): self.shift_pressed = False def on_press(self, event): trace_selected = False if event.button == 1: # and event.dblclick: for i, ax in enumerate(self.fig.axes): if ax.get_visible(): ymin, ymax = ax.get_position().ymin, ax.get_position().ymax xmin, xmax = ax.get_position().xmin, ax.get_position().xmax xfig, yfig = self._event_to_fig_coords(event) if ymin <= yfig <= ymax and xmin <= xfig <= xmax: trace_selected = True if self.shift_pressed: if self._selected_traces: self.trace_selected.emit(i) self.selection_made.emit(True) self._selected_traces.add(i) else: self.trace_selected.emit(i) self.selection_made.emit(True) self._selected_traces = {i} break # if the user clicked out of any trace (and he's not using shift), then deselect all if not trace_selected and not self.shift_pressed: self._selected_traces = set() self.selection_made.emit(False) # Now update selection status on plots for i, plot in enumerate(self.trace_plots): plot.set_selected(i in self._selected_traces) self.draw() def _event_to_fig_coords(self, event): inv = self.fig.transFigure.inverted() return inv.transform((event.x, event.y)) def set_stream(self, stream): self.stream = stream self._selected_traces = set() # Clear canvas for plot in self.trace_plots: plot.remove() self.trace_plots = [] # Plot stream traces for i, trace in enumerate(self.stream.traces): self.trace_plots.append( TracePlot(self, trace, fig_nrows=len(stream), ax_pos=i + 1)) # Draw canvas self.subplots_adjust() self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.fig.bbox) self.draw() def refresh_stream_data(self): for plot in self.trace_plots: plot.update_data() def draw(self): self.canvas.draw() #self.draw_animate() def draw_animate(self): size = self.fig.bbox.width, self.fig.bbox.height if size != self.size: self.size = size self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.fig.bbox) self.canvas.restore_region(self.background) for artist in self._get_animated_artists(): if artist.get_visible(): ax = artist.get_axes() if ax is not None: if artist.get_axes().get_visible(): self.fig.draw_artist(artist) else: self.fig.draw_artist(artist) self.canvas.blit(self.fig.bbox) def _get_animated_artists(self): artists = [] for ax in self.fig.axes: artists.extend(ax.images) artists.extend(ax.lines) artists.append(ax.xaxis) artists.append(ax.yaxis) artists.extend(ax.patches) artists.extend(ax.spines.values()) for artist in artists: if artist.get_animated(): yield artist def set_visible(self, value): self.canvas.setVisible(value) def get_visible(self): return self.canvas.isVisible() def remove_trace(self, idx): self.trace_plots.pop(idx).remove() def subplots_adjust(self): visible_subplots = [ ax for ax in self.fig.get_axes() if ax.get_visible() ] for i, ax in enumerate(visible_subplots): correct_geometry = (len(visible_subplots), 1, i + 1) if correct_geometry != ax.get_geometry(): ax.change_geometry(len(visible_subplots), 1, i + 1) # Adjust space between subplots self.fig.subplots_adjust(left=0.02, right=0.98, bottom=0.02, top=0.98, hspace=0.05) def showEvent(self, event): self.draw() def resizeEvent(self, event): self.draw() def update_markers(self): for plot in self.trace_plots: plot.update_markers() self.draw() def visualize_stream_range(self, start_trace=None, end_trace=None): for i, ax in enumerate(self.fig.axes): ax.set_visible(start_trace <= i < end_trace) self.subplots_adjust() self.canvas.draw()
class StreamViewerWidget(QtGui.QWidget): """Shows the entire signal and allows the user to navigate through it. Provides an scrollable selector over the entire signal. Attributes: xmin: Selector lower limit (measured in h-axis units). xmax: Selector upper limit (measured in h-axis units). step: Selector length (measured in h-axis units). """ trace_selected = QtCore.Signal(int) selection_made = QtCore.Signal(bool) def __init__(self, parent, stream=None): super(StreamViewerWidget, self).__init__(parent) self.fig = plt.figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Policy.Expanding, QtGui.QSizePolicy.Policy.Expanding)) self.canvas.setMinimumHeight(320) self.canvas.setFocusPolicy(QtCore.Qt.ClickFocus) self.canvas.setFocus() self.graphArea = QtGui.QScrollArea(self) self.graphArea.setWidget(self.canvas) self.graphArea.setWidgetResizable(True) self.graphArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) # Set the layout self.layout = QtGui.QVBoxLayout(self) self.layout.addWidget(self.graphArea) # Animation related attrs. self.background = [] self.animated = False self.size = (self.fig.bbox.width, self.fig.bbox.height) # Set TracePlot list self.trace_plots = [] self.stream = None if stream is not None: self.set_stream(stream) # Event handling self.visible_axes = [] self._selected_traces = set() self.shift_pressed = False self.press_selector = None self.fig.canvas.mpl_connect('motion_notify_event', self.on_move) self.fig.canvas.mpl_connect('button_press_event', self.on_press) self.fig.canvas.mpl_connect('key_press_event', self.on_key_press) self.fig.canvas.mpl_connect('key_release_event', self.on_key_release) @property def selected_traces(self): if self.stream is not None: return [self.stream.traces[i] for i in self._selected_traces] return [] def on_move(self, event): axes_selected = False for i, axes in enumerate(self.fig.axes): if axes.get_visible(): ymin, ymax = axes.get_position().ymin, axes.get_position().ymax xmin, xmax = axes.get_position().xmin, axes.get_position().xmax xfig, yfig = self._event_to_fig_coords(event) if ymin <= yfig <= ymax and xmin <= xfig <= xmax: self.canvas.setToolTip(self.stream.traces[i].name) axes_selected = True break if not axes_selected: self.canvas.setToolTip("") def on_key_press(self, event): if event.key == 'control': self.shift_pressed = True def on_key_release(self, event): self.shift_pressed = False def on_press(self, event): trace_selected = False if event.button == 1:# and event.dblclick: for i, ax in enumerate(self.fig.axes): if ax.get_visible(): ymin, ymax = ax.get_position().ymin, ax.get_position().ymax xmin, xmax = ax.get_position().xmin, ax.get_position().xmax xfig, yfig = self._event_to_fig_coords(event) if ymin <= yfig <= ymax and xmin <= xfig <= xmax: trace_selected = True if self.shift_pressed: if self._selected_traces: self.trace_selected.emit(i) self.selection_made.emit(True) self._selected_traces.add(i) else: self.trace_selected.emit(i) self.selection_made.emit(True) self._selected_traces = {i} break # if the user clicked out of any trace (and he's not using shift), then deselect all if not trace_selected and not self.shift_pressed: self._selected_traces = set() self.selection_made.emit(False) # Now update selection status on plots for i, plot in enumerate(self.trace_plots): plot.set_selected(i in self._selected_traces) self.draw() def _event_to_fig_coords(self, event): inv = self.fig.transFigure.inverted() return inv.transform((event.x, event.y)) def set_stream(self, stream): self.stream = stream self._selected_traces = set() # Clear canvas for plot in self.trace_plots: plot.remove() self.trace_plots = [] # Plot stream traces for i, trace in enumerate(self.stream.traces): self.trace_plots.append(TracePlot(self, trace, fig_nrows=len(stream), ax_pos=i + 1)) # Draw canvas self.subplots_adjust() self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.fig.bbox) self.draw() def refresh_stream_data(self): for plot in self.trace_plots: plot.update_data() def draw(self): self.canvas.draw() #self.draw_animate() def draw_animate(self): size = self.fig.bbox.width, self.fig.bbox.height if size != self.size: self.size = size self.canvas.draw() self.background = self.canvas.copy_from_bbox(self.fig.bbox) self.canvas.restore_region(self.background) for artist in self._get_animated_artists(): if artist.get_visible(): ax = artist.get_axes() if ax is not None: if artist.get_axes().get_visible(): self.fig.draw_artist(artist) else: self.fig.draw_artist(artist) self.canvas.blit(self.fig.bbox) def _get_animated_artists(self): artists = [] for ax in self.fig.axes: artists.extend(ax.images) artists.extend(ax.lines) artists.append(ax.xaxis) artists.append(ax.yaxis) artists.extend(ax.patches) artists.extend(ax.spines.values()) for artist in artists: if artist.get_animated(): yield artist def set_visible(self, value): self.canvas.setVisible(value) def get_visible(self): return self.canvas.isVisible() def remove_trace(self, idx): self.trace_plots.pop(idx).remove() def subplots_adjust(self): visible_subplots = [ax for ax in self.fig.get_axes() if ax.get_visible()] for i, ax in enumerate(visible_subplots): correct_geometry = (len(visible_subplots), 1, i + 1) if correct_geometry != ax.get_geometry(): ax.change_geometry(len(visible_subplots), 1, i + 1) # Adjust space between subplots self.fig.subplots_adjust(left=0.02, right=0.98, bottom=0.02, top=0.98, hspace=0.05) def showEvent(self, event): self.draw() def resizeEvent(self, event): self.draw() def update_markers(self): for plot in self.trace_plots: plot.update_markers() self.draw() def visualize_stream_range(self, start_trace=None, end_trace=None): for i, ax in enumerate(self.fig.axes): ax.set_visible(start_trace <= i < end_trace) self.subplots_adjust() self.canvas.draw()
class MiniMap(QtGui.QWidget): """Shows the entire signal and allows the user to navigate through it. Provides an scrollable selector over the entire signal. Attributes: xmin: Selector lower limit (measured in h-axis units). xmax: Selector upper limit (measured in h-axis units). step: Selector length (measured in h-axis units). """ def __init__(self, parent, ax, record=None): super(MiniMap, self).__init__(parent) self.ax = ax self.xmin = 0.0 self.xmax = 0.0 self.step = 10.0 self.xrange = np.array([]) self.minimapFig = plt.figure() self.minimapFig.set_figheight(0.75) self.minimapFig.add_axes((0, 0, 1, 1)) self.minimapCanvas = FigureCanvas(self.minimapFig) self.minimapCanvas.setFixedHeight(64) self.minimapSelector = self.minimapFig.axes[0].axvspan(0, self.step, color='gray', alpha=0.5, animated=True) self.minimapSelection = self.minimapFig.axes[0].axvspan(0, self.step, color='LightCoral', alpha = 0.5, animated=True) self.minimapSelection.set_visible(False) self.minimapBackground = [] self.minimapSize = (self.minimapFig.bbox.width, self.minimapFig.bbox.height) self.press_selector = None self.playback_marker = None self.minimapCanvas.mpl_connect('button_press_event', self.onpress) self.minimapCanvas.mpl_connect('button_release_event', self.onrelease) self.minimapCanvas.mpl_connect('motion_notify_event', self.onmove) # Animation related attrs. self.background = None self.animated = False # Set the layout self.layout = QtGui.QVBoxLayout(self) self.layout.addWidget(self.minimapCanvas) # Animation related attributes self.parentViewer = parent # Set Markers dict self.markers = {} self.record = None if record is not None: self.set_record(record) def set_record(self, record, step): self.record = record self.step = step self.xrange = np.linspace(0, len(self.record.signal) / self.record.fs, num=len(self.record.signal), endpoint=False) self.xmin = self.xrange[0] self.xmax = self.xrange[-1] self.markers = {} ax = self.minimapFig.axes[0] ax.lines = [] formatter = FuncFormatter(lambda x, pos: str(datetime.timedelta(seconds=x))) ax.xaxis.set_major_formatter(formatter) ax.grid(True, which='both') # Set dataseries to plot xmin = self.xmin * self.record.fs xmax = self.xmax * self.record.fs pixel_width = np.ceil(self.minimapFig.get_figwidth() * self.minimapFig.get_dpi()) x_data, y_data = plotting.reduce_data(self.xrange, self.record.signal, pixel_width, xmin, xmax) # self._plot_data.set_xdata(x_data) # self._plot_data.set_ydata(y_data) ax.plot(x_data, y_data, color='black', rasterized=True) ax.set_xlim(self.xmin, self.xmax) plotting.adjust_axes_height(ax) # Set the playback marker self.playback_marker = PlayBackMarker(self.minimapFig, self) self.playback_marker.markers[0].set_animated(True) # Draw canvas self.minimapCanvas.draw() self.minimapBackground = self.minimapCanvas.copy_from_bbox(self.minimapFig.bbox) self.draw_animate() def onpress(self, event): self.press_selector = event xdata = round(self.get_xdata(event), 2) xmin = round(xdata - (self.step / 2.0), 2) xmax = round(xdata + (self.step / 2.0), 2) self.parentViewer._set_animated(True) self.set_selector_limits(xmin, xmax) def onrelease(self, event): self.press_selector = None # Finish parent animation self.parentViewer._set_animated(False) def onmove(self, event): if self.press_selector is not None: xdata = round(self.get_xdata(event), 2) xmin = round(xdata - (self.step / 2.0), 2) xmax = round(xdata + (self.step / 2.0), 2) self.set_selector_limits(xmin, xmax) def get_xdata(self, event): inv = self.minimapFig.axes[0].transData.inverted() xdata, _ = inv.transform((event.x, event.y)) return xdata def set_selector_limits(self, xmin, xmax): step = xmax - xmin if step >= self.xmax - self.xmin: xleft = self.xmin xright = self.xmax if xmin < self.xmin: xleft = self.xmin xright = self.step elif xmax > self.xmax: xleft = self.xmax - step xright = self.xmax else: xleft = xmin xright = xmax if (xleft, xright) != (self.minimapSelector.xy[1, 0], self.minimapSelector.xy[2, 0]): self.step = step self.minimapSelector.xy[:2, 0] = xleft self.minimapSelector.xy[2:4, 0] = xright self.ax.set_xlim(xleft, xright) self.draw_animate() else: self.parentViewer.draw() def get_selector_limits(self): return self.minimapSelector.xy[0, 0], self.minimapSelector.xy[2, 0] def draw(self): self.draw_animate() def draw_animate(self): size = self.minimapFig.bbox.width, self.minimapFig.bbox.height if size != self.minimapSize: self.minimapSize = size self.minimapCanvas.draw() self.minimapBackground = self.minimapCanvas.copy_from_bbox(self.minimapFig.bbox) self.minimapCanvas.restore_region(self.minimapBackground) self.minimapFig.draw_artist(self.minimapSelection) self.minimapFig.draw_artist(self.minimapSelector) self.minimapFig.draw_artist(self.playback_marker.markers[0]) for marker in self.markers.values(): self.minimapFig.draw_artist(marker) self.minimapCanvas.blit(self.minimapFig.bbox) def set_visible(self, value): self.minimapCanvas.setVisible(value) def get_visible(self): return self.minimapCanvas.isVisible() def set_selection_limits(self, xleft, xright): self.minimapSelection.xy[:2, 0] = xleft self.minimapSelection.xy[2:4, 0] = xright self.draw_animate() def set_selection_visible(self, value): self.minimapSelection.set_visible(value) self.draw_animate() def create_marker(self, key, position, **kwargs): if self.xmin <= position <= self.xmax: marker = self.minimapFig.axes[0].axvline(position, animated=True) self.markers[key] = marker self.markers[key].set(**kwargs) def set_marker_position(self, key, value): marker = self.markers.get(key) if marker is not None: if self.xmin <= value <= self.xmax: marker.set_xdata(value) def set_marker(self, key, **kwargs): marker = self.markers.get(key) if marker is not None: kwargs.pop("animated", None) # marker's animated property must be always true to be drawn properly marker.set(**kwargs) def delete_marker(self, key): marker = self.markers.get(key) if marker is not None: self.minimapFig.axes[0].lines.remove(marker) self.markers.pop(key)