class SliceViewWidget(LayoutCanvas): def __init__(self, context, width=11.7, height=8.3, dpi=100, parent=None): """ :type context: segyviewlib.SliceViewContext """ super(SliceViewWidget, self).__init__(width, height, dpi, parent) self._assigned_slice_models = list(context.models) """ :type: list[SliceModel] """ self._slice_views = {} """ :type: dict[matplotlib.axes.Axes,SliceView] """ self._colormappable = ScalarMappable(cmap=context.colormap) self._colormappable.set_array([]) self._context = context context.context_changed.connect(self._context_changed) context.data_changed.connect(self._data_changed) context.data_source_changed.connect(self._layout_changed) self.layout_changed.connect(self._layout_changed) self.subplot_pressed.connect(self._subplot_clicked) self.subplot_scrolled.connect(self._subplot_scrolled) self.subplot_motion.connect(self._subplot_motion) def _create_context(self): return self._context.create_context(self._assigned_slice_models) def _layout_changed(self): fig = self.layout_figure() axes = fig.layout_axes() self._slice_views.clear() for model in self._context.models: model.visible = False for index, ax in enumerate(axes): ax.clear() if index < len(self._assigned_slice_models): model = self._assigned_slice_models[index] model.visible = True self._slice_views[ax] = SliceView(ax, model) self._slice_views[ax].create_slice(self._create_context()) colormap_axes = self.layout_figure().colormap_axes() colorbar = self.layout_figure().colorbar(self._colormappable, cax=colormap_axes, use_gridspec=True) colorbar.ax.tick_params(labelsize=9) self._context.load_data() self._data_changed() def _data_changed(self): context = self._create_context() for slice_view in self._slice_views.values(): slice_view.data_changed(context) self._context_changed() def _context_changed(self): ctx = self._create_context() for slice_view in self._slice_views.values(): slice_view.context_changed(ctx) self._colormappable.set_cmap(ctx['colormap']) self._colormappable.set_clim(ctx['min'], ctx['max']) self.draw() def _create_slice_view_context_menu(self, subplot_index): context_menu = QMenu("Slice Model Reassignment Menu", self) def reassign(model): def fn(): self._assigned_slice_models[subplot_index] = model self._layout_changed() return fn for model in self._context.models: action = QAction(model.title, self) action.triggered.connect(reassign(model)) context_menu.addAction(action) return context_menu def _subplot_clicked(self, event): keys = event['key'] if self._context.indicators and event['button'] == 1 and not keys: x = int(event['x']) y = int(event['y']) slice_model = self._get_slice_view(event).model() self._context.update_index_for_direction(slice_model.x_index_direction, x) self._context.update_index_for_direction(slice_model.y_index_direction, y) elif event['button'] == 3 and (not keys or keys.state(ctrl=True)): subplot_index = event['subplot_index'] context_menu = self._create_slice_view_context_menu(subplot_index) context_menu.exec_(self.mapToGlobal(QPoint(event['mx'], self.height() - event['my']))) def _subplot_scrolled(self, event): keys = event['key'] if not keys: # at least 1 step per event but a maximum of 5 step = max(1, abs(int(event['step']))) step = min(step, 5) step = copysign(step, event['step']) slice_model = self._get_slice_view(event).model() index = int(slice_model.index + step) if 0 <= index < len(slice_model): self._context.update_index_for_direction(slice_model.index_direction, index) elif keys.state(ctrl=True) or keys.state(shift=True): x = event['x'] y = event['y'] step = max(1, abs(int(event['step']))) step = copysign(step / 20.0, event['step']) slice_view = self._get_slice_view(event) if slice_view.zoom(x, y, step): self._context_changed() def _get_slice_view(self, event): subplot_index = event['subplot_index'] fig = self.layout_figure() ax = fig.layout_axes()[subplot_index] slice_view = self._slice_views[ax] return slice_view def _subplot_motion(self, event): keys = event['key'] if keys.state(shift=True) or keys.state(ctrl=True): dx = event['dx'] dy = event['dy'] if dx is not None and dy is not None: slice_view = self._get_slice_view(event) slice_view.pan(dx, dy) self._context_changed()
class Plotter(object): def __init__(self, notify, figure, settings): self.notify = notify self.figure = figure self.settings = settings self.axes = None # self.bar = None self.scalarMap = None self.barBase = None self.threadPlot = None self.extent = None self.lines = {} self.labels = {} self.overflowLabels = {} self.overflow = {'left': [], 'right': [], 'top': [], 'bottom': []} self.__setup_plot() self.set_grid(self.settings.grid) def __setup_plot(self): formatter = ScalarFormatter(useOffset=False) gs = GridSpec(1, 2, width_ratios=[9.5, 0.5]) self.axes = self.figure.add_subplot(gs[0], facecolor=self.settings.background) self.axes.set_xlabel("Frequency (MHz)") self.axes.set_ylabel('Level (dB/Hz)') self.axes.xaxis.set_major_formatter(formatter) self.axes.yaxis.set_major_formatter(formatter) self.axes.xaxis.set_minor_locator(AutoMinorLocator(10)) self.axes.yaxis.set_minor_locator(AutoMinorLocator(10)) self.axes.set_xlim(self.settings.start, self.settings.stop) self.axes.set_ylim(-50, 0) self.bar_ax = self.figure.add_subplot(gs[1]) norm = Normalize(vmin=-50, vmax=0) # self.barBase = ColorbarBase(self.bar_ax, norm=norm) self.scalarMap = ScalarMappable(norm=norm) self.barBase = self.figure.colorbar(self.scalarMap, cax=self.bar_ax) self.set_colourmap_use(self.settings.colourMapUse) self.__setup_measure() self.__setup_overflow() self.hide_measure() def __setup_measure(self): dashesAvg = [4, 5, 1, 5, 1, 5] dashesGM = [5, 5, 5, 5, 1, 5, 1, 5] dashesHalf = [1, 5, 5, 5, 5, 5] self.lines[Markers.MIN] = Line2D([0, 0], [0, 0], linestyle='--', color='black') self.lines[Markers.MAX] = Line2D([0, 0], [0, 0], linestyle='-.', color='black') self.lines[Markers.AVG] = Line2D([0, 0], [0, 0], dashes=dashesAvg, color='magenta') self.lines[Markers.GMEAN] = Line2D([0, 0], [0, 0], dashes=dashesGM, color='green') self.lines[Markers.HP] = Line2D([0, 0], [0, 0], dashes=dashesHalf, color='purple') self.lines[Markers.HFS] = Line2D([0, 0], [0, 0], dashes=dashesHalf, color='purple') self.lines[Markers.HFE] = Line2D([0, 0], [0, 0], dashes=dashesHalf, color='purple') self.lines[Markers.OP] = Line2D([0, 0], [0, 0], dashes=dashesHalf, color='#996600') self.lines[Markers.OFS] = Line2D([0, 0], [0, 0], dashes=dashesHalf, color='#996600') self.lines[Markers.OFE] = Line2D([0, 0], [0, 0], dashes=dashesHalf, color='#996600') if matplotlib.__version__ >= '1.3': effect = patheffects.withStroke(linewidth=3, foreground="w", alpha=0.75) self.lines[Markers.MIN].set_path_effects([effect]) self.lines[Markers.MAX].set_path_effects([effect]) self.lines[Markers.AVG].set_path_effects([effect]) self.lines[Markers.GMEAN].set_path_effects([effect]) self.lines[Markers.HP].set_path_effects([effect]) self.lines[Markers.HFS].set_path_effects([effect]) self.lines[Markers.HFE].set_path_effects([effect]) self.lines[Markers.OP].set_path_effects([effect]) self.lines[Markers.OFS].set_path_effects([effect]) self.lines[Markers.OFE].set_path_effects([effect]) for line in list(self.lines.values()): self.axes.add_line(line) bbox = self.axes.bbox box = dict(boxstyle='round', fc='white', ec='black', clip_box=bbox) self.labels[Markers.MIN] = Text(0, 0, 'Min', fontsize='xx-small', ha="right", va="bottom", bbox=box, color='black') self.labels[Markers.MAX] = Text(0, 0, 'Max', fontsize='xx-small', ha="right", va="top", bbox=box, color='black') box['ec'] = 'magenta' self.labels[Markers.AVG] = Text(0, 0, 'Mean', fontsize='xx-small', ha="right", va="center", bbox=box, color='magenta') box['ec'] = 'green' self.labels[Markers.GMEAN] = Text(0, 0, 'GMean', fontsize='xx-small', ha="right", va="center", bbox=box, color='green') box['ec'] = 'purple' self.labels[Markers.HP] = Text(0, 0, '-3dB', fontsize='xx-small', ha="right", va="center", bbox=box, color='purple') self.labels[Markers.HFS] = Text(0, 0, '-3dB Start', fontsize='xx-small', ha="center", va="top", bbox=box, color='purple') self.labels[Markers.HFE] = Text(0, 0, '-3dB End', fontsize='xx-small', ha="center", va="top", bbox=box, color='purple') box['ec'] = '#996600' self.labels[Markers.OP] = Text(0, 0, 'OBW', fontsize='xx-small', ha="right", va="center", bbox=box, color='#996600') self.labels[Markers.OFS] = Text(0, 0, 'OBW Start', fontsize='xx-small', ha="center", va="top", bbox=box, color='#996600') self.labels[Markers.OFE] = Text(0, 0, 'OBW End', fontsize='xx-small', ha="center", va="top", bbox=box, color='#996600') for label in list(self.labels.values()): self.axes.add_artist(label) def __setup_overflow(self): bbox = self.axes.bbox box = dict(boxstyle='round', fc='white', ec='black', alpha=0.5, clip_box=bbox) self.overflowLabels['left'] = Text(0, 0.9, '', fontsize='xx-small', ha="left", va="top", bbox=box, transform=self.axes.transAxes, alpha=0.5) self.overflowLabels['right'] = Text(1, 0.9, '', fontsize='xx-small', ha="right", va="top", bbox=box, transform=self.axes.transAxes, alpha=0.5) self.overflowLabels['top'] = Text(0.9, 1, '', fontsize='xx-small', ha="right", va="top", bbox=box, transform=self.axes.transAxes, alpha=0.5) self.overflowLabels['bottom'] = Text(0.9, 0, '', fontsize='xx-small', ha="right", va="bottom", bbox=box, transform=self.axes.transAxes, alpha=0.5) for label in list(self.overflowLabels.values()): self.axes.add_artist(label) def __clear_overflow(self): for label in self.overflowLabels: self.overflow[label] = [] def __draw_hline(self, marker, y): line = self.lines[marker] label = self.labels[marker] xLim = self.axes.get_xlim() yLim = self.axes.get_ylim() if yLim[0] <= y <= yLim[1]: line.set_visible(True) line.set_xdata([xLim[0], xLim[1]]) line.set_ydata([y, y]) self.axes.draw_artist(line) label.set_visible(True) label.set_position((xLim[1], y)) self.axes.draw_artist(label) elif y is not None and y < yLim[0]: self.overflow['bottom'].append(marker) elif y is not None and y > yLim[1]: self.overflow['top'].append(marker) def __draw_vline(self, marker, x): line = self.lines[marker] label = self.labels[marker] yLim = self.axes.get_ylim() xLim = self.axes.get_xlim() if x is not None: if xLim[0] <= x <= xLim[1]: line.set_visible(True) line.set_xdata([x, x]) line.set_ydata([yLim[0], yLim[1]]) self.axes.draw_artist(line) label.set_visible(True) label.set_position((x, yLim[1])) self.axes.draw_artist(label) elif x < xLim[0]: self.overflow['left'].append(marker) elif x > xLim[1]: self.overflow['right'].append(marker) def __draw_overflow(self): for pos, overflow in list(self.overflow.items()): if len(overflow) > 0: text = '' for measure in overflow: if len(text) > 0: text += '\n' text += self.labels[measure].get_text() label = self.overflowLabels[pos] if pos == 'top': textMath = '$\\blacktriangle$\n' + text elif pos == 'bottom': textMath = '$\\blacktriangledown$\n' + text elif pos == 'left': textMath = '$\\blacktriangleleft$\n' + text elif pos == 'right': textMath = '$\\blacktriangleright$\n' + text label.set_text(textMath) label.set_visible(True) self.axes.draw_artist(label) def draw_measure(self, measure, show): if self.axes.get_renderer_cache() is None: return self.hide_measure() self.__clear_overflow() if show[Measure.MIN]: y = measure.get_min_p()[1] self.__draw_hline(Markers.MIN, y) if show[Measure.MAX]: y = measure.get_max_p()[1] self.__draw_hline(Markers.MAX, y) if show[Measure.AVG]: y = measure.get_avg_p() self.__draw_hline(Markers.AVG, y) if show[Measure.GMEAN]: y = measure.get_gmean_p() self.__draw_hline(Markers.GMEAN, y) if show[Measure.HBW]: xStart, xEnd, y = measure.get_hpw() self.__draw_hline(Markers.HP, y) self.__draw_vline(Markers.HFS, xStart) self.__draw_vline(Markers.HFE, xEnd) if show[Measure.OBW]: xStart, xEnd, y = measure.get_obw() self.__draw_hline(Markers.OP, y) self.__draw_vline(Markers.OFE, xStart) self.__draw_vline(Markers.OFE, xEnd) self.__draw_overflow() def hide_measure(self): for line in list(self.lines.values()): line.set_visible(False) for label in list(self.labels.values()): label.set_visible(False) for label in list(self.overflowLabels.values()): label.set_visible(False) def scale_plot(self, force=False): if self.extent is not None: if self.settings.autoF or force: self.axes.set_xlim(self.extent.get_f()) if self.settings.autoL or force: self.axes.set_ylim(self.extent.get_l()) if self.settings.plotFunc == PlotFunc.VAR and len( self.axes.collections) > 0: norm = self.axes.collections[0].norm self.scalarMap.set_clim((norm.vmin, norm.vmax)) else: self.scalarMap.set_clim(self.extent.get_l()) norm = Normalize(vmin=self.extent.get_l()[0], vmax=self.extent.get_l()[1]) for collection in self.axes.collections: collection.set_norm(norm) try: self.barBase.draw_all() except: pass def redraw_plot(self): if self.figure is not None: post_event(self.notify, EventThread(Event.DRAW)) def get_axes(self): return self.axes def get_axes_bar(self): return self.barBase.ax def get_bar(self): return self.barBase def get_plot_thread(self): return self.threadPlot def set_title(self, title): self.axes.set_title(title, fontsize='medium') def set_plot(self, spectrum, extent, annotate=False): self.extent = extent self.threadPlot = ThreadPlot(self, self.settings, self.axes, spectrum, self.extent, self.scalarMap, self.barBase, annotate) self.threadPlot.start() return self.threadPlot def clear_plots(self): children = self.axes.get_children() for child in children: if child.get_gid() is not None: if child.get_gid() in [ 'plot', 'peak', 'peakText', 'peakShadow', 'peakThres' ]: child.remove() def set_grid(self, on): self.axes.grid(on) self.redraw_plot() def set_bar(self, on): self.barBase.ax.set_visible(on) if on: self.axes.change_geometry(1, 2, 1) self.axes.get_subplotspec().get_gridspec().set_width_ratios( [9.5, 0.5]) else: self.axes.change_geometry(1, 1, 1) self.figure.subplots_adjust() def set_axes(self, on): if on: self.axes.set_axis_on() self.bar_ax.set_axis_on() else: self.axes.set_axis_off() self.bar_ax.set_axis_off() def set_colourmap_use(self, on): self.set_bar(on) if on: colourMap = self.settings.colourMap else: colourMap = get_colours()[0] self.set_colourmap(colourMap) def set_colourmap(self, colourMap): self.settings.colourMap = colourMap for collection in self.axes.collections: collection.set_cmap(colourMap) if get_colours().index(colourMap) < 4: self.set_bar(False) else: self.set_bar(True) # this is deprecated, work on the scalarMap directly # self.barBase.set_cmap(colourMap) self.scalarMap.set_cmap(colourMap) try: self.barBase.draw_all() except: pass def close(self): self.figure.clear() self.figure = None