Python - Matplotlib重绘了没有前一行的行

时间:2013-11-29 18:58:20

标签: python math python-2.7 numpy matplotlib

此类绘制Matplotlib中的曲线。用户鼠标输入部分会更改set_data()x,y个坐标。看来,PQ正确重置。但是,如果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 calculationcla(),但在这种情况下它们似乎不起作用。 事实上,在这个程序的任何一点上它们甚至可能都不是必需的,因为我使用的clf()方法应该足以根据每次点击的新数据重绘。要证明这一点,只需取消注释我班级中的整个set_data()方法,并在plotGraph()中注释掉相同的代码,您就会看到点update()P将是点击后设置新的。真正的问题是左上角的Q点,线条和文本框。

2 个答案:

答案 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.rxdataself.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方法:

    <{1}}中的
  • __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评论所示,并在我对该问题的其他答案中实现。