How to find distance traveled Tkinter ButtonPress - python

I can track where the user clicks and where they release but I want to track distance traveled.
from Tkinter import *
root = Tk()
class DragCursor():
def __init__(self, location):
self.label = location
location.bind('<ButtonPress-1>', self.StartMove)
location.bind('<ButtonRelease-1>', self.StopMove)
def StartMove(self, event):
startx = event.x
starty = event.y
print [startx, starty]
def StopMove(self, event):
self.StartMove
stopx = event.x
stopy = event.y
print [stopx, stopy]
location = Canvas(root, width = 300, height = 300)
DragCursor(location)
location.pack()
root.mainloop()

You just need to use the distance formula for determining the distance between two points in an xy-plane,
Also, you need to include some kind of instance variable that will save the coordinates for the start and end points so that you can compute it after the mouse release.
This is pretty much your code just with a new distancetraveled function that is printed at the end of StopMove using self.positions.
from Tkinter import *
root = Tk()
class DragCursor():
def __init__(self, location):
self.label = location
location.bind('<ButtonPress-1>', self.StartMove)
location.bind('<ButtonRelease-1>', self.StopMove)
self.positions = {}
def StartMove(self, event):
startx = event.x
starty = event.y
self.positions['start'] = (startx, starty)
def StopMove(self, event):
stopx = event.x
stopy = event.y
self.positions['stop'] = (stopx, stopy)
print self.distancetraveled()
def distancetraveled(self):
x1 = self.positions['start'][0]
x2 = self.positions['stop'][0]
y1 = self.positions['start'][1]
y2 = self.positions['stop'][1]
return ((x2-x1)**2 + (y2-y1)**2)**0.5
location = Canvas(root, width = 300, height = 300)
DragCursor(location)
location.pack()
root.mainloop()

Related

How to move a shape while retaining original size in tkinter

I am trying to have the rectangle I create spawn at the bottom of my tkinter canvas instead of spawning at the top without affecting its size. I have only found this post and this doesn't help me as it does not describe how to move the shape to the bottom of the canvas
Here's my code:
from tkinter import *
from tkinter.ttk import *
#Temporary
width = 1024
height=768
class GFG:
def __init__(self, master = None):
self.master = master
self.master.configure(bg="black") #Apart from the canvas colour
self.x = 0
self.y = 0
self.canvas = Canvas(master, background="black", highlightthickness=0, width=width, height=height) #Canvas colour
self.rectangle = self.canvas.create_rectangle(5, 5, 25, 25, fill='white')
self.canvas.pack()
self.movement()
def movement(self):
self.canvas.move(self.rectangle, self.x, self.y)
self.canvas.after(100, self.movement)
def left(self, event):
print(event.keysym)
self.x = -5
self.y = 0
def right(self, event):
print(event.keysym)
self.x = 5
self.y = 0
if __name__ == "__main__":
master = Tk()
master.title("Black Hole Settlers")
gfg = GFG(master)
master.bind("<KeyPress-Left>", lambda e: gfg.left(e))
master.bind("<KeyPress-Right>", lambda e: gfg.right(e))
mainloop()
Pastebin
Thanks,
swanserquack

Change the position of a frame in Tkinter, Involving Classes and functions

I'm trying to move a frame on my GUI, but I am unable to do so because of my classes. How can I move the frame2 with a
Slider1.SetVal(x=20, y=30)
while keeping my classes.
My Code
class Slider:
def __init__ (self,Name, x, y, Height, Width, KnobHeight, KnobWidth, LowVal, HighVal, BgColor, FgColor):
self.Name = Name
self.x = x
self.y = y
self.Height = Height
self.Width = Width
self.KnobHeight = KnobHeight
self.KnobWidth = KnobWidth
self.LowVal = LowVal
self.HighVal = HighVal
self.BgColor = BgColor
self.FgColor = FgColor
def Display(self):
frame = Frame(self.Name, width=self.Width, height=self.Height, colormap="new", bg=self.BgColor)
frame.place(x=self.x, y=self.y)
frame2 = Frame(self.Name, width=self.KnobWidth, height=self.KnobHeight, colormap="new", bg=self.FgColor)
frame2.place(x=(self.x + self.Width/2 - self.KnobWidth/2), y=(self.Height - self.KnobHeight/2 + self.y))
def SetVal(self):
Main.update()
frame2.place(x=33, y=5)
window1 = Window(Main)
Slider1 = Slider(Main,100,60,200,15,15,35,0,80,"White","Green")
Slider1.Display()
Slider1.SetVal(3,30)
Main.mainloop()

How to fix key bindings in tkinter

I want to make a sprite move around on the canvas and have tried to use key bindings as controls. When I run the program it doesn't move until I try the correct key. I've tested with mouse buttons and it works fine.
adding code:
from tkinter import *
class MainGame:
def __init__(self):
self.grid = [[""]*15 for n in range(15)]
self.grid[14][3] = 1
print(self.grid)
self.canvas = Canvas(root, width = 900, height = 900)
self.canvas.pack()
self.a, self.b = 45, 175
def noreaction(self, event):
print("It clicked")
print(self.a)
self.a += 50
self.b += 50
self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p2Tag")
self.canvas.delete("p1Tag")
self.canvas.tag_bind("p2Tag", "<Key-q>", self.noreaction)
def run(self):
self.pl = PhotoImage(file = "player.png")
self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p1Tag")
self.canvas.tag_bind("p1Tag", "<Key>", self.noreaction)
self.x0, self.y0, self.x1, self.y1 = -30, 150, 20, 200
for self.row in self.grid:
for self.column in self.row:
self.x0 += 50
self.x1 += 50
self.cell = self.canvas.create_rectangle(self.x0, self.y0, self.x1, self.y1)
self.y0 += 50
self.y1 += 50
self.x0 = -30
self.x1 = 20
root = Tk()
root.focus_set()
obj = MainGame()
obj.run()
root.mainloop()
After messing with your code for a bit it would seam that tag_bind only works with mouse clicks. After reviewing all the documentation I could find in tag_bind there is nothing that says you can bind a key to a drawn object. So the solution here would be to bind either the root window to the key or to bind the frame the canvas is placed in.
I have changed up your code a little to make this work but it should help you move in the right direction. Without more information as to what you are trying to accomplish in the long run its hard to provide a good answer but I think this will help. Let me know if you have any more questions.
from tkinter import *
class MainGame:
def __init__(self, parent):
self.parent = parent
self.grid = [[""]*15 for n in range(15)]
self.grid[14][3] = 1
self.canvas = Canvas(self.parent, width = 900, height = 900)
self.canvas.pack()
self.a, self.b = 45, 175
#added an argument here so we can better control the player object.
def noreaction(self, old):
print("It clicked")
print(self.a)
self.a += 50
self.b += 50
self.canvas.delete(old)
# assigned the object to a class attribute
self.player_obj = self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p2Tag")
# you will see <Key-q> and <q> work the same here.
self.parent.bind("<Key-q>", lambda x: self.noreaction(self.player_obj))
def run(self):
self.pl = PhotoImage(file = "./Colors/blk.gif")
# assigned the object to a class attribute
self.player_obj = self.canvas.create_image(self.a, self.b, image = self.pl, tags = "p1Tag")
# you will see <Key-q> and <q> work the same here.
self.parent.bind("<q>", lambda x: self.noreaction(self.player_obj))
self.x0, self.y0, self.x1, self.y1 = -30, 150, 20, 200
for self.row in self.grid:
for self.column in self.row:
self.x0 += 50
self.x1 += 50
self.cell = self.canvas.create_rectangle(self.x0, self.y0, self.x1, self.y1)
self.y0 += 50
self.y1 += 50
self.x0 = -30
self.x1 + 20
root = Tk()
root.focus_set()
obj = MainGame(root)
obj.run()
root.mainloop()
"<Key>" is not a valid keysym to bind an event on. Try a valid one, for a full list you can look here http://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm
edit: appears I was mistaken, didn't know it was a valid event. You learn something every day :)

Tkinter Canvas : Scale on moving object

I do not know if my question is a stupid one or a tricky one.
So, using Tkinter and Canvas, I succeed to implement a scrolling/zooming function that work perfectly, thanks to this post Move and zoom a tkinter canvas with mouse. I also add a binding to resize the canvas size when the window size change without trouble.
Using coords and after, I have no trouble to move object around.
The trouble came when I tried to combine everything.
Moving and scrolling : no trouble
Scrolling and Zooming : ok
Zooming, moving and scrolling : do not work
The code bellow reproduce the trouble (python 2.7, work on windows). For what I can see, the trouble come from the scaling, maybe caused by the change of coords of the objects, that induce the canvas resizing, and then disable the scaling? If it is the case, I need help to solve this issue. If it is not the case, I need help to found the issue...
By removing/disable the line self.master.after(50, self.Display), moving do no occur anymore.
import Tkinter as tk
import math
class Example:
def __init__ (self, master):
self.master = master
self.interval = 0
self.SizeX, self.SizeY = master.winfo_width(), master.winfo_height()
#Canvas Frame
self.SystemCanvasFrame = tk.Frame(master, bg='black')
self.SystemCanvasFrame.grid(row=0, column=0)
#Canvas
self.SystemCanvas = tk.Canvas(self.SystemCanvasFrame, width=int(self.SizeX*0.75)-20, height=self.SizeY-20, bg="black")
self.SystemCanvas.focus_set()
self.xsb = tk.Scrollbar(self.SystemCanvasFrame, orient="horizontal", command=self.SystemCanvas.xview)
self.ysb = tk.Scrollbar(self.SystemCanvasFrame, orient="vertical", command=self.SystemCanvas.yview)
self.SystemCanvas.configure(scrollregion=(-500,-500,500,500))
self.SystemCanvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set)
#add the canvas with scroll bar in grid format
self.xsb.grid(row=1, column=0, sticky="ew")
self.ysb.grid(row=0, column=1, sticky="ns")
self.SystemCanvas.grid(row=0, column=0, sticky="nsew")
# This is what enables using the mouse to slide the window:
self.SystemCanvas.bind("<ButtonPress-1>", self.move_start)
self.SystemCanvas.bind("<B1-Motion>", self.move_move)
#windows scroll
self.SystemCanvas.bind("<MouseWheel>",self.zoomer)
#resize the main window
self.master.bind('<Configure>', self.UpdateCanvasSize)
#Create Objects
self.Size = 5 #object Size
x0 = 0
y0 = 0
x1 = self.Size
y1 = self.Size
self.SystemCanvas.create_oval(x0,y0,x1,y1, fill='green', outline='green', width=3, tags='Green')
self.SystemCanvas.create_oval(x0,y0,x1,y1, fill='red', outline='red', width=3, tags='Red')
self.SystemCanvas.create_oval(x0,y0,x1,y1, fill='yellow', outline='yellow', width=1, tags='Yellow')
self.Display()
def Display(self):
self.interval += 0.5 #speed parameter
GreenPos = self.UpdatePosition(0.1*self.interval, (0,0), 50)
RedPos = self.UpdatePosition(0.02*self.interval+180, (0,0), 200)
YellowPos = self.UpdatePosition(0.3*self.interval, RedPos, 10)
self.MoveObject('Green', GreenPos)
self.MoveObject('Red', RedPos)
self.MoveObject('Yellow', YellowPos)
self.master.after(50, self.Display) #Disable to zoom
def MoveObject (self, Obj, pos): #only move object that are in the field of view
"""Move Obj to the given position (tuple - xy)"""
ID = self.SystemCanvas.find_withtag(Obj)
#Convert the Center of the object to the coo need for tk
x0 = pos[0] - self.Size/2.0 #radius of the circle
y0 = pos[1] - self.Size/2.0
x1 = pos[0] + self.Size/2.0
y1 = pos[1] + self.Size/2.0
self.SystemCanvas.coords(ID, x0,y0,x1,y1)
def UpdatePosition(self, angle, center, distance):
"""Calculate next object position around the Center at the Distance and speed determine by Angle (in Radian) - Center of the object"""
h = center[0]
k = center[1]
radius = distance
Rad = angle
x = h+radius*math.cos(Rad)
y = k+radius*math.sin(Rad)
return (x, y)
def UpdateCanvasSize(self, event):
"""Permit to resize the canvas to the window"""
self.SizeX, self.SizeY = self.master.winfo_width(), self.master.winfo_height()
self.SystemCanvas.config(width=int(self.SizeX*0.75)-20, height=self.SizeY-20)
def move_start(self, event):
"""Detect the beginning of the move"""
self.SystemCanvas.scan_mark(event.x, event.y)
self.SystemCanvas.focus_set() #security, set the focus on the Canvas
def move_move(self, event):
"""Detect the move of the mouse"""
self.SystemCanvas.scan_dragto(event.x, event.y, gain=1)
def zoomer(self,event):
"""Detect the zoom action by the mouse. Zoom on the mouse focus"""
true_x = self.SystemCanvas.canvasx(event.x)
true_y = self.SystemCanvas.canvasy(event.y)
if (event.delta > 0):
self.SystemCanvas.scale("all", true_x, true_y, 1.2, 1.2)
elif (event.delta < 0):
self.SystemCanvas.scale("all", true_x, true_y, 0.8, 0.8)
self.SystemCanvas.configure(scrollregion = self.SystemCanvas.bbox("all"))
if __name__ == '__main__':
root = tk.Tk()
root.geometry('1125x750')
app = Example(root)
root.mainloop()
I'm new to Tkinter so this might not be the most elegant solution but I hope it gives you an idea on how to solve the problem.
The zoomer method scales your coordinates but these coordinates are reset anytime you call MoveObject or UpdatePosition. I added code that keeps track of the scale factor, self.scale, and a method update_coord that scales a given coordinate based on the scale factor. Finally, I called update_coord in the MoveObject and UpdatePosition methods.
Here is the working code;
import Tkinter as tk
import math
class Example:
def __init__ (self, master):
self.scale = 1 #Added
self.master = master
self.interval = 0
self.SizeX, self.SizeY = master.winfo_width(), master.winfo_height()
#Canvas Frame
self.SystemCanvasFrame = tk.Frame(master, bg='black')
self.SystemCanvasFrame.grid(row=0, column=0)
#Canvas
self.SystemCanvas = tk.Canvas(self.SystemCanvasFrame, width=int(self.SizeX*0.75)-20, height=self.SizeY-20, bg="black")
self.SystemCanvas.focus_set()
self.xsb = tk.Scrollbar(self.SystemCanvasFrame, orient="horizontal", command=self.SystemCanvas.xview)
self.ysb = tk.Scrollbar(self.SystemCanvasFrame, orient="vertical", command=self.SystemCanvas.yview)
self.SystemCanvas.configure(scrollregion=(-500,-500,500,500))
self.SystemCanvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set)
#add the canvas with scroll bar in grid format
self.xsb.grid(row=1, column=0, sticky="ew")
self.ysb.grid(row=0, column=1, sticky="ns")
self.SystemCanvas.grid(row=0, column=0, sticky="nsew")
# This is what enables using the mouse to slide the window:
self.SystemCanvas.bind("<ButtonPress-1>", self.move_start)
self.SystemCanvas.bind("<B1-Motion>", self.move_move)
#windows scroll
self.SystemCanvas.bind("<MouseWheel>",self.zoomer)
#resize the main window
self.master.bind('<Configure>', self.UpdateCanvasSize)
#Create Objects
self.Size = 5 #object Size
x0 = 0
y0 = 0
x1 = self.Size
y1 = self.Size
self.SystemCanvas.create_oval(x0,y0,x1,y1, fill='green', outline='green', width=3, tags='Green')
self.SystemCanvas.create_oval(x0,y0,x1,y1, fill='red', outline='red', width=3, tags='Red')
self.SystemCanvas.create_oval(x0,y0,x1,y1, fill='yellow', outline='yellow', width=1, tags='Yellow')
self.Display()
#**Added Method
def update_coord(self, coord):
"""Calculate the scaled cordinate for a given cordinate based on the zoomer scale factor"""
new_coord = [coord_i * self.scale for coord_i in coord]
return new_coord
def Display(self):
self.interval += 0.5 #speed parameter
GreenPos = self.UpdatePosition(0.1*self.interval, (0,0), 50)
RedPos = self.UpdatePosition(0.02*self.interval+180, (0,0), 200)
YellowPos = self.UpdatePosition(0.3*self.interval, RedPos, 10)
self.MoveObject('Green', GreenPos)
self.MoveObject('Red', RedPos)
self.MoveObject('Yellow', YellowPos)
self.master.after(1, self.Display) #Disable to zoom
def MoveObject (self, Obj, pos): #only move object that are in the field of view
"""Move Obj to the given position (tuple - xy)"""
ID = self.SystemCanvas.find_withtag(Obj)
#Convert the Center of the object to the coo need for tk
x0 = pos[0] - self.Size/2.0 #radius of the circle
y0 = pos[1] - self.Size/2.0
x1 = pos[0] + self.Size/2.0
y1 = pos[1] + self.Size/2.0
c_0 = self.update_coord([x0, y0]) #Added
c_1 = self.update_coord([x1, y1]) #Added
self.SystemCanvas.coords(ID, c_0[0], c_0[1], c_1[0], c_1[1]) #Added/Edited
def UpdatePosition(self, angle, center, distance):
"""Calculate next object position around the Center at the Distance and speed determine by Angle (in Radian) - Center of the object"""
h = center[0]
k = center[1]
radius = distance
Rad = angle
x = h+radius*math.cos(Rad)
y = k+radius*math.sin(Rad)
return self.update_coord([x, y]) #Added/Edited
def UpdateCanvasSize(self, event):
"""Permit to resize the canvas to the window"""
self.SizeX, self.SizeY = self.master.winfo_width(), self.master.winfo_height()
self.SystemCanvas.config(width=int(self.SizeX*0.75)-20, height=self.SizeY-20)
def move_start(self, event):
"""Detect the beginning of the move"""
self.SystemCanvas.scan_mark(event.x, event.y)
self.SystemCanvas.focus_set() #security, set the focus on the Canvas
def move_move(self, event):
"""Detect the move of the mouse"""
self.SystemCanvas.scan_dragto(event.x, event.y, gain=1)
def zoomer(self,event):
"""Detect the zoom action by the mouse. Zoom on the mouse focus"""
true_x = self.SystemCanvas.canvasx(event.x)
true_y = self.SystemCanvas.canvasy(event.y)
if (event.delta > 0):
self.SystemCanvas.scale("all", true_x, true_y, 1.2, 1.2)
self.scale *= 1.2 #**Added
elif (event.delta < 0):
self.SystemCanvas.scale("all", true_x, true_y, 0.8, 0.8)
self.scale *= 0.8 #**Added
#self.SystemCanvas.configure(scrollregion = self.SystemCanvas.bbox("all")) #**Removed (This disables scrollbar after zoom)
if __name__ == '__main__':
root = tk.Tk()
root.geometry('1125x750')
app = Example(root)
root.mainloop()

simple Tk application - draw on the click of a button

Can anyone tell me why my 'addLine' method is failing to call when the self.rec button is clicked?
from tkinter import *
from tkinter import ttk
root = Tk()
class Paint:
def __init__(self, parent):
self.parent = parent
self.whiteBoard = Canvas(self.parent)
self.whiteBoard.grid(column=0, row=0, sticky=(N,W,E,S))
self.lastx = 0
self.lasty = 0
self.rec = self.whiteBoard.create_rectangle((10, 10, 30, 30), fill="red")
self.whiteBoard.tag_bind(self.rec, "<Button-1>", self.getClick)
def xy(self, event):
self.lastx, self.lasty = event.x, event.y
print (event.x, " is the x coordinate")
print (event.y, " is the y coordinate")
def addLine(self, event):
canvas.create_line((lastx, lasty, event.x, event.y))
self.lastx, self.lasty = event.x, event.y
def getClick(self, event):
self.whiteBoard.bind("<Button-1>", self.xy)
self.whiteBoard.bind("B1-Motion>", self.addLine)
white = Paint(root)
root.mainloop()
This is all part of an attempt to make a MS paint clone using Tkinter.
First, you are binding to B1-Motion> (note the missing <). But, more importantly, don't do bindings like those. It looks like the getClick method is actually "select the line tool". Then, add the bindings <Button-1> and <B1-Motion> to the canvas itself. When the callbacks are called, you do the actions according to the selected tool.
Here is rough sketch that follows this suggestion (with a RECTANGLE tool as a bonus):
import tkinter
# TOOLS
LINE, RECTANGLE = list(range(2))
class Paint:
def __init__(self, canvas):
self.canvas = canvas
self._tool, self._obj = None, None
self.lastx, self.lasty = None, None
self.canvas.bind('<Button-1>', self.update_xy)
self.canvas.bind('<B1-Motion>', self.draw)
def draw(self, event):
if self._tool is None or self._obj is None:
return
x, y = self.lastx, self.lasty
if self._tool in (LINE, RECTANGLE):
self.canvas.coords(self._obj, (x, y, event.x, event.y))
def update_xy(self, event):
if self._tool is None:
return
x, y = event.x, event.y
if self._tool == LINE:
self._obj = self.canvas.create_line((x, y, x, y))
elif self._tool == RECTANGLE:
self._obj = self.canvas.create_rectangle((x, y, x, y))
self.lastx, self.lasty = x, y
def select_tool(self, tool):
print('Tool', tool)
self._tool = tool
class Tool:
def __init__(self, whiteboard, parent=None):
self.whiteboard = whiteboard
frame = tkinter.Frame(parent)
self._curr_tool = None
for i, (text, t) in enumerate((('L', LINE), ('R', RECTANGLE))):
lbl = tkinter.Label(frame, text=text, width=2, relief='raised')
lbl._tool = t
lbl.bind('<Button-1>', self.update_tool)
lbl.pack(padx=6, pady=6*(i % 2))
frame.pack(side='left', fill='y', expand=True, pady=6)
def update_tool(self, event):
lbl = event.widget
if self._curr_tool:
self._curr_tool['relief'] = 'raised'
lbl['relief'] = 'sunken'
self._curr_tool = lbl
self.whiteboard.select_tool(lbl._tool)
root = tkinter.Tk()
canvas = tkinter.Canvas(highlightbackground='black')
whiteboard = Paint(canvas)
tool = Tool(whiteboard)
canvas.pack(fill='both', expand=True, padx=6, pady=6)
root.mainloop()

Categories

Resources