scipy 在matplotlib中更改缩放矩形的边缘颜色

9o685dep  于 2022-11-10  发布在  其他
关注(0)|答案(4)|浏览(196)

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

wz8daaqr

wz8daaqr1#

在文件中

后端处理器_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()

只需在上面的代码中添加/更改矩形绘制部分。

vhipe2zx

vhipe2zx2#

这可以通过子类化和覆盖(而不是扩展)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。

sqougxex

sqougxex3#

请原谅死灵术,但我想为任何试图用tkinter解决这个问题的人戴上帽子(因为这里的大多数答案都涉及Qt)。
你需要重写NavigationToolbar2Tk.draw_rubberband()方法。这里我使用了一个自定义类,但这不是唯一的方法:

class CustomToolbar(mptk.NavigationToolbar2Tk):
    def __init__(self, figcanvas, parent):
        super().__init__(figcanvas, parent)  # init the base class as usual

    # you can copy the method 'draw_rubberband()' right from
    # NavigationToolbar2Tk - we're only changing one line
    def draw_rubberband(self, event, x0, y0, x1, y1):
        self.remove_rubberband()
        height = self.canvas.figure.bbox.height
        y0 = height - y0
        y1 = height - y1
        # this is the line we want to change
        self.lastrect = self.canvas._tkcanvas.create_rectangle(
            x0, y0, x1, y1
            outline = 'red'  # add your outline color here
        )
        # hex color strings -> '#FF3344' and named colors -> 'gainsboro' both work

您可以像使用NavigationToolbar2Tk一样按名称使用CustomToolbar

self.toolbar = CustomToolbar(self.root, self.canvas, self.frame)  # for example...

实现一个继承自NavigationToolbar2Tk的自定义工具栏类还提供了其他可能性,比如修改工具栏按钮、添加自定义工具等等......但这是另一篇文章的内容。

bvhaajcl

bvhaajcl4#

除了Gloweye的React,在PyQt5中你应该这样做。

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()

适用于MATLAB库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)

相关问题