此类绘制Matplotlib中的曲线。用户鼠标输入部分会更改set_data()
个x,y
个坐标。看来,P
和Q
正确重置。但是,如果R
未使用相同的方法(set_data()
或set_x()
或set_y()
)进行计算,则会导致错误:
TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'
当R
计算留在此结果中时会出现错误:
AttributeError: 'list' object has no attribute 'set_xdata'
整个班级(它有点大,但方法是相互依赖的,我不想遗漏一些可能与此相关的东西):
from mpl_toolkits.axes_grid.axislines import SubplotZero
import numpy as np
import matplotlib.pyplot as plt
from math import sqrt
class ECC(object):
def __init__(self,a,b,px,qx,qy):
"""
initialize input variables
"""
self.a = a
self.b = b
self.pxlam = px
self.qxlam = qx
self.invertQy = qy
self.fig = plt.figure(1)
self.ax = SubplotZero(self.fig, 111)
self.xr = 0
self.yr = 0
def onclick(self, event):
x = event.xdata
if event.button == 1:
self.pxlam = x
if event.button == 3:
self.qxlam = x
pylam = self.ecclambda(self.pxlam,self.a,self.b) # calculate P from pxlam
qylam = self.ecclambda(self.qxlam,self.a,self.b) # calculate Q from qxlam
if self.invertQy == 1: qylam = -qylam # optional, inverts qy to negative on the plot
plt.plot([self.pxlam,self.qxlam], [pylam,qylam], color = "c", linewidth=1)
self.p = plt.plot([self.pxlam], [pylam], "mo")[0]
self.q = plt.plot([self.qxlam], [qylam], "mo")[0]
self.pt = plt.text(self.pxlam-0.25,pylam+0.5, '$P$')
self.qt = plt.text(self.qxlam-0.25,self.qxlam+0.5, '$Q$')
self.xr,self.yr = self.dataToPlotR(pylam,qylam)
plt.plot([self.xr],[self.yr],"mo")
plt.plot([self.xr],[-1*(self.yr)],"co")
self.rxdata = [self.qxlam,self.xr]; self.rydata = [qylam,self.yr]
self.r, = plt.plot(self.rxdata, self.rydata, color = "c", linewidth=1)
#plt.plot([xr,xr], [yr,-yr], "x--")
self.plotR(qylam)
plt.text(self.xr+0.25,self.yr, '$-R$'); plt.text(self.xr+0.25,-1*(self.yr), '$R$')
plt.text(-9,6,' P: (%s ,%s) \n Q: (%s ,%s) \n R: (%s ,%s) \n a: %s \n b: %s '
%(self.pxlam,pylam,self.qxlam,qylam,self.xr,-1*(self.yr),self.a,self.b),
fontsize=10, color = 'blue',bbox=dict(facecolor='tan', alpha=0.5))
self.update()
def update(self):
pylam = self.ecclambda(self.pxlam,self.a,self.b) # calculate P from pxlam
qylam = self.ecclambda(self.qxlam,self.a,self.b) # calculate Q from qxlam
self.p.set_data([self.pxlam], [pylam])
self.q.set_data([self.qxlam], [qylam])
self.pt.set_x(self.pxlam-0.25)
self.pt.set_y(pylam+0.5)
self.qt.set_x(self.qxlam-0.25)
self.qt.set_y(qylam+0.5)
self.xr,self.yr = self.dataToPlotR(pylam,qylam)
#self.rxdata.set_xdata([self.qxlam,self.xr]) # R calculations
#self.rydata.set_ydata([qylam,self.yr]) # R calculations
plt.gcf().canvas.draw()
#self.plotR(self.xr,self.yr,qylam)
我在上面提到的关于R
方法是保留还是遗漏的代码行在方法update()
中被注释掉,并在之后用{注释} {1}}。
我现在正在教自己Matplotlib,所以我确信一个初级程序员可以在很短的时间内看到我明显的错误,但我已经有一段时间了,并且无处可去。
我想在这里做的主要事情就是在每次点击后重新绘制线条和点,而没有任何先前设置的点保留在图形上。与图表左上角的文本框类似,每次点击后都应重置值,而不是在每个前一个文本字符串上重写。
编辑:
我尝试了# R calculation
和cla()
,但在这种情况下它们似乎不起作用。
事实上,在这个程序的任何一点上它们甚至可能都不是必需的,因为我使用的clf()
方法应该足以根据每次点击的新数据重绘。要证明这一点,只需取消注释我班级中的整个set_data()
方法,并在plotGraph()
中注释掉相同的代码,您就会看到点update()
和P
将是点击后设置新的。真正的问题是左上角的Q
点,线条和文本框。
答案 0 :(得分:2)
我已将问题简化为最低限度,并通过在SO上搜索set_xdata
并按照 tcaswell 提供的链接,我找到了this subject,这很清楚
以下是5分钟内编写的演示代码:
import matplotlib.pyplot as plt
class OnClickTest(object):
def __init__(self):
self.fig = plt.figure()
plt.plot([0, 1, 2], [0, 4, 3])
self.line, self.text, self.prev_click = None, None, None
self.fig.canvas.mpl_connect('button_press_event', self.onClick)
plt.show()
def onClick(self, event):
x, y = event.xdata, event.ydata
if self.line is None:
# creating the object
self.line, = plt.plot([0, x], [0, y])
self.text = plt.text(x, y, "My click")
self.prev_click = (x, y)
else:
# updating the object
self.line.set_xdata([self.prev_click[0], x])
self.line.set_ydata([self.prev_click[1], y])
self.text.set_position((x, y))
self.prev_click = (x, y)
self.fig.canvas.draw()
o = OnClickTest()
答案 1 :(得分:1)
嗯,首先self.rxdata
和self.rydata
是列表,没有set_xdata
方法,因此出错。也许你想做self.my_plot.set_xdata(...)
之类的事情?
无论如何,有一个非常聪明的 其他方法:matplotlib
是面向对象的,这意味着它将图形内容作为对象处理;你可以添加东西,also remove some of them by calling their methods,但为此你需要他们的参考。
因此,在您的情况下,您只需要保留对您无法添加/删除的对象的引用:
在__init_()
定义中,只需添加参考跟踪器:
def __init__(...):
(...)
self.text = None
并在onClick()
定义中使用此引用:
if self.text is not None: # if text has already been drawn before,
self.text.remove() # simply remove it
self.text = plt.text(-9, 6, # and re-create it, keeping the reference
' P: (%s ,%s) \n Q: (%s ,%s) \n R: (%s ,%s) \n a: %s \n b: %s '
(...))
只有这个添加,并保持两行返回错误注释,文本框会在图表上的每次单击时刷新。
我认为你明白了这一点,并且可以将它重现给你想删除和重新创建的每个对象;没有兴趣重新绘制省略图。
好的,remove
退出所有对象,但事实上,plt.plot
会返回一个包含一个元素的列表。因此,解决方案只是创建一个将刷新的所有对象的列表,并为每个人调用此remove
方法:
:
__init__()
在def __init__(...):
(...)
self._tracker = []
中,我们必须返回引用:
plotR()
在def plotR(self,qylam):
r1, = plt.plot([self.qxlam, self.xr], [qylam, self.yr], color = "c", linewidth=1)
r2, = plt.plot([self.xr, self.xr], [self.yr, -1*(self.yr)], "x--")
return r1, r2
中,我建议使用以下代码:
onClick()
这是否解决了这个问题?
注意:另一个解决方案可能是更改对象的参数(位置,数据),{edit},如下面的tcaswell评论所示,并在我对该问题的其他答案中实现。