Up

Moving Figures

Python, Tkinter, Frame, Canvas

 

Script in Listing 1 displays figures, which are moving in the canvas (Figure 1).

Figure 1

 

Listing 1 Script for moving figures.

 

import tkinter as tk

import numpy as np

 

root = tk.Tk()

 

frame = tk.Frame(root)

 

button = tk.Button(frame, text = 'Close')

button.grid(row = 0, column = 10, padx = 5, sticky = 'e')

 

def f_close(event):

    """

        A call_back or event handler function

    """

    root.destroy()

    print('Event...\n{}'.format(event))

    print('Good-bye.')

 

#

#       Bind the event of clicking on the button to the function f_close

#

button.bind('<Button-1>', f_close) 

 

canvas = tk.Canvas(frame, bg = '#ffffff', width = 500, height = 500)

canvas.grid(row = 1, columnspan = 11, rowspan = 1)

 

frame.pack()

 

ctr_x = 250     #   x-coordinate of the center of the canvas

ctr_y = 250     #   y-coordinare of the center of the canvas

 

r0 = 120        #   radius of the large disc

 

#       Angle of rotation

class CAngle:

    def __init__(self):

        self.angle = 0.0

 

cangle = CAngle()

 

#       A large green disc created at the center

canvas.create_oval(ctr_x - r0, ctr_y - r0, ctr_x + r0, ctr_y + r0,

                   fill = '#00ff00', tags = 'circle0')

 

r1 = 20         #   The radius of the small disc

r = r0 + r1     #   The radius of the circle of the trajectory of the small disc

 

x0 = ctr_x + r * np.cos(cangle.angle)  #   The x-coordinate of the small disc

y0 = ctr_y + r * np.sin(cangle.angle)  #   The y-coordinate of the small disc

#       The small blue disc created 

canvas.create_oval(x0 - r1, y0 - r1, x0 + r1, y0 + r1, fill = '#0000ff', tags = 'circle1')

 

#

#   Creating moving discs

#

def re_draw(cangle):

    #   Incrementing the angle, which is kept not to be larger than 2*pi

    cangle.angle += 0.1

    if cangle.angle > 2 * np.pi:

        cangle.angle -= 2 * np.pi

 

    #   The large disc is rotated by the amount of v_ang

    scale = np.cos(cangle.angle)

    #   Delete the current disc

    canvas.delete('circle0')

    #   Create the new disc

    canvas.create_oval(ctr_x - scale * r0, ctr_y - r0,

                      ctr_x + scale * r0, ctr_y + r0,

                      fill = '#00ff00', tags = 'circle0')

    #

    #   Get the current position of the small disc

    #

    coords = canvas.coords('circle1')

    #   coords = [x1, y1, x2, y2], which are the corners of the box enclosing the disc

    crrntx = (coords[0] + coords[2]) / 2    #   the x-coordinate of the center of the disc

    crrnty = (coords[1] + coords[3]) / 2    #   the y-coordinate of the center of the disc

 

    nxtx = ctr_x + r * np.cos(cangle.angle)        #   the new x-coordinate

    nxty = ctr_y + r * np.sin(cangle.angle)        #   the new y-coordinate

    #   Move the disc from the current to the new position

    canvas.move('circle1', nxtx - crrntx, nxty - crrnty)

   

    root.after(100, re_draw, cangle)         #   Call this function after 100msec  

 

 

re_draw(cangle)

 

root.mainloop()

 

 

 

The large green disc is moved by deleting the current one and re-creating a new one.

    canvas.delete('circle0')

    canvas.create_oval(ctr_x - scale * r0, ctr_y - r0,

                      ctr_x + scale * r0, ctr_y + r0,

                      fill = '#00ff00', tags = 'circle0')

 

The small blue disc is moved by the method canvas.move, which is executed by calling the function re_draw.

    canvas.move('circle1', nxtx - crrntx, nxty - crrnty)

 

These movements are repeated by calling the function after as follows.

    root.after(100, re_draw, cangle)

The above code sets the function re_draw(cangle) to be executed after 100 msec.

 

Figure 2

 

The form in Figure 2 has three buttons, Clockwise, Counter, and Stop, adding to Close button.  When you click on these buttons, the direction of rotation changes or the movement stops. The script for Figure 2 is developed from the script of Listing 1, adding a few codes with minor changes.  The complete script for Figure 2 is shown in listing 2 as follows.

 

Listing 2  Script for the window of Figure 2

 

import tkinter as tk

import numpy as np

 

root = tk.Tk()

 

frame = tk.Frame(root)

 

button = tk.Button(frame, text = 'Close')

button.grid(row = 0, column = 10, padx = 5, sticky = 'ew')

buttonC = tk.Button(frame, text = 'Clockwise')

buttonC.grid(row = 0, column = 0, padx = 2, sticky = 'ew')

buttonA = tk.Button(frame, text = 'Counter')

buttonA.grid(row = 0, column = 1, padx = 2, sticky = 'ew')

buttonS = tk.Button(frame, text = 'Stop')

buttonS.grid(row = 0, column = 2, padx = 2, sticky = 'ew')

 

def f_close(event):

    """

        A call_back or event handler function

    """

    root.destroy()

    print('Event...\n{}'.format(event))

    print('Good-bye.')

 

#

#       Bind the event of clicking on the button to the function f_close

#

button.bind('<Button-1>', f_close)

 

global step         #   The amount of change in angle for iteration

step = 0.1

 

def f_clock(event):

    global step

    step = 0.1      #   clockwise rotation

 

buttonC.bind('<Button-1>', f_clock)

 

def f_counter(event):

    global step

    step = -0.1     #   counter clockwise rotation

 

buttonA.bind('<Button-1>', f_counter)

 

def f_stop(event):

    global step

    step = 0.0      #   no rotation

 

buttonS.bind('<Button-1>', f_stop)

 

canvas = tk.Canvas(frame, bg = '#ffffff', width = 500, height = 500)

canvas.grid(row = 1, columnspan = 11, rowspan = 1)

 

frame.pack()

 

ctr_x = 250     #   x-coordinate of the center of the canvas

ctr_y = 250     #   y-coordinare of the center of the canvas

 

r0 = 120        #   radius of the large disc

 

#   A class of rotation angle

class CAngle:

    def __init__(self):

        self.angle = 0.0

 

cangle = CAngle()       #   The object of CAngle

 

#       A large green disc created at the center

canvas.create_oval(ctr_x - r0, ctr_y - r0, ctr_x + r0, ctr_y + r0,

                   fill = '#00ff00', tags = 'circle0')

 

r1 = 20         #   The radius of the small disc

r = r0 + r1     #   The radius of the circle of the trajectory of the small disc

 

x0 = ctr_x + r * np.cos(cangle.angle)  #   The x-coordinate of the small disc

y0 = ctr_y + r * np.sin(cangle.angle)  #   The y-coordinate of the small disc

#       The small blue disc created 

canvas.create_oval(x0 - r1, y0 - r1, x0 + r1, y0 + r1, fill = '#0000ff', tags = 'circle1')

 

#

#   Creating moving discs

#

def re_draw(cangle):

    global step

    #

    #       The next angle is set

    #

    cangle.angle += step

    if cangle.angle > 2 * np.pi:    #   angle is kept not to be larger than 2 * pi

        cangle.angle -= 2 * np.pi

    if cangle.angle < -2 * np.pi:

        cangle.angle += 2 * np.pi   #   angle is kept not to be smaller than -2 * pi

 

    #   The large disc is rotated by the amount of v_ang

    scale = np.cos(cangle.angle)

    #   Delete the current disc

    canvas.delete('circle0')

    #   Create the new disc

    canvas.create_oval(ctr_x - scale * r0, ctr_y - r0,

                      ctr_x + scale * r0, ctr_y + r0,

                      fill = '#00ff00', tags = 'circle0')

    #

    #   Get the current position of the small disc

    #

    coords = canvas.coords('circle1')

    #   coords = [x1, y1, x2, y2], which are the corners of the box enclosing the disc

    crrntx = (coords[0] + coords[2]) / 2    #   the x-coordinate of the center of the disc

    crrnty = (coords[1] + coords[3]) / 2    #   the y-coordinate of the center fo the disc

 

    nxtx = ctr_x + r * np.cos(cangle.angle)        #   the new x-coordinate

    nxty = ctr_y + r * np.sin(cangle.angle)        #   the new y-coordinate

    #   Move the disc from the current to the new position

    canvas.move('circle1', nxtx - crrntx, nxty - crrnty)

   

    root.after(100, re_draw, cangle)         #   Call this function after 100msec  

 

 

re_draw(cangle)

 

root.mainloop()

 

 

The script of Listing 2 has three more buttons than that of Listing1 as follows

buttonC = tk.Button(frame, text = 'Clickwise')

buttonC.grid(row = 0, column = 0, padx = 2, sticky = 'ew')

buttonA = tk.Button(frame, text = 'Counter')

buttonA.grid(row = 0, column = 1, padx = 2, sticky = 'ew')

buttonS = tk.Button(frame, text = 'Stop')

buttonS.grid(row = 0, column = 2, padx = 2, sticky = 'ew')

These buttons are bound to the functions, which control rotation by setting the amount of change in rotation angle.

def f_clock(event):

    global step

    step = 0.1      #   clockwise rotation

 

buttonC.bind('<Button-1>', f_clock)

 

def f_counter(event):

    global step

    step = -0.1     #   counter clockwise rotation

 

buttonA.bind('<Button-1>', f_counter)

 

def f_stop(event):

    global step

    step = 0.0      #   no rotation

 

buttonS.bind('<Button-1>', f_stop)

 

 

Up