I am trying to put an image on a TKinter canvas with other buttons under the image. For some reason I can not get this picture to appear. I have not yet implemented the buttons. Here is my code thus far.
class GUI_Control:
def __init__(self, player):
self.player = player
self.map = Tk()
self.MAP_WIDTH = 600
self.MAP_HEIGHT = 375
#define map gui here
self.canvas = Canvas(self.map, width=self.MAP_WIDTH, height=self.MAP_HEIGHT)
self.map_picture = PhotoImage(file=r"images/archipelago.gif")
self.canvas.create_image(0, 0, image=self.map_picture)
#define level gui's here
def open(self):
self.map.mainloop()
def hide_map(self):
self.map.destroy()
#debugging
if __name__ == "__main__":
gui = GUI_Control(Player.Player())
gui.open()
You'll need to use one of Tk’s geometry-management mechanisms to tell it where to render the canvas within it's container.
the simplest way would be to add self.canvas.pack() like so:
#define map gui here
self.canvas = Canvas(self.map, width=self.MAP_WIDTH, height=self.MAP_HEIGHT)
self.canvas.pack()
self.map_picture = PhotoImage(file=r"images/archipelago.gif")
self.canvas.create_image(0, 0, image=self.map_picture)
#define level gui's here
You need to call the pack() (or grid()) method of widgets for them to be displayed:
class GUI_Control:
def __init__(self, player):
self.player = player
self.map = Tk()
self.MAP_WIDTH = 600
self.MAP_HEIGHT = 375
#define map gui here
self.canvas = Canvas(self.map, width=self.MAP_WIDTH, height=self.MAP_HEIGHT)
self.canvas.pack(expand=YES, fill=BOTH) # ADDED
self.map_picture = PhotoImage(file="images/archipelago.gif")
self.canvas.create_image(0, 0, image=self.map_picture, anchor='nw')
#define level gui's here
def open(self):
self.map.mainloop()
def hide_map(self):
self.map.destroy()
#debugging
if __name__ == "__main__":
gui = GUI_Control(Player.Player())
gui.open()
Related
I'm making a chess game.
I've created a main file with the Tkinter code in a class "Window". In this class, I created a canvas.
Then I've created a second file with the name "pieces", where I put the behaviour of the different pieces. In this one, I have a superclass "Pieces", and a subclass "Bishop" (because I haven't created the classes for the other pieces yet)
What I tried first to do, is to create a bishop's icon in the constructor of the class "Bishop".
My class "Bishop" has the argument "color", so that, when we create an object "Bishop", we can choose if he's black or white.
So I wrote :
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, self.icon)
The problem is, when I create a new object of the bishop, it makes a loop.
I don't really know how to fix this, a solution would be to put the whole code in the same file, but I don't like it because it's not clean.
If you need the whole code, I can give it to you.
that's the complete error:
Traceback (most recent call last):
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 2, in <module>
import pieces
File "C:\Users\CSP\PycharmProjects\chess_game\pieces.py", line 3, in <module>
import main
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 28, in <module>
fen = Window()
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 20, in __init__
pieces.Bishop("black", 5, 5)
AttributeError: partially initialized module 'pieces' has no attribute 'Bishop' (most likely due to a circular import)
I've written import main in the file pieces, because I want to create the image in the constructor of the class Bishop, I thought it was cleaner than do it in the class Windows, because I would have to do I 32 times (one per piece) and it would be very heavy.
And to create the image in the class bishop, I need to import the module main (because I use canvas.create_image(), and the canvas is in the class Windows)
But if I write import main it makes a loop, so do you have an idea to fix this?
Here's the code, very simplified
main.py
from tkinter import *
import pieces
#GUI class
class Window:
def __init__(self):
self.window = Tk()
self.window.title("My chess game")
self.frame = Frame(self.window, bg='#41B77F')
self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
self.canvas.pack()
bishop = Bishop("black",5 , 5)
self.frame.pack(expand=YES)
win = Window()
win.window.mainloop()
pieces.py
from tkinter import PhotoImage
import main
#superclass
class Pieces:
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, image=self.icon)
If you are familiar with Model/View approach of writing code, it will help you find your way around tkinter applications. In such a case you would place all the code relating to views in one class and all the data is managed in the Model class(es).
In your case, you could start with the structure illustrated below and grow from it:
# model.py
from tkinter import *
class GameModel():
# maintains game state/data
# may include positions of pieces on board
# number of moves made by each player
pass
#superclass, inherits from Tk().Canvas
class Pieces(Canvas):
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
# main.Fenetre.canvas.create_image(x_position, y_position, image=self.icon)
self.create_image(x_position, y_position, image=self.icon)
# because bishop inherits from pieces which inherits from Canvas, its possible
# to call .create_image() from within Bishop class using self.create_image()
# gameinterface.py
from tkinter import *
#GUI/View class
class GameInterface():
def __init__(self, window: Tk()):
self.window = window
self.frame = Frame(self.window, bg='#41B77F')
self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
self.canvas.pack()
self.frame.pack(expand=YES)
def play(self, game: GameModel):
# the game objects give access to state of game and all data
self.window.mainloop()
# main.py
from tkinter import *
def main() -> None:
"""Entry point to gameplay."""
window = Tk()
# window = Tk()
#set title and position at center of screen
window.title("My chess game")
# game object gives you access to all relevant data
game = GameModel()
# app object gives you access to view
app = GameInterface(window)
# app.play combines the model and the view
app.play(game)
if __name__ == '__main__':
main()
If you need a reference to the window in Bishop, you have to pass it as an argument:
from tkinter import PhotoImage
class Pieces:
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, win, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
win.canvas.create_image(x_position, y_position, image=self.icon)
I am creating an online monopoly game using canvas and tkinter and having trouble trying to make the "player1.png" .ie my character to move across the board. Please help!
class Example(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.master.title("Monopoly Physics Edition")
self.pack(fill=BOTH, expand=1)
canvas = Canvas(self)
global player1
load= Image.open("player1.png")
player1 = ImageTk.PhotoImage(load)
img = Label(image=player1)
img.place(x=834, y=60)
def main():
canvas = Tk()
ex = Example()
canvas.geometry("1150x820+800+700")
if __name__ == '__main__':
main()
Ok so I programmed in the basics here:
import tkinter as tk
from PIL import Image, ImageTk
class Player:
def __init__(self, canvas, sprite, pos):
self.posx, self.posy = pos
self.canvas = canvas
self.sprite = sprite
self.tk_image = None
self.canvas_id = None
def display(self):
# This will display the train card on the board
self.load_sprite()
# If we already displayed the object hide it first
if self.canvas_id is not None:
self.hide_from_canvas()
# We need to create a new canvas object and get its id
# so that later we can move/remove it from the canvas
self.canvas_id = self.canvas.create_image(self.posx, self.posy, image=self.tk_image)
def hide_from_canvas(self):
# This will remove the sprite from the canvas
self.canvas.delete(self.canvas_id)
def load_sprite(self):
# If we already loaded the sprite just don't do anything
if self.tk_image is not None:
return None
# Later you can determine the filename from `self.name`
filename = "img_small.png"
pillow_image = Image.open(filename)
# You must keep a reference to this `tk_image`
# Otherwise the image would show up on the board
self.tk_image = ImageTk.PhotoImage(pillow_image)
def move(self, change_x, change_y):
# Move the object by change_x, change_y
self.canvas.move(self.canvas_id, change_x, change_y)
class Property:
def __init__(self, canvas, name, cost, pos):
self.posx, self.posy = pos
self.canvas = canvas
self.name = name
self.cost = cost
self.tk_image = None
self.canvas_id = None
def display(self):
# This will display the train card on the board
self.load_sprite()
# If we already displayed the object hide it first
if self.canvas_id is not None:
self.hide_from_canvas()
# We need to create a new canvas object and get its id
# so that later we can move/remove it from the canvas
self.canvas_id = self.canvas.create_image(self.posx, self.posy, image=self.tk_image)
def hide_from_canvas(self):
# This will remove the sprite from the canvas
self.canvas.delete(self.canvas_id)
def load_sprite(self):
# If we already loaded the sprite just don't do anything
if self.tk_image is not None:
return None
# Later you can determine the filename from `self.name`
filename = "img_small.png"
pillow_image = Image.open(filename)
# You must keep a reference to this `tk_image`
# Otherwise the image would show up on the board
self.tk_image = ImageTk.PhotoImage(pillow_image)
def move(self, change_x, change_y):
# Move the object by change_x, change_y
self.canvas.move(self.canvas_id, change_x, change_y)
class App(tk.Canvas):
def __init__(self, master, **kwargs):
# Create the canvas
super().__init__(master, **kwargs)
# lets show all of the monopoly properties on the screen
self.init()
# I will also bind to key presses (You might want to use them later)
master.bind("<Key>", self.on_key_press)
# I will also bind to mouse presses
super().bind("<Button-1>", self.on_mouse_press)
def on_key_press(self, event):
print("You pressed this character:", repr(event.char))
print("For non character keys (like Escape):", repr(event.keysym))
# For now I will move the player sprite by 10 pixels to the right
# when "d" or the right key is pressed
if (event.char == "d") or (event.keysym == "Right"):
# Move the first player of list
self.players[0].move(10, 0)
def on_mouse_press(self, event):
print("You clicked with the mouse at this position:", (event.x, event.y))
def init(self):
# A list that will hold all of the properties:
self.properties = []
# A list that will hold all of the players playing
self.players = []
# I will only create 1 property (You can add the rest)
# I will create 1 of the trains where the cost is 200 and
# its position is (50, 50). Note (0, 0) is the top left
# corner of the canvas
train1 = Property(self, "Train", 200, (50, 50))
train1.display()
self.properties.append(train1)
# I will also create a player at position (100, 100)
player1 = Player(self, "player_sprite1", (100, 100))
player1.display()
self.players.append(player1)
# Create the window
root = tk.Tk()
# Make it so that the user can't resize the window
# Otherwise you will have a lot of problems with your images not being in
# the correct places
root.resizable(False, False)
# Create the App, I also passed in `width=200, height=200` those are in pixels
main_app = App(root, width=200, height=200)
# Show the App on the screen:
main_app.pack()
# Enter tkinter's mainloop
root.mainloop()
It allows us to move/hide/display any of the sprites we create. I also added a way for use to detect key/mouse presses (might come in handy later). Using this it shouldn't be hard to create the full program. Note that most of the methods in the Player and Property class are the same so if you know how to use inheritance, you should be able to simplify my code by a lot.
I'm trying to make a grid with a canvas using tkinter and after I made some change the lines stopped being drawn. I'm not sure what I did but it might have to do with how the Canvas object is nested in the Frame object, but it was working just fine like that at one point.
I also tried using shapes and couldn't get them to draw.
from tkinter import *
class Application(Frame):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.canvas = CanvasClass()
class CanvasClass(Canvas):
def __init__(self):
super().__init__()
self.CanvasHeight = 600
self.CanvasWidth = 800
self.c = Canvas(width=self.CanvasWidth, height=self.CanvasHeight)
self.CreateBackground(20)
self.c.pack(expand=True)
def CreateBackground(self, d):
print(self.CanvasHeight)
print(self.CanvasWidth)
for x in range(d, self.CanvasWidth, d):
print(x)
self.create_line(x, 0, x, self.CanvasHeight)
root = Tk()
root.title = "TESTING"
app = Application()
app.canvas.create_polygon(50,200,50,200)
root.mainloop()
from tkinter import *
class Application(Frame):
def __init__(self,p):
super().__init__(p)
self.initUI()
def initUI(self):
self.canvas = CanvasClass(self)
self.canvas.pack()#1
class CanvasClass(Canvas):
def __init__(self,p):#2
super().__init__(p)
self.CanvasHeight = 600
self.CanvasWidth = 800
self.c = Canvas(width=self.CanvasWidth, height=self.CanvasHeight)
self.CreateBackground(20)
self.c.pack(expand=True)
def CreateBackground(self, d):
print(self.CanvasHeight)
print(self.CanvasWidth)
for x in range(d, self.CanvasWidth, d):
print(x)
self.create_line(x, 0, x, self.CanvasHeight)
root = Tk()
root.title = "TESTING"
app = Application(root)
app.canvas.create_polygon(50,200,50,200)
app.pack()#3
root.mainloop()
you forgot to add the parent and insert it into the window I have used pack() you can change if you want. and I got grids of lines in the window at the bottom when I execute after packing
I am trying to write a basic tkinter example that will show a box stretching across a frame. At this point the code below will only print the final result, and not show the box moving. How do I fix the code so that it will work over time with out using something like move, so that I can modify the shape over time later?
from tkinter import Tk, Canvas, Frame, BOTH
from time import sleep
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("Board")
self.pack(fill=BOTH, expand=1)
self.canvas = Canvas(self)
self.ctr = 10
self.initUI()
def initUI(self):
print(self.ctr)
#The first four parameters are the x,y coordinates of the two bounding points.
#The top-left and the bottom-right.
r = self.canvas.create_rectangle((self.ctr * 10), 0, (self.ctr * 10 + 50), 50,
outline="#fb0", fill="#fb0")
'''
canvas.create_rectangle(50, 0, 100, 50,
outline="#f50", fill="#f50")
canvas.create_rectangle(100, 0, 150, 50,
outline="#05f", fill="#05f")
'''
self.canvas.pack(fill=BOTH, expand=1)
if self.ctr > 0:
self.updateUI()
def updateUI(self):
self.ctr -= 1
sleep(1)
self.initUI()
def main():
root = Tk()
root.geometry("400x100+300+300")
ex = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
This should get you partway there (you'll need to fix the indenting, the offsets are not correct and the counter doesn't get reset). In future, make sure you don't call sleep when you are using the event loop of a GUI for example. Most GUI's have a method to hook something into their event loops (the root.after call in this case). All I've done is make your code work partially - this should not be seen as indicative of idiomatic python.
from tkinter import Tk, Canvas, Frame, BOTH
from time import sleep
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("Board")
self.pack(fill=BOTH, expand=1)
self.canvas = Canvas(self)
self.ctr = 10
def initUI(self):
print(self.ctr)
#The first four parameters are the x,y coordinates of the two bounding points.
#The top-left and the bottom-right.
r = self.canvas.create_rectangle((self.ctr * 10), 0, (self.ctr * 10 + 50), 50,
outline="#fb0", fill="#fb0")
'''
canvas.create_rectangle(50, 0, 100, 50,
outline="#f50", fill="#f50")
canvas.create_rectangle(100, 0, 150, 50,
outline="#05f", fill="#05f")
'''
self.canvas.pack(fill=BOTH, expand=1)
self.ctr += 1
if self.ctr > 0:
self.parent.after(1000, self.initUI)
def main():
root = Tk()
ex = Example(root)
root.geometry("400x100+300+300")
root.after(1000, ex.initUI)
root.mainloop()
if __name__ == '__main__':
main()
When I load a transparent image with:
def load_image(path):
img = Image.open(path)
return ImageTk.PhotoImage(img)
class Home_Screen(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.home = Tk()
self.home.resizable(width = False,height = False)
self.home.geometry("700x500+300+100")
self.start()
def run(self):
self.images()
Label(self.home, image = self.background).pack() # Put it in the display window
button_instructions = Label(self.home,image = self.b_instructions).place(x = 300,y = 200)
self.home.mainloop()
def images(self):
self.background = load_image("Images/night-sky.jpg")
self.b_instructions = load_image("button_instructions.png")
def start_game(self):
self.home.destroy()
Home = Home_Screen()
I get an image with a white border around it. Does anyone know why the original transparency was not retained? If so could you please offer a solution.
Use a Canvas instead of Label widgets. The transparency isn't getting lost, because what you see is the background of the widget.
def run(self):
img_sky = ImageTk.PhotoImage(file="Images/night-sky.jpg")
img_button = ImageTk.PhotoImage(file="button_instructions.png")
self.canvas = Canvas(self.home, width=700, height=500)
self.canvas.create_image(0, 0, image=img_sky)
self.canvas.create_image(300, 200, image=img_button)
self.canvas.pack()
self.home.mainloop()