2

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()
Mumlare
  • 39
  • 4
  • Please don't change your question to ask something completely different as it invalidates all of the existing answers. If you need to ask something new, create a new question. – Bryan Oakley Apr 30 '20 at 15:05
  • I'm sorry if the question became convoluted, the fact is however, that the code is not working yet, and i dont like to leave things half done. Thank you for your help with correcting it so far! – Mumlare May 02 '20 at 22:26
  • stackoverflow isn't designed for conversations. The site guidelines say you should ask a single question at a time. You've asked two completely separate questions. – Bryan Oakley May 02 '20 at 22:29
  • I see, i guess i better move the second part to a new question then, to anyone reading this in the future, the code doesent work yet, so dont go crazy trying to make it work with what is here! – Mumlare May 02 '20 at 22:34
  • Please take a look at my answer, I just edited the code to include a jump action to the ball – EnriqueBet May 02 '20 at 22:41
  • Nvm, Enrique solved it! – Mumlare May 02 '20 at 23:04

2 Answers2

2

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)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you for a great answer, this helped me better understand how classes work in python! Still something wrong that i posted in my updated question, but you have really helped me regardless! – Mumlare Apr 30 '20 at 12:10
1

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.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
EnriqueBet
  • 1,482
  • 2
  • 15
  • 23
  • 2
    `Hero` shouldn't inherit from `mainWindow`. Doing so means that `Hero` _is a_ `mainWindow, which is not the case. – Bryan Oakley Apr 30 '20 at 01:39
  • True that @BryanOakley, didn't notice it when I was pasting the code, I remove the inheritance, which clearly was incorrect. – EnriqueBet Apr 30 '20 at 02:53
  • 1
    Thank you for a great answer, i made some modifications and i think the world is now in the child unfortionetly, when i try to call the "space_bar_down" function i seem to be getting a new kind of error, but it feels like an improvement! – Mumlare Apr 30 '20 at 12:12
  • Hi @Mumlare, I am not sure how does the space_bar method should look like in tkiner, I added it as a suggestion on how to connect the hero's movement method with your interface which is your window. I'll try to complete my answer to make it work fully, but I will need some time! :D Let me know how does it go! – EnriqueBet Apr 30 '20 at 16:38
  • This is great! I'm still not certain why initializing the class call from the phesics file with a call to a mainWindow function doesent work, but that seems to be a rule. Really nice how you can bind keyboard buttons to functions, that will be very useful going forward! Thank you for all your help! – Mumlare May 02 '20 at 23:03
  • You are welcome! It doesn't work to call the hero movement from the ```physics.py``` file because the ```mainWindow ``` class is in a loop, this loop refreshes every change inside ```mainWindow``` Class. If you use separate statements, the ````moveHeroJump``` method will be out of the 'inifinite' loop, once this loop finishes, when you close the app, then the method will be executed. – EnriqueBet May 02 '20 at 23:08