我有以下代码,它与matplotlib 2.0.2 (在Python 3.6.3 下)非常有效,但引发了类型错误(TypeError:'Rectangle'对象matplotlib 2.1.0 ,不可调用。 它的目的是通过“按下并移动”矩形在嵌入PyQt5窗口的图形上交互式绘制矩形,矩形在用户释放鼠标按钮1时给出图形的新限制(即像Matlab那样的缩放行为)。
我做错了什么?
# Python 3.6
...
from PyQt5 import QtCore, QtGui, QtWidgets
...
from matplotlib.patches import Rectangle
...
class MplWidget(Canvas):
zoom_in = False
cid_zoom_P = None
cid_zoom_R = None
cid_zoom_M = None
def __init__(self, parent, dpi = 100, hold = False, **kwargs):
super().__init__(Figure())
self.parent = parent
self.setParent(parent)
self.figure = Figure(dpi = dpi)
self.canvas = Canvas(self.figure)
self.repres = self.figure.add_subplot(111)
...
def zoomManager(self, bouton_pan, bouton_tip):
if self.zoom_in is not True:
self.zoom_in = True
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.x0 = None
self.y0 = None
self.x1 = None
self.y1 = None
self.cid_zoom_P = self.mpl_connect("button_press_event", self.zoomOnPress)
self.cid_zoom_R = self.mpl_connect("button_release_event", self.zoomOnRelease)
self.cid_zoom_M = self.mpl_connect("motion_notify_event", self.zoomOnMotion)
else:
self.zoom_in = False
QtWidgets.QApplication.restoreOverrideCursor()
self.mpl_disconnect(self.cid_zoom_P)
self.mpl_disconnect(self.cid_zoom_R)
self.mpl_disconnect(self.cid_zoom_M)
...
def zoomOnPress(self, event):
if event.button == 1:
if not bool(event.inaxes):
return
self.zoom_pressed = True
self.x0 = event.xdata
self.y0 = event.ydata
self.rect = Rectangle((0,0), 1, 1, alpha=0.25, ls='--', lw=1, ec='k')
self.repres.add_patch(self.rect)
def zoomOnRelease(self, event):
if event.button == 1:
self.zoom_pressed = False
self.x1 = event.xdata
if not self.x1:
self.x1 = self.x1_old
else:
self.x1_old = self.x1
self.y1 = event.ydata
if not self.y1:
self.y1 = self.y1_old
else:
self.y1_old = self.y1
self.rect.remove()
self.repres.set_xlim([min(self.x1, self.x0), max(self.x1, self.x0)])
self.repres.set_ylim([min(self.y1, self.y0), max(self.y1, self.y0)])
self.draw()
def zoomOnMotion(self, event):
if self.zoom_pressed is False:
return
self.x1 = event.xdata
if not self.x1:
self.x1 = self.x1_old
else:
self.x1_old = self.x1
self.y1 = event.ydata
if not self.y1:
self.y1 = self.y1_old
else:
self.y1_old = self.y1
self.rect.set_width(self.x1 - self.x0)
self.rect.set_height(self.y1 - self.y0)
self.rect.set_xy((self.x0, self.y0))
self.draw()
...
答案 0 :(得分:0)
以下代码适用于2.0.2和2.1.0 matplotlib版本。 但我认为这并不代表我遇到的问题。无论如何我都是这样做的,因为对于那些对这个机制感兴趣的人来说这是一个例子(我在网上找不到相应的例子)。
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
xdata = np.linspace(0,9*np.pi, num=301)
ydata = np.sin(xdata)
fig, ax = plt.subplots()
line, = ax.plot(xdata, ydata)
class Zoom(object):
zoom_in = False
def __init__(self, fig, ax):
self.fig = fig
self.ax = ax
self.x0 = None
self.y0 = None
self.x1 = None
self.y1 = None
self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press)
self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release)
self.ax.figure.canvas.mpl_connect("motion_notify_event", self.on_motion)
def on_press(self, event):
print('press')
if event.button == 1:
if not bool(event.inaxes):
return
self.x0 = event.xdata
self.y0 = event.ydata
self.rect = Rectangle((0,0), 1, 1, alpha=0.25, ls='--', lw=1, ec='k')
self.ax.add_patch(self.rect)
self.zoom_in = True
def on_release(self, event):
print('release')
if event.button == 1:
self.x1 = event.xdata
if not self.x1: # user is out of axis
self.x1 = self.x1_old
else: # user is over axis
self.x1_old = self.x1
self.y1 = event.ydata
if not self.y1:
self.y1 = self.y1_old
else:
self.y1_old = self.y1
self.rect.remove()
self.ax.set_xlim([min(self.x1, self.x0), max(self.x1, self.x0)])
self.ax.set_ylim([min(self.y1, self.y0), max(self.y1, self.y0)])
self.ax.figure.canvas.draw()
def on_motion(self, event):
if self.zoom_in is True:
self.x1 = event.xdata
if not self.x1:
self.x1 = self.x1_old
else:
self.x1_old = self.x1
self.y1 = event.ydata
if not self.y1:
self.y1 = self.y1_old
else:
self.y1_old = self.y1
self.rect.set_width(self.x1 - self.x0)
self.rect.set_height(self.y1 - self.y0)
self.rect.set_xy((self.x0, self.y0))
#self.draw() # Statement that is problematic
#plt.draw() # Its equivalent under 'Pyplot'
self.ax.figure.canvas.draw()
a = Zoom(fig, ax)
plt.show()
在我的研究的这一点上,我认为问题不在于补丁。矩形'而是在以下几行中'自我'是我的' matplotlib小部件'对于PyQt5:
self.cid_zoom_P = self.mpl_connect("button_press_event", self.zoomOnPress)
self.cid_zoom_R = self.mpl_connect("button_release_event", self.zoomOnRelease)
self.cid_zoom_M = self.mpl_connect("motion_notify_event", self.zoomOnMotion)
无论如何,我会花更多的时间来解决这个问题。如果我无法找到我做错的事情,我会将Matplotlib降级到2.0.2。
答案 1 :(得分:0)
我找到了这个问题的解决方案,现在,我可以使用我的缩放与PyQt5和Matplotlib 2.1.2(我有另一个问题Matplotlib 2.2.2,其中tkagg不再有cursord属性)。
这是名为 backend_qt5agg.py 的文件,它引发了我的错误( Rectangle不可调用)。 第89行和下一行,有:
if self._bbox_queue:
bbox_queue = self._bbox_queue
else:
try:
painter.eraseRect(self.rect())
except:
pass
bbox_queue = [
Bbox([[0, 0], [self.renderer.width, self.renderer.height]])]
我刚添加了一个' try / except'阻止这样:
{{1}}
它工作正常。