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.