Collision Detection Problems and Invalid Command Name? - python
from Tkinter import *
root = Tk()
drawpad = Canvas(root, width=600,height=600, background='white')
player = drawpad.create_rectangle(260,590,340,595, fill = "blue")
ball = drawpad.create_oval(293,576,307,590, fill = "white")
brick1 = drawpad.create_rectangle(30,20,80,50, fill='green')
brick2 = drawpad.create_rectangle(30,100,80,130, fill='green')
brick3 = drawpad.create_rectangle(30,180,80,210, fill='green')
brick4 = drawpad.create_rectangle(100,20,150,50, fill='green')
brick5 = drawpad.create_rectangle(100,100,150,130, fill='green')
brick6 = drawpad.create_rectangle(100,180,150,210, fill='green')
brick7 = drawpad.create_rectangle(170,20,220,50, fill='green')
brick8 = drawpad.create_rectangle(170,100,220,130, fill='green')
brick9 = drawpad.create_rectangle(170,180,220,210, fill='green')
brick10= drawpad.create_rectangle(240,20,290,50, fill='green')
brick11= drawpad.create_rectangle(240,100,290,130, fill='green')
brick12= drawpad.create_rectangle(240,180,290,210, fill='green')
brick13= drawpad.create_rectangle(310,20,360,50, fill='green')
brick14= drawpad.create_rectangle(310,100,360,130, fill='green')
brick15= drawpad.create_rectangle(310,180,360,210, fill='green')
brick16= drawpad.create_rectangle(380,20,430,50, fill='green')
brick17= drawpad.create_rectangle(380,100,430,130, fill='green')
brick18= drawpad.create_rectangle(380,180,430,210, fill='green')
brick19= drawpad.create_rectangle(450,20,500,50, fill='green')
brick20= drawpad.create_rectangle(450,100,500,130, fill='green')
brick21= drawpad.create_rectangle(450,180,500,210, fill='green')
brick22= drawpad.create_rectangle(520,20,570,50, fill='green')
brick23= drawpad.create_rectangle(520,100,570,130, fill='green')
brick24= drawpad.create_rectangle(520,180,570,210, fill='green')
brickA1 = drawpad.create_rectangle(60,60,110,90, fill='cyan')
brickA2 = drawpad.create_rectangle(60,140,110,170, fill='cyan')
brickA3 = drawpad.create_rectangle(130,60,180,90, fill='cyan')
brickA4 = drawpad.create_rectangle(130,140,180,170, fill='cyan')
brickA5 = drawpad.create_rectangle(200,60,250,90, fill='cyan')
brickA6 = drawpad.create_rectangle(200,140,250,170, fill='cyan')
brickA7 = drawpad.create_rectangle(270,60,320,90, fill='cyan')
brickA8 = drawpad.create_rectangle(270,140,320,170, fill='cyan')
brickA9 = drawpad.create_rectangle(340,60,390,90, fill='cyan')
brickA10= drawpad.create_rectangle(340,140,390,170, fill='cyan')
brickA11= drawpad.create_rectangle(410,60,460,90, fill='cyan')
brickA12= drawpad.create_rectangle(410,140,460,170, fill='cyan')
brickA13= drawpad.create_rectangle(480,60,530,90, fill='cyan')
brickA14= drawpad.create_rectangle(480,140,530,170, fill='cyan')
bricklist = [brick1,brick2,brick3,brick4,brick5,brick6,brick7,brick8,brick9,brick10,brick11,brick12,brick13,brick14,brick15,brick16,brick17,brick18,brick19,brick20,brick21,brick22,brick23,brick24,brickA1,brickA2,brickA3,brickA4,brickA5,brickA6,brickA7,brickA8,brickA9,brickA10,brickA11,brickA12,brickA13,brickA14]
direction = 0
import random
randAngle = 0
angle = 0
overlap = 0
listPlace = 0
length = 0
brick = 0
class myApp(object):
def __init__(self, parent):
global drawpad
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
# Score
self.prompt = "Score:"
self.label1 = Label(root, text=self.prompt, width=len(self.prompt), bg='green')
self.label1.pack()
self.score = 0
self.scoreTxt = Label(root, text=str(self.score), width=3, bg='green')
self.scoreTxt.pack()
drawpad.pack()
root.bind_all('<Key>', self.key)
self.animate()
def animate(self):
global drawpad
global ball
global direction
global angle
global randAngle
global listPlace
global brick
x1,y1,x2,y2 = drawpad.coords(ball)
px1,py1,px2,py2 = drawpad.coords(player)
if y1 <= 0:
direction = 5
elif x1 >= px1 and x2 <= px2 and y2 >= py1:
direction = -5
randAngle = random.randint(0,12)
angle = randAngle - 6
elif x1 <= 0 and y2 <= 600 or x2 >= 600 and y2 <= 600:
angle = -angle
didWeHit = self.collisionDetect
if didWeHit == True:
#for x in bricklist:
# if x == brick:
brick = bricklist[listPlace]
bx1,by1,bx2,by2 = drawpad.coords(brick)
if x1 <= bx1 or x2 >= bx2:
angle = -angle
if y1 <= by1 or by2 >= y2:
direction = -direction
drawpad.delete(brick)
drawpad.move(ball, angle, direction)
drawpad.after(5,self.animate)
def key(self,event):
global drawpad
global player
global ball
global direction
x1,y1,x2,y2 = drawpad.coords(ball)
px1,py1,px2,py2 = drawpad.coords(player)
if event.char == " ":
direction = -5
if event.char == "a":
if x1 != 293 and y1 != 576 and x2 != 307 and y2 != 590 and px1 > 0:
drawpad.move(player,-8,0)
if event.char == "d":
if x1 != 293 and y1 != 576 and x2 != 307 and y2 != 590 and px2 < 600:
drawpad.move(player,8,0)
def collisionDetect(self):
global drawpad
global bricklist
global direction
global angle
global overlap
global listPlace
global length
x1,y1,x2,y2 = drawpad.coords(ball)
overlap = drawpad.find_overlapping(x1,y1,x2,y2)
length = len(overlap)
if length > 1:
listPlace = overlap[1] - 3
return True
self.score = self.score + 5
self.scoreTxt.config(text=str(self.score))
app = myApp(root)
root.mainloop()
For a project in my computer class I am creating a brick breaker style game, and I'm just about done with writing it however the collision detection isn't working. There is a message when I run the program that says:
invalid command name "182718608Lcallit"
while executing
"182718608Lcallit"
("after" script)
What does that mean and why isn't the collision detection functioning?
Okay edit:
I have another slightly different version of the same code, which I know the collision detection is working but only the score changes, the bricks don't disappear and the ball doesn't bounce. I don't understand why this following version has a working score while the first doesn't, and I don't understand why neither of the codes successfully delete the brick and bounce the ball. This code also has a similar error message, with just different numbers. Can someone please explain, I'm new to coding.
from Tkinter import *
root = Tk()
drawpad = Canvas(root, width=600,height=600, background='white')
player = drawpad.create_rectangle(260,590,340,595, fill = "blue")
ball = drawpad.create_oval(293,576,307,590, fill = "white")
brick1 = drawpad.create_rectangle(30,20,80,50, fill='green')
brick2 = drawpad.create_rectangle(30,100,80,130, fill='green')
brick3 = drawpad.create_rectangle(30,180,80,210, fill='green')
brick4 = drawpad.create_rectangle(100,20,150,50, fill='green')
brick5 = drawpad.create_rectangle(100,100,150,130, fill='green')
brick6 = drawpad.create_rectangle(100,180,150,210, fill='green')
brick7 = drawpad.create_rectangle(170,20,220,50, fill='green')
brick8 = drawpad.create_rectangle(170,100,220,130, fill='green')
brick9 = drawpad.create_rectangle(170,180,220,210, fill='green')
brick10= drawpad.create_rectangle(240,20,290,50, fill='green')
brick11= drawpad.create_rectangle(240,100,290,130, fill='green')
brick12= drawpad.create_rectangle(240,180,290,210, fill='green')
brick13= drawpad.create_rectangle(310,20,360,50, fill='green')
brick14= drawpad.create_rectangle(310,100,360,130, fill='green')
brick15= drawpad.create_rectangle(310,180,360,210, fill='green')
brick16= drawpad.create_rectangle(380,20,430,50, fill='green')
brick17= drawpad.create_rectangle(380,100,430,130, fill='green')
brick18= drawpad.create_rectangle(380,180,430,210, fill='green')
brick19= drawpad.create_rectangle(450,20,500,50, fill='green')
brick20= drawpad.create_rectangle(450,100,500,130, fill='green')
brick21= drawpad.create_rectangle(450,180,500,210, fill='green')
brick22= drawpad.create_rectangle(520,20,570,50, fill='green')
brick23= drawpad.create_rectangle(520,100,570,130, fill='green')
brick24= drawpad.create_rectangle(520,180,570,210, fill='green')
brickA1 = drawpad.create_rectangle(60,60,110,90, fill='cyan')
brickA2 = drawpad.create_rectangle(60,140,110,170, fill='cyan')
brickA3 = drawpad.create_rectangle(130,60,180,90, fill='cyan')
brickA4 = drawpad.create_rectangle(130,140,180,170, fill='cyan')
brickA5 = drawpad.create_rectangle(200,60,250,90, fill='cyan')
brickA6 = drawpad.create_rectangle(200,140,250,170, fill='cyan')
brickA7 = drawpad.create_rectangle(270,60,320,90, fill='cyan')
brickA8 = drawpad.create_rectangle(270,140,320,170, fill='cyan')
brickA9 = drawpad.create_rectangle(340,60,390,90, fill='cyan')
brickA10= drawpad.create_rectangle(340,140,390,170, fill='cyan')
brickA11= drawpad.create_rectangle(410,60,460,90, fill='cyan')
brickA12= drawpad.create_rectangle(410,140,460,170, fill='cyan')
brickA13= drawpad.create_rectangle(480,60,530,90, fill='cyan')
brickA14= drawpad.create_rectangle(480,140,530,170, fill='cyan')
bricklist = [brick1,brick2,brick3,brick4,brick5,brick6,brick7,brick8,brick9,brick10,brick11,brick12,brick13,brick14,brick15,brick16,brick17,brick18,brick19,brick20,brick21,brick22,brick23,brick24,brickA1,brickA2,brickA3,brickA4,brickA5,brickA6,brickA7,brickA8,brickA9,brickA10,brickA11,brickA12,brickA13,brickA14]
direction = 0
import random
randAngle = 0
angle = 0
overlap = 0
listPlace = 0
length = 0
brick = 0
class myApp(object):
def __init__(self, parent):
global drawpad
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
# Score
self.prompt = "Score:"
self.label1 = Label(root, text=self.prompt, width=len(self.prompt), bg='green')
self.label1.pack()
self.score = 0
self.scoreTxt = Label(root, text=str(self.score), width=3, bg='green')
self.scoreTxt.pack()
drawpad.pack()
root.bind_all('<Key>', self.key)
self.animate()
def animate(self):
global drawpad
global ball
global direction
global angle
global randAngle
global listPlace
global brick
x1,y1,x2,y2 = drawpad.coords(ball)
px1,py1,px2,py2 = drawpad.coords(player)
if y1 <= 0:
direction = 5
elif x1 >= px1 and x2 <= px2 and y2 >= py1:
direction = -5
randAngle = random.randint(0,12)
angle = randAngle - 6
elif x1 <= 0 and y2 <= 600 or x2 >= 600 and y2 <= 600:
angle = -angle
didWeHit = self.collisionDetect()
if didWeHit == True:
brick = bricklist[listPlace]
bx1,by1,bx2,by2 = drawpad.coords(brick)
if x1 <= bx1 or x2 >= bx2:
angle = -angle
if y1 <= by1 or by2 >= y2:
direction = -direction
drawpad.move(ball, angle, direction)
drawpad.after(5,self.animate)
def key(self,event):
global drawpad
global player
global ball
global direction
x1,y1,x2,y2 = drawpad.coords(ball)
px1,py1,px2,py2 = drawpad.coords(player)
if event.char == " ":
direction = -5
if event.char == "a":
if x1 != 293 and y1 != 576 and x2 != 307 and y2 != 590 and px1 > 0:
drawpad.move(player,-8,0)
if event.char == "d":
if x1 != 293 and y1 != 576 and x2 != 307 and y2 != 590 and px2 < 600:
drawpad.move(player,8,0)
def collisionDetect(self):
global drawpad
global bricklist
global direction
global angle
global overlap
global listPlace
global length
x1,y1,x2,y2 = drawpad.coords(ball)
overlap = drawpad.find_overlapping(x1,y1,x2,y2)
length = len(overlap)
if length > 1:
listPlace = overlap[1] - 3
brick = bricklist[listPlace]
bx1,by1,bx2,by2 = drawpad.coords(brick)
if x1 <= bx1 or x2 >= bx2:
angle = -angle
if y1 <= by1 or by2 >= y2:
direction = -direction
self.score = self.score + 5
self.scoreTxt.config(text=str(self.score))
return True
drawpad.delete(brick)
app = myApp(root)
root.mainloop()
Your collisionDetect method deletes a brick, but then you try to do things with the brick later (in the if didWeHit part of your code). Make sure that you wait to delete the brick until you've done everything you need to with it!
In Tkinter, this kind of error occurs if you try to do something with an object that has been deleted. Tkinter assigns a number to each Tk object that it creates, which is the funny number in your error that you got. It is trying to execute the command that it named 182718608Lcallit, but that doesn't exist any more since you deleted the corresponding brick.
Related
Unable to record the score and high score properly
I'm tryin to make a snake game, but I am unable to record the score and high-score properly. Whenever the snake collects an item, all that happens is that the high score increases, but not the general score. from tkinter import * from settings import * import random import turtle import pygame as pg import keyboard from os import path global game_state class Snake: def __init__(self): self.body_size = BODY_PARTS self.coordinates = [] self.squares = [] for i in range(0, BODY_PARTS): self.coordinates.append([0, 0]) for x, y in self.coordinates: square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR, tag="snake") self.squares.append(square) class Food: def __init__(self): x = random.randint(0, (GAME_WIDTH / SPACE_SIZE)-1) * SPACE_SIZE y = random.randint(0, (GAME_HEIGHT / SPACE_SIZE) - 1) * SPACE_SIZE self.coordinates = [x, y] canvas.create_oval(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=FOOD_COLOR, tag="food") class TryAgain: def __init__(self): x = (GAME_WIDTH / SPACE_SIZE)-1 * SPACE_SIZE y = (GAME_HEIGHT / SPACE_SIZE) - 1 * SPACE_SIZE self.coordinates = [x, y] def show_title_screen(): canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/4, font=('times new roman',70), text="SNAKE!", fill="Green", tag="title") canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2, font=('times new roman',22), text="Use the arrow keys to move", fill="Green", tag="title") def next_turn(snake, food): x, y = snake.coordinates[0] if direction == "up": y -= SPACE_SIZE elif direction == "down": y += SPACE_SIZE elif direction == "left": x -= SPACE_SIZE elif direction == "right": x += SPACE_SIZE snake.coordinates.insert(0, (x, y)) square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR) snake.squares.insert(0, square) if x == food.coordinates[0] and y == food.coordinates[1]: global score global high_score score += 1 label.config(text="Score:{}".format(score)) if score >= high_score: high_score = score label.config(text="High Score:{}".format(high_score)) canvas.delete("food") food = Food() else: del snake.coordinates[-1] canvas.delete(snake.squares[-1]) del snake.squares[-1] if check_collisions(snake): game_over() else: window.after(SPEED, next_turn, snake, food) def change_direction(new_direction): global direction if new_direction == 'left': if direction != 'right': direction = new_direction elif new_direction == 'right': if direction != 'left': direction = new_direction elif new_direction == 'up': if direction != 'down': direction = new_direction elif new_direction == 'down': if direction != 'up': direction = new_direction def check_collisions(snake): x, y = snake.coordinates[0] if x < 0 or x >= GAME_WIDTH: return True elif y < 0 or y >= GAME_HEIGHT: return True for body_part in snake.coordinates[1:]: if x == body_part[0] and y == body_part[1]: return True return False def game_over(): canvas.delete(ALL) canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/4, font=('times new roman',70), text="GAME OVER", fill="orange", tag="gameover") button = TryAgain() def load_data(self): self.dir = path.dirname(__dirname__) with open(path.join(self.dir, HS_FILE), 'w') as f: try: self.highscore = int(f.read()) except: self.highscore = 0 window = Tk() window.title("Snake game") window.resizable(False, False) score = 0 high_score = 0 direction = 'down' label = Label(window, text="Score:{}".format(score), font=('times new roman', 40)) label.pack() label = Label(window, text="High Score:{}".format(high_score), font=('times new roman', 40)) label.pack() canvas = Canvas(window, bg=BACKGROUND_COLOR, height=GAME_HEIGHT, width=GAME_WIDTH) canvas.pack() window.update() window_width = window.winfo_width() window_height = window.winfo_height() screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x = int((screen_width/2) - (window_width/2)) y = int((screen_height/2) - (window_height/2)) window.geometry(f"{window_width}x{window_height}+{x}+{y}") show_title_screen() window.bind('<Left>', lambda event: change_direction('left')) window.bind('<Right>', lambda event: change_direction('right')) window.bind('<Up>', lambda event: change_direction('up')) window.bind('<Down>', lambda event: change_direction('down')) snake = Snake() food = Food() next_turn(snake, food) window.mainloop() In one instance, I tried to rearrange the canvas codes that involved "score" and "high score", and tried to make it so that when the high score is greater than the score, it would start counting, but that still doesn't work.
I don't know anything about tkinter. But I think the problem is due to this: label = Label(window, text="Score:{}".format(score), font=('times new roman', 40)) label.pack() label = Label(window, text="High Score:{}".format(high_score), font=('times new roman', 40)) label.pack() You're using the same variable name for both labels. And then when you update the scores: score += 1 label.config(text="Score:{}".format(score)) if score >= high_score: high_score = score label.config(text="High Score:{}".format(high_score)) The variable name label refers only to the high score label. I think you can fix this by using different variable names for the two labels, i.e. something like score_label and high_score_label.
tkinter breakout game beginner problems
I am trying to build a Breakout game for python tkinter with different levels. I don't quite understand the self and __init__ functions in this code. Is there a way to create the game without those functions or replacing them with simpler functions if it's possible? Also, I don't quite understand the + self.radius parts in the code as well. import tkinter as tk def new_window(): root1 = tk.Tk() root1.title('Jeu') game = Game(root1) class GameObject(object): def __init__(self, canvas, item): self.canvas = canvas self.item = item def get_position(self): return self.canvas.coords(self.item) def move(self, x, y): self.canvas.move(self.item, x, y) def delete(self): self.canvas.delete(self.item) class Ball(GameObject): def __init__(self, canvas, x, y): self.radius = 10 self.direction = [1, -1] # increase the below value to increase the speed of ball self.speed = 5 item = canvas.create_oval(x - self.radius, y - self.radius, x + self.radius, y + self.radius, fill='white') super(Ball, self).__init__(canvas, item) def update(self): coords = self.get_position() width = self.canvas.winfo_width() if coords[0] <= 0 or coords[2] >= width: self.direction[0] *= -1 if coords[1] <= 0: self.direction[1] *= -1 x = self.direction[0] * self.speed y = self.direction[1] * self.speed self.move(x, y) def collide(self, game_objects): coords = self.get_position() x = (coords[0] + coords[2]) * 0.5 if len(game_objects) > 1: self.direction[1] *= -1 elif len(game_objects) == 1: game_object = game_objects[0] coords = game_object.get_position() if x > coords[2]: self.direction[0] = 1 elif x < coords[0]: self.direction[0] = -1 else: self.direction[1] *= -1 for game_object in game_objects: if isinstance(game_object, Brick): game_object.hit() class Paddle(GameObject): def __init__(self, canvas, x, y): self.width = 80 self.height = 10 self.ball = None item = canvas.create_rectangle(x - self.width / 2, y - self.height / 2, x + self.width / 2, y + self.height / 2, fill='#FFB643') super(Paddle, self).__init__(canvas, item) def set_ball(self, ball): self.ball = ball def move(self, offset): coords = self.get_position() width = self.canvas.winfo_width() if coords[0] + offset >= 0 and coords[2] + offset <= width: super(Paddle, self).move(offset, 0) if self.ball is not None: self.ball.move(offset, 0) class Brick(GameObject): COLORS = {1: '#4535AA', 2: '#ED639E', 3: '#8FE1A2'} def __init__(self, canvas, x, y, hits): self.width = 75 self.height = 20 self.hits = hits color = Brick.COLORS[hits] item = canvas.create_rectangle(x - self.width / 2, y - self.height / 2, x + self.width / 2, y + self.height / 2, fill=color, tags='brick') super(Brick, self).__init__(canvas, item) def hit(self): self.hits -= 1 if self.hits == 0: self.delete() else: self.canvas.itemconfig(self.item, fill=Brick.COLORS[self.hits]) class Game(tk.Frame): def __init__(self, master): super(Game, self).__init__(master) self.lives = 3 self.width = 610 self.height = 400 self.canvas = tk.Canvas(self, bg='#D6D1F5', width=self.width, height=self.height, ) self.canvas.pack() self.pack() self.items = {} self.ball = None self.paddle = Paddle(self.canvas, self.width / 2, 326) self.items[self.paddle.item] = self.paddle # adding brick with different hit capacities - 3,2 and 1 for x in range(5, self.width - 5, 75): self.add_brick(x + 37.5, 50, 3) self.add_brick(x + 37.5, 70, 2) self.add_brick(x + 37.5, 90, 1) self.hud = None self.setup_game() self.canvas.focus_set() self.canvas.bind('<Left>', lambda _: self.paddle.move(-10)) self.canvas.bind('<Right>', lambda _: self.paddle.move(10)) def setup_game(self): self.add_ball() self.update_lives_text() self.text = self.draw_text(300, 200, 'Press Space to start') self.canvas.bind('<space>', lambda _: self.start_game()) def add_ball(self): if self.ball is not None: self.ball.delete() paddle_coords = self.paddle.get_position() x = (paddle_coords[0] + paddle_coords[2]) * 0.5 self.ball = Ball(self.canvas, x, 310) self.paddle.set_ball(self.ball) def add_brick(self, x, y, hits): brick = Brick(self.canvas, x, y, hits) self.items[brick.item] = brick def draw_text(self, x, y, text, size='40'): font = ('Forte', size) return self.canvas.create_text(x, y, text=text, font=font) def update_lives_text(self): text = 'Lives: %s' % self.lives if self.hud is None: self.hud = self.draw_text(50, 20, text, 15) else: self.canvas.itemconfig(self.hud, text=text) def start_game(self): self.canvas.unbind('<space>') self.canvas.delete(self.text) self.paddle.ball = None self.game_loop() def game_loop(self): self.check_collisions() num_bricks = len(self.canvas.find_withtag('brick')) if num_bricks == 0: self.ball.speed = None self.draw_text(300, 200, 'You win! You the Breaker of Bricks.') elif self.ball.get_position()[3] >= self.height: self.ball.speed = None self.lives -= 1 if self.lives < 0: self.draw_text(300, 200, 'You Lose! Game Over!') else: self.after(1000, self.setup_game) else: self.ball.update() self.after(50, self.game_loop) def check_collisions(self): ball_coords = self.ball.get_position() items = self.canvas.find_overlapping(*ball_coords) objects = [self.items[x] for x in items if x in self.items] self.ball.collide(objects) game.mainloop() root = tk.Tk() root.title('Jeu') game = Game(root) btn1= tk.Button(root, text='Click me', bd= '5',command=new_window)
Is there a way to create the game without those functions or replacing them with simpler functions if it's possible? As a proof of concept below a much simpler code of the game for beginners rewritten to get rid of classes as an example that it is possible to write the game without them. You can compare the rewritten code with the original code (link is given in a comment at the beginning of code) to see how it can be done and then rewrite yourself a more complex code: # https://codingshiksha.com/python/python-3-tkinter-2d-brick-breaker-breakout-game-in-gui-desktop-app-full-project-for-beginners/ from tkinter import Tk, Canvas import random import time tk = Tk() tk.title("Game") tk.resizable(0, 0) tk.wm_attributes("-topmost", 1) canvas_height = 400 canvas_width = 500 canvas = Canvas(tk, width=canvas_width, height=canvas_height, bd=0, highlightthickness=0) canvas.pack() tk.update() ball_id = canvas.create_oval(10, 10, 25, 25, fill='red') canvas.move(ball_id, 245, 100) starts = [-3, -2, -1, 1, 2, 3] random.shuffle(starts) ball_x = starts[0] ball_y = -3 paddle_id = canvas.create_rectangle(0, 0, 100, 10, fill='blue') canvas.move(paddle_id, 200, 300) paddle_x = 0 def draw_ball(): global ball_x, ball_y canvas.move(ball_id, ball_x, ball_y) pos = canvas.coords(ball_id) if pos[1] <= 0: ball_y = 3 if pos[3] >= canvas_height: ball_y = -3 if hit_paddle(pos) == True: ball_y = -3 if pos[0] <= 0: ball_x = 3 if pos[2] >= canvas_width: ball_x = -3 def hit_paddle(pos): paddle_pos = canvas.coords(paddle_id) if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]: if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]: return True return False def turn_left(evt): global paddle_x paddle_x = -2 def turn_right(evt): global paddle_x paddle_x = 2 def draw_paddle(): global paddle_x canvas.move(paddle_id, paddle_x, 0) pos = canvas.coords(paddle_id) if pos[0] <= 0: paddle_x = 0 elif pos[2] >= canvas_width: paddle_x = 0 canvas.bind_all('<KeyPress-Left>' , turn_left) canvas.bind_all('<KeyPress-Right>', turn_right) while True: draw_ball() draw_paddle() tk.update_idletasks() tk.update() time.sleep(0.01) By the way: not always usage of classes makes sense if there is a simpler and sometimes even more intuitive way of achieving the same result.
The init function is a part of a class that is run every time you create an instance of that class. Self makes the variable an attribute to the class, or that it can be accessed in other parts of your code by, for example ball = Ball() print(ball.radius). There is more information here at the documentation: https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces . If you are using object-oriented programming, there aren't any alternatives to self and init. The only way not to use them would be to not use classes in your code. The self.radius part of the code is creating a variable representing the size of the ball. This link describes how the different points you set creates the shape of the oval: https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/create_oval.html. The short answer is that self.radius creates the size by spacing out the two points of the oval.
error : _tkinter.TclError: invalid command name ".!canvas"
I was trying to make a catch the egg game but it didn't go to plan and it brought up an error saying _tkinter.TclError: invalid command name ".!canvas". I have searched the whole of the internet about the problem but none of the solutions that people the person helped me at all. anyways this is what I have written so far it would be appreciated if someone helped especailly because I'm new. from itertools import cycle from random import randrange from tkinter import Canvas, Tk, messagebox, font canvas_width = 800 canvas_height = 400 root = Tk() c = Canvas(root, width = canvas_width, height = canvas_height, background="deep sky blue") c.create_rectangle(-5, canvas_height - 100, canvas_width + 5, canvas_height + 5, fill = "sea green", width=0) c.create_oval(-80, -80, 120, 120, fill="orange", width=0) c.pack() color_cycle = cycle(["light blue", "light green", "light pink", "light yellow", "light cyan"]) egg_width = 45 egg_height = 55 egg_score = 10 egg_speed = 500 egg_interval = 4000 difficulty_factor = 0.95 catcher_color = "magenta" catcher_width = 100 catcher_height = 100 catcher_start_x = canvas_width / 2 - catcher_width / 2 catcher_start_y = canvas_height - catcher_width - 20 catcher_start_x2 = catcher_start_x = catcher_width catcher_start_y2 = catcher_start_y + catcher_height catcher = c.create_arc(catcher_start_x, catcher_start_y, catcher_start_x2, catcher_start_y2, start = 200, extent = 140, \ style = "arc", outline = catcher_color, width = 3) game_font = font.nametofont("TkFixedFont") game_font.config(size = 18) score = 0 score_text = c.create_text(10, 10, anchor="nw" , font=game_font, fill="darkblue", text="Score: " + str(score)) lives_remaining = 1 lives_text = c.create_text(canvas_width - 10, 10, anchor="ne", font = game_font, fill="darkblue", text = "Lives " + str(lives_remaining)) eggs = [] def create_egg(): x = randrange(10, 740) y = 40 new_egg = c.create_oval(x, y, x + egg_width, y + egg_height, fill = next(color_cycle), width=0) eggs.append(new_egg) root.after(egg_interval, create_egg) def move_eggs(): for egg in eggs: (egg_x, egg_y, egg_x2, egg_y2) = c.coords(egg) c.move(egg, 0, 10) if egg_y2 > canvas_height: egg_dropped(egg) root.after(egg_speed, move_eggs) def egg_dropped(egg): eggs.remove(egg) lose_a_life() if lives_remaining == 0: messagebox.showinfo("Game Over!", "Final Score: " + str(score)) root.destroy() def lose_a_life(): global lives_remaining lives_remaining -= 1 c.itemconfigure(lives_text, text = "Lives: " + str(lives_remaining)) def check_catch(): (catcher_x, catcher_y, catcher_x2, catcher_y2) = c.coords(catcher) for egg in eggs: (egg_x, egg_y, egg_x2, egg_y2) = c.coords(egg) if catcher_x < egg_x and egg_x2 < catcher_x2 and catcher_y2 - egg_y2 - egg_y2 < 40: eggs.remove(egg) c.delete(egg) increase_score(egg_score) root.after(100, check_catch) def increase_score(points): global score, egg_speed, egg_interval score += points egg_speed = int(egg_speed * difficulty_factor) egg_interval = int(egg_interval * difficulty_factor) c.itemconfigure(score_text, text="Score: " + str(score)) def move_left(event): (x1, y1, x2, y2) = c.coords(catcher) if x1 > 0: c.move(catcher, -20, 0) def move_right(event): (x1, y1, x2, y2) = c.coords(catcher) if x2 < canvas_width: c.move(catcher, 20, 0) c.bind("<Left>" , move_left) c.bind("<Right>" , move_right) c.focus_set() root.after(1000, create_egg) root.after(1000, move_eggs) root.after(1000, check_catch) root.mainloop()
I changed the way it checks for hits and it works now. def check_catch(): catcher_x, catcher_y, catcher_x2, catcher_y2 = c.coords(catcher) for egg in eggs: egg_x, egg_y, egg_x2, egg_y2 = c.coords(egg) print(c.coords(egg)) if egg_x<catcher_x<egg_x2 and (catcher_y2 - egg_y2) < 5: eggs.remove(egg) c.delete(egg) increase_score(egg_score) root.after(100, check_catch)
moving circle using tkinter
i am new to programming and am currently trying to get a chemistry stimulation with beaker which produces bubbles. after a lot of research I have managed to get a circle moving and also created a rectangle representing a beaker. however I would like the circles to be formed at the top of the beaker and move upwards in random and destroy itself when it meets the top which I just cant figure out. I would be very grateful if anyone can help me. thank you in advance. my code is: from tkinter import * x = 10 y = 10 a = 50 b = 50 x_vel = 5 y_vel = 5 def move(): global x global y global x_vel global y_vel if x < 0: x_vel = 5 if x > 350: x_vel = -5 if y < 0: y_vel = 5 if y > 150: y_vel = -5 canvas1.move(circle, x_vel, y_vel) coordinates = canvas1.coords(circle) x = coordinates[0] y = coordinates[1] window.after(33, move) window = Tk() window.geometry("1000x1000") canvas1=Canvas(window, height = 1000, width= 1000) canvas1.grid (row=0, column=0, sticky=W) coord = [x, y, a, b ] circle = canvas1.create_oval(coord, outline="red", fill="red") coord = [230, 270, 270, 310] rect2 = canvas1.create_rectangle(coord, outline="Blue", fill="Blue") move() window.mainloop ()
Using random you can move it randomly. If y < -height then object left screen and you can move it to start position. import tkinter as tk import random def move(): global x global y global x_vel global y_vel # get random move x_vel = random.randint(-5, 5) y_vel = -5 canvas1.move(circle, x_vel, y_vel) coordinates = canvas1.coords(circle) x = coordinates[0] y = coordinates[1] # if outside screen move to start position if y < -height: x = start_x y = start_y canvas1.coords(circle, x, y, x+width, y+height) window.after(33, move) # --- main --- start_x = 230 start_y = 270 x = start_x y = start_y width = 50 height = 50 x_vel = 5 y_vel = 5 window = tk.Tk() window.geometry("1000x1000") canvas1 = tk.Canvas(window, height=1000, width=1000) canvas1.grid(row=0, column=0, sticky='w') coord = [x, y, x+width, y+height] circle = canvas1.create_oval(coord, outline="red", fill="red") coord = [x, y, x+40, y+40] rect2 = canvas1.create_rectangle(coord, outline="Blue", fill="Blue") move() window.mainloop () EDIT: using class you can easily have many items import tkinter as tk import random class Bubble(): def __init__(self, canvas, x, y, size, color='red'): self.canvas = canvas self.x = x self.y = y self.start_x = x self.start_y = y self.size = size self.color = color self.circle = canvas.create_oval([x, y, x+size, y+size], outline=color, fill=color) def move(self): x_vel = random.randint(-5, 5) y_vel = -5 self.canvas.move(self.circle, x_vel, y_vel) coordinates = self.canvas.coords(self.circle) self.x = coordinates[0] self.y = coordinates[1] # if outside screen move to start position if self.y < -self.size: self.x = self.start_x self.y = self.start_y self.canvas.coords(self.circle, self.x, self.y, self.x + self.size, self.y + self.size) def move(): for item in bubbles: item.move() window.after(33, move) # --- main --- start_x = 230 start_y = 270 window = tk.Tk() window.geometry("1000x1000") canvas = tk.Canvas(window, height=1000, width=1000) canvas.grid(row=0, column=0, sticky='w') bubbles = [] for i in range(5): offset = random.randint(10, 20) b = Bubble(canvas, start_x+10, start_y-offset, 20, 'red') bubbles.append(b) for i in range(5): offset = random.randint(0, 10) b = Bubble(canvas, start_x+10, start_y-offset, 20, 'green') bubbles.append(b) coord = [start_x, start_y, start_x+40, start_y+40] rect = canvas.create_rectangle(coord, outline="Blue", fill="Blue") move() window.mainloop ()
Python: How to get score board on canvas
I want the score to show on the canvas but so far to no avail. I thought I could just use: self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is:",self.score) Didn't work unfortunately, so how do I get the score to show on the canvas? Also wondering how to get a message box to pop up saying "You won!" when the score = 10. And lastly, how to get a message box to pop up saying "You lost if the score < 10 AND there is no even numbers on the canvas. Code so far using python 3.3: def click(self, event): if self.canvas.find_withtag(CURRENT): item_uid = event.widget.find_closest(event.x, event.y)[0] is_even = False try: # clicked oval self.bubbles[item_uid] except KeyError: # clicked the text for key, value in self.bubbles.iteritems(): if item_uid == value[5]: # comparing to text_id if value[4] % 2 == 0: is_even = True self.canvas.delete(key) # deleting oval self.canvas.delete(item_uid) # deleting text else: if self.bubbles[item_uid][4] % 2 == 0: is_even = True self.canvas.delete(item_uid) # deleting oval self.canvas.delete(self.bubbles[item_uid][5]) # deleting text if is_even: self.score += 1 else: self.score -= 1 if self.score == 10: print ("You won") print (self.score)
That is a fun game! The script I gave below shows you how to do everything you want (display the current score and show messages). The lines with ## above them are what I added: from tkinter import * import random ## from tkinter.messagebox import showinfo class BubbleFrame: def __init__(self, root): root.title("Math Bubbles") self.bubbles = {} self.score = 0 Button(root, text="Start", width=8, command=self.initialize_bubbles).pack() # This button starts the game, making the bubbles move across the screen Button(root, text="Quit", width=8, command=quit).pack() self.canvas = Canvas(root, width=800, height=650, bg='#afeeee') self.canvas.create_text(400, 30, fill="darkblue", font="Times 20 italic bold", text="Click the bubbles that are multiples of two.") ## self.current_score = self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is:") self.canvas.pack() def initialize_bubbles(self): for each_no in range(1, 21): xval = random.randint(5, 765) yval = random.randint(5, 615) oval_id = self.canvas.create_oval(xval, yval, xval + 30, yval + 30,fill="#00ffff", outline="#00bfff", width=5, tags="bubble") text_id = self.canvas.create_text(xval + 15, yval + 15, text=each_no, tags="bubble") self.canvas.tag_bind("bubble", "<Button-1>", lambda x: self.click(x)) self.bubbles[oval_id] = (xval, yval, 0, 0, each_no, text_id) def click(self, event): if self.canvas.find_withtag(CURRENT): item_uid = event.widget.find_closest(event.x, event.y)[0] is_even = False try: self.bubbles[item_uid] except KeyError: for key, value in self.bubbles.iteritems(): if item_uid == value[5]: if value[4] % 2 == 0: is_even = True self.canvas.delete(key) self.canvas.delete(item_uid) else: if self.bubbles[item_uid][4] % 2 == 0: is_even = True self.canvas.delete(item_uid) self.canvas.delete(self.bubbles[item_uid][5]) if is_even: self.score += 1 else: self.score -= 1 if self.score == 10: ## showinfo("Winner", "You won!") ## self.canvas.delete(self.current_score) ## self.current_score = self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is: %s"%self.score) def loop(self, root): for oval_id, (x, y, dx, dy, each_no, text_id) in self.bubbles.items(): dx += random.randint(-1, 1) dy += random.randint(-1, 1) dx, dy = max(-5, min(dx, 5)), max(-5, min(dy, 5)) if not 0 < x < 770: dx = -dx if not 0 < y < 620: dy = -dy self.canvas.move(oval_id, dx, dy) self.canvas.move(text_id, dx, dy) self.bubbles[oval_id] = (x + dx, y + dy, dx, dy, each_no, text_id) root.after(100, self.loop, root) if __name__ == "__main__": root = Tk() frame = BubbleFrame(root) frame.loop(root) root.mainloop()
from http://www.tutorialspoint.com/python/tk_messagebox.htm import tkMessageBox tkMessageBox.showinfo("Winner", "You won!") you can use place instead of pack to put elements above the canvas in the same Frame or Tk. Label(root, text="Quit").place() place() relx, rely : Horizontal and vertical offset as a float between 0.0 and 1.0, as a fraction of the height and width of the parent widget. x, y : Horizontal and vertical offset in pixels. from http://www.tutorialspoint.com/python/tk_place.htm