I have constructed a lattice of particles with tkinter and now I would like each particle in the lattice to move according to a list of x and y coordinates that I read into an array from two text files. I have tried to create a class with a function inside that defines movement with the canvas.move function but I get the error TclError: wrong # coordinates: expected 0 or 4, got 400. How to get around this?
from Tkinter import *
import random
import time
import csv
tk = Tk()
N = 100
T = 500
canvas = Canvas(tk, width=100, height=100)
tk.title("Test")
canvas.pack()
n = 5
t = 10
step1 = []
step2 = []
textFile1 = open('/Users/francislempp/Desktop/major project/C++ programs/Molecular Dynamics 2D/Molecular_Dynamics_2D-gupnvjunowwmjcfiyoursdhzytow/Build/Products/Debug/motionX', 'r')
lines = textFile1.readlines()
for line in lines:
step1.append(line.split(" "))
textFile2 = open('/Users/francislempp/Desktop/major project/C++ programs/Molecular Dynamics 2D/Molecular_Dynamics_2D-gupnvjunowwmjcfiyoursdhzytow/Build/Products/Debug/motionY', 'r')
lines = textFile2.readlines()
for line in lines:
step2.append(line.split(" "))
def moves(xspeed, yspeed):
canvas.move(xspeed, yspeed)
class Ball:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.shape = canvas.create_oval((x,y,x,y), fill = color)
def move(self):
canvas.move(self.shape, self.x, self.y)
pos = canvas.coords(self.shape)
if pos[3] >= 100 or pos[1] <= 0:
self.y = -self.y
if pos[2] > 100 or pos[0] <= 0:
self.x = -self.x
def delete(self):
canvas.delete(self.shape)
balls = []
for x in range(4,100,10):
for y in range(4,100,10):
#canvas.create_oval((x,y,x,y), fill='red')
Ball(x,y,"red")
tk.update()
for i in step1:
for j in step2:
Ball(i,j,"red")
Ball.move()
tk.update()
tk.mainloop()
You have to save items on list and use this list to move
I copy example from previous question
import tkinter as tk
import random
# --- functions ---
def move():
for point_id in points:
x = random.randint(-1, 1)
y = random.randint(-1, 1)
canvas.move(point_id, x, y)
root.after(100, move)
# --- main ---
points = []
root = tk.Tk()
canvas = tk.Canvas(root, width=100, height=100)
canvas.pack()
for x in range(4, 100, 10):
for y in range(4, 100, 10):
point_id = canvas.create_oval(x, y, x, y, fill="red")
points.append(point_id)
move()
root.mainloop()
EDIT: with your class but without data from file.
I use random instead of data from files to show how to create particles and move them.
Every ball moves with own speed/direction which change when touch border.
import tkinter as tk
import random
# --- classes ---
class Ball:
def __init__(self, x, y, speed_x, speed_y, color):
self.speed_x = speed_x
self.speed_y = speed_y
self.shape = canvas.create_oval((x, y, x, y), fill=color)
def move(self):
canvas.move(self.shape, self.speed_x, self.speed_y)
pos = canvas.coords(self.shape)
# change speed/direction when touch border
if pos[3] >= 100 or pos[1] <= 0:
self.speed_y = -self.speed_y
if pos[2] > 100 or pos[0] <= 0:
self.speed_x = -self.speed_x
def delete(self):
canvas.delete(self.shape)
# --- functions ---
def move():
for ball in all_balls:
ball.move()
root.after(100, move)
# --- main ---
root = tk.Tk()
canvas = tk.Canvas(root, width=100, height=100)
canvas.pack()
all_balls = []
for x in range(4, 100, 10):
for y in range(4, 100, 10):
offset_x = random.randint(-2, 2)
offset_y = random.randint(-2, 2)
all_balls.append(Ball(x, y, offset_x, offset_y, "red"))
move()
root.mainloop()
Related
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.
import time
from tkinter import *
import random
class SpaceField:
def __init__(self):
self.window = Tk()
self.window.title("Asteriods")
self.canvas = self.canvas_display() #creates canvas
self.asteriods = self.asteriod_creation_seperation() #creates asteroids
self.active = True
self.move_active() #Moves asteroids
self.canvas.update()
def asteriod_creation_seperation(self): #creation of multple asteriods
asteriod_spacingx = random.randint(1,800)
asteriod_spacingy = random.randint(1,800)
asteriod_list = list() # could list([])
for i in range(15):
asteriod = self.canvas.create_oval( 30, 50 , 80 , 100 , tags="asteriod", width=2, outline="white")
asteriod_list.append("asteriod")
self.canvas.move(asteriod, asteriod_spacingx, asteriod_spacingy)
asteriod_spacingx = random.randint(1,500)
asteriod_spacingy = random.randint(1,500)
print(asteriod_spacingy)
return asteriod_list
Asteroid Creation. Creates asteroids and gives them random positions.
def asteriod_update(self): #asteriods movement method #MAin problem
x12 = 1
self.canvas.move("asteriod", 3, x12)
pos = self.canvas.coords("asteriod")
print(pos)
if (pos)[2] > 500:
x12 *= 5
I think this is where I need to add the collision detection. I just have no idea how to combine the lists of the circles and the collisions.
def move_active(self): #Basically a true loop
if self.active:
self.asteriod_update()
self.window.after(40, self.move_active)
def canvas_display(self): #canvas
canvas = Canvas(self.window, width=500, height=400, background='black')
canvas.pack(expand=True, fill="both")
canvas.update()
return canvas
Canvas display nothing special
def run(self):
self.window.mainloop()
if __name__ == '__main__':
SpaceF = SpaceField()
SpaceF.run()
Asteroids is a classic game but there were a number of problems in your code. The main one was calling move_active during initialization. This prevented the code from completing its mainloop initialization.
The other problem was the asteroid_update method that basically didn't do anything, also using tags to control all asteroids didn't work either.
Everything else was OK, although you might consider using polygons.
Here is one way to produce a bouncing objects program. I've inserted remarks that describe the methods used.
Objects change the speed and direction when they hit the boundary so their trajectories are randomized.
from tkinter import *
from random import randint as rnd
class SpaceField:
def __init__(self):
self.window = Tk()
self.window.title("Asteriods")
# Define canvas size and active flag
self.wide, self.high, self.active = 500, 400, True
self.canvas_display()
self.asteriod_creation_seperation()
def asteriod_creation_seperation(self):
self.asteroids, self.speed = [], []
size, radius = 50, 25
for i in range(15):
spacex = rnd(size, self.wide - size)
spacey = rnd(size, self.high - size)
self.asteroids.append( # Store oval item id
self.canvas.create_oval(
spacex, spacey, spacex+size, spacey+size,
width=2, tags = "asteriod", outline = "white"))
self.speed.append((rnd(1,4),rnd(1,4))) # Store random speed x, y
def asteriod_update(self): # MAIN DRIVER: Work on ALL asteroids
for i, a in enumerate(self.asteroids):
xx, yy = self.speed[i] # get speed data
x, y, w, h = self.canvas.coords(a)
# check for boundary hit then change direction and speed
if x < 0 or w > self.wide:
xx = -xx * rnd(1, 4)
if y < 0 or h > self.high:
yy = -yy * rnd(1, 4)
# Limit max and min speed then store it
self.speed[i] = (max( -4, min( xx, 4)), max( -4, min( yy, 4 )))
self.canvas.move(a, xx, yy) # update asteroid position
def move_active(self):
if self.active:
self.asteriod_update()
self.window.after(40, self.move_active)
def canvas_display(self):
self.canvas = Canvas(
self.window, width = self.wide,
height = self.high, background = "black")
self.canvas.pack(expand = True, fill = "both")
def run(self): # Begin asteroids here so that mainloop is executed
self.window.after(200, self.move_active)
self.window.mainloop()
if __name__ == "__main__":
SpaceF = SpaceField()
SpaceF.run()
So, yeah I'm a beginner to Tkinter Canvas Games and it's being too difficult for me to code than I thought.
Okk, let's come to the chase, below is my code:
import time
from tkinter import *
from numpy import random
class Paddle:
def __init__(self, Main_Frame):
Main_Frame.title("Paddle")
Main_Frame.geometry("300x300+630+150")
self.Random_X = random.randint(270)
self.Random_Y = random.randint(120)
self.x = 3
self.y = 3
self.Y_Position = 288
self.Can = Canvas(Main_Frame, height = 300, width = 300)
self.Can.pack()
self.paddle = self.Can.create_rectangle(0, 288, 90, 300, fill = "Aqua", outline = "Aqua")
self.ball = self.Can.create_oval(self.Random_X, self.Random_Y, self.Random_X + 20, self.Random_Y + 20, outline = "Red", fill = "Red")
self.Can.bind("<Motion>", self.Move_Paddle)
self.Move_Ball()
def Move_Ball(self):
Ball_x1, Ball_y1, Ball_x2, Ball_y2 = self.Can.coords(self.ball)
if Ball_x2 > 300:
self.x = -self.x
if Ball_y2 > 300:
self.y = -self.y
if Ball_x1 < 0:
self.x = -self.x
if Ball_y2 < 10:
self.y = -self.y
self.Can.moveto(self.ball, self.x, self.y)
Window.after(1, self.Move_Ball)
def Move_Paddle(self, event):
self.X_Position = event.x
self.Can.moveto(self.paddle, self.X_Position, self.Y_Position)
Window = Tk()
Class = Paddle(Window)
Window.mainloop()
So, my problem here is that even after calling the Move_Ball() function, it had no effect on the ball, (okk there is no error occurring though) when I tried to call the function under Move_Paddle() function (before adding the Window.after(1, self.Move_Ball) line), it worked well when I moved the mouse around the Canvas, well, I want it to automatically activate when the Paddle class is called.
I was trying to check the problem in the code by placing the function call in Move_Paddel() (with the Window.after(1, self.Move_Ball) line as in the code), it increased the trouble and insanely increased the speed of the ball.
If anyone could help me with these couple of problems, it would be highly appreciated.
Thanks in advance!
Use move() instead of moveto() method. move() method moves each of the items given by tagOrId in the canvas coordinate space by adding X amount to the x-coordinate of each point associated with the item and y amount to the y-coordinate of each point associated with the item.
Then you may increase the delay in the after() method.
Here is the working code:
import time
from tkinter import *
from numpy import random
class Paddle:
def __init__(self, Main_Frame):
Main_Frame.title("Paddle")
Main_Frame.geometry("300x300+630+150")
self.Random_X = random.randint(270)
self.Random_Y = random.randint(120)
self.x = 3
self.y = 3
self.Y_Position = 288
self.Can = Canvas(Main_Frame, height = 300, width = 300)
self.Can.pack()
self.paddle = self.Can.create_rectangle(0, 288, 90, 300, fill = "Aqua", outline = "Aqua")
self.ball = self.Can.create_oval(self.Random_X, self.Random_Y, self.Random_X + 20, self.Random_Y + 20, outline = "Red", fill = "Red")
self.Can.bind("<Motion>", self.Move_Paddle)
self.Move_Ball()
def Move_Ball(self):
Ball_x1, Ball_y1, Ball_x2, Ball_y2 = self.Can.coords(self.ball)
if Ball_x2 > 300:
self.x = -self.x
if Ball_y2 > 300:
self.y = -self.y
if Ball_x1 < 0:
self.x = -self.x
if Ball_y2 < 10:
self.y = -self.y
self.Can.move(self.ball, self.x, self.y)
Window.after(50 ,self.Move_Ball)
def Move_Paddle(self, event):
self.X_Position = event.x
self.Can.moveto(self.paddle, self.X_Position, self.Y_Position)
Window = Tk()
Class = Paddle(Window)
Window.mainloop()
I rewrote your code and now I think it works as you wished. Red ball slowly goes down:
import time
from tkinter import *
from numpy import random
class Paddle:
def __init__(self, main_frame):
self.root = main_frame
random.seed(int(time.time()))
self.x = random.randint(270)
self.y = random.randint(120)
self.Y_Position = 288
self.Can = Canvas(self.root, height=300, width=300)
self.Can.pack()
self.paddle = self.Can.create_rectangle(0, 288, 90, 300, fill="Aqua", outline="Aqua")
self.ball = self.Can.create_oval(self.x, self.y, self.x + 20, self.y + 20, outline="Red", fill="Red")
self.Can.bind("<Motion>", self.move_paddle)
self.move_ball()
def move_ball(self):
Ball_x1, Ball_y1, Ball_x2, Ball_y2 = self.Can.coords(self.ball)
# if Ball_x2 > 300:
# self.x = -self.x
# if Ball_y2 > 300:
# self.y = -self.y
# if Ball_x2 < 0:
# self.x = 300
if Ball_y2 > 300:
self.y = 0
self.y += 0.1
self.Can.moveto(self.ball, self.x, self.y)
self.root.after(1, self.move_ball)
def move_paddle(self, event):
self.X_Position = event.x
self.Can.moveto(self.paddle, self.X_Position, self.Y_Position)
root = Tk()
root.title("Paddle")
root.geometry("300x300+630+150")
Class = Paddle(root)
root.mainloop()
And please read about PEP8
You should use move() instead of moveto() inside Move_Ball() and after(1, ...) is too frequent so the ball will be moved very fast. Try after(50, ...) instead:
def Move_Ball(self):
...
self.Can.move(self.ball, self.x, self.y)
Window.after(50, self.Move_Ball)
I have created a class object Dot(), a function update() that updates its position and then generated many instances of it on canvas, adding them to a list. I run a loop that keeps updating the position of the dots, and this works fine, but I also want to delete one instantiation of Dot() at each run of the loop.
I've tried many ways but with no success. By using canvas.delete() function I only "freeze" the instantiation, but it remains on screen. How can I fix this?
import numpy as np
import random
import time
from tkinter import *
#create Canvas#
screen = Tk()
hei = 600
wid = 600
canvas = Canvas(screen,width=wid,height=hei)
canvas.pack()
colors = ["red", "green", "blue"]
class Dot:
def __init__(self):
self.l = random.randrange(100,200)
self.t = random.randrange(100,200)
self.l2= self.l-10
self.t2 = self.t-10
color = random.choice(colors)
self.shape = canvas.create_oval(self.l, self.t, self.l2, self.t2, fill=color)
self.speedx = random.randrange(1,3)
self.speedy = random.randrange(1,3)
def update(self):
canvas.move(self.shape, self.speedx, self.speedy)
pos = canvas.coords(self.shape)
jitter = random.randrange(0,200)
if pos[2] >= wid+jitter or pos[0] <= -jitter:
self.speedx *= -1
if pos[3] >= hei+jitter or pos[1] <= -jitter:
self.speedy *= -1
dots = []
for i in range(100):
dots.append(Dot())
while True:
del dots[-1]
canvas.delete(dots[-1])
for dot in dots:
dot.update()
screen.update()
time.sleep(0.005)
screen.mainloop()
The delete method takes the identifier of a canvas item, not the canvas item itself.
Additionally, you should probably do the deletion in a timeout function, otherwise, the GUI won't function properly.
Try this code:
import random
from tkinter import *
screen = Tk()
hei = 600
wid = 600
canvas = Canvas(screen, width=wid, height=hei)
canvas.pack()
colors = ["red", "green", "blue"]
class Dot:
def __init__(self):
self.l = random.randrange(100, 200)
self.t = random.randrange(100, 200)
self.l2 = self.l-10
self.t2 = self.t-10
color = random.choice(colors)
self.shape = canvas.create_oval(self.l, self.t, self.l2, self.t2, fill=color)
self.speedx = random.randrange(1, 3)
self.speedy = random.randrange(1, 3)
def update(self):
canvas.move(self.shape, self.speedx, self.speedy)
pos = canvas.coords(self.shape)
jitter = random.randrange(0, 200)
if pos[2] >= wid+jitter or pos[0] <= -jitter:
self.speedx *= -1
if pos[3] >= hei+jitter or pos[1] <= -jitter:
self.speedy *= -1
dots = []
for i in range(100):
dots.append(Dot())
def timeout():
if dots:
canvas.delete(dots[-1].shape)
del dots[-1]
for dot in dots:
dot.update()
screen.after(5, timeout)
screen.after(5, timeout)
screen.mainloop()
(Tested with Python 3)
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 ()