Search code examples
pythontkinterinputtrigonometrytkinter-canvas

How can I make the line in Tkinter Python stay where it is without moving when the mouse updates


I have been testing some code to implement into a final project I have for a computer science class in a separate project (so ignore the poorly made movement code, I have better), and the basic final idea is to make a circle that can move around a window, with a line in the middle, and the line can follow the mouse and shoot out bullets when the mouse is clicked to kill enemies. I am using python with Tkinter. The main problem I am encountering currently, is that when I try to move the line with canvas.move, and then update the mouse, it teleports back to the starting position in the middle of the screen. Without updating the mouse, it moves fine, but it also keeps on in its original position without pointing towards the mouse in its current position. I am pretty new to Tkinter, and am even somewhat new to Python, but I have a decent amount of coding experience, especially in Java, so if possible explain any Tkinter concepts as if I have no idea about them, but I'm pretty confident with coding knowledge in general. Here is the code for you to help me with:

# importing tkinter and math for their porpoises
from tkinter import *
import math

# Declaring vars, width and height for the window and canvas, starting x and y for the line, line length, and default staring mouse positions for the equations that kinda but not really break without this idk it's weird
w = 600
h = 600
x, y = w//2, h//2
dist = 30
mousex, mousey = 0, 0

# tkinter setup
root = Tk()
root.title("Line Tests")
root.geometry("{}x{}".format(w, h))
photo = PhotoImage(file="icon.png")
root.iconphoto(False, photo)
root.resizable(False, False)
canvas = Canvas(root, bg="white", width=w, height=h)
canvas.pack()
# making the line
line = canvas.create_line(x, y, x, y+30, fill="red", width="5")

# movement functions
def left(event):
    x = -10
    y = 0
    canvas.move(line, x, y)
def down(event):
    x = 0
    y = -10
    canvas.move(line, x, y)
def right(event):
    x = 10
    y = 0
    canvas.move(line, x, y)
def up(event):
    x = 0
    y = 10
    canvas.move(line, x, y)
def move(event):
    # sets the global variables to x and y pos of the mouse
    mousex, mousey = event.x, event.y
    print('{}, {}'.format(mousex, mousey))
    # equation explanation here: https://www.desmos.com/calculator/gb1udr257b
    canvas.coords(line, x, y, x+math.cos(math.atan((mousey-y)/(mousex-x)))*((mousex-x)/abs(mousex-x))*dist, y+math.sin(math.atan((mousey-y)/(mousex-x)))*((mousex-x)/abs(mousex-x))*dist)

# it's just root binding different functions to events (keys, mouse movement)
root.bind('<Motion>', move)
root.bind('<Up>', up)
root.bind('<Down>', down)
root.bind('<Left>', left)
root.bind('<Right>', right)

# loops the script
root.mainloop()

If you want a demonstration and a bad explanation of the equation I used for finding the end point of the line to make it not hit the mouse, the desmos link is here

I tried changing canvas.move to canvas.coords, but whenever I pressed a key, it just made the line disappear. e.g. I changed

def left(event):
    x = -10
    y = 0
    canvas.move(line, x, y)

to

def left(event):
    x = -10
    y = 0
    canvas.coords(line, x, y, x+math.cos(math.atan((mousey-y)/(mousex-x)))*((mousex-x)/abs(mousex-x))*dist, y+math.sin(math.atan((mousey-y)/(mousex-x)))*((mousex-x)/abs(mousex-x))*dist)
    # The same equation as before (to find the ending point of the line, not just drawing it to the mouse)

I still believe something like this is still the solution, or possibly something with canvas.itemconfig, but I can't seem to figure it out.


Solution

  • We need to add new x, y coordinates to the move function after shifting the line

    x, y = canvas.coords(line)[:2].

    And be sure to check mousex-x == 0 to eliminate errors when dividing.