Python Forum
tkinter moving an class object with keybinds - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: tkinter moving an class object with keybinds (/thread-32445.html)



tkinter moving an class object with keybinds - gr3yali3n - Feb-10-2021

ive been pulling out my hair trying to figure out how to get this to work correctly, i dont fully understand why i got this to work. could someone explain this to me, i have been messing around with this for a few days now.. the xv and yv are my constants i was fighting with the class variables so i just set those.
and that is i think on of the keys to my issue.. but i have tried many ways to get this image to move, and i just landed on this..
is the event.symkey needing to be replaced with a while loop of some kind?

from tkinter import *
from PIL import ImageTk,Image
r=Tk()
c1 =Canvas(r,height=400,width=720)
c1.pack()
xv = 1
xy = 1

class ship():
    def __init__(self, x=0,y=0):
        self.img = PhotoImage(file="./8bitship.png")
        self.id = c1.create_image(0,0,anchor=NW,image=self.img)
        self.x = x
        self.y = y

    def move(self,x,y):
        c1.move(self.id, x, y)

    def right(self):
       c1.move(1,0)

    def key(self,event):
        #r.bind("<Right>",right)
        #r.bind("Right", lambda x: s.move(x = 5,y=0))
        if event.symkey == 'Right': self.xv =1
            
    r.bind("<Right>", lambda x: s.move(x=5,y=0))



s = ship(c1)

#r.bind("<Right>",lambda x: s.move(x = 5,y = 0 ))   
#r.bind("<Right>",right)



r.mainloop()



RE: tkinter moving an class object with keybinds - Larz60+ - Feb-10-2021

First a note about coding style.

Using global statements outside of a class is better done using a function, generally placed at the end of your script.
Placing it all at the top of the script makes it ugly, and difficult to read.

General suggested script structure:
  1. imports
  2. class definitions
  3. Dispatcher (outside of class, at bottom of script)
    this is a function that:
    • Collects and passes needed arguments for clases used
    • Instantiates needed classes with collected argument data.
    • any external processing, like mainloop for tkinter
  4. An entry that will allow the script to be executed on its own,
    without interfering with the ability to import from another script.
    this usually is something like this:
       if __name__ == '__main__':
           dispatch()
       
    dispatch can be replaced with your dispatcher function name.



RE: tkinter moving an class object with keybinds - BashBedlam - Feb-10-2021

You were closer that you thought. Here is a working example :

from tkinter import *
from PIL import ImageTk,Image
r=Tk()
c1 =Canvas(r,height=400,width=720)
c1.pack()
 
class ship():
	def __init__(self, x=0,y=0):
		self.img = PhotoImage(file="./alien.png")
		self.id = c1.create_image(0,0,anchor=NW,image=self.img)
		self.x = x
		self.y = y
		r.bind("<Right>", lambda x: s.move(x=5,y=0))
		r.bind("<Left>", lambda x: s.move(x=-5,y=0))
		r.bind("<Up>", lambda x: s.move(x=0,y=-5))
		r.bind("<Down>", lambda x: s.move(x=0,y=5))

	def move(self,x,y):
		c1.move(self.id, x, y)
 
s = ship()
r.mainloop()



RE: tkinter moving an class object with keybinds - deanhystad - Feb-10-2021

I would pass the canvas as an argument to ship.__init__ instead of depending on the global c1 and move bind outside the ship class.

I'd also toss the ship.x and ship.y since they are unused, or modify the ship.move() method to keep track of where the ship is.
def move(self,x,y):
    self.x += x
    self.y += y
    self.move(self.id, x, y)



RE: tkinter moving an class object with keybinds - gr3yali3n - Feb-10-2021

thank all of you, bashbedlam especially, i don't know why that did not occur to me to try :( , but that got it moving...! and whats better about it is i understand why it is working now.
as far as binding outside of the ships class , at the start of this i was attempting to but i moved it inside of the class because i couldn't get that to work.


RE: tkinter moving an class object with keybinds - deanhystad - Feb-10-2021

The main purpose of a class is to define a template for reusable code. Your ship class can never be used anywhere that doesn't have a canvas named c1, a main window named r, and worst of all, an instance of ship named s. It is rare that a class references an instance of itself unless it is a singleton, or maintains a list of instances for some reason (pool allocation for example). My inclination is to write the code like this:
from tkinter import *

class Ship():
    def __init__(self, parent, wide, high):
        self.canvas = Canvas(parent, width=wide, height=high)
        self.canvas.pack()
        self.img = PhotoImage(file = "test_image.png")
        img_wide = self.img.width()
        img_high = self.img.height()
        self.xmax = wide - img_wide
        self.ymax = high - img_high
        self.x = int((wide - img_wide) / 2)
        self.y = int((high - img_high) / 2)
        self.img_id = self.canvas.create_image(self.x, self.y, anchor=NW, image=self.img)

    def move(self, x, y):
        x = max(-self.x, min(x, self.xmax - self.x))
        y = max(-self.y, min(y, self.ymax - self.y))
        self.x += x
        self.y += y
        self.canvas.move(self.img_id, x, y)
 
root = Tk()
ship = Ship(root, 500, 400)
root.bind("<Right>", lambda event: ship.move(x=5, y=0))
root.bind("<Left>", lambda event: ship.move(x=-5, y=0))
root.bind("<Up>", lambda event: ship.move(x=0, y=-5))
root.bind("<Down>", lambda event: ship.move(x=0, y=5))
root.mainloop()
I also changed things so the ship starts out in the middle of the sea (canvas) and is prevented from sailing off the edge.