【问题标题】:Python - PyQt - matplotlib: drawRectangle methodPython - PyQt - matplotlib:drawRectangle 方法
【发布时间】:2018-10-02 09:42:04
【问题描述】:

我正在使用 matplotlib backend_qt5agg 处理将 matplotlib 画布绘制到 qt 窗口中。用户可以在绘图上绘制矩形(matplotlib.patches.Rectangle 类型),我通过调用canvas.draw() 方法显示矩形。如果画布包含大量数据,此方法可能会很慢,我想加快速度。

寻找解决方案,我在backend_qt5agg 手册中发现存在一个名为drawRectangle(rect) 的方法。希望此方法可以仅绘制补丁而不重绘整个画布,我尝试使用矩形补丁作为输入来调用此方法。所以我打电话给canvas.drawRectangle(my_rect),而不是打电话给canvas.draw()。这不会画任何东西。

不幸的是,backend_qt5agg 手册纯粹是记录在案的。所以我的问题是:drawRectangle 方法是如何工作的,它是否应该比重绘整个画布更好?

最小的例子(当鼠标点击并在画布内移动时显示一个矩形)(只需将self.canvas.draw() 更改为on_motion 内的self.canvas.drawRectangle(self.rect) 以测试drawRectangle 方法):

import sys
import matplotlib
from PyQt5 import QtCore
import PyQt5.QtWidgets as QtW
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.patches import Rectangle


class MainWindow(QtW.QMainWindow):

  def __init__(self):
    super().__init__()
    self.setWindowTitle('MyWindow')
    self._main = QtW.QWidget()
    self.setCentralWidget(self._main) 

    # Set canvas properties
    self.fig = matplotlib.figure.Figure(figsize=(5,5))
    self.canvas = FigureCanvasQTAgg(self.fig)
    self.ax = self.fig.add_subplot(1,1,1)
    self.rect = Rectangle((0,0), 0.2, 0.2, color='k', fill=None, alpha=1)
    self.ax.add_patch(self.rect); self.rect.set_visible(False)
    self.canvas.draw()

    # set Qlayout properties and show window
    self.gridLayout = QtW.QGridLayout(self._main)
    self.gridLayout.addWidget(self.canvas)
    self.setLayout(self.gridLayout)
    self.show()

    # connect mouse events to canvas
    self.fig.canvas.mpl_connect('button_press_event', self.on_click)
    self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion)


  def on_click(self, event):
    if event.button == 1 or event.button == 3:
    # left or right click: get the x and y coordinates
        if event.inaxes is not None:
          self.xclick = event.xdata
          self.yclick = event.ydata
          self.on_press = True

  def on_motion(self, event):
    # draw the square
    if event.button == 1 or event.button == 3 and self.on_press == True:
      if (self.xclick is not None and self.yclick is not None):
        x0, y0 = self.xclick, self.yclick
        x1, y1 = event.xdata, event.ydata
        if (x1 is not None or y1 is not None):
          self.rect.set_width(x1 - x0)
          self.rect.set_height(y1 - y0)
          self.rect.set_xy((x0, y0))
          self.rect.set_visible(True)
          self.canvas.draw() # self.canvas.drawRectangle(self.rect)


if __name__ == '__main__':
    app = QtCore.QCoreApplication.instance()
    if app is None: app = QtW.QApplication(sys.argv)
    win = MainWindow()
    app.aboutToQuit.connect(app.deleteLater)
    app.exec_()

【问题讨论】:

  • source code 看来,此函数旨在绘制当您在交互式窗口中放大图形的一部分时显示的虚线矩形。
  • @Isma 我加了一个例子
  • @DizietAsahi 感谢您指出这一点,但我仍然不清楚该方法的使用。 rect 输入应该是什么?
  • @Isma,当鼠标在画布内按下并移动时应该显示矩形

标签: python matplotlib canvas pyqt


【解决方案1】:

您可以使用已包含在 matplotlib.widgets 中的 RectangleSelector 小部件,而不是自己添加矩形。

这是一个示例,您需要完善它并使其适应您的需求,但我希望它能让您继续前进:

import sys
import matplotlib
from PyQt5 import QtCore
import PyQt5.QtWidgets as QtW
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.widgets import RectangleSelector


class MainWindow(QtW.QMainWindow):

    def __init__(self):
        super().__init__()
        self.setWindowTitle('MyWindow')
        self._main = QtW.QWidget()
        self.setCentralWidget(self._main) 

        # Set canvas properties
        self.fig = matplotlib.figure.Figure(figsize=(5,5))
        self.canvas = FigureCanvasQTAgg(self.fig)
        self.ax = self.fig.add_subplot(1,1,1)
        self.canvas.draw()

        self.rs = RectangleSelector(self.ax, self.line_select_callback,
                                                drawtype='box', useblit=True,
                                                button=[1, 3],  # don't use middle button
                                                minspanx=5, minspany=5,
                                                spancoords='pixels',
                                                interactive=True)

        # set Qlayout properties and show window
        self.gridLayout = QtW.QGridLayout(self._main)
        self.gridLayout.addWidget(self.canvas)
        self.setLayout(self.gridLayout)
        self.show()

        # connect mouse events to canvas
        self.fig.canvas.mpl_connect('button_press_event', self.on_click)

    def on_click(self, event):
        if event.button == 1 or event.button == 3 and not self.rs.active:
            self.rs.set_active(True)
        else:
            self.rs.set_active(False)

    def line_select_callback(self, eclick, erelease):
        x1, y1 = eclick.xdata, eclick.ydata
        x2, y2 = erelease.xdata, erelease.ydata
        print("(%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2))
        print(" The button you used were: %s %s" % (eclick.button, erelease.button))

if __name__ == '__main__':
    app = QtCore.QCoreApplication.instance()
    if app is None: app = QtW.QApplication(sys.argv)
    win = MainWindow()
    app.aboutToQuit.connect(app.deleteLater)
    app.exec_()

参考:https://matplotlib.org/examples/widgets/rectangle_selector.html

【讨论】:

  • 感谢您的示例,您的选择解决方案非常优雅。
  • 另一方面,我的问题有点笼统。例如,选择后我希望一个矩形留在画布上,假设我想根据其他用户操作(例如 QPushButton)更改矩形属性(如线条或颜色)。在这种情况下,我必须重新绘制画布。我的问题仍然是:是否可以在不重绘整个画布的情况下修改该矩形?
  • 是的,你可以,你做得对,但你需要传递一个列表而不是一个矩形,例如 self.canvas.drawRectangle([x0, y0, x1, y1])
  • 感谢您的回复,但如果我在代码中使用self.canvas.drawRectangle([x0, y0, x1, y1]) 而不是self.canvas.draw(),它仍然不会绘制任何内容。
  • 是的,我知道。在我的示例中,x0、y0 等是真实值,由鼠标事件给出,如果我使用 self.canvas.draw() 绘制画布,它会起作用,但如果我使用 self.canvas.drawRectangle([x0, y0, x1, y1]),它就不起作用。你测试了吗?
猜你喜欢
  • 2019-06-30
  • 1970-01-01
  • 1970-01-01
  • 2017-11-06
  • 2011-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-16
相关资源
最近更新 更多