1

I'm a bit new to python in terms of learning Tkinter and it's ability to code a GUI. As a result, I'm trying my hand at doing a simple JPEG image on a GUI using python 2.7.3. I've seen a number of different solutions using the "self" word and I think I understand the purpose. Unfortunately, that's now how my code is laid out since I'm kind of just coding as I think of stuff at the moment. Here is how my code is set up current:

from Tkinter import *
from random import randint
from PIL import Image, ImageTk

# Global root item for using TKinter
root = Tk()

PLAYER_IMAGE_PATH = 'Path_to_image'

# Player class
class Player:
    playerHp = 0
    playerAtk = 0
    playerDef = 0
    playerImg = ''
    playerPositionX = 0
    playerPositionY = 0

    def __init__(self, hitpoints, attackPower, defensePower, pathToImage, positionX, positionY):
        self.playerHp = hitpoints
        self.playerAtk = attackPower
        self.playerDef = defensePower
        self.playerImg = pathToImage
        self.playerPositionX = positionX
        self.playerPositionY = positionY


# Method for building the frame.
def build_frame(screenHeight, screenWidth):
    canvas = Canvas(root, bg = 'blue', height = screenHeight, width = screenWidth)
    canvas.pack()

    player = create_random_player()
    display_player_image(canvas, player)
    #display_player_stats(frame, player)

    bind_all_keys(player)


# Key binding events.
def bind_all_keys(player):
    root.bind('<Left>', lambda event, arg=player: left_key(event, arg))
    root.bind('<Right>', lambda event, arg=player: right_key(event, arg))
    root.bind('<Up>', lambda event, arg=player: up_key(event, arg))
    root.bind('<Down>', lambda event, arg=player: down_key(event, arg))

def left_key(event, player):
    print "Player coordinates(X,Y): " + str(player.playerPositionX) + "," + str(player.playerPositionY)
    player.playerPositionX -= 1

def right_key(event, player):
    print "Player coordinates(X,Y): " + str(player.playerPositionX) + "," + str(player.playerPositionY)
    player.playerPositionX += 1

def up_key(event, player):
    print  "Player coordinates(X,Y): " + str(player.playerPositionX) + "," + str(player.playerPositionY)
    player.playerPositionY -= 1

def down_key(event, player):
    print "Player coordinates(X,Y): " + str(player.playerPositionX) + "," + str(player.playerPositionY)
    player.playerPositionY += 1
# End key binding events.


def create_random_player():
    return Player(randint(0,9), randint(0,9), randint(0,9), PLAYER_IMAGE_PATH, 0, 0)


def display_player_image(canvas, player):
    canvas.create_rectangle(50, 50, 250, 100, fill = "green")
    tkImage = ImageTk.PhotoImage(Image.open(player.playerImg))
    canvas.create_image(100, 100, image = tkImage, anchor = NE)


def display_player_stats(frame, player):
    hitPoints = Text(frame, height = 1)
    hitPoints.insert(INSERT, "HP: " + str(player.playerHp))
    hitPoints.pack()

    attackPower = Text(frame, height = 1)
    attackPower.insert(INSERT, "Attack: " + str(player.playerAtk))
    attackPower.pack()

    defensePower = Text(frame, height = 1)
    defensePower.insert(INSERT, "Defense: " + str(player.playerDef))
    defensePower.pack()

    xPos = Text(frame, height = 1)
    xPos.insert(INSERT, "X Pos: " + str(player.playerPositionX))
    xPos.pack()

    yPos = Text(frame, height = 1)
    yPos.insert(INSERT, "Y Pos: " + str(player.playerPositionY))
    yPos.pack()


# Main method.  Calculates height at 70% then sets width to same height to create square on screen.
def main(root): 
    height = root.winfo_screenheight() * 0.7
    width = height
    build_frame(screenHeight = height, screenWidth = width)
    root.mainloop()


# Entry method.
if __name__ == "__main__":
    main(root)

So, you can see that I create a player class and set the path to the JPEG in the creat_random_player method. I create my canvas and proceed to try and create my image and nothing appears. I've tried a number of things and I know some people will come on here and say I need to pass "self" but I'm not sure how to do this as is. I appreciate any input people can offer because I'm at a bit of a loss.

Also, I'm aware this code is probably sloppy but it's a first pass and I will be cleaning it up as I continue to code but this is how it is now. Please refrain from comments on code structure unless there is no other way to code the solution except to change everything.

Michael Platt
  • 1,297
  • 12
  • 25
  • local variables are garbage collected when the function exits so tkImage does not exist after that. See www.effbot.org/pyfaq/why-do-my-tkinter-images-not-appear.htm. In your case you can add it to the canvas instance, canvas.image=ImageTk.PhotoImage etc. –  Mar 28 '16 at 21:14
  • Possible duplicate of [tkinter canvas image not displaying](http://stackoverflow.com/questions/26479728/tkinter-canvas-image-not-displaying) – Terry Jan Reedy Mar 28 '16 at 21:52
  • Searching SO for `[tkinter] display canvas image`, for instance, turns up multiple previous questions with the same answer. – Terry Jan Reedy Mar 28 '16 at 21:54
  • If I could vote I would VTC: https://stackoverflow.com/questions/16424091/why-does-tkinter-image-not-show-up-if-created-in-a-function – WinEunuuchs2Unix Dec 03 '19 at 03:46

1 Answers1

3

You're image is getting garbage collected by python's garbage collector. You need to save a reference to the image.

Here's a solution to get your playerImg to display

On your line(s)

def display_player_image(canvas, player):

    canvas.create_rectangle(50, 50, 250, 100)
    tkImage = ImageTk.PhotoImage(Image.open(player.playerImg))
    canvas.create_image(100, 100, image = tkImage, anchor = NE)
    player.playerImg = tkImage #Reference

there's other ways to save a reference in your code. This is just the quickest one i saw.

Pythonista
  • 11,377
  • 2
  • 31
  • 50
  • So this solution worked just fine and as such I'm going to mark it as the solution for the question. I appreciate the response. You mentioned that there are other ways to save a reference in my code, I was curious if you could elaborate? I'm looking to optimize my code as I'm going through so knowing a better way would make a world of difference going forward. – Michael Platt Mar 29 '16 at 13:26