Example #1
0
def mathTex_to_QPixmap(mathTex, fs):
    ''' E/ mathTEx : The formula to be displayed on screen in LaTeX
        E/ fs : The desired font size 
        S/ qpixmap : The image to be displayed in Qpixmap format '''
    # Set up a mpl figure instance #
    fig = Figure()
    fig.patch.set_facecolor('none')
    fig.set_canvas(FigureCanvas(fig))
    renderer = fig.canvas.get_renderer()

    # Plot the mathTex expression #
    ax = fig.add_axes([0, 0, 1, 1])
    ax.axis('off')
    ax.patch.set_facecolor('none')
    t = ax.text(0, 0, mathTex, ha='left', va='bottom', fontsize=fs)

    # Fit figure size to text artist #
    fwidth, fheight = fig.get_size_inches()
    fig_bbox = fig.get_window_extent(renderer)
    text_bbox = t.get_window_extent(renderer)
    tight_fwidth = text_bbox.width * fwidth / fig_bbox.width
    tight_fheight = text_bbox.height * fheight / fig_bbox.height
    fig.set_size_inches(tight_fwidth, tight_fheight)

    # Convert mpl figure to QPixmap #
    buf, size = fig.canvas.print_to_buffer()
    qimage = QtGui.QImage.rgbSwapped(QtGui.QImage(buf, size[0], size[1],
                                                  QtGui.QImage.Format_ARGB32))
    qpixmap = QtGui.QPixmap(qimage)

    return qpixmap
Example #2
0
def mathTex_to_QPixmap(mathTex, fs):

    # ---- set up a mpl figure instance ----
    fig = Figure()
    fig.patch.set_facecolor('none')
    fig.set_canvas(FigureCanvasAgg(fig))
    renderer = fig.canvas.get_renderer()

    # ---- plot the mathTex expression ----

    ax = fig.add_axes([0, 0, 1, 1])
    ax.axis('off')
    ax.patch.set_facecolor('none')
    t = ax.text(0, 0, mathTex, ha='left', va='bottom', fontsize=fs)

    # ---- fit figure size to text artist ----

    fwidth, fheight = fig.get_size_inches()
    fig_bbox = fig.get_window_extent(renderer)

    text_bbox = t.get_window_extent(renderer)

    tight_fwidth = text_bbox.width * fwidth / fig_bbox.width
    tight_fheight = text_bbox.height * fheight / fig_bbox.height

    fig.set_size_inches(tight_fwidth, tight_fheight)

    # ---- convert mpl figure to QPixmap ----

    buf, size = fig.canvas.print_to_buffer()
    qimage = QtGui.QImage.rgbSwapped(
        QtGui.QImage(buf, size[0], size[1], QtGui.QImage.Format_ARGB32))
    qpixmap = QtGui.QPixmap(qimage)

    return qpixmap
Example #3
0
    def latex_to_qtpixmap(str_, fontsize_=None):
        figure_ = Figure()
        figure_.set_canvas(FigureCanvasAgg(figure_))
        figure_.patch.set_facecolor("none")
        ax_ = figure_.add_axes([0, 0, 1, 1])
        ax_.axis('off')
        text_ = ax_.text(0,
                         0,
                         str_,
                         ha='left',
                         va='bottom',
                         fontsize=fontsize_)

        f_w_, f_h_ = figure_.get_size_inches()
        bbox_f_ = figure_.get_window_extent(figure_.canvas.get_renderer())
        bbox_t_ = text_.get_window_extent(figure_.canvas.get_renderer())

        figure_.set_size_inches((bbox_t_.width / bbox_f_.width) * f_w_,
                                (bbox_t_.height / bbox_f_.height) * f_h_)

        buf, size = figure_.canvas.print_to_buffer()
        return QtGui.QImage.rgbSwapped(
            QtGui.QImage(buf, *size, QtGui.QImage.Format_ARGB32))
Example #4
0
class MplCanvas(FigureCanvasQTAgg_modified):
    """Class to represent the FigureCanvas widget"""

    def __init__(self):
        # setup Matplotlib Figure and Axis
        self.fig = Figure()
        bbox = self.fig.get_window_extent().transformed(
            self.fig.dpi_scale_trans.inverted())
        width, height = bbox.width * self.fig.dpi, bbox.height * self.fig.dpi
        self.fig.subplots_adjust(
            left=40 / width,
            bottom=20 / height,
            right=1 - 5 / width,
            top=1 - 30 / height,
            hspace=0.0)
        # left=0.07, right=0.98,
        # top=0.94, bottom=0.07, hspace=0.0)
        self._define_axes(1)
        self.set_toNight(True)
        FigureCanvasQTAgg_modified.__init__(self, self.fig)
        FigureCanvasQTAgg_modified.setSizePolicy(
            self, QtWidgets.QSizePolicy.Expanding,
            QtWidgets.QSizePolicy.Expanding)
        FigureCanvasQTAgg_modified.updateGeometry(self)

    def _define_axes(self, h_cake):
        self.gs = GridSpec(100, 1)
        self.ax_pattern = self.fig.add_subplot(self.gs[h_cake + 1:99, 0])
        self.ax_cake = self.fig.add_subplot(self.gs[0:h_cake, 0],
                                            sharex=self.ax_pattern)
        self.ax_pattern.set_ylabel('Intensity (arbitrary unit)')
        self.ax_pattern.ticklabel_format(
            axis='y', style='sci', scilimits=(-2, 2))
        self.ax_pattern.get_yaxis().get_offset_text().set_position(
            (-0.04, -0.1))

    def resize_axes(self, h_cake):
        self.fig.clf()
        self._define_axes(h_cake)
        if h_cake == 1:
            self.ax_cake.tick_params(
                axis='y', colors=self.objColor, labelleft=False)
            self.ax_cake.spines['right'].set_visible(False)
            self.ax_cake.spines['left'].set_visible(False)
            self.ax_cake.spines['top'].set_visible(False)
            self.ax_cake.spines['bottom'].set_visible(False)
        elif h_cake >= 10:
            self.ax_cake.set_ylabel("Azimuth (degrees)")

    def set_toNight(self, NightView=True):
        if NightView:
            try:
                mplstyle.use(
                    os.path.join(os.path.curdir, 'mplstyle', 'night.mplstyle'))
            except:
                mplstyle.use('dark_background')
            self.bgColor = 'black'
            self.objColor = 'white'
        else:
            try:
                mplstyle.use(
                    os.path.join(os.path.curdir, 'mplstyle', 'day.mplstyle'))
            except:
                mplstyle.use('classic')
            self.bgColor = 'white'
            self.objColor = 'black'
#        self.fig.clf()
#        self.ax_pattern.cla()
#        Cursor(self.ax, useblit=True, color=self.objColor, linewidth=2 )
        self.fig.set_facecolor(self.bgColor)
        self.ax_cake.tick_params(which='both', axis='x',
                                 colors=self.objColor, direction='in',
                                 labelbottom=False, labeltop=False)
        self.ax_cake.tick_params(axis='both', which='both', length=0)

        self.ax_pattern.xaxis.set_label_position('bottom')
Example #5
0
class Panel_Plotting_Helper(wx.Panel):
    def __init__(self, parent):
        w, h = parent.GetSize()
        wx.Panel.__init__(self,
                          parent=parent,
                          size=(w, 0.7 * h),
                          style=wx.SUNKEN_BORDER)
        self.parent = parent
        self.legends = []
        self.legendpos = [0.5, 1]

        self.fig = Figure(
            figsize=(12, 6),
            dpi=90)  # create a figure size 8x6 inches, 80 dots per inches
        self.splts = []
        self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
        self.toolbar = Toolbar(self.canvas)  # matplotlib toolbar
        # additional toolbar
        status_txt = wx.StaticText(self.toolbar, label='    Status on hover: ', pos=(230, 7), \
                                   size=(100, 17))
        self.status = wx.TextCtrl(self.toolbar, pos=(330,4), size=(300, 22), \
                                  style=wx.TE_READONLY)
        self.toolbar.Realize()

        self.figw, self.figh = self.fig.get_window_extent(
        ).width, self.fig.get_window_extent().height

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.toolbar, 0, wx.GROW)
        sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(sizer)
        self.box_width_fraction = 1.0
        self.box_height_fraction = 0.9
        self.lines = []
        self.lined = dict()
        self.draggableList = ['Text', 'Legend']
        # print(self.toolbar.GetBackgroundColour())

        self.fig.canvas.mpl_connect('resize_event', self.squeeze_legend)
        self.fig.canvas.mpl_connect('pick_event', self.on_pick)
        self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion)
        self.fig.canvas.mpl_connect('figure_leave_event', self.on_leave)

    def plot_J(self, J, theta, format, r, count):
        index = count % 3 + 3
        self.splts[index].plot(np.arange(len(J)),
                               J,
                               color=format['color'],
                               linewidth=format['linewidth'],
                               linestyle=format['linestyle'],
                               label=format['label'],
                               picker=True)
        self.splts[index].set_xlabel("Number of Iteration", fontsize=FONT_SIZE)
        self.splts[index].set_ylabel("Cost value", fontsize=FONT_SIZE)
        self.set_ticks(self.splts[index], np.arange(len(J)), J)
        comment = r + ': [\n'
        for t in theta:
            comment += '    ' + str(t) + '\n'
        comment += ']'
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
        annotate = self.splts[index].annotate(comment, xy=(len(J)-1, J[len(J)-1]), xytext=(len(J)/2, (J[0]+J[len(J)-1])/2), \
                                                  arrowprops=dict(facecolor='black', shrink=0.05), bbox=props, fontsize=FONT_SIZE, picker=True)
        annotate.draggable(True)

    def plot_data_gradient_descent(self, X, y, format):
        print("Plotting data ...")
        for i in range(int(round(len(self.splts) / 2))):
            self.plot_data(self.splts[i], X, y, format)
        self.update_canvas()

    def plot_data_normal_equation(self, X, y, format):
        print("Plotting data ...")
        for i in range(int(round((len(self.splts) + 1) / 2))):
            self.plot_data(self.splts[i], X, y, format)
        self.update_canvas()

    def plot_data(self, splt, X, y, format):
        line, = splt.plot(X,
                          y,
                          'ro',
                          color=format['color'],
                          label=format['label'],
                          picker=True)
        self.set_ticks(splt, X, y)
        self.lines.append(line)
        splt.set_xlabel("X1", fontsize=FONT_SIZE)
        splt.set_ylabel("Y", fontsize=FONT_SIZE)

    def set_ticks(self, splt, X, y):
        xticks = self.make_ticks(X)
        yticks = self.make_ticks(y)
        splt.set_xticks(xticks)
        splt.set_yticks(yticks)
        for tick in splt.get_xticklabels():
            tick.set_rotation(45)
            tick.set_fontsize(FONT_SIZE)
        for tick in splt.get_yticklabels():
            tick.set_rotation(45)
            tick.set_fontsize(FONT_SIZE)

    def plot_all_gradient_descent(self, object):
        print(
            "Plotting Linear-Regression (Gradient Descent) and J-Convergence ..."
        )
        count = 0
        for r in object:
            c = self.random_color()
            self.splts[count].plot(object[r]['data']['x'],
                                   object[r]['data']['y'],
                                   color=c,
                                   linestyle="-",
                                   label="Linear Regression (alpha=" + r + ")",
                                   picker=True)
            self.set_ticks(self.splts[count], object[r]['data']['x'],
                           object[r]['data']['y'])
            self.plot_J(
                object[r]['J_history'], object[r]['theta'], {
                    "color": c,
                    "linewidth": 5,
                    "linestyle": "-",
                    "label": "Convergence of J"
                }, r, count)
            count += 1
        self.show_legend()
        self.update_canvas()

    def plot_all_normal_equation(self, object):
        print("Plotting Linear-Regression (Normal Equation) ...")
        count = 0
        for r in object:
            c = self.random_color()
            line, = self.splts[count].plot(
                object[r]['data']['x'],
                object[r]['data']['y'],
                color=c,
                linestyle="-",
                label="Linear Regression (Normal Equation)",
                picker=True)
            self.lines.append(line)
            self.set_ticks(self.splts[count], object[r]['data']['x'],
                           object[r]['data']['y'])
            comment = 'Theta: [\n'
            for t in object[r]['theta']:
                comment += '    ' + str(t[0]) + '\n'
            comment += ']'
            # place a text box in upper left in axes coords
            props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
            annotate = self.splts[count].annotate(comment, xy=(min(object[r]['data']['x']), max(object[r]['data']['y'])), \
                                                      xytext=(min(object[r]['data']['x']), max(object[r]['data']['y'])), bbox=props, fontsize=FONT_SIZE, picker=True)
            annotate.draggable(True)
            count += 1
        self.show_legend()
        self.update_canvas()

    def show_legend(self):
        self.legends = []
        for i in range(len(self.splts)):
            splt = self.splts[i]
            # Shrink current axis by 20%
            box = splt.get_position()
            splt.set_position([box.x0, box.y0, box.width * self.box_width_fraction, \
                               box.height * self.box_height_fraction])
            # Now add the legend with some customizations.
            legend = splt.legend(loc='upper center',
                                 ncol=1,
                                 fancybox=True,
                                 shadow=True)
            legend.set_bbox_to_anchor((self.legendpos[0], \
                                       self.legendpos[1] + legend.get_window_extent().height/self.figh + 0.25))
            legend.figure.canvas.mpl_connect('pick_event', self.on_pick)
            legend.draggable(True)
            # lined = dict()
            # for legline, origline in zip(legend.get_lines(), self.lines):
            #    legline.set_picker(5)  # 5 pts tolerance
            #    self.lined[legline] = origline
            self.legends.append(legend)
            if legend:
                # The frame is matplotlib.patches.Rectangle instance surrounding the legend.
                frame = legend.get_frame()
                frame.set_facecolor('0.90')
                # Set the fontsize
                for label in legend.get_texts():
                    label.set_fontsize(FONT_SIZE)
                for label in legend.get_lines():
                    label.set_linewidth(0.75)  # the legend line width
            else:
                pass

    def make_ticks(self, data):
        minn = np.min(data)
        maxx = np.max(data)
        return np.arange(minn, maxx, int((maxx - minn) / 3))

    def squeeze_legend(self, evt):
        new_height = self.fig.get_window_extent().height
        self.box_height_fraction = new_height / self.figh
        self.figh = new_height
        new_width = self.fig.get_window_extent().width
        self.box_width_fraction = new_width / self.figw
        self.figw = new_width
        self.show_legend()
        self.update_canvas()

    def on_pick(self, evt):
        if isinstance(evt.artist, Text):
            # box_points = evt.artist.get_position()
            global TEXT_DRAGGABLE
            TEXT_DRAGGABLE = not TEXT_DRAGGABLE
            evt.artist.draggable(TEXT_DRAGGABLE)
        elif isinstance(evt.artist, Line2D):
            # box_points = evt.artist.get_clip_box()
            pass
        elif isinstance(evt.artist, Legend):
            # box_points = evt.artist.get_clip_box()
            global LEGEND_DRAGGABLE
            LEGEND_DRAGGABLE = not LEGEND_DRAGGABLE
            evt.artist.draggable(LEGEND_DRAGGABLE)
        else:
            print(evt.artist)
            pass

        # print("You've clicked on a bar with coords:\n %r, %r" % (box_points , evt.artist))
        self.update_canvas()

    def on_motion(self, mouseevt):
        w, h = self.canvas.GetSize()
        if mouseevt.x in range(0, int(w + 1)) and mouseevt.y in range(
                0, int(h + 1)):
            self.status.SetValue('Click on %r for dragging On/Off' %
                                 self.draggableList)
        else:
            pass

    def on_leave(self, mouseevt):
        self.status.SetValue('')

    def make_figure(self, type):
        self.fig.clf()
        if type == 'gd':
            gs = GridSpec(2, 3)
            gs.update(hspace=0.7, wspace=0.8)
            self.splts = [
                self.fig.add_subplot(gs[int(i / 3), int(i % 3)])
                for i in range(2 * 3)
            ]  # grid nxn
        elif type == 'ne':
            gs = GridSpec(1, 1)
            gs.update(hspace=0.7, wspace=0.8)
            self.splts = [
                self.fig.add_subplot(gs[int(i / 3), int(i % 3)])
                for i in range(1 * 1)
            ]  # grid nxn
        else:
            pass

    def random_color(self):
        rgbl = [0, random.random(), random.random()]
        return tuple(rgbl)

    def update_canvas(self):
        self.fig.canvas.draw()
        self.canvas.Refresh()
        self.toolbar.update()
Example #6
0
class BackendMatplotlib(BackendBase.BackendBase):
    """Base class for Matplotlib backend without a FigureCanvas.

    For interactive on screen plot, see :class:`BackendMatplotlibQt`.

    See :class:`BackendBase.BackendBase` for public API documentation.
    """

    def __init__(self, plot, parent=None):
        super(BackendMatplotlib, self).__init__(plot, parent)

        # matplotlib is handling keep aspect ratio at draw time
        # When keep aspect ratio is on, and one changes the limits and
        # ask them *before* next draw has been performed he will get the
        # limits without applying keep aspect ratio.
        # This attribute is used to ensure consistent values returned
        # when getting the limits at the expense of a replot
        self._dirtyLimits = True
        self._axesDisplayed = True
        self._matplotlibVersion = _parse_version(matplotlib.__version__)

        self.fig = Figure()
        self.fig.set_facecolor("w")

        self.ax = self.fig.add_axes([.15, .15, .75, .75], label="left")
        self.ax2 = self.ax.twinx()
        self.ax2.set_label("right")
        # Make sure background of Axes is displayed
        self.ax2.patch.set_visible(True)

        # Set axis zorder=0.5 so grid is displayed at 0.5
        self.ax.set_axisbelow(True)

        # disable the use of offsets
        try:
            self.ax.get_yaxis().get_major_formatter().set_useOffset(False)
            self.ax.get_xaxis().get_major_formatter().set_useOffset(False)
            self.ax2.get_yaxis().get_major_formatter().set_useOffset(False)
            self.ax2.get_xaxis().get_major_formatter().set_useOffset(False)
        except:
            _logger.warning('Cannot disabled axes offsets in %s '
                            % matplotlib.__version__)

        # critical for picking!!!!
        self.ax2.set_zorder(0)
        self.ax2.set_autoscaley_on(True)
        self.ax.set_zorder(1)
        # this works but the figure color is left
        if self._matplotlibVersion < _parse_version('2'):
            self.ax.set_axis_bgcolor('none')
        else:
            self.ax.set_facecolor('none')
        self.fig.sca(self.ax)

        self._background = None

        self._colormaps = {}

        self._graphCursor = tuple()

        self._enableAxis('right', False)
        self._isXAxisTimeSeries = False

    def _overlayItems(self):
        """Generator of backend renderer for overlay items"""
        for item in self._plot.getItems():
            if (item.isOverlay() and
                    item.isVisible() and
                    item._backendRenderer is not None):
                yield item._backendRenderer

    def _hasOverlays(self):
        """Returns whether there is an overlay layer or not.

        The overlay layers contains overlay items and the crosshair.

        :rtype: bool
        """
        if self._graphCursor:
            return True  # There is the crosshair

        for item in self._overlayItems():
            return True  # There is at least one overlay item
        return False

    # Add methods

    def addCurve(self, x, y,
                 color, symbol, linewidth, linestyle,
                 yaxis,
                 xerror, yerror, z, selectable,
                 fill, alpha, symbolsize):
        for parameter in (x, y, color, symbol, linewidth, linestyle,
                          yaxis, z, selectable, fill, alpha, symbolsize):
            assert parameter is not None
        assert yaxis in ('left', 'right')

        if (len(color) == 4 and
                type(color[3]) in [type(1), numpy.uint8, numpy.int8]):
            color = numpy.array(color, dtype=numpy.float) / 255.

        if yaxis == "right":
            axes = self.ax2
            self._enableAxis("right", True)
        else:
            axes = self.ax

        picker = 3 if selectable else None

        artists = []  # All the artists composing the curve

        # First add errorbars if any so they are behind the curve
        if xerror is not None or yerror is not None:
            if hasattr(color, 'dtype') and len(color) == len(x):
                errorbarColor = 'k'
            else:
                errorbarColor = color

            # Nx1 error array deprecated in matplotlib >=3.1 (removed in 3.3)
            if (isinstance(xerror, numpy.ndarray) and xerror.ndim == 2 and
                        xerror.shape[1] == 1):
                xerror = numpy.ravel(xerror)
            if (isinstance(yerror, numpy.ndarray) and yerror.ndim == 2 and
                    yerror.shape[1] == 1):
                yerror = numpy.ravel(yerror)

            errorbars = axes.errorbar(x, y,
                                      xerr=xerror, yerr=yerror,
                                      linestyle=' ', color=errorbarColor)
            artists += list(errorbars.get_children())

        if hasattr(color, 'dtype') and len(color) == len(x):
            # scatter plot
            if color.dtype not in [numpy.float32, numpy.float]:
                actualColor = color / 255.
            else:
                actualColor = color

            if linestyle not in ["", " ", None]:
                # scatter plot with an actual line ...
                # we need to assign a color ...
                curveList = axes.plot(x, y,
                                      linestyle=linestyle,
                                      color=actualColor[0],
                                      linewidth=linewidth,
                                      picker=picker,
                                      marker=None)
                artists += list(curveList)

            scatter = axes.scatter(x, y,
                                   color=actualColor,
                                   marker=symbol,
                                   picker=picker,
                                   s=symbolsize**2)
            artists.append(scatter)

            if fill:
                artists.append(axes.fill_between(
                    x, FLOAT32_MINPOS, y, facecolor=actualColor[0], linestyle=''))

        else:  # Curve
            curveList = axes.plot(x, y,
                                  linestyle=linestyle,
                                  color=color,
                                  linewidth=linewidth,
                                  marker=symbol,
                                  picker=picker,
                                  markersize=symbolsize)
            artists += list(curveList)

            if fill:
                artists.append(
                    axes.fill_between(x, FLOAT32_MINPOS, y, facecolor=color))

        for artist in artists:
            artist.set_animated(True)
            artist.set_zorder(z + 1)
            if alpha < 1:
                artist.set_alpha(alpha)

        return _PickableContainer(artists)

    def addImage(self, data, origin, scale, z, selectable, draggable, colormap, alpha):
        # Non-uniform image
        # http://wiki.scipy.org/Cookbook/Histograms
        # Non-linear axes
        # http://stackoverflow.com/questions/11488800/non-linear-axes-for-imshow-in-matplotlib
        for parameter in (data, origin, scale, z, selectable, draggable):
            assert parameter is not None

        origin = float(origin[0]), float(origin[1])
        scale = float(scale[0]), float(scale[1])
        height, width = data.shape[0:2]

        picker = (selectable or draggable)

        # All image are shown as RGBA image
        image = Image(self.ax,
                      interpolation='nearest',
                      picker=picker,
                      zorder=z + 1,
                      origin='lower')

        if alpha < 1:
            image.set_alpha(alpha)

        # Set image extent
        xmin = origin[0]
        xmax = xmin + scale[0] * width
        if scale[0] < 0.:
            xmin, xmax = xmax, xmin

        ymin = origin[1]
        ymax = ymin + scale[1] * height
        if scale[1] < 0.:
            ymin, ymax = ymax, ymin

        image.set_extent((xmin, xmax, ymin, ymax))

        # Set image data
        if scale[0] < 0. or scale[1] < 0.:
            # For negative scale, step by -1
            xstep = 1 if scale[0] >= 0. else -1
            ystep = 1 if scale[1] >= 0. else -1
            data = data[::ystep, ::xstep]

        if data.ndim == 2:  # Data image, convert to RGBA image
            data = colormap.applyToData(data)

        image.set_data(data)
        image.set_animated(True)
        self.ax.add_artist(image)
        return image

    def addTriangles(self, x, y, triangles, color, z, selectable, alpha):
        for parameter in (x, y, triangles, color, z, selectable, alpha):
            assert parameter is not None

        # 0 enables picking on filled triangle
        picker = 0 if selectable else None

        color = numpy.array(color, copy=False)
        assert color.ndim == 2 and len(color) == len(x)

        if color.dtype not in [numpy.float32, numpy.float]:
            color = color.astype(numpy.float32) / 255.

        collection = TriMesh(
            Triangulation(x, y, triangles),
            alpha=alpha,
            picker=picker,
            zorder=z + 1)
        collection.set_color(color)
        collection.set_animated(True)
        self.ax.add_collection(collection)

        return collection

    def addItem(self, x, y, shape, color, fill, overlay, z,
                linestyle, linewidth, linebgcolor):
        if (linebgcolor is not None and
                shape not in ('rectangle', 'polygon', 'polylines')):
            _logger.warning(
                'linebgcolor not implemented for %s with matplotlib backend',
                shape)
        xView = numpy.array(x, copy=False)
        yView = numpy.array(y, copy=False)

        linestyle = normalize_linestyle(linestyle)

        if shape == "line":
            item = self.ax.plot(x, y, color=color,
                                linestyle=linestyle, linewidth=linewidth,
                                marker=None)[0]

        elif shape == "hline":
            if hasattr(y, "__len__"):
                y = y[-1]
            item = self.ax.axhline(y, color=color,
                                   linestyle=linestyle, linewidth=linewidth)

        elif shape == "vline":
            if hasattr(x, "__len__"):
                x = x[-1]
            item = self.ax.axvline(x, color=color,
                                   linestyle=linestyle, linewidth=linewidth)

        elif shape == 'rectangle':
            xMin = numpy.nanmin(xView)
            xMax = numpy.nanmax(xView)
            yMin = numpy.nanmin(yView)
            yMax = numpy.nanmax(yView)
            w = xMax - xMin
            h = yMax - yMin
            item = Rectangle(xy=(xMin, yMin),
                             width=w,
                             height=h,
                             fill=False,
                             color=color,
                             linestyle=linestyle,
                             linewidth=linewidth)
            if fill:
                item.set_hatch('.')

            if linestyle != "solid" and linebgcolor is not None:
                item = _DoubleColoredLinePatch(item)
                item.linebgcolor = linebgcolor

            self.ax.add_patch(item)

        elif shape in ('polygon', 'polylines'):
            points = numpy.array((xView, yView)).T
            if shape == 'polygon':
                closed = True
            else:  # shape == 'polylines'
                closed = numpy.all(numpy.equal(points[0], points[-1]))
            item = Polygon(points,
                           closed=closed,
                           fill=False,
                           color=color,
                           linestyle=linestyle,
                           linewidth=linewidth)
            if fill and shape == 'polygon':
                item.set_hatch('/')

            if linestyle != "solid" and linebgcolor is not None:
                item = _DoubleColoredLinePatch(item)
                item.linebgcolor = linebgcolor

            self.ax.add_patch(item)

        else:
            raise NotImplementedError("Unsupported item shape %s" % shape)

        item.set_zorder(z + 1)
        item.set_animated(True)

        return item

    def addMarker(self, x, y, text, color,
                  selectable, draggable,
                  symbol, linestyle, linewidth, constraint, yaxis):
        textArtist = None

        xmin, xmax = self.getGraphXLimits()
        ymin, ymax = self.getGraphYLimits(axis=yaxis)

        if yaxis == 'left':
            ax = self.ax
        elif yaxis == 'right':
            ax = self.ax2
        else:
            assert(False)

        if x is not None and y is not None:
            line = ax.plot(x, y,
                           linestyle=" ",
                           color=color,
                           marker=symbol,
                           markersize=10.)[-1]

            if text is not None:
                if symbol is None:
                    valign = 'baseline'
                else:
                    valign = 'top'
                    text = "  " + text

                textArtist = ax.text(x, y, text,
                                     color=color,
                                     horizontalalignment='left',
                                     verticalalignment=valign)

        elif x is not None:
            line = ax.axvline(x,
                              color=color,
                              linewidth=linewidth,
                              linestyle=linestyle)
            if text is not None:
                # Y position will be updated in updateMarkerText call
                textArtist = ax.text(x, 1., " " + text,
                                     color=color,
                                     horizontalalignment='left',
                                     verticalalignment='top')

        elif y is not None:
            line = ax.axhline(y,
                              color=color,
                              linewidth=linewidth,
                              linestyle=linestyle)

            if text is not None:
                # X position will be updated in updateMarkerText call
                textArtist = ax.text(1., y, " " + text,
                                     color=color,
                                     horizontalalignment='right',
                                     verticalalignment='top')

        else:
            raise RuntimeError('A marker must at least have one coordinate')

        if selectable or draggable:
            line.set_picker(5)

        # All markers are overlays
        line.set_animated(True)
        if textArtist is not None:
            textArtist.set_animated(True)

        artists = [line] if textArtist is None else [line, textArtist]
        container = _MarkerContainer(artists, x, y, yaxis)
        container.updateMarkerText(xmin, xmax, ymin, ymax)

        return container

    def _updateMarkers(self):
        xmin, xmax = self.ax.get_xbound()
        ymin1, ymax1 = self.ax.get_ybound()
        ymin2, ymax2 = self.ax2.get_ybound()
        for item in self._overlayItems():
            if isinstance(item, _MarkerContainer):
                if item.yAxis == 'left':
                    item.updateMarkerText(xmin, xmax, ymin1, ymax1)
                else:
                    item.updateMarkerText(xmin, xmax, ymin2, ymax2)

    # Remove methods

    def remove(self, item):
        try:
            item.remove()
        except ValueError:
            pass  # Already removed e.g., in set[X|Y]AxisLogarithmic

    # Interaction methods

    def setGraphCursor(self, flag, color, linewidth, linestyle):
        if flag:
            lineh = self.ax.axhline(
                self.ax.get_ybound()[0], visible=False, color=color,
                linewidth=linewidth, linestyle=linestyle)
            lineh.set_animated(True)

            linev = self.ax.axvline(
                self.ax.get_xbound()[0], visible=False, color=color,
                linewidth=linewidth, linestyle=linestyle)
            linev.set_animated(True)

            self._graphCursor = lineh, linev
        else:
            if self._graphCursor:
                lineh, linev = self._graphCursor
                lineh.remove()
                linev.remove()
                self._graphCursor = tuple()

    # Active curve

    def setCurveColor(self, curve, color):
        # Store Line2D and PathCollection
        for artist in curve.get_children():
            if isinstance(artist, (Line2D, LineCollection)):
                artist.set_color(color)
            elif isinstance(artist, PathCollection):
                artist.set_facecolors(color)
                artist.set_edgecolors(color)
            else:
                _logger.warning(
                    'setActiveCurve ignoring artist %s', str(artist))

    # Misc.

    def getWidgetHandle(self):
        return self.fig.canvas

    def _enableAxis(self, axis, flag=True):
        """Show/hide Y axis

        :param str axis: Axis name: 'left' or 'right'
        :param bool flag: Default, True
        """
        assert axis in ('right', 'left')
        axes = self.ax2 if axis == 'right' else self.ax
        axes.get_yaxis().set_visible(flag)

    def replot(self):
        """Do not perform rendering.

        Override in subclass to actually draw something.
        """
        # TODO images, markers? scatter plot? move in remove?
        # Right Y axis only support curve for now
        # Hide right Y axis if no line is present
        self._dirtyLimits = False
        if not self.ax2.lines:
            self._enableAxis('right', False)

    def saveGraph(self, fileName, fileFormat, dpi):
        # fileName can be also a StringIO or file instance
        if dpi is not None:
            self.fig.savefig(fileName, format=fileFormat, dpi=dpi)
        else:
            self.fig.savefig(fileName, format=fileFormat)
        self._plot._setDirtyPlot()

    # Graph labels

    def setGraphTitle(self, title):
        self.ax.set_title(title)

    def setGraphXLabel(self, label):
        self.ax.set_xlabel(label)

    def setGraphYLabel(self, label, axis):
        axes = self.ax if axis == 'left' else self.ax2
        axes.set_ylabel(label)

    # Graph limits

    def setLimits(self, xmin, xmax, ymin, ymax, y2min=None, y2max=None):
        # Let matplotlib taking care of keep aspect ratio if any
        self._dirtyLimits = True
        self.ax.set_xlim(min(xmin, xmax), max(xmin, xmax))

        if y2min is not None and y2max is not None:
            if not self.isYAxisInverted():
                self.ax2.set_ylim(min(y2min, y2max), max(y2min, y2max))
            else:
                self.ax2.set_ylim(max(y2min, y2max), min(y2min, y2max))

        if not self.isYAxisInverted():
            self.ax.set_ylim(min(ymin, ymax), max(ymin, ymax))
        else:
            self.ax.set_ylim(max(ymin, ymax), min(ymin, ymax))

        self._updateMarkers()

    def getGraphXLimits(self):
        if self._dirtyLimits and self.isKeepDataAspectRatio():
            self.ax.apply_aspect()
            self.ax2.apply_aspect()
            self._dirtyLimits = False
        return self.ax.get_xbound()

    def setGraphXLimits(self, xmin, xmax):
        self._dirtyLimits = True
        self.ax.set_xlim(min(xmin, xmax), max(xmin, xmax))
        self._updateMarkers()

    def getGraphYLimits(self, axis):
        assert axis in ('left', 'right')
        ax = self.ax2 if axis == 'right' else self.ax

        if not ax.get_visible():
            return None

        if self._dirtyLimits and self.isKeepDataAspectRatio():
            self.ax.apply_aspect()
            self.ax2.apply_aspect()
            self._dirtyLimits = False

        return ax.get_ybound()

    def setGraphYLimits(self, ymin, ymax, axis):
        ax = self.ax2 if axis == 'right' else self.ax
        if ymax < ymin:
            ymin, ymax = ymax, ymin
        self._dirtyLimits = True

        if self.isKeepDataAspectRatio():
            # matplotlib keeps limits of shared axis when keeping aspect ratio
            # So x limits are kept when changing y limits....
            # Change x limits first by taking into account aspect ratio
            # and then change y limits.. so matplotlib does not need
            # to make change (to y) to keep aspect ratio
            xmin, xmax = ax.get_xbound()
            curYMin, curYMax = ax.get_ybound()

            newXRange = (xmax - xmin) * (ymax - ymin) / (curYMax - curYMin)
            xcenter = 0.5 * (xmin + xmax)
            ax.set_xlim(xcenter - 0.5 * newXRange, xcenter + 0.5 * newXRange)

        if not self.isYAxisInverted():
            ax.set_ylim(ymin, ymax)
        else:
            ax.set_ylim(ymax, ymin)

        self._updateMarkers()

    # Graph axes

    def setXAxisTimeZone(self, tz):
        super(BackendMatplotlib, self).setXAxisTimeZone(tz)

        # Make new formatter and locator with the time zone.
        self.setXAxisTimeSeries(self.isXAxisTimeSeries())

    def isXAxisTimeSeries(self):
        return self._isXAxisTimeSeries

    def setXAxisTimeSeries(self, isTimeSeries):
        self._isXAxisTimeSeries = isTimeSeries
        if self._isXAxisTimeSeries:
            # We can't use a matplotlib.dates.DateFormatter because it expects
            # the data to be in datetimes. Silx works internally with
            # timestamps (floats).
            locator = NiceDateLocator(tz=self.getXAxisTimeZone())
            self.ax.xaxis.set_major_locator(locator)
            self.ax.xaxis.set_major_formatter(
                NiceAutoDateFormatter(locator, tz=self.getXAxisTimeZone()))
        else:
            try:
                scalarFormatter = ScalarFormatter(useOffset=False)
            except:
                _logger.warning('Cannot disabled axes offsets in %s ' %
                                matplotlib.__version__)
                scalarFormatter = ScalarFormatter()
            self.ax.xaxis.set_major_formatter(scalarFormatter)

    def setXAxisLogarithmic(self, flag):
        # Workaround for matplotlib 2.1.0 when one tries to set an axis
        # to log scale with both limits <= 0
        # In this case a draw with positive limits is needed first
        if flag and self._matplotlibVersion >= _parse_version('2.1.0'):
            xlim = self.ax.get_xlim()
            if xlim[0] <= 0 and xlim[1] <= 0:
                self.ax.set_xlim(1, 10)
                self.draw()

        self.ax2.set_xscale('log' if flag else 'linear')
        self.ax.set_xscale('log' if flag else 'linear')

    def setYAxisLogarithmic(self, flag):
        # Workaround for matplotlib 2.0 issue with negative bounds
        # before switching to log scale
        if flag and self._matplotlibVersion >= _parse_version('2.0.0'):
            redraw = False
            for axis, dataRangeIndex in ((self.ax, 1), (self.ax2, 2)):
                ylim = axis.get_ylim()
                if ylim[0] <= 0 or ylim[1] <= 0:
                    dataRange = self._plot.getDataRange()[dataRangeIndex]
                    if dataRange is None:
                        dataRange = 1, 100  # Fallback
                    axis.set_ylim(*dataRange)
                    redraw = True
            if redraw:
                self.draw()

        self.ax2.set_yscale('log' if flag else 'linear')
        self.ax.set_yscale('log' if flag else 'linear')

    def setYAxisInverted(self, flag):
        if self.ax.yaxis_inverted() != bool(flag):
            self.ax.invert_yaxis()

    def isYAxisInverted(self):
        return self.ax.yaxis_inverted()

    def isKeepDataAspectRatio(self):
        return self.ax.get_aspect() in (1.0, 'equal')

    def setKeepDataAspectRatio(self, flag):
        self.ax.set_aspect(1.0 if flag else 'auto')
        self.ax2.set_aspect(1.0 if flag else 'auto')

    def setGraphGrid(self, which):
        self.ax.grid(False, which='both')  # Disable all grid first
        if which is not None:
            self.ax.grid(True, which=which)

    # Data <-> Pixel coordinates conversion

    def _mplQtYAxisCoordConversion(self, y):
        """Qt origin (top) to/from matplotlib origin (bottom) conversion.

        :rtype: float
        """
        height = self.fig.get_window_extent().height
        return height - y

    def dataToPixel(self, x, y, axis):
        ax = self.ax2 if axis == "right" else self.ax

        pixels = ax.transData.transform_point((x, y))
        xPixel, yPixel = pixels.T

        # Convert from matplotlib origin (bottom) to Qt origin (top)
        yPixel = self._mplQtYAxisCoordConversion(yPixel)

        return xPixel, yPixel

    def pixelToData(self, x, y, axis):
        ax = self.ax2 if axis == "right" else self.ax

        # Convert from Qt origin (top) to matplotlib origin (bottom)
        y = self._mplQtYAxisCoordConversion(y)

        inv = ax.transData.inverted()
        x, y = inv.transform_point((x, y))
        return x, y

    def getPlotBoundsInPixels(self):
        bbox = self.ax.get_window_extent()
        # Warning this is not returning int...
        return (bbox.xmin,
                self._mplQtYAxisCoordConversion(bbox.ymax),
                bbox.width,
                bbox.height)

    def setAxesDisplayed(self, displayed):
        """Display or not the axes.

        :param bool displayed: If `True` axes are displayed. If `False` axes
            are not anymore visible and the margin used for them is removed.
        """
        BackendBase.BackendBase.setAxesDisplayed(self, displayed)
        if displayed:
            # show axes and viewbox rect
            self.ax.set_axis_on()
            self.ax2.set_axis_on()
            # set the default margins
            self.ax.set_position([.15, .15, .75, .75])
            self.ax2.set_position([.15, .15, .75, .75])
        else:
            # hide axes and viewbox rect
            self.ax.set_axis_off()
            self.ax2.set_axis_off()
            # remove external margins
            self.ax.set_position([0, 0, 1, 1])
            self.ax2.set_position([0, 0, 1, 1])
        self._synchronizeBackgroundColors()
        self._synchronizeForegroundColors()
        self._plot._setDirtyPlot()

    def _synchronizeBackgroundColors(self):
        backgroundColor = self._plot.getBackgroundColor().getRgbF()

        dataBackgroundColor = self._plot.getDataBackgroundColor()
        if dataBackgroundColor.isValid():
            dataBackgroundColor = dataBackgroundColor.getRgbF()
        else:
            dataBackgroundColor = backgroundColor

        if self.ax2.axison:
            self.fig.patch.set_facecolor(backgroundColor)
            if self._matplotlibVersion < _parse_version('2'):
                self.ax2.set_axis_bgcolor(dataBackgroundColor)
            else:
                self.ax2.set_facecolor(dataBackgroundColor)
        else:
            self.fig.patch.set_facecolor(dataBackgroundColor)

    def _synchronizeForegroundColors(self):
        foregroundColor = self._plot.getForegroundColor().getRgbF()

        gridColor = self._plot.getGridColor()
        if gridColor.isValid():
            gridColor = gridColor.getRgbF()
        else:
            gridColor = foregroundColor

        for axes in (self.ax, self.ax2):
            if axes.axison:
                axes.spines['bottom'].set_color(foregroundColor)
                axes.spines['top'].set_color(foregroundColor)
                axes.spines['right'].set_color(foregroundColor)
                axes.spines['left'].set_color(foregroundColor)
                axes.tick_params(axis='x', colors=foregroundColor)
                axes.tick_params(axis='y', colors=foregroundColor)
                axes.yaxis.label.set_color(foregroundColor)
                axes.xaxis.label.set_color(foregroundColor)
                axes.title.set_color(foregroundColor)

                for line in axes.get_xgridlines():
                    line.set_color(gridColor)

                for line in axes.get_ygridlines():
                    line.set_color(gridColor)
Example #7
0
class Panel_Plotting_Helper(wx.Panel):

    def __init__(self, parent):
        w, h = parent.GetSize()
        wx.Panel.__init__(self, parent=parent, size=(w, 0.7*h), style=wx.SUNKEN_BORDER)
        self.parent = parent
        self.legends = []
        self.legendpos = [0.5, 1]

        self.fig = Figure(figsize=(12, 6), dpi=90)  # create a figure size 8x6 inches, 80 dots per inches
        self.splts = []
        self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
        self.toolbar = Toolbar(self.canvas)  # matplotlib toolbar
        # additional toolbar
        status_txt = wx.StaticText(self.toolbar, label='    Status on hover: ', pos=(230, 7), \
                                   size=(100, 17))
        self.status = wx.TextCtrl(self.toolbar, pos=(330,4), size=(300, 22), \
                                  style=wx.TE_READONLY)
        self.toolbar.Realize()

        self.figw, self.figh = self.fig.get_window_extent().width, self.fig.get_window_extent().height

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.toolbar, 0, wx.GROW)
        sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(sizer)
        self.box_width_fraction = 1.0
        self.box_height_fraction = 0.9
        self.lines = []
        self.lined = dict()
        self.draggableList = ['Text', 'Legend']
        # print(self.toolbar.GetBackgroundColour())

        self.fig.canvas.mpl_connect('resize_event', self.squeeze_legend)
        self.fig.canvas.mpl_connect('pick_event', self.on_pick)
        self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion)
        self.fig.canvas.mpl_connect('figure_leave_event', self.on_leave)

    def plot_J(self, J, theta, format, r, count):
        index = count%3 + 3
        self.splts[index].plot(np.arange(len(J)), J, color=format['color'], linewidth=format['linewidth'], linestyle=format['linestyle'], label=format['label'], picker=True)
        self.splts[index].set_xlabel("Number of Iteration", fontsize=FONT_SIZE)
        self.splts[index].set_ylabel("Cost value", fontsize = FONT_SIZE)
        self.set_ticks(self.splts[index], np.arange(len(J)), J)
        comment = r + ': [\n'
        for t in theta:
            comment += '    ' + str(t) + '\n'
            comment += ']'
        props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
        annotate = self.splts[index].annotate(comment, xy=(len(J)-1, J[len(J)-1]), xytext=(len(J)/2, (J[0]+J[len(J)-1])/2), \
                                                  arrowprops=dict(facecolor='black', shrink=0.05), bbox=props, fontsize=FONT_SIZE, picker=True)
        annotate.draggable(True)

    def plot_data_gradient_descent(self, X, y, format):
        print("Plotting data ...")
        for i in range(int(round(len(self.splts)/2))):
            self.plot_data(self.splts[i], X, y, format)
        self.update_canvas()

    def plot_data_minimize_cost_function(self, X, y, format):
        print("Plotting data ...")
        for i in range(int(round((len(self.splts)+1)/2))):
            self.plot_data(self.splts[i], X, y, format)
        self.update_canvas()

    def plot_data(self, splt, X, y, format):
        line, = splt.plot(X, y, 'ro', color=format['color'], label=format['label'], picker=True)
        self.set_ticks(splt, X, y)
        self.lines.append(line)
        splt.set_xlabel("X1", fontsize=FONT_SIZE)
        splt.set_ylabel("Y", fontsize=FONT_SIZE)

    def set_ticks(self, splt, X, y):
        xticks = self.make_ticks(X)
        yticks = self.make_ticks(y)
        splt.set_xticks(xticks)
        splt.set_yticks(yticks)
        for tick in splt.get_xticklabels():
            tick.set_rotation(45)
            tick.set_fontsize(FONT_SIZE)
        for tick in splt.get_yticklabels():
            tick.set_rotation(45)
            tick.set_fontsize(FONT_SIZE)

    def plot_all_gradient_descent(self, object):
        print("Plotting Linear-Regression (Gradient Descent) and J-Convergence ...")
        count = 0
        for r in object:
            c = self.random_color()
            self.splts[count].plot(object[r]['data']['x'], object[r]['data']['y'], color=c, linestyle="-", label="Linear Regression (alpha="+r+")", picker=True)
            self.set_ticks(self.splts[count], object[r]['data']['x'], object[r]['data']['y'])
            self.plot_J(object[r]['J_history'], object[r]['theta'], {"color": c, "linewidth": 5, "linestyle": "-", "label": "Convergence of J"}, r, count)
            count += 1
        self.show_legend()
        self.update_canvas()

    def plot_all_minimize_cost_function(self, object):
        print("Plotting Linear-Regression (Normal Equation) ...")
        count = 0
        for r in object:
            c = self.random_color()
            line, = self.splts[count].plot(object[r]['data']['x'], object[r]['data']['y'], color=c, linestyle="-", label="Linear Regression (Normal Equation)", picker=True)
            self.lines.append(line)
            self.set_ticks(self.splts[count], object[r]['data']['x'], object[r]['data']['y'])
            comment = 'Theta: [\n'
            for t in object[r]['theta']:
                comment += '    ' + str(t[0]) + '\n'
            comment += ']'
            # place a text box in upper left in axes coords
            props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
            annotate = self.splts[count].annotate(comment, xy=(min(object[r]['data']['x']), max(object[r]['data']['y'])), \
                                                      xytext=(min(object[r]['data']['x']), max(object[r]['data']['y'])), bbox=props, fontsize=FONT_SIZE, picker=True)
            annotate.draggable(True)
            count += 1
        self.show_legend()
        self.update_canvas()

    def show_legend(self):
        self.legends = []
        for i in range(len(self.splts)):
            splt = self.splts[i]
            # Shrink current axis by 20%
            box = splt.get_position()
            splt.set_position([box.x0, box.y0, box.width * self.box_width_fraction, \
                               box.height * self.box_height_fraction])
            # Now add the legend with some customizations.
            legend = splt.legend(loc='upper center', ncol=1, fancybox=True, shadow=True)
            legend.set_bbox_to_anchor((self.legendpos[0], \
                                       self.legendpos[1] + legend.get_window_extent().height/self.figh + 0.25))
            legend.figure.canvas.mpl_connect('pick_event', self.on_pick)
            legend.draggable(True)
            # lined = dict()
            # for legline, origline in zip(legend.get_lines(), self.lines):
            #    legline.set_picker(5)  # 5 pts tolerance
            #    self.lined[legline] = origline
            self.legends.append(legend)
            if legend:
                # The frame is matplotlib.patches.Rectangle instance surrounding the legend.
                frame = legend.get_frame()
                frame.set_facecolor('0.90')
                # Set the fontsize
                for label in legend.get_texts():
                    label.set_fontsize(FONT_SIZE)
                for label in legend.get_lines():
                    label.set_linewidth(0.75)  # the legend line width
            else:
                pass

    def make_ticks(self, data):
        minn = np.min(data)
        maxx = np.max(data)
        return np.arange(minn, maxx, int((maxx-minn)/3))

    def squeeze_legend(self, evt):
        new_height = self.fig.get_window_extent().height
        self.box_height_fraction = new_height / self.figh
        self.figh = new_height
        new_width = self.fig.get_window_extent().width
        self.box_width_fraction = new_width / self.figw
        self.figw = new_width
        self.show_legend()
        self.update_canvas()

    def on_pick(self, evt):
        if isinstance(evt.artist, Text):
            # box_points = evt.artist.get_position()
            global TEXT_DRAGGABLE
            TEXT_DRAGGABLE = not TEXT_DRAGGABLE
            evt.artist.draggable(TEXT_DRAGGABLE)
        elif isinstance(evt.artist, Line2D):
            # box_points = evt.artist.get_clip_box()
            pass
        elif isinstance(evt.artist, Legend):
            # box_points = evt.artist.get_clip_box()
            global LEGEND_DRAGGABLE
            LEGEND_DRAGGABLE = not LEGEND_DRAGGABLE
            evt.artist.draggable(LEGEND_DRAGGABLE)
        else:
            print(evt.artist)
            pass

        # print("You've clicked on a bar with coords:\n %r, %r" % (box_points , evt.artist))
        self.update_canvas()

    def on_motion(self, mouseevt):
        w, h = self.canvas.GetSize()
        if mouseevt.x in range(0, int(w+1)) and mouseevt.y in range(0, int(h+1)):
            self.status.SetValue('Click on %r for dragging On/Off' % self.draggableList)
        else:
            pass

    def on_leave(self, mouseevt):
        self.status.SetValue('')

    def make_figure(self, type):
        self.fig.clf()
        if type == 'gd':
            pass
        elif type == 'mc':
            gs = GridSpec(1, 1)
            gs.update(hspace=0.7, wspace=0.8)
            self.splts = [self.fig.add_subplot(gs[int(i/3), int(i%3)]) for i in range(1*1)]  # grid nxn
        else:
            pass

    def random_color(self):
        rgbl = [0, random.random(), random.random()]
        return tuple(rgbl)

    def update_canvas(self):
        self.fig.canvas.draw()
        self.canvas.Refresh()
        self.toolbar.update()
Example #8
0
class BackendMatplotlib(BackendBase.BackendBase):
    """Base class for Matplotlib backend without a FigureCanvas.

    For interactive on screen plot, see :class:`BackendMatplotlibQt`.

    See :class:`BackendBase.BackendBase` for public API documentation.
    """

    def __init__(self, plot, parent=None):
        super(BackendMatplotlib, self).__init__(plot, parent)

        # matplotlib is handling keep aspect ratio at draw time
        # When keep aspect ratio is on, and one changes the limits and
        # ask them *before* next draw has been performed he will get the
        # limits without applying keep aspect ratio.
        # This attribute is used to ensure consistent values returned
        # when getting the limits at the expense of a replot
        self._dirtyLimits = True
        self._axesDisplayed = True
        self._matplotlibVersion = _parse_version(matplotlib.__version__)

        self.fig = Figure()
        self.fig.set_facecolor("w")

        self.ax = self.fig.add_axes([.15, .15, .75, .75], label="left")
        self.ax2 = self.ax.twinx()
        self.ax2.set_label("right")

        # disable the use of offsets
        try:
            self.ax.get_yaxis().get_major_formatter().set_useOffset(False)
            self.ax.get_xaxis().get_major_formatter().set_useOffset(False)
            self.ax2.get_yaxis().get_major_formatter().set_useOffset(False)
            self.ax2.get_xaxis().get_major_formatter().set_useOffset(False)
        except:
            _logger.warning('Cannot disabled axes offsets in %s '
                            % matplotlib.__version__)

        # critical for picking!!!!
        self.ax2.set_zorder(0)
        self.ax2.set_autoscaley_on(True)
        self.ax.set_zorder(1)
        # this works but the figure color is left
        if self._matplotlibVersion < _parse_version('2'):
            self.ax.set_axis_bgcolor('none')
        else:
            self.ax.set_facecolor('none')
        self.fig.sca(self.ax)

        self._overlays = set()
        self._background = None

        self._colormaps = {}

        self._graphCursor = tuple()

        self._enableAxis('right', False)
        self._isXAxisTimeSeries = False

    # Add methods

    def addCurve(self, x, y, legend,
                 color, symbol, linewidth, linestyle,
                 yaxis,
                 xerror, yerror, z, selectable,
                 fill, alpha, symbolsize):
        for parameter in (x, y, legend, color, symbol, linewidth, linestyle,
                          yaxis, z, selectable, fill, alpha, symbolsize):
            assert parameter is not None
        assert yaxis in ('left', 'right')

        if (len(color) == 4 and
                type(color[3]) in [type(1), numpy.uint8, numpy.int8]):
            color = numpy.array(color, dtype=numpy.float) / 255.

        if yaxis == "right":
            axes = self.ax2
            self._enableAxis("right", True)
        else:
            axes = self.ax

        picker = 3 if selectable else None

        artists = []  # All the artists composing the curve

        # First add errorbars if any so they are behind the curve
        if xerror is not None or yerror is not None:
            if hasattr(color, 'dtype') and len(color) == len(x):
                errorbarColor = 'k'
            else:
                errorbarColor = color

            # On Debian 7 at least, Nx1 array yerr does not seems supported
            if (isinstance(yerror, numpy.ndarray) and yerror.ndim == 2 and
                    yerror.shape[1] == 1 and len(x) != 1):
                yerror = numpy.ravel(yerror)

            errorbars = axes.errorbar(x, y, label=legend,
                                      xerr=xerror, yerr=yerror,
                                      linestyle=' ', color=errorbarColor)
            artists += list(errorbars.get_children())

        if hasattr(color, 'dtype') and len(color) == len(x):
            # scatter plot
            if color.dtype not in [numpy.float32, numpy.float]:
                actualColor = color / 255.
            else:
                actualColor = color

            if linestyle not in ["", " ", None]:
                # scatter plot with an actual line ...
                # we need to assign a color ...
                curveList = axes.plot(x, y, label=legend,
                                      linestyle=linestyle,
                                      color=actualColor[0],
                                      linewidth=linewidth,
                                      picker=picker,
                                      marker=None)
                artists += list(curveList)

            scatter = axes.scatter(x, y,
                                   label=legend,
                                   color=actualColor,
                                   marker=symbol,
                                   picker=picker,
                                   s=symbolsize**2)
            artists.append(scatter)

            if fill:
                artists.append(axes.fill_between(
                    x, FLOAT32_MINPOS, y, facecolor=actualColor[0], linestyle=''))

        else:  # Curve
            curveList = axes.plot(x, y,
                                  label=legend,
                                  linestyle=linestyle,
                                  color=color,
                                  linewidth=linewidth,
                                  marker=symbol,
                                  picker=picker,
                                  markersize=symbolsize)
            artists += list(curveList)

            if fill:
                artists.append(
                    axes.fill_between(x, FLOAT32_MINPOS, y, facecolor=color))

        for artist in artists:
            artist.set_zorder(z)
            if alpha < 1:
                artist.set_alpha(alpha)

        return Container(artists)

    def addImage(self, data, legend,
                 origin, scale, z,
                 selectable, draggable,
                 colormap, alpha):
        # Non-uniform image
        # http://wiki.scipy.org/Cookbook/Histograms
        # Non-linear axes
        # http://stackoverflow.com/questions/11488800/non-linear-axes-for-imshow-in-matplotlib
        for parameter in (data, legend, origin, scale, z,
                          selectable, draggable):
            assert parameter is not None

        origin = float(origin[0]), float(origin[1])
        scale = float(scale[0]), float(scale[1])
        height, width = data.shape[0:2]

        picker = (selectable or draggable)

        # Debian 7 specific support
        # No transparent colormap with matplotlib < 1.2.0
        # Add support for transparent colormap for uint8 data with
        # colormap with 256 colors, linear norm, [0, 255] range
        if self._matplotlibVersion < _parse_version('1.2.0'):
            if (len(data.shape) == 2 and colormap.getName() is None and
                    colormap.getColormapLUT() is not None):
                colors = colormap.getColormapLUT()
                if (colors.shape[-1] == 4 and
                        not numpy.all(numpy.equal(colors[3], 255))):
                    # This is a transparent colormap
                    if (colors.shape == (256, 4) and
                            colormap.getNormalization() == 'linear' and
                            not colormap.isAutoscale() and
                            colormap.getVMin() == 0 and
                            colormap.getVMax() == 255 and
                            data.dtype == numpy.uint8):
                        # Supported case, convert data to RGBA
                        data = colors[data.reshape(-1)].reshape(
                            data.shape + (4,))
                    else:
                        _logger.warning(
                            'matplotlib %s does not support transparent '
                            'colormap.', matplotlib.__version__)

        if ((height * width) > 5.0e5 and
                origin == (0., 0.) and scale == (1., 1.)):
            imageClass = ModestImage
        else:
            imageClass = AxesImage

        # All image are shown as RGBA image
        image = imageClass(self.ax,
                           label="__IMAGE__" + legend,
                           interpolation='nearest',
                           picker=picker,
                           zorder=z,
                           origin='lower')

        if alpha < 1:
            image.set_alpha(alpha)

        # Set image extent
        xmin = origin[0]
        xmax = xmin + scale[0] * width
        if scale[0] < 0.:
            xmin, xmax = xmax, xmin

        ymin = origin[1]
        ymax = ymin + scale[1] * height
        if scale[1] < 0.:
            ymin, ymax = ymax, ymin

        image.set_extent((xmin, xmax, ymin, ymax))

        # Set image data
        if scale[0] < 0. or scale[1] < 0.:
            # For negative scale, step by -1
            xstep = 1 if scale[0] >= 0. else -1
            ystep = 1 if scale[1] >= 0. else -1
            data = data[::ystep, ::xstep]

        if self._matplotlibVersion < _parse_version('2.1'):
            # matplotlib 1.4.2 do not support float128
            dtype = data.dtype
            if dtype.kind == "f" and dtype.itemsize >= 16:
                _logger.warning("Your matplotlib version do not support "
                                "float128. Data converted to float64.")
                data = data.astype(numpy.float64)

        if data.ndim == 2:  # Data image, convert to RGBA image
            data = colormap.applyToData(data)

        image.set_data(data)

        self.ax.add_artist(image)

        return image

    def addItem(self, x, y, legend, shape, color, fill, overlay, z,
                linestyle, linewidth, linebgcolor):
        if (linebgcolor is not None and
                shape not in ('rectangle', 'polygon', 'polylines')):
            _logger.warning(
                'linebgcolor not implemented for %s with matplotlib backend',
                shape)
        xView = numpy.array(x, copy=False)
        yView = numpy.array(y, copy=False)

        linestyle = normalize_linestyle(linestyle)

        if shape == "line":
            item = self.ax.plot(x, y, label=legend, color=color,
                                linestyle=linestyle, linewidth=linewidth,
                                marker=None)[0]

        elif shape == "hline":
            if hasattr(y, "__len__"):
                y = y[-1]
            item = self.ax.axhline(y, label=legend, color=color,
                                   linestyle=linestyle, linewidth=linewidth)

        elif shape == "vline":
            if hasattr(x, "__len__"):
                x = x[-1]
            item = self.ax.axvline(x, label=legend, color=color,
                                   linestyle=linestyle, linewidth=linewidth)

        elif shape == 'rectangle':
            xMin = numpy.nanmin(xView)
            xMax = numpy.nanmax(xView)
            yMin = numpy.nanmin(yView)
            yMax = numpy.nanmax(yView)
            w = xMax - xMin
            h = yMax - yMin
            item = Rectangle(xy=(xMin, yMin),
                             width=w,
                             height=h,
                             fill=False,
                             color=color,
                             linestyle=linestyle,
                             linewidth=linewidth)
            if fill:
                item.set_hatch('.')

            if linestyle != "solid" and linebgcolor is not None:
                item = _DoubleColoredLinePatch(item)
                item.linebgcolor = linebgcolor

            self.ax.add_patch(item)

        elif shape in ('polygon', 'polylines'):
            points = numpy.array((xView, yView)).T
            if shape == 'polygon':
                closed = True
            else:  # shape == 'polylines'
                closed = numpy.all(numpy.equal(points[0], points[-1]))
            item = Polygon(points,
                           closed=closed,
                           fill=False,
                           label=legend,
                           color=color,
                           linestyle=linestyle,
                           linewidth=linewidth)
            if fill and shape == 'polygon':
                item.set_hatch('/')

            if linestyle != "solid" and linebgcolor is not None:
                item = _DoubleColoredLinePatch(item)
                item.linebgcolor = linebgcolor

            self.ax.add_patch(item)

        else:
            raise NotImplementedError("Unsupported item shape %s" % shape)

        item.set_zorder(z)

        if overlay:
            item.set_animated(True)
            self._overlays.add(item)

        return item

    def addMarker(self, x, y, legend, text, color,
                  selectable, draggable,
                  symbol, linestyle, linewidth, constraint):
        legend = "__MARKER__" + legend

        textArtist = None

        xmin, xmax = self.getGraphXLimits()
        ymin, ymax = self.getGraphYLimits(axis='left')

        if x is not None and y is not None:
            line = self.ax.plot(x, y, label=legend,
                                linestyle=" ",
                                color=color,
                                marker=symbol,
                                markersize=10.)[-1]

            if text is not None:
                if symbol is None:
                    valign = 'baseline'
                else:
                    valign = 'top'
                    text = "  " + text

                textArtist = self.ax.text(x, y, text,
                                          color=color,
                                          horizontalalignment='left',
                                          verticalalignment=valign)

        elif x is not None:
            line = self.ax.axvline(x,
                                   label=legend,
                                   color=color,
                                   linewidth=linewidth,
                                   linestyle=linestyle)
            if text is not None:
                # Y position will be updated in updateMarkerText call
                textArtist = self.ax.text(x, 1., " " + text,
                                          color=color,
                                          horizontalalignment='left',
                                          verticalalignment='top')

        elif y is not None:
            line = self.ax.axhline(y,
                                   label=legend,
                                   color=color,
                                   linewidth=linewidth,
                                   linestyle=linestyle)

            if text is not None:
                # X position will be updated in updateMarkerText call
                textArtist = self.ax.text(1., y, " " + text,
                                          color=color,
                                          horizontalalignment='right',
                                          verticalalignment='top')

        else:
            raise RuntimeError('A marker must at least have one coordinate')

        if selectable or draggable:
            line.set_picker(5)

        # All markers are overlays
        line.set_animated(True)
        if textArtist is not None:
            textArtist.set_animated(True)

        artists = [line] if textArtist is None else [line, textArtist]
        container = _MarkerContainer(artists, x, y)
        container.updateMarkerText(xmin, xmax, ymin, ymax)
        self._overlays.add(container)

        return container

    def _updateMarkers(self):
        xmin, xmax = self.ax.get_xbound()
        ymin, ymax = self.ax.get_ybound()
        for item in self._overlays:
            if isinstance(item, _MarkerContainer):
                item.updateMarkerText(xmin, xmax, ymin, ymax)

    # Remove methods

    def remove(self, item):
        # Warning: It also needs to remove extra stuff if added as for markers
        self._overlays.discard(item)
        try:
            item.remove()
        except ValueError:
            pass  # Already removed e.g., in set[X|Y]AxisLogarithmic

    # Interaction methods

    def setGraphCursor(self, flag, color, linewidth, linestyle):
        if flag:
            lineh = self.ax.axhline(
                self.ax.get_ybound()[0], visible=False, color=color,
                linewidth=linewidth, linestyle=linestyle)
            lineh.set_animated(True)

            linev = self.ax.axvline(
                self.ax.get_xbound()[0], visible=False, color=color,
                linewidth=linewidth, linestyle=linestyle)
            linev.set_animated(True)

            self._graphCursor = lineh, linev
        else:
            if self._graphCursor is not None:
                lineh, linev = self._graphCursor
                lineh.remove()
                linev.remove()
                self._graphCursor = tuple()

    # Active curve

    def setCurveColor(self, curve, color):
        # Store Line2D and PathCollection
        for artist in curve.get_children():
            if isinstance(artist, (Line2D, LineCollection)):
                artist.set_color(color)
            elif isinstance(artist, PathCollection):
                artist.set_facecolors(color)
                artist.set_edgecolors(color)
            else:
                _logger.warning(
                    'setActiveCurve ignoring artist %s', str(artist))

    # Misc.

    def getWidgetHandle(self):
        return self.fig.canvas

    def _enableAxis(self, axis, flag=True):
        """Show/hide Y axis

        :param str axis: Axis name: 'left' or 'right'
        :param bool flag: Default, True
        """
        assert axis in ('right', 'left')
        axes = self.ax2 if axis == 'right' else self.ax
        axes.get_yaxis().set_visible(flag)

    def replot(self):
        """Do not perform rendering.

        Override in subclass to actually draw something.
        """
        # TODO images, markers? scatter plot? move in remove?
        # Right Y axis only support curve for now
        # Hide right Y axis if no line is present
        self._dirtyLimits = False
        if not self.ax2.lines:
            self._enableAxis('right', False)

    def saveGraph(self, fileName, fileFormat, dpi):
        # fileName can be also a StringIO or file instance
        if dpi is not None:
            self.fig.savefig(fileName, format=fileFormat, dpi=dpi)
        else:
            self.fig.savefig(fileName, format=fileFormat)
        self._plot._setDirtyPlot()

    # Graph labels

    def setGraphTitle(self, title):
        self.ax.set_title(title)

    def setGraphXLabel(self, label):
        self.ax.set_xlabel(label)

    def setGraphYLabel(self, label, axis):
        axes = self.ax if axis == 'left' else self.ax2
        axes.set_ylabel(label)

    # Graph limits

    def setLimits(self, xmin, xmax, ymin, ymax, y2min=None, y2max=None):
        # Let matplotlib taking care of keep aspect ratio if any
        self._dirtyLimits = True
        self.ax.set_xlim(min(xmin, xmax), max(xmin, xmax))

        if y2min is not None and y2max is not None:
            if not self.isYAxisInverted():
                self.ax2.set_ylim(min(y2min, y2max), max(y2min, y2max))
            else:
                self.ax2.set_ylim(max(y2min, y2max), min(y2min, y2max))

        if not self.isYAxisInverted():
            self.ax.set_ylim(min(ymin, ymax), max(ymin, ymax))
        else:
            self.ax.set_ylim(max(ymin, ymax), min(ymin, ymax))

        self._updateMarkers()

    def getGraphXLimits(self):
        if self._dirtyLimits and self.isKeepDataAspectRatio():
            self.replot()  # makes sure we get the right limits
        return self.ax.get_xbound()

    def setGraphXLimits(self, xmin, xmax):
        self._dirtyLimits = True
        self.ax.set_xlim(min(xmin, xmax), max(xmin, xmax))
        self._updateMarkers()

    def getGraphYLimits(self, axis):
        assert axis in ('left', 'right')
        ax = self.ax2 if axis == 'right' else self.ax

        if not ax.get_visible():
            return None

        if self._dirtyLimits and self.isKeepDataAspectRatio():
            self.replot()  # makes sure we get the right limits

        return ax.get_ybound()

    def setGraphYLimits(self, ymin, ymax, axis):
        ax = self.ax2 if axis == 'right' else self.ax
        if ymax < ymin:
            ymin, ymax = ymax, ymin
        self._dirtyLimits = True

        if self.isKeepDataAspectRatio():
            # matplotlib keeps limits of shared axis when keeping aspect ratio
            # So x limits are kept when changing y limits....
            # Change x limits first by taking into account aspect ratio
            # and then change y limits.. so matplotlib does not need
            # to make change (to y) to keep aspect ratio
            xmin, xmax = ax.get_xbound()
            curYMin, curYMax = ax.get_ybound()

            newXRange = (xmax - xmin) * (ymax - ymin) / (curYMax - curYMin)
            xcenter = 0.5 * (xmin + xmax)
            ax.set_xlim(xcenter - 0.5 * newXRange, xcenter + 0.5 * newXRange)

        if not self.isYAxisInverted():
            ax.set_ylim(ymin, ymax)
        else:
            ax.set_ylim(ymax, ymin)

        self._updateMarkers()

    # Graph axes

    def setXAxisTimeZone(self, tz):
        super(BackendMatplotlib, self).setXAxisTimeZone(tz)

        # Make new formatter and locator with the time zone.
        self.setXAxisTimeSeries(self.isXAxisTimeSeries())

    def isXAxisTimeSeries(self):
        return self._isXAxisTimeSeries

    def setXAxisTimeSeries(self, isTimeSeries):
        self._isXAxisTimeSeries = isTimeSeries
        if self._isXAxisTimeSeries:
            # We can't use a matplotlib.dates.DateFormatter because it expects
            # the data to be in datetimes. Silx works internally with
            # timestamps (floats).
            locator = NiceDateLocator(tz=self.getXAxisTimeZone())
            self.ax.xaxis.set_major_locator(locator)
            self.ax.xaxis.set_major_formatter(
                NiceAutoDateFormatter(locator, tz=self.getXAxisTimeZone()))
        else:
            try:
                scalarFormatter = ScalarFormatter(useOffset=False)
            except:
                _logger.warning('Cannot disabled axes offsets in %s ' %
                                matplotlib.__version__)
                scalarFormatter = ScalarFormatter()
            self.ax.xaxis.set_major_formatter(scalarFormatter)

    def setXAxisLogarithmic(self, flag):
        # Workaround for matplotlib 2.1.0 when one tries to set an axis
        # to log scale with both limits <= 0
        # In this case a draw with positive limits is needed first
        if flag and self._matplotlibVersion >= _parse_version('2.1.0'):
            xlim = self.ax.get_xlim()
            if xlim[0] <= 0 and xlim[1] <= 0:
                self.ax.set_xlim(1, 10)
                self.draw()

        self.ax2.set_xscale('log' if flag else 'linear')
        self.ax.set_xscale('log' if flag else 'linear')

    def setYAxisLogarithmic(self, flag):
        # Workaround for matplotlib 2.0 issue with negative bounds
        # before switching to log scale
        if flag and self._matplotlibVersion >= _parse_version('2.0.0'):
            redraw = False
            for axis, dataRangeIndex in ((self.ax, 1), (self.ax2, 2)):
                ylim = axis.get_ylim()
                if ylim[0] <= 0 or ylim[1] <= 0:
                    dataRange = self._plot.getDataRange()[dataRangeIndex]
                    if dataRange is None:
                        dataRange = 1, 100  # Fallback
                    axis.set_ylim(*dataRange)
                    redraw = True
            if redraw:
                self.draw()

        self.ax2.set_yscale('log' if flag else 'linear')
        self.ax.set_yscale('log' if flag else 'linear')

    def setYAxisInverted(self, flag):
        if self.ax.yaxis_inverted() != bool(flag):
            self.ax.invert_yaxis()

    def isYAxisInverted(self):
        return self.ax.yaxis_inverted()

    def isKeepDataAspectRatio(self):
        return self.ax.get_aspect() in (1.0, 'equal')

    def setKeepDataAspectRatio(self, flag):
        self.ax.set_aspect(1.0 if flag else 'auto')
        self.ax2.set_aspect(1.0 if flag else 'auto')

    def setGraphGrid(self, which):
        self.ax.grid(False, which='both')  # Disable all grid first
        if which is not None:
            self.ax.grid(True, which=which)

    # Data <-> Pixel coordinates conversion

    def _mplQtYAxisCoordConversion(self, y):
        """Qt origin (top) to/from matplotlib origin (bottom) conversion.

        :rtype: float
        """
        height = self.fig.get_window_extent().height
        return height - y

    def dataToPixel(self, x, y, axis):
        ax = self.ax2 if axis == "right" else self.ax

        pixels = ax.transData.transform_point((x, y))
        xPixel, yPixel = pixels.T

        # Convert from matplotlib origin (bottom) to Qt origin (top)
        yPixel = self._mplQtYAxisCoordConversion(yPixel)

        return xPixel, yPixel

    def pixelToData(self, x, y, axis, check):
        ax = self.ax2 if axis == "right" else self.ax

        # Convert from Qt origin (top) to matplotlib origin (bottom)
        y = self._mplQtYAxisCoordConversion(y)

        inv = ax.transData.inverted()
        x, y = inv.transform_point((x, y))

        if check:
            xmin, xmax = self.getGraphXLimits()
            ymin, ymax = self.getGraphYLimits(axis=axis)

            if x > xmax or x < xmin or y > ymax or y < ymin:
                return None  # (x, y) is out of plot area

        return x, y

    def getPlotBoundsInPixels(self):
        bbox = self.ax.get_window_extent()
        # Warning this is not returning int...
        return (bbox.xmin,
                self._mplQtYAxisCoordConversion(bbox.ymax),
                bbox.width,
                bbox.height)

    def setAxesDisplayed(self, displayed):
        """Display or not the axes.

        :param bool displayed: If `True` axes are displayed. If `False` axes
            are not anymore visible and the margin used for them is removed.
        """
        BackendBase.BackendBase.setAxesDisplayed(self, displayed)
        if displayed:
            # show axes and viewbox rect
            self.ax.set_axis_on()
            self.ax2.set_axis_on()
            # set the default margins
            self.ax.set_position([.15, .15, .75, .75])
            self.ax2.set_position([.15, .15, .75, .75])
        else:
            # hide axes and viewbox rect
            self.ax.set_axis_off()
            self.ax2.set_axis_off()
            # remove external margins
            self.ax.set_position([0, 0, 1, 1])
            self.ax2.set_position([0, 0, 1, 1])
        self._synchronizeBackgroundColors()
        self._synchronizeForegroundColors()
        self._plot._setDirtyPlot()

    def _synchronizeBackgroundColors(self):
        backgroundColor = self._plot.getBackgroundColor().getRgbF()

        dataBackgroundColor = self._plot.getDataBackgroundColor()
        if dataBackgroundColor.isValid():
            dataBackgroundColor = dataBackgroundColor.getRgbF()
        else:
            dataBackgroundColor = backgroundColor

        if self.ax.axison:
            self.fig.patch.set_facecolor(backgroundColor)
            if self._matplotlibVersion < _parse_version('2'):
                self.ax.set_axis_bgcolor(dataBackgroundColor)
            else:
                self.ax.set_facecolor(dataBackgroundColor)
        else:
            self.fig.patch.set_facecolor(dataBackgroundColor)

    def _synchronizeForegroundColors(self):
        foregroundColor = self._plot.getForegroundColor().getRgbF()

        gridColor = self._plot.getGridColor()
        if gridColor.isValid():
            gridColor = gridColor.getRgbF()
        else:
            gridColor = foregroundColor

        if self.ax.axison:
            self.ax.spines['bottom'].set_color(foregroundColor)
            self.ax.spines['top'].set_color(foregroundColor)
            self.ax.spines['right'].set_color(foregroundColor)
            self.ax.spines['left'].set_color(foregroundColor)
            self.ax.tick_params(axis='x', colors=foregroundColor)
            self.ax.tick_params(axis='y', colors=foregroundColor)
            self.ax.yaxis.label.set_color(foregroundColor)
            self.ax.xaxis.label.set_color(foregroundColor)
            self.ax.title.set_color(foregroundColor)

            for line in self.ax.get_xgridlines():
                line.set_color(gridColor)

            for line in self.ax.get_ygridlines():
                line.set_color(gridColor)