Mumlare
Mumlare

Reputation: 39

Passing a tkinter canvas between classes without calling the child from within the parent

I am somewhat of a beginner when it comes to Python, but i decided i want to write a basic 2-d physics playground. Unfortionetly i ran straigt into trouble when trying to setup the basic structure.

My plan is to create a GUI with a canvas in a parent function named mainWindow, then i figured i would create a child class (Hero) which creates a circle the user can manipulate on the canvas. This seems to work fairly well.

The problem occurs when i try to do anything with the Hero class, like call a function to delete the circle so i can redraw it in some direction. I can't seem to pass the canvas from the mainWindow to the Hero class. Any help would be greatly appreciated, including telling me that this is the wrong way to do things.

Im adding the two documents im working with since my rambling is probably hard to follow.

I run the program from the phesics.py document, resulting in the GUI poping up with my canvas and a red circle. When i close the window i get the following error:

classes.py", line 29, in moveHeroBody canvas.delete(heroBody) NameError: name 'canvas' is not defined

Unfortionetly i dont know how to get the "world" into the child

classes.py

from tkinter import *


class mainWindow():
    def __init__(self):
            #Setup the GUI
            root = Tk()
            root.geometry('800x600')
            # Setup the canvas within the GUI (master)
            world = Canvas(root, height = 600, width = 800, bg = "#FFFFFF")
            world.place(relx = 0.5, rely = 0.5, anchor = CENTER)

            Hero(world)

            root.mainloop()

class Hero(mainWindow):
    def __init__(self,world):
        #Initial creation of hero at coordinates
        x1 = 10
        y1 = 10
        x2 = 70
        y2 = 70
        heroBody = world.create_oval(x1,y1,x2,y2, fill = "#FF0000", outline = "#FF0000")

    #Move the hero
    def moveHeroBody():
        print("moveHeroBody")
        world.delete(heroBody)

phesics.py

from tkinter import *
from classes import *

mainWindow1 = mainWindow()
moveHero = Hero.moveHeroBody()

Upvotes: 2

Views: 812

Answers (2)

EnriqueBet
EnriqueBet

Reputation: 1473

I think you need to initialize the class Hero in your mainWindow class. The modifications needed to do in the code are:

classes.py

from tkinter import *
from time import sleep


class mainWindow():
    def __init__(self):
            #Setup the GUI
            self.jump_gap = 25
            root = Tk()
            root.geometry('800x600')
            # Setup the canvas within the GUI (master)
            self.world = Canvas(root, height = 600, width = 800, bg = "#FFFFFF")
            self.world.place(relx = 0.5, rely = 0.5, anchor = CENTER)

            self.hero = Hero(self.world)
            self.world.pack()
            root.bind("<space>",self.jump) # -> [1] Binds the SPACE BAR Key to the function jump 
            root.mainloop()

    def jump(self,event):
        gaps = list(range(self.jump_gap))
        for i in gaps:
            self.world.after(1,self.hero.moveHeroJump(h=i))  # [2] -> Binds the moveHeroJump method with the window action to a queue of updates
            self.world.update() #[2] updates the canvas
            sleep(0.01*i) # Added some linear wait time to add some look to it
        gaps.reverse()
        for i in gaps:
            self.world.after(1,self.hero.moveHeroJump(h=-i))
            self.world.update()
            sleep(0.01*i)


class Hero():
    def __init__(self,world):
        #Initial creation of hero at coordinates
        self.world = world
        self.x1 = 10
        self.y1 = 410
        self.x2 = 70
        self.y2 = 470
        self.heroBody = self.world.create_oval(self.x1,self.y1,self.x2,self.y2, fill = "#FF0000", outline = "#FF0000")

    #Move the hero
    def moveHeroJump(self,h):
        print("moveHeroBody")
        self.y1 -= h
        self.y2 -= h
        self.world.delete(self.heroBody)
        self.heroBody = self.world.create_oval(self.x1,self.y1,self.x2,self.y2, fill = "#FF0000", outline = "#FF0000")

physics.py

from tkinter import *
from classes import *

mainWindow1 = mainWindow()

Edit

So this got me playing some minutes ago, and I researched some sources from stack in order to complete this question. Here are the sources (referenced in the code as well):

  1. How to bind spacebar key to a certain method in tkinter python
  2. Moving Tkinter Canvas

The solution edited above is capable to perform a simple animation of a ball jumping. self.jump_gap is a fixed quantity that tells the ball how much does it needs to jump. The jump parses a certain height h to the moveHeroJump method to make the ball change its position, after the change of position is queued into the Canvas an update is called to see the changes on the ball.

Upvotes: 1

Bryan Oakley
Bryan Oakley

Reputation: 386210

You're passing it ok, but you're throwing the value away. Also, Hero shouldn’t inherit from mainWindow.

You need to save world as an attribute so that you can reference it later.

class Hero():
    def __init__(self,world):
        self.world = world
        ...

Then, you can use self.world to reference the canvas:

def moveHeroBody():
    print("moveHeroBody")
    self.world.delete(heroBody)

Though, the above code will fail because heroBody is a variable local to the __init__ - you need to do the same with it:

class Hero():
    def __init__(self,world):
        self.world = world
        ...
        self.heroBody = world.create_oval(...)

    #Move the hero
    def moveHeroBody():
        print("moveHeroBody")
        self.world.delete(self.heroBody)

Upvotes: 2

Related Questions