from tkinter import * import math from R2Graph import * import lparser parser = lparser.Parser() functionDefined = False def func(x): # return math.sin(2.0*x)*(4.0/(1.0 + 0.2*x*x)) # return x*x*x - x - 1 if functionDefined: return parser.evaluate(x = x) else: return 0. SCALEX = 40 SCALEY = SCALEX def main(): root = Tk() root.title("Plot Function") root.geometry("800x600") panel = Frame(root) drawArea = Canvas(root, bg="white") panel.pack(side=TOP, fill=X) drawButton = Button(panel, text="Draw") clearButton = Button(panel, text="Clear") functionLabel = Label(panel, text="f(x):", fg="DarkBlue") functionText = StringVar() # Control variable connected with text entry functionEntry = Entry( panel, bg="white", textvariable=functionText, fg="DarkBlue", width=20 ) message = Label(panel, text="", fg="DarkBlue") drawButton.pack(side=LEFT, padx=4, pady=4) clearButton.pack(side=LEFT, padx=4, pady=4) functionLabel.pack(side=LEFT, padx=4, pady=4) functionEntry.pack(side=LEFT, fill=X, expand=True, padx=4, pady=4) message.pack(side=LEFT, padx=4, pady=4) drawArea.pack(side=TOP, fill=BOTH, expand=True, padx=4, pady=4) root.update() def map(t): '''Map mathematical coordinates into pixel coordinates R2Point --> (x, y)''' w = drawArea.winfo_width() h = drawArea.winfo_height() ox = w/2. oy = h/2. return (ox + t.x*SCALEX, oy - t.y*SCALEY) def invmap(p): '''Map pixel coordinates into mathematical coordinates (x, y) --> R2Point''' w = drawArea.winfo_width() h = drawArea.winfo_height() ox = w/2. oy = h/2. x = float(p[0] - ox)/SCALEX y = float(oy - p[1])/SCALEY return R2Point(x, y) def xmin(): w = drawArea.winfo_width() return (-w/2.)/SCALEX def xmax(): w = drawArea.winfo_width() return (w/2.)/SCALEX def ymin(): h = drawArea.winfo_height() return (-h/2.)/SCALEY def ymax(): h = drawArea.winfo_height() return (h/2.)/SCALEY def drawGrid(): x0 = xmin(); x1 = xmax() y0 = ymin(); y1 = ymax() # Grid for x in range(int(x0), int(x1) + 1): if x == 0: continue drawArea.create_line( map(R2Point(x, y0)), map(R2Point(x, y1)), fill="lightGray" ) for y in range(int(y0), int(y1) + 1): if y == 0: continue drawArea.create_line( map(R2Point(x0, y)), map(R2Point(x1, y)), fill="lightGray" ) # Coordinate axes drawArea.create_line( map(R2Point(x0, 0.)), map(R2Point(x1, 0.)), fill="black", width=2 ) drawArea.create_line( map(R2Point(0., y0)), map(R2Point(0., y1)), fill="black", width=2 ) def plotFunc(f, color="blue"): x0 = xmin(); x1 = xmax() y0 = ymin(); y1 = ymax() yext = (y1 - y0)*0.5 y0 -= yext; y1 += yext dx = 1./SCALEX line = [] x = x0 while x <= x1: yDefined = False try: y = f(x) if y0 <= y and y <= y1: yDefined = True except: pass if not yDefined: if len(line) > 1: drawArea.create_line( line, fill=color, width=3 ) line.clear() else: line.append(map(R2Point(x, y))) x += dx if len(line) > 1: drawArea.create_line( line, fill=color, width=3 ) def onDraw(): global functionDefined text = functionEntry.get() parser.setParseLine(text) (success, errorText) = parser.compile() message.configure(text = errorText) drawArea.delete("all") drawGrid() functionDefined = success if functionDefined: plotFunc(func, color="blue") def onClear(): global functionDefined drawArea.delete("all") drawGrid() functionText.set("") functionDefined = False drawButton.configure(command = onDraw) clearButton.configure(command = onClear) def redraw(): drawArea.delete("all") drawGrid() if functionDefined: plotFunc(func, color="blue") def onConfigure(e): redraw() drawArea.bind("", onConfigure) functionEntry.bind("", lambda e: onDraw()) def onMouseWheel(e): '''Process MouseWheel event: scale a picture''' # print(e) global SCALEX, SCALEY increment = 0 scaleFactor = 1.2 # Respond to Linux or Windows/MacOS wheel event if e.num == 4 or e.delta > 0: increment = 1 elif e.num == 5 or e.delta < 0: increment = (-1) if increment > 0: SCALEX *= scaleFactor; SCALEY = SCALEX redraw() elif increment < 0: if SCALEX <= 8: return SCALEX /= scaleFactor; SCALEY = SCALEX redraw() drawArea.bind("", onMouseWheel) # for Windows/MacOS drawArea.bind("", onMouseWheel) # for Linux (scroll up) drawArea.bind("", onMouseWheel) # for Linux (scroll down) root.update() drawGrid() functionText.set("sin(x)") onDraw() root.mainloop() if __name__ == "__main__": main()