How to make objects collide with tkinter - python-3.x

I am very new to programming and I'm trying to make a simple animation where I append 10 balls of random size, random color and random speed to the canvas. Here's the code:
from tkinter import *
import random
import time
WIDTH = 900
HEIGHT = 700
tk = Tk()
canvas = Canvas(tk, width = WIDTH, height = HEIGHT)
tk.title("Drawing")
canvas.pack()
COLORS = ["white", "black", "red", "blue", "green", "yellow", "purple", "orange", "gray"]
balls = []
class Ball:
def __init__(self, color, size):
self.shape = canvas.create_oval(10, 10, size, size, fill = color)
self.xspeed = random.randrange(-11, 11)
self.yspeed = random.randrange(-11, 11)
def move(self):
canvas.move(self.shape, self.xspeed, self.yspeed)
pos = canvas.coords(self.shape)
if pos[3] >= HEIGHT or pos[1] <= 0:
self.yspeed = -self.yspeed
if pos[2] >= WIDTH or pos[0] <= 0:
self.xspeed = -self.xspeed
for i in range(10):
size = random.randrange(20, 150)
color = random.choice(COLORS)
balls.append(Ball(color, size))
while True:
for ball in balls:
ball.move()
tk.update()
time.sleep(0.01)
tk.mainloop()
The problem is that balls are able to overlap each other i. e. when balls touch each other, nothing happens. I want the balls to bounce off of each other when they touch. I have spent several hours searching for answer and trying different things but nothing seems to help. Could you guys help me?

Related

canvas circle getting distorted in python

I am writing a code where there are multiple balls (or circles) of various sizes in Tkinter canvas. The problem is that during the running process when ever ball gets close to the canvas boundary and sometimes even away from the boundary the shape of the ball gets distorted. I am unsure why this is happening. is there something I am missing during the update of the window object?
This is the main file:
from tkinter import *
from Ball import *
import time
HEIGHT = 500
WIDTH = 500
window = Tk()
canvas = Canvas(window, height=HEIGHT, width=WIDTH)
canvas.pack()
volley_ball = Ball(canvas, 0, 0, 100, 2, 2,'red')
cricket_ball = Ball(canvas, 10, 10, 30, 4, 2,'green')
golf_ball = Ball(canvas, 100, 100, 20, 8, 6,'white')
while True:
volley_ball.move()
cricket_ball.move()
golf_ball.move()
window.update()
time.sleep(0.01)
window.mainloop()
This is the Ball class module code:
class Ball:
def __init__(self, canvas, x, y, diameter, xVelocity, yVelocity, color):
self.canvas = canvas
self.image = canvas.create_oval(x, y, diameter, diameter, fill=color)
self.xVelocity = xVelocity
self.yVelocity = yVelocity
self.diameter = diameter
def move(self):
self.coordinates = self.canvas.coords(self.image)
if(self.coordinates[2] >= self.canvas.winfo_width() or self.coordinates[0] < 0):
self.xVelocity = -self.xVelocity
if (self.coordinates[3] >= self.canvas.winfo_height() or self.coordinates[1] < 0):
self.yVelocity = -self.yVelocity
self.canvas.move(self.image, self.xVelocity, self.yVelocity)
The visual artifacts are created by over driving your move function. Try changing delay to 1 millisecond and observe the artifacts! Slow it down a little and use xVelocity, yVelocity to control speed.
Also try changing your while loop for window.after.
def animate():
volley_ball.move()
cricket_ball.move()
golf_ball.move()
window.after(20, animate)
window.after(500, animate)
window.mainloop()

Why window size out of screen tkinter?

I created a billing system in my Linux system.in Linux my code works fine and fit to screen
I use place() in code
When I run the same code in windows 10 window size enlarge and shows half of gui
Help me how to fix it
No errors in code but problem in only window size
How to adjust
https://github.com/vijaySai-22/python/tree/main/billing
import tkinter as tk
from tkinter import *
class App():
def __init__(self,root):
##########--------------Company name-------------##########
Label(root,text="Green Vegetables Market",font=('Arial',30,'bold','underline'),width=(84),bg="black",fg="white").place(x=0,y=0)
Label(root,text="Customer Details",font=('Arial',30),width=(84),bg="grey",fg="black").place(x=0,y=60)
Label(root,text="Enter Custmor Name",font=('Arial',18)).place(x=5,y=120)
self.c_name=tk.StringVar()
self.cname_entry=Entry(root,font=('Arial',18),width=(20),textvariable=self.c_name).place(x=245,y=120)
Label(root,text="Enter contact number",font=('Arial',18)).place(x=525,y=120)
self.c_phno=tk.StringVar()
self.phno_entry=Entry(root,font=('Arial',18),width=(20),textvariable=self.c_phno).place(x=769,y=120)
Label(root,text="Enter Address",font=('Arial',18)).place(x=1045,y=120)
self.c_add=tk.StringVar()
self.add_entry=Entry(root,font=('Arial',18),width=(46),textvariable=self.c_add).place(x=1210,y=120)
# Label(root,text="Age",font=('Arial',18)).place(x=1695,y=120)
# c_age=tk.StringVar()
# self.age_entry=Entry(root,font=('Arial',18),width=(4)).place(x=1750,y=120)
##########---------------Particulars-------------###########
Label(root,text="Particulars",font=('Arial',30),fg='black',bg='grey',width=(50)).place(x=5,y=180)
self.veg=['Potato','Tomato','Carrot','Brinjal','Onions','Cabage','Ginger','Chilli','Garlic','Beetroot']
self.veg_price=[20,22,30,25,30,30,70,32,60,53]
self.ck=[None]*len(self.veg)
self.qty=[None]*len(self.veg)
self.dis=[None]*len(self.veg)
self.each_price=[None]*len(self.veg)
self.y_value=300
Label(root,text=" ItemNo Vegetables Qty Discount Total ",font=('Arial',23)).place(x=10,y=240)
for i in range(len(self.veg)):
self.item_no='Item'+str(i+1)
Label(root,text=self.item_no,font=('Arial',23)).place(x=50,y=self.y_value)
self.ck[i]=tk.IntVar()
name = Checkbutton(root, text = self.veg[i],variable = self.ck[i],
onvalue = 1,offvalue = 0,
font=('Arial',23),width = 18,anchor="w"
).place(x=180,y=self.y_value)
self.qty[i]=tk.DoubleVar()
self.qty[i].set(0)
Entry(root,font=('Arial',23),textvariable=self.qty[i],width=3).place(x=600,y=self.y_value)
Label(root,text=".kg",font=('Arial',23)).place(x=660,y=self.y_value)
self.dis[i]=tk.DoubleVar()
self.dis[i].set(0)
Entry(root,font=('Arial',23),textvariable=self.dis[i],width=3).place(x=760,y=self.y_value)
Label(root,text="% ",font=('Arial',23)).place(x=820,y=self.y_value)
self.each_price[i]=Label(root,text="0 Rs",font=('Arial',23))
self.each_price[i].place(x=950,y=self.y_value)
self.y_value=self.y_value+60
self.ext=Button(root,text="Exit",font=('Arial',23),bg="red",fg="black",command=self.close).place(x=50,y=900)
self.clr=Button(root,text="Clear",font=('Arial',23),bg="yellow",fg="black",command=self.clear).place(x=600,y=900)
self.cal=Button(root,text="Total=",font=('Arial',23),bg="green",fg="black",command=self.calculate).place(x=760,y=900)
self.total_price=Label(root,text="0 RS",font=('Arial',27),width=9)
self.total_price.place(x=900,y=900)
##########-----------line-------------##########
Label(root,font=(30),fg='black',bg='grey',width=(3),height=(40)).place(x=1100,y=180)
##########-----------Bill-------------##########
Label(root,text="Bill",font=('Arial',30),fg='black',bg='grey',width=(32)).place(x=1130,y=180)
self.bill_text=Text(root)
self.bill_text.insert("insert", "\n\nGreen Vegetables Market\n")
self.bill_text.insert("end","One and only market where you can find Organic Vegetables\nSrikakulam, Andhrapradesh, 532484\nContact 9900887766\nGST no:X1234567890XYZ")
self.bill_text.tag_add("head", "3.0", "3.23")
self.bill_text.tag_add("rest", "4.0", "7.22")
self.bill_text.tag_config("head", font=('Arial',30,'bold','underline'),justify='center')
self.bill_text.tag_config("rest", font=('Arial',15),justify='center')
self.bill_text.config(state='disabled',width=75,height=38)
self.bill_text.place(x=1180,y=240)
self.complete_and_save=Button(root,text="Complete and Save",font=('Arial',23),command=self.save_bill).place(x=1500,y=900)
def calculate(self):
if self.c_name.get()!='' and self.c_phno.get()!='' and self.c_add.get()!='':
tot=0.0
line=13.0
self.bill_text.config(state='normal')
self.bill_text.delete("8.0","end")
for i in range(len(self.veg)):
each_tot=0.0
if self.ck[i].get()==1 and self.qty[i].get()>0:
self.bill_text.insert("end", "\nCustomer Details\n\tCustmor Name: "+self.c_name.get()+
"\n\tContact number: "+self.c_phno.get()+"\n\tAddress: "+self.c_add.get())
self.bill_text.tag_add("details_head", "8.0","8.16")
self.bill_text.tag_config("details_head", font=('Arial',20,'underline'),justify='center')
self.bill_text.tag_add("details_body", "9.0","11.50")
self.bill_text.tag_config("details_body", font=('Arial',15),justify='left')
self.bill_text.insert("end", "\n\n"+"\t"+"item"+"\t\t"+"qty"+"\t\t"+"dis"+"\t\t"+"Total\n")
each_tot=each_tot+self.veg_price[i]*self.qty[i].get()
each_tot=each_tot-((each_tot/100)*self.dis[i].get())
self.each_price[i].config(text=str(each_tot)+" Rs")
tot=tot+each_tot
self.total_price.config(text=str(tot)+" Rs")
self.bill_text.config(state='normal')
self.bill_text.insert("end","\n"+"\t"+self.veg[i]+"\t\t"+str(self.qty[i].get())+"\t\t"+str(self.dis[i].get())+"\t\t"+str(each_tot))
self.bill_text.tag_add("items",str(line),"end")
self.bill_text.tag_config("items",font=('Arial',15),justify='left')
self.bill_text.config(state='disabled')
line=line+1
else:
self.each_price[i].config(text="0 Rs")
self.qty[i].set(0)
self.dis[i].set(0)
self.bill_text.config(state='normal')
self.bill_text.insert("end", "\n\n\t\t\t\t\t GST(18%) :"+"{:.2f}".format(((tot)/100)*18))
self.bill_text.insert("end", "\n\n\t\t\t\t\t Total amt :"+"{:.2f}".format((tot+((tot)/100)*18)))
self.bill_text.config(state='disabled')
def close(self):
exit()
def clear(self):
for i in range(len(self.veg)):
self.ck[i].set(0)
self.each_price[i].config(text="0 Rs")
self.qty[i].set(0)
self.dis[i].set(0)
self.bill_text.config(state='normal')
self.bill_text.delete("8.0","end")
self.bill_text.config(state='disabled')
def save_bill(self):
import datetime
from tkinter import messagebox
current_time = datetime.datetime.now()
self.file_name=str(self.c_name)+str(current_time)
self.file=open(self.file_name+".txt","w")
self.file.write(self.bill_text.get("1.0","end"))
self.file.close()
self.clear()
messagebox.showinfo("showinfo", "Saved Successfully")
if __name__ == "__main__":
win=tk.Tk()
#title
win.title("billing system")
#setting tkinter window size
width= win.winfo_screenwidth()
height= win.winfo_screenheight()
win.geometry("%dx%d" % (width, height))
#bg color
win.config(bg="black")
app=App(win)
win.mainloop()
You hardcoded (x, y) positions for all your widgets, so it only fits on that resolution. Instead, you should have fullscreened, got window height & width info, then positioned widgets as divisions of those dimensions.
root .attributes( '-zoomed', True )
root .update_idletasks()
Width, Height = root .winfo_width(), root .winfo_height()
self .clr = Button( root, text = "Clear", font = ( 'Arial', 23 ), bg = "yellow",
fg = "black", command = self .clear ) .place( x = 600, y = 900 )
should read something more along the lines of:
self .clr = Button( root, text = "Clear", font = ( 'Arial', 23 ), bg = "yellow",
fg = "black", command = self .clear ) .place( x = Width *0.5, y = Height *0.75 )
Or use .grid() instead of .place() - it does a lot of that for you -- https://www.geeksforgeeks.org/python-grid-method-in-tkinter/

How to make Canvas widget color darker compared to the original color?

I have this canvas widget with a green rectangle:
canvas = tk.Canvas(root)
canvas.create_rectangle(0,0,50,100, fill='green')
I want to darken it. So:
canvas.itemconfig(1, fill=?????)
The issue is, what should I put in the ????? aka the 'darker' color of the original color.
Sure, I can just find a hex for a darker shade of green or something like that, but the point is: How can I find the darkened the widget based on the original color?
I don't necessarily need to find the darker version of the color if there is something like: canvas.itemconfig(1, darkness=-100).
Here is a simple example of how to use a function to lower the color based on a RGB tuple that is converted to hex. We start out with a lightish green and with each button press it gets darker. This is a simple example but I am sure you can adapt it for your needs.
import tkinter as tk
root = tk.Tk()
cv = (110, 160, 50)
canvas = tk.Canvas(root)
rect = canvas.create_rectangle(0,0,50,100, fill="#%02x%02x%02x" % cv)
canvas.pack()
def darker():
global cv
greater = True
cv = (cv[0]- 10, cv[1] - 10, cv[2] - 10)
for item in cv:
if item < 0:
greater = False
if greater == True:
canvas.itemconfig(rect, fill = "#%02x%02x%02x" % cv)
tk.Button(root, text="Darker", command=darker).pack()
root.mainloop()
Or you can do this using the current preferred concatenation with format():
import tkinter as tk
root = tk.Tk()
cv = (110, 160, 50)
canvas = tk.Canvas(root)
rect = canvas.create_rectangle(0,0,50,100, fill = "#{}".format("".join("{:02X}".format(a) for a in cv)))
canvas.pack()
def darker():
global cv
greater = True
cv = (cv[0]- 10, cv[1] - 10, cv[2] - 10)
for item in cv:
if item < 0:
greater = False
if greater == True:
canvas.itemconfig(rect, fill = "#{}".format("".join("{:02X}".format(a) for a in cv)))
tk.Button(root, text="Darker", command=darker).pack()
root.mainloop()

How do I pass a rect into pygame.display.update() to update a specific area of the window?

On the documentation page for pygame.display.update(), it says you can pass a rect into the method to update part of the screen. However, all of the examples I see just pass an existing rect from an image or shape in the program. How can I tell it to update an area on the screen directly? For example, when drawing a rectangle, I could use a rect argument of (100,200,30,40). This would draw a rectangle with a top at 200, a left at 100, a width of 30, and a height of 40. How can I pass a similar argument into pygame.display.update()? I tried pygame.display.update((100,200,30,40)), but this updates the entire window.
Just define a rect and pass it to pygame.display.update() to update only this specific region of the display. You can also pass a list of rects.
import random
import pygame as pg
from pygame.math import Vector2
# A simple sprite, just to have something moving on the screen.
class Ball(pg.sprite.Sprite):
def __init__(self, screen_rect):
super().__init__()
radius = random.randrange(5, 31)
self.image = pg.Surface((radius*2, radius*2), pg.SRCALPHA)
pg.draw.circle(self.image, pg.Color('dodgerblue1'), (radius, radius), radius)
pg.draw.circle(self.image, pg.Color('dodgerblue3'), (radius, radius), radius-2)
self.rect = self.image.get_rect(center=screen_rect.center)
self.vel = Vector2(random.uniform(-2, 2), random.uniform(-2, 2))
self.pos = Vector2(self.rect.center)
self.screen_rect = screen_rect
self.lifetime = 350
def update(self):
self.pos += self.vel
self.rect.center = self.pos
self.lifetime -= 1
if not self.screen_rect.contains(self.rect) or self.lifetime <= 0:
self.kill()
def main():
screen = pg.display.set_mode((800, 600))
screen.fill((20, 40, 70))
pg.display.update()
screen_rect = screen.get_rect()
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
# Pass this rect to `pg.display.update` to update only this area.
update_rect = pg.Rect(50, 50, 500, 400)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
all_sprites.add(Ball(screen_rect))
all_sprites.update()
screen.fill((20, 50, 90))
all_sprites.draw(screen)
# Update only the area that we specified with the `update_rect`.
pg.display.update(update_rect)
clock.tick(60)
if __name__ == '__main__':
pg.init()
main()
pg.quit()

Generate 10 balls in tkinter canvas

Ok guys.
I am trying to generate 10 balls of random color in Tkinter canvas when I click the generate button.
Program works, and random color choice works for the ball, but I only get one ball generated at a time.
Every time I click the button it randomly moves the ball around, but all I want is 10 balls in 10 random positions at a time. I am using Python 3.4 on a Linux box.
This is a code I've got:
from tkinter import *
import random # to generate random balls
colors = ["red", "blue", "purple", "green", "violet", "black"]
class RandomBalls:
"""
Boilerplate code for window in Tkinter
window = Tk()
window.title("Random title")
window.mainloop()
"""
def __init__(self):
"""
Initialize the window and add two frames, one with button, and another one with
canvas
:return:
"""
window = Tk()
window.title("Random balls")
# A canvas frame
frame1 = Frame(window)
frame1.pack()
self.canvas = Canvas(frame1, width = 200, height = 300, bg = "white")
self.canvas.pack()
# A button frame
frame2 = Frame(window)
frame2.pack()
displayBtn = Button(frame2, text = "Display", command = self.display)
displayBtn.pack()
window.mainloop()
def display(self):
for i in range(0, 10):
self.canvas.delete("circle") # delete references to the old circle
self.x1 = random.randrange(150)
self.y1 = random.randrange(200)
self.x2 = self.x1 + 5
self.y2 = self.y1 + 5
self.coords = self.x1, self.y1, self.x2, self.y2
self.canvas.create_oval(self.coords, fill = random.choice(colors), tags = "circle")
self.canvas.update()
RandomBalls()
Every time through your loop you are deleting everything you created before, including what you created the previous iteration. Move the delete statement outside of the loop:
def display(self):
self.canvas.delete("circle")
for i in range(0, 10):
...

Resources