在matplotlib中更改zoom-rect的边缘颜色

时间:2015-02-19 05:15:01

标签: python numpy matplotlib scipy pyqt4

我使用python + matplotlib + pyqt编写了一个光谱分析应用程序。这些图需要在应用程序中使用带有白色轴和符号的黑色背景。我保留了matplotlib的默认导航工具栏。由于反转颜色设置,我遇到的一个问题是缩放矩形的边缘是不可见的,因为它是黑色的。有没有一种简单的方法可以将缩放矩形的边缘颜色更改为明亮的颜色,例如白色。

提前谢谢你。

3 个答案:

答案 0 :(得分:1)

文件中的

backend_qt4agg.py

# draw the zoom rectangle to the QPainter
#changed code below...
# change the color of zooming rectangle from black to red 
if self.drawRect:
    p.setPen( QtGui.QPen( QtCore.Qt.red, 1, QtCore.Qt.DotLine ) )
    p.drawRect( self.rect[0], self.rect[1], self.rect[2], self.rect[3] )
p.end()

只需将矩形绘图部分添加/更改为上面的代码。

答案 1 :(得分:1)

可以通过子类化和覆盖(不扩展)paintevent来完成:(代码是从原始paintevent复制粘贴,将颜色更改为变量)

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg 
import PyQt4.QtCore as QCore
import PyQt4.QtGui as QGui
class FigureCanvas(FigureCanvasQTAgg):
    """
    Subclassing to change the paint event hosted in matplotlib.backends.backend_qt5agg. 
    Removed all comments for sake of brevity. 
    Paintcolor can be set by settings canvas.rectanglecolor to a QColor.
    """
    def paintEvent(self, e):
        paintcolor = QCore.Qt.black if not hasattr(self, "rectanglecolor") else self.rectanglecolor
        if not hasattr(self, 'renderer'):
            return
        if self.blitbox is None:
            if QCore.QSysInfo.ByteOrder == QCore.QSysInfo.LittleEndian:
                stringBuffer = self.renderer._renderer.tostring_bgra()
            else:
                stringBuffer = self.renderer._renderer.tostring_argb()
            refcnt = sys.getrefcount(stringBuffer)
            qImage = QGui.QImage(stringBuffer, self.renderer.width,
                                  self.renderer.height,
                                  QGui.QImage.Format_ARGB32)
            rect = qImage.rect()
            p = QGui.QPainter(self)
            p.eraseRect(rect)
            p.drawPixmap(QCore.QPoint(0, 0), QGui.QPixmap.fromImage(qImage))
            if self._drawRect is not None:
                p.setPen(QGui.QPen(paintcolor, 1, QCore.Qt.DotLine))
                x, y, w, h = self._drawRect
                p.drawRect(x, y, w, h)
            p.end()
            del qImage
            if refcnt != sys.getrefcount(stringBuffer):
                _decref(stringBuffer)
        else:
            bbox = self.blitbox
            l, b, r, t = bbox.extents
            w = int(r) - int(l)
            h = int(t) - int(b)
            t = int(b) + h
            reg = self.copy_from_bbox(bbox)
            stringBuffer = reg.to_string_argb()
            qImage = QGui.QImage(stringBuffer, w, h,
                                  QGui.QImage.Format_ARGB32)
            if QT_API == 'PySide' and six.PY3:
                ctypes.c_long.from_address(id(stringBuffer)).value = 1
            pixmap = QGui.QPixmap.fromImage(qImage)
            p = QGui.QPainter(self)
            p.drawPixmap(QCore.QPoint(l, self.renderer.height-t), pixmap)
            if self._drawRect is not None:
                p.setPen(QGui.QPen(paintcolor, 1, QCore.Qt.DotLine))
                x, y, w, h = self._drawRect
                p.drawRect(x, y, w, h)
            p.end()
            self.blitbox = None

根据您的应用程序,您可能会缩短一点(例如杀死那里的PySide特定部分)。上面的工作,您可以像往常一样使用FigureCanvas。

答案 2 :(得分:1)

在PyQt5中添加Gloweye的回复你应该。

import six

import ctypes
import sys

from PyQt5 import QtCore, QtGui

from PyQt5.QtWidgets import QSizePolicy, QWidget, QVBoxLayout
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar)

QT_API = 'PyQt5'
DEBUG = False

_decref = ctypes.pythonapi.Py_DecRef
_decref.argtypes = [ctypes.py_object]
_decref.restype = None

class MplCanvas(FigureCanvas):
    def __init__(self):
        FigureCanvas.__init__(self,self.fig)
        FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)


    #Change de color of rectangle zoom toolbar rewriting painEvent
    #the original code is in the backend_qt5agg.py file inside
    #matplotlib/backends directory
    def paintEvent(self, 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 the canvas does not have a renderer, then give up and wait for
        # FigureCanvasAgg.draw(self) to be called
        if not hasattr(self, 'renderer'):
            return

        if DEBUG:
            print('FigureCanvasQtAgg.paintEvent: ', self,
                  self.get_width_height())

        if len(self.blitbox) == 0:
            # matplotlib is in rgba byte order.  QImage wants to put the bytes
            # into argb format and is in a 4 byte unsigned int.  Little endian
            # system is LSB first and expects the bytes in reverse order
            # (bgra).
            if QtCore.QSysInfo.ByteOrder == QtCore.QSysInfo.LittleEndian:
                stringBuffer = self.renderer._renderer.tostring_bgra()
            else:
                stringBuffer = self.renderer._renderer.tostring_argb()

            refcnt = sys.getrefcount(stringBuffer)

            # convert the Agg rendered image -> qImage
            qImage = QtGui.QImage(stringBuffer, self.renderer.width,
                                  self.renderer.height,
                                  QtGui.QImage.Format_ARGB32)
            if hasattr(qImage, 'setDevicePixelRatio'):
                # Not available on Qt4 or some older Qt5.
                qImage.setDevicePixelRatio(self._dpi_ratio)
            # get the rectangle for the image
            rect = qImage.rect()
            p = QtGui.QPainter(self)
            # reset the image area of the canvas to be the back-ground color
            p.eraseRect(rect)
            # draw the rendered image on to the canvas
            p.drawPixmap(QtCore.QPoint(0, 0), QtGui.QPixmap.fromImage(qImage))

            # draw the zoom rectangle to the QPainter
            ########################################################
            #        HERE CHANGE THE COLOR, IN THIS EXAMPLE        #
            #         THE COLOR IS WHITE                           #
            ########################################################
            if self._drawRect is not None:
                pen = QtGui.QPen(QtCore.Qt.white, 1 / self._dpi_ratio,
                                 QtCore.Qt.DotLine)
                p.setPen(pen)
                x, y, w, h = self._drawRect
                p.drawRect(x, y, w, h)
            p.end()

            # This works around a bug in PySide 1.1.2 on Python 3.x,
            # where the reference count of stringBuffer is incremented
            # but never decremented by QImage.
            # TODO: revert PR #1323 once the issue is fixed in PySide.
            del qImage
            if refcnt != sys.getrefcount(stringBuffer):
                _decref(stringBuffer)
        else:
            p = QtGui.QPainter(self)

            while len(self.blitbox):
                bbox = self.blitbox.pop()
                l, b, r, t = bbox.extents
                w = int(r) - int(l)
                h = int(t) - int(b)
                t = int(b) + h
                reg = self.copy_from_bbox(bbox)
                stringBuffer = reg.to_string_argb()
                qImage = QtGui.QImage(stringBuffer, w, h,
                                      QtGui.QImage.Format_ARGB32)
                if hasattr(qImage, 'setDevicePixelRatio'):
                    # Not available on Qt4 or some older Qt5.
                    qImage.setDevicePixelRatio(self._dpi_ratio)
                # Adjust the stringBuffer reference count to work
                # around a memory leak bug in QImage() under PySide on
                # Python 3.x
                if QT_API == 'PySide' and six.PY3:
                    ctypes.c_long.from_address(id(stringBuffer)).value = 1

                origin = QtCore.QPoint(l, self.renderer.height - t)
                pixmap = QtGui.QPixmap.fromImage(qImage)
                p.drawPixmap(origin / self._dpi_ratio, pixmap)

            # draw the zoom rectangle to the QPainter
            if self._drawRect is not None:
                pen = QtGui.QPen(QtCore.Qt.black, 1 / self._dpi_ratio,
                                 QtCore.Qt.DotLine)
                p.setPen(pen)
                x, y, w, h = self._drawRect
                p.drawRect(x, y, w, h)

            p.end()

FOR MATPLOTLIB 2.2.2

from PyQt5.QtWidgets import QSizePolicy, QWidget, QVBoxLayout
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar)
class MplCanvas(FigureCanvas):
    def __init__(self):
        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self,
                                   QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

# HERE CHANGE THE COLOR OF ZOOM RECTANGLE
    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):
                # IN THIS EXAMPLE CHANGE BLACK FOR WHITE
                pen = QtGui.QPen(QtCore.Qt.white, 1 / self._dpi_ratio,
                             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()

class MplWidget (QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.canvas = MplCanvas()
        # add the toolbar
        self.ntb = NavigationToolbar(self.canvas, self)
        self.vbl = QVBoxLayout()
        self.vbl.addWidget(self.canvas)
        self.vbl.addWidget(self.ntb)
        self.setLayout(self.vbl)