Bouncing dvd logo in python tkinter - python

I am trying to make a bouncing DVD logo in tkinter, but I don't really know how to make it, it works with a ball, the logo doesn't move. The logo
# 1) create main window
from tkinter import *
from PIL import ImageTk, Image
fen = Tk()
fen.title('AllTech - Bouncing ball')
fen.resizable(False, False)
# 2) create canvas and ball
WIDTH, HEIGHT = 400, 300
canvas = Canvas(fen, width=WIDTH, height=HEIGHT)
canvas.pack()
img = ImageTk.PhotoImage(Image.open("dvd.gif"))
# ball = canvas.create_oval(10, 10, 50, 50, fill='black')
# 3) move the ball
xspeed = yspeed = 3
frame = Frame(fen, width=600, height=400)
frame.pack()
frame.place(anchor='center', relx=0.5, rely=0.5)
label = Label(frame, image = img)
label.pack()
def moveBall():
global xspeed, yspeed
canvas.move(canvas, xspeed, yspeed)
(leftPos, topPos, rightPos, bottomPos) = canvas.coords(img)
if leftPos <= 0 or rightPos >= WIDTH:
xspeed = -xspeed
if topPos <= 0 or bottomPos >= HEIGHT:
yspeed = -yspeed
img.after(30, moveBall)
canvas.after(30, moveBall)
fen.mainloop()
I tried with a ball ad it's work, but I don't know why, it doesn't with the logo.

You need to put the image using canvas.create_image() and then you can move the image using canvas.move().
from tkinter import *
from PIL import ImageTk, Image
fen = Tk()
fen.title('AllTech - Bouncing ball')
fen.resizable(False, False)
WIDTH, HEIGHT = 400, 300
canvas = Canvas(fen, width=WIDTH, height=HEIGHT, bg="white")
canvas.pack()
img = ImageTk.PhotoImage(Image.open("dvd.gif"))
# put the image into canvas
logo = canvas.create_image(0, 0, image=img, anchor="nw")
xspeed = yspeed = 3
def moveLogo():
global xspeed, yspeed
# move the image
canvas.move(logo, xspeed, yspeed)
# get bounding box of the image
(leftPos, topPos, rightPos, bottomPos) = canvas.bbox(logo)
if leftPos <= 0 or rightPos >= WIDTH:
xspeed = -xspeed
if topPos <= 0 or bottomPos >= HEIGHT:
yspeed = -yspeed
canvas.after(30, moveLogo)
canvas.after(30, moveLogo)
fen.mainloop()

Related

Python | Tkinter : Move multiple and different objects to gether

I have searched a lot but, I couldnt find the exact answer whats I need ( or I couldnt understand properly ).
In my code there are 1 rectangle, 8 lines. They are moving synchronously. But I want to add a bunch of circles in my canvas. To do that, Im creating a lot of row ''create_oval'' objects. This is meaningless because in next step may be I need thousand of balls in the canvas. I have tried a lot of loops, etc. to find a way to create circles with shortened method but I couldn't achieve what I need.
This my codes.
from tkinter import *
from random import randint
tk = Tk()
W, H =600, 500
canvas = Canvas(tk, width=W, height=H)
canvas.pack()
rectangle= canvas.create_rectangle(50, 250, 550, 50, fill="blue")
line1 = canvas.create_line(370, 70, 550,70,fill="black")
line2 = canvas.create_line(330, 90, 550,90,fill="black")
line3 = canvas.create_line(290, 110, 550,110,fill="black")
line4 = canvas.create_line(250, 130, 550,130,fill="black")
line5 = canvas.create_line(210, 150, 550,150,fill="black")
line6 = canvas.create_line(170, 170, 550,170,fill="black")
line7 = canvas.create_line(130, 190, 550,190,fill="black")
line8 = canvas.create_line(90, 210, 550,210,fill="black")
line9 = canvas.create_line(50, 230, 550,230,fill="black")
ball1 = canvas.create_oval(550,50,545,55, fill="red")
ball2 = canvas.create_oval(550,50,545,55, fill="red")
ball3 = canvas.create_oval(550,50,545,55, fill="red")
ball4 = canvas.create_oval(550,50,545,55, fill="red")
ball5 = canvas.create_oval(550,50,545,55, fill="red")
ball6 = canvas.create_oval(550,50,545,55, fill="red")
ball7 = canvas.create_oval(550,50,545,55, fill="red")
ball8 = canvas.create_oval(550,50,545,55, fill="red")
Xspeed = 10
Yspeed = 0
def moveTable():
global Xspeed,Yspeed
canvas.move(table, Xspeed, Yspeed)
canvas.move(line1, Xspeed, Yspeed)
canvas.move(line2, Xspeed, Yspeed)
canvas.move(line3, Xspeed, Yspeed)
canvas.move(line4, Xspeed, Yspeed)
canvas.move(line5, Xspeed, Yspeed)
canvas.move(line6, Xspeed, Yspeed)
canvas.move(line7, Xspeed, Yspeed)
canvas.move(line8, Xspeed, Yspeed)
canvas.move(line9, Xspeed, Yspeed)
canvas.move(ball1, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball2, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball3, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball4, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball5, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball6, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball7, Xspeed-randint(5,10),Yspeed+randint(5,10))
canvas.move(ball8, Xspeed-randint(5,10),Yspeed+randint(5,10))
left,top,right,bot = canvas.coords(table)
if(left == 60 or left == 40):
Xspeed = -Xspeed
canvas.after(100, moveTable)
moveTable()
tk.mainloop()
It's probably very simple but I couldn't see it.
If you assign a tag to one or more widgets, you can affect them all at the same time.
Here's an example that moves all of the red items vertically, and all of the green items horizontally, all using only two move commands:
import tkinter as tk
import random
root = tk.Tk()
canvas = tk.Canvas(root, background="black", width=400, height=400)
canvas.pack(fill="both", expand=True)
for i in range(20):
x = random.randint(10, 110)
y = random.randint(10, 110)
width = random.randint(10, 30)
height = random.randint(10, 30)
canvas.create_rectangle(x, y, x+width, y+height, fill="red", tags=("red",))
canvas.create_rectangle(x, y, x+width, y+height, fill="green", tags=("green",))
def animate():
canvas.move("red", 0, 1)
canvas.move("green", 1, 0)
canvas.after(20, animate)
animate()
root.mainloop()
You could also just save the items to a list and iterate over the list
items = []
for i in range(100):
item = canvas.create_oval(...)
items.append(item)
...
for item in items:
canvas.move(item, ...)

Moving image in python tkinter

I am writing a simple program in which I want the (ball image png) to bounce off the walls. So far, I have writen this code:
import tkinter as tk
root = tk.Tk()
WIDTH = 500
HEIGHT = 500
canvas = tk.Canvas(root,bg="white",width=WIDTH,height=HEIGHT)
canvas.pack()
img = tk.PhotoImage(file="images/ball1.png")
ball = canvas.create_image(0,0,anchor="nw",image=img)
yspeed = 2
xspeed = 2
def move_ball():
global xspeed,yspeed,ball
canvas.move(ball,xspeed,yspeed)
canvas.after(10,move_ball)
move_ball()
root.mainloop()
You can get the current position with the coords method, then code in a check for each of the 4 walls. Here's the first one:
def move_ball():
global xspeed,yspeed,ball
xpos, ypos = canvas.coords(ball)
if xpos + width_of_ball > WIDTH:
# ball hit the right edge, reverse x direction
xspeed *= -1
canvas.move(ball,xspeed,yspeed)
canvas.after(10,move_ball)
This answer is the same thing as #Novel answer (even though I wrote it before I saw theirs). The only difference is in the fact that the logic of update doesn't expect you to make any edits for it to work, it considers both horizontal and vertical directions, and resizing the main window is compensated for.
import tkinter as tk
root = tk.Tk()
root.title('Infinite Bounce Simulator')
root.geometry('400x300+300+300')
xspeed = 4
yspeed = 3
canvas = tk.Canvas(root, highlightthickness=0, bg='#111')
canvas.pack(expand=True, fill='both')
ball = canvas.create_oval((0, 0, 20, 20), fill='red')
def update():
global xspeed, yspeed, ball
canvas.move(ball, xspeed, yspeed)
#Left, Top, Right, Bottom coordinates
l, t, r, b = canvas.coords(ball)
#flip speeds when edges are reached
if r > canvas.winfo_width() or l < 0:
xspeed = -xspeed
if b > canvas.winfo_height() or t < 0:
yspeed = -yspeed
#do it all again in 10 milliseconds
root.after(10, update)
root.after_idle(update)
root.mainloop()

Python Tkinter - Draw Shape to Screen Without Creating New Instance

I'm tying to create a basic program for drawing to the screen by creating ovals every frame when the mouse is clicked. However, as the program runs for a bit, it starts becoming very choppy and the circles stop forming cohesive lines, due to the code not running fast enough to process precise mouse movement.
Here is my code -
import tkinter as tk
DRAW_HEIGHT = 560
DRAW_WIDTH = 560
PALETTE_HEIGHT = 40
def draw_palette(canvas):
canvas.create_rectangle(0, 0, DRAW_WIDTH, PALETTE_HEIGHT, fill = 'light grey', width= 0)
canvas.create_rectangle(DRAW_WIDTH/8, PALETTE_HEIGHT/5, 3*DRAW_WIDTH/8, 4*PALETTE_HEIGHT/5, fill = 'dark grey', width = 1)
canvas.create_rectangle(5*DRAW_WIDTH/8, PALETTE_HEIGHT/5, 7*DRAW_WIDTH/8, 4*PALETTE_HEIGHT/5, fill = 'dark grey',width = 1)
canvas.create_text(DRAW_WIDTH/4, PALETTE_HEIGHT/2, text = 'clear screen') #non-functional
class Brush():
def __init__(self,stroke_size,stroke_color):
self.size = stroke_size
self.color = stroke_color
self.mode = 'draw'
self.pos = (0,0)
self.clicked = False
def render(self,canvas):
if self.clicked:
canvas.create_oval( self.pos.x-self.size/2, self.pos.y-self.size/2,
self.pos.x+self.size/2, self.pos.y+self.size/2,
width = 0, fill = self.color )
def mouse_moved(self,event):
self.pos = event
def mouse_clicked(self,throwaway):
self.clicked = True
def mouse_released(self,throwaway):
self.clicked = False
#set up root window and canvas
root = tk.Tk()
root.geometry('{}x{}'.format(DRAW_WIDTH,DRAW_HEIGHT+PALETTE_HEIGHT))
c = tk.Canvas(root, width = DRAW_WIDTH, height = DRAW_HEIGHT + PALETTE_HEIGHT, bg = 'white')
c.pack()
b = Brush(40,'black')
#bind actions to functions
c.bind("<Button-1>",b.mouse_clicked)
c.bind("<ButtonRelease-1>",b.mouse_released)
c.bind("<Motion>",b.mouse_moved)
#main loop
while 1:
b.render(c)
draw_palette(c)
root.update()
I suppose I'm just asking if there's any way I can speed this up, but specifically I'm wondering if I can draw the shapes to the screen without using create_shape() every time.
For example,
oval = c.create_oval()
while 1:
canvas.draw(oval)
I know you can do something similar with canvas.move(), but I couldn't find anything that fit my situation.
I don't understand why you created loop while 1 and run render() and draw_palette() hundreds of times even if you don't need it.
I draw new circle in mouse_moved() and use root.mainloop() and it runs much better and create smoother line. Probably if I would draw line from previous place to current place or many ovals with some step then I would get even better line
EDIT: I changed little to draw first oval in mouse_click() - so I can see first oval even if I only click and don't move.
import tkinter as tk
# --- constanst ---
DRAW_HEIGHT = 560
DRAW_WIDTH = 560
PALETTE_HEIGHT = 40
# --- classes ---
class Brush():
def __init__(self,stroke_size,stroke_color):
self.size = stroke_size
self.color = stroke_color
self.mode = 'draw'
self.pos = (0,0)
self.clicked = False
def draw(self):
s = self.size/2
c.create_oval(
self.pos.x-s, self.pos.y-s,
self.pos.x+s, self.pos.y+s,
width=0, fill=self.color
)
def mouse_moved(self, event):
if self.clicked:
self.pos = event
self.draw()
def mouse_clicked(self, event):
self.clicked = True
self.pos = event
self.draw()
def mouse_released(self, event):
self.clicked = False
# --- functions ---
def draw_palette(canvas):
canvas.create_rectangle(0, 0, DRAW_WIDTH, PALETTE_HEIGHT, fill='light grey', width=0)
canvas.create_rectangle(DRAW_WIDTH/8, PALETTE_HEIGHT/5, 3*DRAW_WIDTH/8, 4*PALETTE_HEIGHT/5, fill='dark grey', width=1)
canvas.create_rectangle(5*DRAW_WIDTH/8, PALETTE_HEIGHT/5, 7*DRAW_WIDTH/8, 4*PALETTE_HEIGHT/5, fill='dark grey', width=1)
canvas.create_text(DRAW_WIDTH/4, PALETTE_HEIGHT/2, text='clear screen') #non-functional
# --- main ---
#set up root window and canvas
root = tk.Tk()
root.geometry('{}x{}'.format(DRAW_WIDTH, DRAW_HEIGHT+PALETTE_HEIGHT))
c = tk.Canvas(root, width=DRAW_WIDTH, height=DRAW_HEIGHT+PALETTE_HEIGHT, bg='white')
c.pack()
b = Brush(40, 'black')
#bind actions to functions
c.bind("<Button-1>", b.mouse_clicked)
c.bind("<ButtonRelease-1>", b.mouse_released)
c.bind("<Motion>", b.mouse_moved)
draw_palette(c)
root.mainloop()
EDIT:
I added function which adds ovals if distance between previous and current position is too big and there is gap. Now line is smooth even if mouse moves fast.
import tkinter as tk
# --- constanst ---
DRAW_HEIGHT = 560
DRAW_WIDTH = 560
PALETTE_HEIGHT = 40
# --- classes ---
class Brush():
def __init__(self,stroke_size,stroke_color):
self.size = stroke_size
self.color = stroke_color
self.mode = 'draw'
self.pos = None
self.prev = None
self.clicked = False
def draw_oval(self, x, y):
r = self.size/2 # radius
c.create_oval(x-r, y-r, x+r, y+r, width=0, fill=self.color)
def draw(self):
if self.pos:
self.draw_oval(self.pos.x, self.pos.y)
if self.prev:
# calculate distance between ovals
dx = self.pos.x - self.prev.x
dy = self.pos.y - self.prev.y
max_diff = max(abs(dx), abs(dy))
# add ovals if distance bigger then some size of oval (tested with //4, //8, //6, //5)
if max_diff > (self.size//6):
# how many ovals to add
parts = max_diff//(self.size//6)
# distance between ovals
step_x = dx/parts
step_y = dy/parts
# add ovals except first which is already on canvas
for i in range(1, parts):
x = self.pos.x - i*step_x
y = self.pos.y - i*step_y
self.draw_oval(x, y)
def mouse_moved(self, event):
if self.clicked:
self.prev = self.pos
self.pos = event
self.draw()
def mouse_clicked(self, event):
self.clicked = True
self.prev = None
self.pos = event
self.draw()
def mouse_released(self, event):
self.clicked = False
self.prev = None
self.pos = None
# --- functions ---
def draw_palette(canvas):
canvas.create_rectangle(0, 0, DRAW_WIDTH, PALETTE_HEIGHT, fill='light grey', width=0)
canvas.create_rectangle(DRAW_WIDTH/8, PALETTE_HEIGHT/5, 3*DRAW_WIDTH/8, 4*PALETTE_HEIGHT/5, fill='dark grey', width=1)
canvas.create_rectangle(5*DRAW_WIDTH/8, PALETTE_HEIGHT/5, 7*DRAW_WIDTH/8, 4*PALETTE_HEIGHT/5, fill='dark grey', width=1)
canvas.create_text(DRAW_WIDTH/4, PALETTE_HEIGHT/2, text='clear screen') #non-functional
# --- main ---
#set up root window and canvas
root = tk.Tk()
root.geometry('{}x{}'.format(DRAW_WIDTH, DRAW_HEIGHT+PALETTE_HEIGHT))
c = tk.Canvas(root, width=DRAW_WIDTH, height=DRAW_HEIGHT+PALETTE_HEIGHT, bg='white')
c.pack()
b = Brush(40, 'black')
#bind actions to functions
c.bind("<Button-1>", b.mouse_clicked)
c.bind("<ButtonRelease-1>", b.mouse_released)
c.bind("<Motion>", b.mouse_moved)
draw_palette(c)
root.mainloop()

How should I implement a keybinded space function in python TKinter?

I am trying to make a GUI using python's Tkinter Module that will make a picture of a spaceship land on a platform. I want to be able to keybind the spacebar so that it is an event that will increase the velocity of y(vy) by 3 meters/second. But for some reason, when I try to implement it , it doesn't work. This is my code, can anyone help me?
from Tkinter import Tk, Canvas
from PIL import Image, ImageTk
w,h= 800,600
g = -1.6
dt = 0.05
y = 100.0
vy = 0.0
t = 0.0
def tick():
global y, vy, t
y+= vy*dt
vy += g*dt
t+= dt
print t, y, vy
yp = 0.7*h-0.7*h*y/100.0
canvas.coords(ship, w/4, yp)
canvas.itemconfigure(txt,text='%0.2f'%vy)
if y >= 0.0:
canvas.after(1,tick)
root = Tk()
canvas = Canvas(root, width = w, height = h, bg = 'black')
canvas.pack()
img1 = Image.open('earthrise.jpg').resize((w,h))
pmg1 = ImageTk.PhotoImage(img1)
ship = canvas.create_image(w/2,h/2, image = pmg1)
img2 = Image.open('eagle.jpg').resize((200,200))
pmg2 = ImageTk.PhotoImage(img2)
ship = canvas.create_image(w/4,0, image = pmg2)
canvas.create_rectangle(w/4-150, int(0.5+0.7*h)+100,w/4 + 150,
int(0.5+0.7*h)+125, outline = 'green', fill = 'green')
f=('Times',36,'bold')
txt=canvas.create_text(w-100,50,text='0.0',font=f,fill='white')
def space(evt):
vy+=3
root.bind('<space>',space)
print t,y,vy
canvas.after(1000, tick)
root.mainloop()

Troubles with displaying background on Python. Canvas

from tkinter import *
WIDTH = 800
HEIGHT = 950
RAD = 20
SPEED = 30
class mainHero():
def __init__(self):
self.image = PhotoImage(file="darth_vader.png")
self.imageSize = [112,180]
self.right_side = WIDTH/2+self.imageSize[0]/2
self.left_side = WIDTH/2-self.imageSize[0]/2
self.obj = canv.create_image((WIDTH/2,HEIGHT-self.imageSize[0]/2), image = self.image)
def move(self, event):
if event.keysym == "Right":
if self.right_side+SPEED<WIDTH:
canv.move(self.obj, SPEED, 0)
self.right_side += SPEED
self.left_side += SPEED
if event.keysym == "Left":
if self.left_side-SPEED>0:
canv.move(self.obj, -SPEED, 0)
self.right_side -= SPEED
self.left_side -= SPEED
print(self.left_side, self.right_side)
root = Tk()
root.title("YeGame")
root.minsize(width = WIDTH, height = HEIGHT)
root.maxsize(width = WIDTH, height = HEIGHT)
canv = Canvas(root,width =WIDTH,height = HEIGHT, bg="green")
canv.create_rectangle(0, 0, WIDTH%SPEED-1, HEIGHT, fill = "yellow")
canv.create_rectangle(WIDTH-WIDTH%SPEED+1, 0, WIDTH, HEIGHT, fill = "yellow")
m = canv.create_image((100,100), image = PhotoImage(file="smallnight.gif"))
canv.pack()
canv.focus_set()
me = mainHero()
canv.bind("<KeyPress>", me.move)
root.mainloop()
This code should create game. But I don't understand why I can see Darth Vader on Canvas but can't see background of Canvas. It should be image of Space. Code which add Darth Vader works, but code which add background doesn't work. Show me please how I can do it in right way.
Photo
The image should be declared before calling create_image like you did above for mister Vader:
root = Tk()
root.title("YeGame")
root.minsize(width = WIDTH, height = HEIGHT)
root.maxsize(width = WIDTH, height = HEIGHT)
canv = Canvas(root,width =WIDTH,height = HEIGHT, bg="green")
canv.create_rectangle(0, 0, WIDTH%SPEED-1, HEIGHT, fill = "yellow")
canv.create_rectangle(WIDTH-WIDTH%SPEED+1, 0, WIDTH, HEIGHT, fill = "yellow")
background = PhotoImage(file="smallnight.gif")
m = canv.create_image((100,100), image = background)
canv.pack()
canv.focus_set()
me = mainHero()
canv.bind("<KeyPress>", me.move)
root.mainloop()

Categories

Resources