Python - 来自鼠标的Matplotlib用户输入用于绘图

时间:2013-11-19 22:50:08

标签: python math python-2.7 numpy matplotlib

该类绘制曲线。但是,输入目前在main()中设置。我想将它们设置为用户驱动的鼠标交互。其中一些是可能的,并且在Matplotlib文档中(参见下面引用的网站),但它仍然没有真正将它设置为“点击和绘图”。因此,理想情况下,用户可以单击按钮来设置P,然后他们下次单击的任何点(在曲线上,必须在曲线上)将是新的P。与Q相同。我敢肯定,对于任何使用过Matplotlib的人来说,这是一个非常简单的问题,但我现在正在自学它,但是可能只需要几分钟的入门级开发来做一些我无处可去的事情。

代码:

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid.axislines import SubplotZero
from math import sqrt



class ECC123(object):

    def __init__(self,a,b,px,qx,qy):
        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)

    def drawAxis(self):
        #fig = plt.figure(1)
        #ax = SubplotZero(fig, 111)
        self.fig.add_subplot(self.ax)
        for direction in ["xzero", "yzero"]:
            self.ax.axis[direction].set_axisline_style("->")
            self.ax.axis[direction].set_visible(True)

    def plotGraph(self):
        self.drawAxis()
        y, x = np.ogrid[-10:10:100j, -10:10:100j]  # range grid  [from : to : how_many_points]
        xlist = x.ravel(); ylist = y.ravel()
        plt.contour(xlist, ylist, self.elliptic_curve(x,y), [0])
        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)
        plt.plot([self.pxlam], [pylam], "mo"); plt.plot([self.qxlam], [qylam], "mo")
        plt.text(self.pxlam-0.25,pylam+0.5, '$P$'); plt.text(self.qxlam-0.25,self.qxlam+0.5, '$Q$')
        s = (pylam - qylam)/(self.pxlam - self.qxlam)  # calculate s slope
        xr = s**2 - self.pxlam - self.qxlam  # x-value of R
        yr = pylam + s*(xr - self.pxlam)  # y-value of -R; -y is R (inverted across x-axis)
        plt.plot([xr],[yr],"mo")
        plt.plot([xr],[-yr],"co")
        plt.plot([self.qxlam,xr], [qylam,yr], color = "c", linewidth=1)
        plt.plot([xr,xr], [yr,-yr], "x--")
        plt.text(xr+0.25,yr, '$-R$'); plt.text(xr+0.25,-yr, '$R$')

        plt.grid(True)
        plt.show()

我一直在浏览Matplotlib中的文档,scipy cookbook以及相关问题,但仍然没有看到如何做到这一点:

http://matplotlib.org/users/event_handling.html

http://matplotlib.org/1.3.1/api/widgets_api.html#matplotlib.widgets.Button.on_clicked

Cursors for data selection in matplotlib

How can I create a frontend for matplotlib?

http://wiki.scipy.org/Cookbook/Matplotlib

到目前为止,当我点击它时,我的红色x会变得很小,而且它们甚至不在曲线内。

1 个答案:

答案 0 :(得分:3)

我稍微修改了你的代码,这样你就可以设置P&的位置了。 Q左派&右键单击,我没有完成所有图形数据更新,剩下的就留给你了:

from mpl_toolkits.axes_grid.axislines import SubplotZero
import numpy as np
import matplotlib.pyplot as plt
from math import sqrt



class ECC(object):
    """
    class to implement elliptic curve and find P+Q=R on the plot
    """

    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)

    def drawAxis(self):
        """
        draw main x,y axis
        """
        #fig = plt.figure(1)
        #ax = SubplotZero(fig, 111)
        self.fig.add_subplot(self.ax)
        for direction in ["xzero", "yzero"]:
            self.ax.axis[direction].set_axisline_style("->")
            self.ax.axis[direction].set_visible(True)

    def ecclambda(self,xl,a,b):
        """
        returns points elliptic curve for P and Q
        y**2 = x**3 + a*x + b
        """
        return sqrt(xl**3 + a*xl + b)

    def elliptic_curve(self,x,y):
        """
        takes in x,y as set of points, returns the elliptic curve
        y**2 = x**3 + a*x + b
        """
        return pow(y, 2) - pow(x, 3) - x * self.a - self.b

    def onclick(self, event):
        x = event.xdata
        if event.button == 1:
            self.pxlam = x
        if event.button == 3:
            self.qxlam = x

        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)
        plt.gcf().canvas.draw()

    def plotGraph(self):
        """
        main plotting of elliptic curve and points/line for P+Q=R
        P+Q=R --->>>  -R is plotted (xr,yr), R is plotted (xr, -yr)
        conditional with invertQy allows inversion of Q across x-axis; set option in main()
        """
        self.drawAxis()
        y, x = np.ogrid[-10:10:100j, -10:10:100j]  # range grid  [from : to : how_many_points]
        xlist = x.ravel(); ylist = y.ravel()
        plt.contour(xlist, ylist, self.elliptic_curve(x,y), [0])
        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$')
        s = (pylam - qylam)/(self.pxlam - self.qxlam)  # calculate s slope
        xr = s**2 - self.pxlam - self.qxlam  # x-value of R
        yr = pylam + s*(xr - self.pxlam)  # y-value of -R; -y is R (inverted across x-axis)
        plt.plot([xr],[yr],"mo")
        plt.plot([xr],[-yr],"co")
        plt.plot([self.qxlam,xr], [qylam,yr], color = "c", linewidth=1)
        plt.plot([xr,xr], [yr,-yr], "x--")
        plt.text(xr+0.25,yr, '$-R$'); plt.text(xr+0.25,-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,xr,-yr,self.a,self.b),
                fontsize=10, color = 'blue',bbox=dict(facecolor='tan', alpha=0.5))
        plt.title(r"Elliptic Curve Implementation $y^{2} = x^{3} + a*x + b$", fontsize = 16, color = 'b')
        self.fig.canvas.mpl_connect('button_press_event', self.onclick)
        #[xi,yi] = plt.ginput(0)
        ##print "ginput ",xi,yi
        plt.grid(True)
        plt.show()




def main():

    a = -2; b = 1; px = -1.55; qx = -0.1
    invertQy = 0 # set to 1 if q should be inverted to negative along its y axis
    ec = ECC(a,b,px,qx,invertQy)
    ec.plotGraph()


if __name__ == '__main__':
    main()