Skip to content Skip to sidebar Skip to footer

How To Change The Color Of The Interactive Zoom Rectangle?

I have a simple interactive plot. When I click on the magnifying glass button I can draw a rectangle to do interactive zooming. You can see the dotted rectangle in the image below.

Solution 1:

It depends on what backend you're using there is no (at least I don't know a) universal solution. As stated in the comments this can be achieved only with monkey-patching. Here is my attempt using Qt5 backend. Note that you need to have PyQt5 installed too in order to make this work.

from PyQt5 import QtGui, QtCore
from matplotlib.backends.backend_qt5 import FigureCanvasQT

# extending the original FigureCanvasQT classclassNewFigureCanvasQT(FigureCanvasQT):
    def__init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    defdrawRectangle(self, rect):
        # Draw the zoom rectangle to the QPainter.  _draw_rect_callback needs# to be called at the end of paintEvent.if rect isnotNone:
            def_draw_rect_callback(painter):
                pen = QtGui.QPen(QtCore.Qt.red, 1 / self._dpi_ratio, # <-- change the color here
                                 QtCore.Qt.DotLine)
                painter.setPen(pen)
                painter.drawRect(*(pt / self._dpi_ratio for pt in rect))
        else:
            def_draw_rect_callback(painter):
                return
        self._draw_rect_callback = _draw_rect_callback
        self.update()

# do the imports and replace the old FigureCanvasQTimport matplotlib
import matplotlib.pyplot as plt
matplotlib.backends.backend_qt5.FigureCanvasQT = NewFigureCanvasQT
# switch backend and setup the dark background
matplotlib.use('Qt5Agg')
matplotlib.style.use('dark_background')

# do the plotting
plt.plot(range(9))
plt.show()

which produces the following picture: Result

EDIT: This seem to be fixed in release 3.3.1. See the release notes.

Solution 2:

For matplotlib 2.1.1 (installed using apt-get install python-matplotlib on Ubuntu 18.04), the answer by Peter does not work on the verison. Hope it will helps someone with legacy system.

Instead, I managed to patch as following:

from PyQt5 import QtCore, QtGui
import matplotlib
try:
    matplotlib.use('Qt5Agg')
except ValueError:
    passimport matplotlib.pyplot as plt

## NOTE: Override paintEvent method to bolden zoom rectangle and change its color to red.defpaintEvent(caller, _e):
    """Copy the image from the Agg canvas to the qt.drawable.

    In Qt, all drawing should be done inside of here when a widget is
    shown onscreen.
    """# if there is a pending draw, run it now as we need the updated render# to paint the widgetif caller._agg_draw_pending:
        try:
            caller.__draw_idle_agg()
        except AttributeError:
            pass# As described in __init__ above, we need to be careful in cases with# mixed resolution displays if dpi_ratio is changing between painting# events.if caller._dpi_ratio != caller._dpi_ratio_prev:
        # We need to update the figure DPI
        caller._update_figure_dpi()
        caller._dpi_ratio_prev = caller._dpi_ratio
        # The easiest way to resize the canvas is to emit a resizeEvent# since we implement all the logic for resizing the canvas for# that event.
        event = QtGui.QResizeEvent(caller.size(), caller.size())
        # We use caller.resizeEvent here instead of QApplication.postEvent# since the latter doesn't guarantee that the event will be emitted# straight away, and this causes visual delays in the changes.
        caller.resizeEvent(event)
        # resizeEvent triggers a paintEvent itself, so we exit this one.return# if the canvas does not have a renderer, then give up and wait for# FigureCanvasAgg.draw(caller) to be calledifnothasattr(caller, 'renderer'):
        return

    painter = QtGui.QPainter(caller)

    if caller._bbox_queue:
        bbox_queue = caller._bbox_queue
    else:
        painter.eraseRect(caller.rect())
        bbox_queue = [
            matplotlib.transforms.Bbox(
                [[0, 0], [caller.renderer.width, caller.renderer.height]])]
    caller._bbox_queue = []
    for bbox in bbox_queue:
        l, b, r, t = map(int, bbox.extents)
        w = r - l
        h = t - b
        reg = caller.copy_from_bbox(bbox)
        buf = reg.to_string_argb()
        qimage = QtGui.QImage(buf, w, h, QtGui.QImage.Format_ARGB32)
        ifhasattr(qimage, 'setDevicePixelRatio'):
            # Not available on Qt4 or some older Qt5.
            qimage.setDevicePixelRatio(caller._dpi_ratio)
        origin = QtCore.QPoint(l, caller.renderer.height - t)
        painter.drawImage(origin / caller._dpi_ratio, qimage)
        # Adjust the buf reference count to work around a memory# leak bug in QImage under PySide on Python 3.import six
        if matplotlib.backends.qt_compat.QT_API == 'PySide'and six.PY3:
            import ctypes
            ctypes.c_long.from_address(id(buf)).value = 1# draw the zoom rectangle to the QPainterif caller._drawRect isnotNone:
        pen = QtGui.QPen(QtCore.Qt.red, 3 / caller._dpi_ratio,
                         QtCore.Qt.DotLine)
        painter.setPen(pen)
        x, y, w, h = caller._drawRect
        painter.drawRect(x, y, w, h)

    painter.end()

# Use custom figure canvas to override zoom rectangle visual
matplotlib.backends.backend_qt5agg.FigureCanvasQTAggBase.paintEvent = paintEvent

Post a Comment for "How To Change The Color Of The Interactive Zoom Rectangle?"