Python -- Matplotlib redrawing lines without previous lines remaining
Question: This class plots a curve in Matplotlib. The user mouse input section changes
the `set_data()` for several `x,y` coordinates. The `P` and `Q` are resetting
properly, it seems. However, when the `R` is not set with calculations using
those same methods (`set_data()` or `set_x()` or `set_y()`), then this results
in the error:

`TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'`

When the `R` calculations are left in this results in the error:

`AttributeError: 'list' object has no attribute 'set_xdata'`

The whole class (it's a little big but the methods are interdependent and I
don't want to leave out something that could be relevant here):

    
    
    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)
    

The lines of code that I'm referring to above, as to whether the `R` methods
are kept in or left out, are 2 commented out in the method `update()` and are
commented afterward with `# R calculation`. I'm teaching myself Matplotlib
right now, so I'm sure a junior programmer can see my obvious error in a very
short time, but I've been at it for some time and getting nowhere fast. The
main thing I want to do here is just get the lines and points to be redrawn
after every click without any of the previously set points to remain on the
graph. Similarly with the textbox on the upper left of the graph, the values
should be reset there after every click and not rewrite themselves over each
previous string of text.

EDIT:

I have tried `cla()` and `clf()` and they don't appear to work in this case.
In fact, they may not even be necessary at any point in this program as
`set_data()` methods I used should be enough to redraw based on the new data
from every click. To prove that, just uncomment the entire `plotGraph()`
method in my class and comment out the same code in `update()` and you'll see
that the points `P` and `Q` will be set new after ever click. The real problem
is the `R` point, the lines, and the text box on the upper left.


Answer: Well, at first `self.rxdata` and `self.rydata` are lists, which have no
`set_xdata` method, hence the error. Maybe you wanted to do something like
`self.my_plot.set_xdata(...)`?

Anyway, there's a ~~much clever~~ _other_ way to do this: `matplotlib` is
object-oriented, meaning that it handles graph stuffs as objects; and as you
can add things, you can [also remove some of them by calling their
methods](http://stackoverflow.com/questions/5600370/python-matplotlib-add-and-
remove-text-to-figure-using-button-events), but for this you need their
reference.

So, in your case, you just have to keep the references to the objects you dant
to add/remove:

  * In `__init_()` definition, simply add a reference tracker:
    
        def __init__(...):
        (...)
        self.text = None
    

  * And use this reference in `onClick()` definition:
    
        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 '
                         (...))
    

With this only addition, and keeping the two lines returning error commented,
the text box is refreshed at every click on the graph.

I think you get the point, and are able to reproduce this to every object you
want to remove and recreate; there is no interest in re-drawing the ellipsis
plot.

* * *

## Edit after comment

Ok, `remove` exits for all objects, but in fact, `plt.plot` return a list with
one element. So, a solution is simply to create a list of all objects that
will be refreshed, and to call this `remove` method for everyone of them:

  * in `__init__()`:
    
        def __init__(...):
        (...)
        self._tracker = []
    

  * in `plotR()`, we have to return the references:
    
        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
    

  * in `onClick()`, I propose the following code:
    
        def onclick(self, event):
         # removing elements that will be regenerated
        for element in self._tracker:      #print "trying to remove", element
            element.remove()
        # reinitializing the tracker
        self._tracker = []
        (...)
    
        _e1 = plt.plot([self.pxlam,self.qxlam], [pylam,qylam], 
                       color = "c", linewidth=1)[0]
        (...)
        _e2 = plt.plot([self.xr],[self.yr],"mo")[0]
        _e3 = plt.plot([self.xr],[-1*(self.yr)],"co")[0]
        (...)
        _e4, _e5 = self.plotR(qylam)
        _e6 = plt.text(self.xr+0.25,self.yr, '$-R$'); plt.text(self.xr+0.25,-1*(self.yr), '$R$')
        _e7 = 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))
        (...)
    
        # adding in the tracker
        self._tracker.extend([self.p, self.q, self.pt, self.qt, self.r,
                              _e1, _e2, _e3, _e4, _e5, _e6, _e7])
    
        self.update()
    

Does this solve the problem?

Note: another solution could be to change parameters (position, data) of the
objects, {edit} as shown by tcaswell's comment below and implemented in my
other answer to the question.

