[英]How to change the color of the interactive zoom rectangle?
I have a simple interactive plot.我有一个简单的交互式 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.
您可以在下图中看到虚线矩形。
However, when I use white grid on a dark background (with plt.style.use('dark_background')
), the zoom rectangle is barely visible.但是,当我在深色背景上使用白色网格(使用
plt.style.use('dark_background')
)时,缩放矩形几乎不可见。 It is still there but black on a largely black plot.它仍然存在,但在很大程度上是黑色的 plot 上是黑色的。
For completeness, the plots where generated with Matplotlib 3.1.3 as follows:为了完整起见,使用 Matplotlib 3.1.3 生成的图如下:
import matplotlib.pyplot as plt
import numpy as np
plt.style.use('dark_background')
fig = plt.figure()
ax = fig.add_subplot(111)
data = 2.5 * np.random.randn(400) + 3
ax.plot(data)
plt.show()
So my question therefore is: how can I change the color of the zoom rectangle?所以我的问题是:如何更改缩放矩形的颜色?
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.
这是我使用 Qt5 后端的尝试。 Note that you need to have PyQt5 installed too in order to make this work.
请注意,您还需要安装 PyQt5 才能使其正常工作。
from PyQt5 import QtGui, QtCore
from matplotlib.backends.backend_qt5 import FigureCanvasQT
# extending the original FigureCanvasQT class
class NewFigureCanvasQT(FigureCanvasQT):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def drawRectangle(self, rect):
# Draw the zoom rectangle to the QPainter. _draw_rect_callback needs
# to be called at the end of paintEvent.
if rect is not None:
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 FigureCanvasQT
import 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:这会产生以下图片:
EDIT : This seem to be fixed in release 3.3.1.编辑:这似乎已在 3.3.1 版中修复。 See the release notes.
请参阅发行说明。
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.对于 matplotlib 2.1.1(在 Ubuntu 18.04 上使用
apt-get install python-matplotlib
),Peter 的答案不适用于版本。 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:
pass
import matplotlib.pyplot as plt
## NOTE: Override paintEvent method to bolden zoom rectangle and change its color to red.
def paintEvent(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 widget
if 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 called
if not hasattr(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)
if hasattr(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 QPainter
if caller._drawRect is not None:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.