Exemple #1
0
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()
Exemple #2
0
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