How do I make collision objects in python tkinter? - python

I'm making a 2D game where I need objects to scroll up the screen while the player avoids the objects. I need the game to end when the player hits the objects.
it should look something like this:
So far I can move the skier(image) left and right:
import tkinter
from tkinter import *
from PIL import ImageTk, Image
root = Tk()
root.geometry("800x600")
#canvas dimensions
w = 800
h = 1000
x = w/2
y = h/2
#canvas
my_canvas = Canvas(root, width=w, height=h, bg="white")
my_canvas.pack(pady=20)
#import player image
img=Image.open(r"C:/Users/Gebruiker/Pictures/player.png")
resized = img.resize((300,225), Image.ANTIALIAS)
new_img = ImageTk.PhotoImage(resized)
new_image = my_canvas.create_image(150,100, anchor=NW,image = new_img)
#player movement
def left(event):
x = -20
y = 0
my_canvas.move(new_image,x,y)
def right(event):
x = 20
y = 0
my_canvas.move(new_image,x,y)
root.bind("<Left>", left)
root.bind("<Right>", right)
How do I randomly spawn objects at regular intervals? I think I need some function to scroll the canvas as the objects spawn.
Similar posts usually had 1 object moving in a changing direction. However, I need lots of objects to spawn continuously.
Also, how do I make a collision border to stop the player going off the canvas?

Related

Image not getting drawn on tkinter canvas

I would like to display a knapsack image on green canvas. The height and width of that canvas is 250X250 pixels.
And the size of the image is 260X280 pixels.
When I try to execute below code, I get the output as shown in screenshot above 1. The location of the code file and image file is same.
from tkinter.font import BOLD
from tkinter import *
import tkinter
from PIL import Image, ImageTk
root = Tk()
def draw():
global canvas
root.geometry('1080x720')
root.state('zoomed')
canvas = Canvas(root,bg='black',highlightthickness=0)
canvas.pack(fill=tkinter.BOTH, expand=True)
sw = root.winfo_screenwidth()
sh = root.winfo_screenheight()
canvas.create_line(int(sw*0.0000),int(sh*0.1736),int(sw*0.6510),int(sh*0.1736),fill='white')
canvas.create_line(int(sw*0.6510),int(sh*0.0000),int(sw*0.6510),int(sh*1.0000),fill='white')
canvas.create_line(int(sw*0.6510),int(sh*0.1157),int(sw*1.0000),int(sh*0.1157),fill='white')
canvas.create_line(int(sw*0.6510),int(sh*0.8101),int(sw*1.0000),int(sh*0.8101),fill='white')
UI_frame1 = Frame(canvas,bg='black',width=int(sw*0.6510),height=int(sh*0.1580))
canvas.create_window(0,0, anchor=NW,window=UI_frame1)
N = Label(UI_frame1,text='N',bg ='black',fg='white',font=(12))
N.grid(row=0,column=0, padx=139,pady=22)
weights = Label(UI_frame1,text='Weights',bg ='black',fg='white',font=(12))
weights.grid(row=0,column=1,padx=140,pady=22)
val = Label(UI_frame1,text='Values',bg ='black',fg='white',font=(12))
val.grid(row=0,column=2,padx=140,pady=22)
n = Entry(UI_frame1,bg='white',width=4,font=(12))
n.grid(row=1,column=0,padx=50,pady=17)
value_arr = Entry(UI_frame1,bg='white',font=(12))
value_arr.grid(row=1,column=1,padx=50,pady=17)
weight_arr = Entry(UI_frame1,bg='white',font=(12))
weight_arr.grid(row=1,column=2,padx=50,pady=17)
Label(canvas,text='i',bg='black',fg='white',font=(14)).place(x=150,y=185)
i = Label(canvas,text=" i ",bg='white',font=(12)).place(x=175,y=185)
Label(canvas,text='j',bg='black',fg='white',font=(14)).place(x=525,y=185)
j = Label(canvas,text=" j ",bg='white',font=(12)).place(x=550,y=185)
table = Canvas(canvas,bg='red',width=600,height=450)
table.place(x=75,y=300)
w = int(600/8)
h = int(450/5)
x=0
y=0
for r in range(5):
for c in range(8):
table.create_rectangle(x,y,x+w,y+h)
x+=w
y+=h
x=0
UI_frame2 = Frame(canvas,bg='blue',width=250,height=250)
canvas.create_window(835,630, anchor=CENTER,window=UI_frame2)
image_c = Canvas(UI_frame2,bg='green',highlightthickness=0,width=250,height=250)
image_c.grid(row=0,column=0,padx=0,pady=0)
photo = ImageTk.PhotoImage(file ='knapsack.png')
image_c.create_image(835,630,image=photo,anchor=NW)
draw()
root.mainloop()
I would like to cover the entire green canvas with single image of knapsack. I am not any error while running the GUI. Anyone could help me out, I'll be really thankful.
There are two problems:
First:
You display in position (835,630) but canvas has visible only (250, 250) - (top left corner is (0,0)) - so photo is in place which you can see.
Second:
There is bug in PhotoImage() which removes image from memory when it is assigned to local variable. One of solution is to use global photo to assign it to global variable.
def draw():
global canvas
global photo # use global variable
#... code ...
photo = ImageTk.PhotoImage(file='knapsack.png')
image_c.create_image(0, 0,image=photo, anchor=NW) # position (0,0)
Doc (on archive.org): PhotoImage - see Note at the botton of doc.
Other problem can be that image has big (transparent) margins around object and it may show object in differnt place then you may expect.
Screenshot shows photo in position (0,0) but object is in center of green canvas.

Image in Button tkinter

I am having a problem in making a login image to a button. I have succeeded in making the image with a transparent background, but I can't succeed to make the button with transparent background.
I attached a screenshot that shows what I mean. The upper 'login' is a image (with transparent background), the lower is Login button but there is a white background around it. I want to make a button with transparent background.
self.login_image = Image.open('images/LoginButton.png')
self.login_image = ImageTk.PhotoImage(self.login_image)
self.main_screen_canvas.create_image(700, 300, image=self.login_image)
self.login_button = Button(self.main_screen_canvas, borderwidth=0, image=self.login_image)
self.login_button.place(x=300,y=400)
What should I do?
BackgroundImage
LoginButtonImage
Here's how to do what I was suggesting in the comment which uses the technique shown in another answer of mine to simulate a tkinter Button on a Canvas that has a transparent image placed on it (instead of text).
One issue I ran into was that fact that your 2421 × 1210 pixel background image was larger than my screen. To deal with it I added a fitrect() helper function to determine a new smaller size for it that would fit. I wrote it a long time ago, but have found it handy to have around many times (like now). Note that in the code ll and ur refer to the lower-left and upper-right corners of the rectangles involved.
Here's the resulting code:
from PIL import Image, ImageTk
import tkinter as tk
class CanvasButton:
""" Create left mouse button clickable canvas image object.
The x, y coordinates are relative to the top-left corner of the canvas.
"""
flash_delay = 100 # Milliseconds.
def __init__(self, canvas, x, y, image_source, command, state=tk.NORMAL):
self.canvas = canvas
if isinstance(image_source, str):
self.btn_image = tk.PhotoImage(file=image_source)
else:
self.btn_image = image_source
self.canvas_btn_img_obj = canvas.create_image(x, y, anchor='nw', state=state,
image=self.btn_image)
canvas.tag_bind(self.canvas_btn_img_obj, "<ButtonRelease-1>",
lambda event: (self.flash(), command()))
def flash(self):
self.set_state(tk.HIDDEN)
self.canvas.after(self.flash_delay, self.set_state, tk.NORMAL)
def set_state(self, state):
""" Change canvas button image's state.
Normally, image objects are created in state tk.NORMAL. Use value
tk.DISABLED to make it unresponsive to the mouse, or use tk.HIDDEN to
make it invisible.
"""
self.canvas.itemconfigure(self.canvas_btn_img_obj, state=state)
def fitrect(r1_ll_x, r1_ll_y, r1_ur_x, r1_ur_y, r2_ll_x, r2_ll_y, r2_ur_x, r2_ur_y):
""" Find the largest rectangle that will fit within rectangle r2 that has
rectangle r1's aspect ratio.
Note: Either the width or height of the resulting rect will be
identical to the corresponding dimension of rect r2.
"""
# Calculate aspect ratios of rects r1 and r2.
deltax1, deltay1 = (r1_ur_x - r1_ll_x), (r1_ur_y - r1_ll_y)
deltax2, deltay2 = (r2_ur_x - r2_ll_x), (r2_ur_y - r2_ll_y)
aspect1, aspect2 = (deltay1 / deltax1), (deltay2 / deltax2)
# Compute size of resulting rect depending on which aspect ratio is bigger.
if aspect1 > aspect2:
result_ll_y, result_ur_y = r2_ll_y, r2_ur_y
delta = deltay2 / aspect1
result_ll_x = r2_ll_x + (deltax2 - delta) / 2.0
result_ur_x = result_ll_x + delta
else:
result_ll_x, result_ur_x = r2_ll_x, r2_ur_x
delta = deltax2 * aspect1
result_ll_y = r2_ll_y + (deltay2 - delta) / 2.0
result_ur_y = result_ll_y + delta
return result_ll_x, result_ll_y, result_ur_x, result_ur_y
def btn_clicked():
""" Prints to console a message every time the button is clicked """
print("Button Clicked")
background_image_path = 'background_image.jpg'
button_image_path = 'button_image.png'
root = tk.Tk()
root.update_idletasks()
background_img = Image.open(background_image_path) # Must use PIL for JPG images.
scrnwidth, scrnheight = root.winfo_screenwidth(), root.winfo_screenheight()
bgrdwidth, bgrdheight = background_img.size
border_width, border_height = 20, 20 # Allow room for window's decorations.
# Determine a background image size that will fit on screen with a border.
bgr_ll_x, bgr_ll_y, bgr_ur_x, bgr_ur_y = fitrect(
0, 0, bgrdwidth, bgrdheight,
0, 0, scrnwidth-border_width, scrnheight-border_height)
bgr_width, bgr_height = int(bgr_ur_x-bgr_ll_x), int(bgr_ur_y-bgr_ll_y)
# Resize background image to calculated size.
background_img = ImageTk.PhotoImage(background_img.resize((bgr_width, bgr_height)))
# Create Canvas same size as fitted background image.
canvas = tk.Canvas(root, bd=0, highlightthickness=0, width=bgr_width, height=bgr_height)
canvas.pack(fill=tk.BOTH)
# Put background image on Canvas.
background = canvas.create_image(0, 0, anchor='nw', image=background_img)
# Put CanvasButton on Canvas centered at the bottom.
button_img = tk.PhotoImage(file=button_image_path)
btn_x, btn_y = (bgr_width/2), (bgr_height-button_img.height())
canvas_btn1 = CanvasButton(canvas, btn_x, btn_y, button_img, btn_clicked)
root.mainloop()
And here's the result of running it:

Tkinker circumference of a circle coords handling python 3

I'm making a little alien project to help to learn graphics in tkinter and I have come across a problem. I am trying to make the aliens eyeball stay inside the eye but still move around however that requires me to detect the edge of the eyeball which is a circle. Not really sure how coords work in tkinter (other than the basics) so any help appreciated. Thanks!
from tkinter import *
from threading import Timer
import random
import time
global canvas, root
root = Tk()
root.title("Alien")
root.attributes("-topmost", 1)
canvas = Canvas(root, height=300, width =400)
canvas.pack()
Blinking = False
class Alien:
def __init__(self):
global canvas, root
self.body = canvas.create_oval(100,150,300,250, fill = "green")
self.eye = canvas.create_oval(170,70,230,130, fill = "white")
self.eyeball = canvas.create_oval(190,90,210,110, fill = "black")
self.mouth = canvas.create_oval(150,220,250,240, fill = "red")
self.neck = canvas.create_line(200,150,200,130)
self.hat = canvas.create_polygon(180,75,220,75,200,20, fill = "blue")
self.words = canvas.create_text(200,800, text = "I'm an alien!", anchor="nw")
root.update()
def openMouth(self):
global canvas, root
canvas.itemconfig(self.mouth, fill = "black")
root.update()
def closeMouth(self):
global canvas, root
canvas.itemconfig(self.mouth, fill = "red")
root.update()
def burp(self, event):
self.openMouth()
canvas.itemconfig(self.words, text = "BURRRRP!")
time.sleep(0.5)
self.closeMouth()
def moveEye(self,event):
global root, canvas
canvas.move(alien.eyeball , random.randint(-1,1) , random.randint(-1,1))
root.update()
def blink(self,event):
canvas.itemconfig(self.eye, fill = "green")
canvas.itemconfig(self.eyeball, state=HIDDEN)
Blinking = True
root.update()
def unblink(self,event):
canvas.itemconfig(self.eye, fill = "white")
canvas.itemconfig(self.eyeball, state=NORMAL)
Blinking = False
root.update()
alien = Alien()
alien.openMouth()
time.sleep(1)
alien.closeMouth()
canvas.bind_all("<Button-1>", alien.burp)
canvas.bind_all("<KeyPress-a>", alien.blink)
Timer(2, alien.moveEye).start()
while not Blinking:
alien.moveEye(event)
if alien.moveEye.event.x > 190:
canvas.move(alien.eyeball, -1 , 0)
Small circle with radius r lies inside big circle with radius R, if
(BigCenter.X - SmallCenter.x)^2 + (BigCenter.Y - SmallCenter.Y)^2 < (R - r)^2
^2 denotes squared value

Remove canvas widgets in Tkinter

from Tkinter import *
import random
root = Tk()
width = 700
height = 600
canvas = Canvas(root, width = width, height = height, bg = "light blue")
canvas.pack()
pipes = []
class NewPipe:
def __init__(self, pipe_pos, pipe_hole):
self.pipe_pos = list(pipe_pos)
def update(self):
self.pipe_pos[0] -= 3
self.pipe_pos[2] -= 3
def draw(self):
canvas.create_rectangle(self.pipe_pos, fill = "green")
def get_pos(self):
return self.pipe_pos
def generate_pipe():
pipe_hole = random.randrange(0, height)
pipe_pos = [width - 100, 0, width, pipe_hole]
pipes.append(NewPipe(pipe_pos, pipe_hole))
draw_items()
canvas.after(2000, generate_pipe)
def draw_items():
for pipe in pipes:
if pipe.get_pos()[2] <= 0 - 5:
pipes.remove(pipe)
else:
pipe.draw()
pipe.update()
canvas.after(100, draw_items)
def jump(press):
pass
canvas.bind("<Button-1>", jump)
canvas.after(2000, generate_pipe)
draw_items()
mainloop()
Right now I am trying to make a game where you have to dodge rectangles, which are pipes. It is basically Flappy Bird, but on Tkinter. In this code I am trying to generate pipes and move them, but the pipes I have drawn before do not leave and they just stay there. This means that when the pipe moves, the position it was just in doesnt change and that shape stays there. Is there any way to delete past shapes, or another way to move them?
canvas.create_rectangle(self.pipe_pos, fill = "green") returns an ID.
You can use this ID to put it into methods like
canvas.coords
canvas.delete
canvas.itemconfigure
canvas.scale
canvas.type
...
Have a look at help(canvas).
The canvas is not a framebuffer on which you paint stuff for one frame. The painted stuff does not go away and you can move it and change all the parameters you can use when creating.

Copy Tkinter Canvas Items

I need top be able to create a copy of a tkinter canvas item, so that a copy of an image can be dragged off of an original. I have dragging working for the images, but I cannot seem to copy the image item. Any help would be greatly appreciated! Thanks.
EDIT: Sorry for not including my code at first. I was able to solve the problem thanks to the answer that was given. Here's a trimmed down example of my code that now works:
from tkinter import *
from PIL import Image, ImageTk
def OnBaseButtonPress(event):
#record the item and its location
drag_data["item"] = c.find_closest(event.x, event.y)
i = c.itemcget(drag_data["item"], "image") #finds the image source of the object
refs.append(i) #keep a reference!
c.create_image(c.coords(drag_data["item"]), image=i, tags="base") #creates an identical object at the position
drag_data["x"] = event.x
drag_data["y"] = event.y
def OnBaseButtonRelease(event):
#reset drag info
drag_data["item"] = None
drag_data["x"] = 0
drag_data["y"] = 0
def OnBaseMotion(event):
#calculate how far the item has moved
delta_x = event.x - drag_data["x"]
delta_y = event.y - drag_data["y"]
#move the object that amount
c.move(drag_data["item"], delta_x, delta_y)
#record the new position
drag_data["x"] = event.x
drag_data["y"] = event.y
#set up canvas and image
root = Tk()
c = Canvas(root, width=800, height=600)
c.pack()
test = ImageTk.PhotoImage(Image.open("test.png"))
c.create_image(400, 300, image=test, tags="base")
refs=[] #used to keep references to images used in functions
#bind mouse keys
c.tag_bind("base", "<ButtonPress-1>", OnBaseButtonPress)
c.tag_bind("base", "<ButtonRelease-1>", OnBaseButtonRelease)
c.tag_bind("base", "<B1-Motion>", OnBaseMotion)
drag_data={"x": 0, "y": 0, "item": None}
mainloop()
You can get item type canvas.type(item), item configuration canvas.itemconfig(item), and etc.
And then you can recreate an identical object.
See also: Tkinter - making a second canvas display the contents of another.

Categories