In general, there is a program, its idea is that when you press the button, the argument t grows each time and the figure is based on other data. However, python does not allow changing a variable in a function unless its value is fixed in the same function. Tell me, can I somehow get around this limitation? If not, how else can one realize body movement according to given equations? Speech about the function but.

from Tkinter import * from math import * root=Tk() root.geometry('760x710') canv=Canvas(root,width=700,height=700,bg="white") canv.place(x=0, y=0) k = 3 r = 3 t = 0 def but(event): x = r*(k-1)*(cos(t)+(cos((k-1)*t))/(k-1)) y = r*(k-1)*(sin(t)-(sin((k-1)*t))/(k-1)) canv.create_oval(x-5+355,y-5+355,x+5+355,y+5+355,fill="red") t += 0.1 button1=Button(root,text='start',width=3,height=4,bg='gold',fg='black', font=10) button1.place(x = 720, y = 355) button1.bind("<Button-1>",but) root.mainloop() 

    2 answers 2

    Read about areas of visibility. Article in Russian . The global operator will help you.

    Try:

     def but(event): global t x = r*(k-1)*(cos(t)+(cos((k-1)*t))/(k-1)) y = r*(k-1)*(sin(t)-(sin((k-1)*t))/(k-1)) canv.create_oval(x-5+355,y-5+355,x+5+355,y+5+355,fill="red") t += 0.1 

    Full example with minor changes:

     # import tkinter as tk import Tkinter as tk from math import cos, sin root = tk.Tk() root.geometry('760x710') canv = tk.Canvas(root, width=700, height=700, bg="white") canv.place(x=0, y=0) k = 15 r = 15 t = 0.0 def but(event): global t for _ in range(100): x = r * (k - 1) * (cos(t) + (cos((k - 1) * t)) / (k - 1)) y = r * (k - 1) * (sin(t) - (sin((k - 1) * t)) / (k - 1)) canv.create_oval(x - 5 + 355, y - 5 + 355, x + 5 + 355, y + 5 + 355, fill="red") t += 0.1 button1 = tk.Button(root, text='start', width=3, height=4, bg='gold', fg='black', font=10) button1.place(x=720, y=355) button1.bind("<Button-1>", but) root.mainloop() 

    Result after a few clicks:

    enter image description here


    Here is the rewritten code with the increment function via the closure proposed by jfs :

    • in this case, you can use closure (of type make_counter () ). Or the object self.t += .1 - jfs
    • I meant something like button.bind("<Button-1>", make_handler(t=0.0)) - jfs
    • @jfs, can you show your answer with this? :) - gil9red

    To click on the screen to move the circle according to a given formula, the simplest way is to use the turtle module :

     #!/usr/bin/env python import turtle from math import sin, cos def onclick(x, y, t=[0], k=15, r=15): turtle.setposition( x=r * (k - 1) * (cos(t[0]) + (cos((k - 1) * t[0])) / (k - 1)), y=r * (k - 1) * (sin(t[0]) - (sin((k - 1) * t[0])) / (k - 1))) t[0] += .1 turtle.shape('circle') turtle.color('red') turtle.onscreenclick(onclick) turtle.mainloop() 

    since int is immutable in Python, a single-item list is used to increase the time with each click. This is equivalent to using a global variable (with each call the same list changes).

    circle goes in ~ circles

    Closure

    In general, mutable global variables are best avoided. For example, if several circles with different parameters simultaneously move, you can use closure to save these parameters:

     #!/usr/bin/env python3 import turtle from math import sin, cos def move_turtle(turtle, *, t=0, k=15, r=15): def onclick(x, y): nonlocal t turtle.setposition( x=r * (k - 1) * (cos(t) + (cos((k - 1) * t)) / (k - 1)), y=r * (k - 1) * (sin(t) - (sin((k - 1) * t)) / (k - 1))) t += .1 return onclick for t0 in range(4): turtle.onscreenclick(move_turtle(turtle.Turtle(), t=t0), add=True) turtle.mainloop() 

    several turtles moves

    Object

    To save the general settings, you can use your object:

     #!/usr/bin/env python3 import turtle from math import sin, cos class TimeTurtle(turtle.Turtle): def __init__(self, t=0, *, color='red'): super().__init__() self.t = t self.shape('circle') self.color(color) def move(self, x, y, k=15, r=15): t = self.t self.setposition( x=r * (k - 1) * (cos(t) + (cos((k - 1) * t)) / (k - 1)), y=r * (k - 1) * (sin(t) - (sin((k - 1) * t)) / (k - 1))) self.t += .1 for t0 in range(4): turtle.onscreenclick(TimeTurtle(t0).move, add=True) turtle.mainloop() 

    red circles moves


    For the code in question, if you intend to move one circle at a click rather than re-create, you can Canvas.move method:

     #!/usr/bin/env python3 import tkinter as tk from math import cos, sin class Oval: def __init__(self, canvas): self.t = 0 self.canvas = canvas x, y = self.coords() self.oval_id = canvas.create_oval( x - 5 + 355, y - 5 + 355, x + 5 + 355, y + 5 + 355, fill="red") def coords(self, k=3, r=3): t = self.t return (r * (k - 1) * (cos(t) + (cos((k - 1) * t)) / (k - 1)), # x r * (k - 1) * (sin(t) - (sin((k - 1) * t)) / (k - 1))) # y def move(self): self.canvas.move(self.oval_id, *self.coords()) self.t += 0.1 root = tk.Tk() root.geometry('760x710') canvas = tk.Canvas(root, width=700, height=700, bg="white") canvas.place(x=0, y=0) button1 = tk.Button(root, text='start', width=3, height=4, bg='gold', fg='black', font=10) button1.place(x=720, y=355) o = Oval(canvas) button1.bind("<Button-1>", lambda e: o.move()) root.mainloop() 

    This is the minimally modified code from the question to which the Oval class is added and is shown how the move() method can be implemented (the code does not concern other possible problems).