# L-31 MCS 260 Wed 30 Mar 2016 : guisample.py

"""
GUI to sample a user given function.
"""

from tkinter import Tk, Label, Entry, Canvas, Button
from tkinter import W, E, N, S, ALL
from math import exp, cos, sin

class SampleFun(object):
    """
    GUI to sample f(x) over [a,b]
    """
    def __init__(self, wdw, size):
        """
        defines layout and data of GUI
        """
        wdw.title("Plot Expressions")
        self.lbl1 = Label(wdw, text="f(x) =")
        self.lbl1.grid(row=0)
        self.fun = Entry(wdw) # for f(x)
        self.fun.grid(row=0, column=1, \
           columnspan=5, sticky=W+E+N+S)
        self.lbl2 = Label(wdw, text="a =")
        self.lbl2.grid(row=1, column=0)
        self.enta = Entry(wdw) # for a
        self.enta.grid(row=1, column=1)
        self.lbl3 = Label(wdw, text="b =")
        self.lbl3.grid(row=1, column=2)
        self.entb = Entry(wdw) # for b
        self.entb.grid(row=1, column=3)
        self.lbl4 = Label(wdw, text="N =")
        self.lbl4.grid(row=1, column=4)
        self.entn = Entry(wdw) # for N
        self.entn.grid(row=1, column=5)
        self.dim = size # dimension of canvas
        self.cnv = Canvas(wdw, width=self.dim, \
           height=self.dim, bg='white')
        self.cnv.grid(row=3, columnspan=6)
        self.btt = Button(wdw, text="sample and plot", \
           command=self.show)
        self.btt.grid(row=2, column=0, \
           columnspan=6, sticky=W+E+N+S)
        self.org = [] # where sampled
        self.val = [] # sampled values
        self.pts = [] # scaled points

    def sample(self):
        """
        samples the function
        """
        agt = float(self.enta.get())
        bgt = float(self.entb.get())
        nbr = int(self.entn.get())
        step = (bgt-agt)/(nbr+1)
        x = agt
        self.org = []
        self.val = []
        for k in range(0, nbr):
            eva = eval(self.fun.get())
            self.org.append(x)
            self.val.append(eva)
            x = x + step

    def scale_point(self, xpt, lft, rgh, low, upp):
        """
        maps xpt from [lft, rgh] into [low, upp]
        """
        return (upp-low)/(rgh-lft)*(xpt-lft) + low

    def scale(self):
        """
        scales the points
        """
        agt = float(self.enta.get())
        bgt = float(self.entb.get())
        miv = min(self.val)
        mav = max(self.val)
        upp = 0.95*self.dim
        low = 0.05*self.dim
        self.pts = []
        for k in range(0, len(self.org)):
            xsc = self.scale_point(self.org[k], agt, bgt, low, upp)
            ysc = self.scale_point(self.val[k], miv, mav, low, upp)
            ysc = upp - ysc + low # origin is topleft
            self.pts.append((xsc, ysc))

    def show(self):
        """
        samples, scales, and shows
        """
        self.sample()
        self.scale()
        self.cnv.delete(ALL) # refresh canvas
        for k in range(0, len(self.pts)):
            self.cnv.create_oval( \
                self.pts[k][0]-6, self.pts[k][1]-6, \
                self.pts[k][0]+6, self.pts[k][1]+6, \
                width=1, outline='black', fill='SkyBlue2')

def main():
    """
    Instantiates the GUI and
    launches the main event loop.
    """
    top = Tk()
    SampleFun(top, 500)
    top.mainloop()

if __name__ == "__main__":
    main()
