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()
Related
When opening the postscript image with online tools all the pixels align correctly but when using pillow, the pixels are in different sizes.
[Image of the problem]
[Image of the desired result]
def saveFile(canvas:tk.Canvas):
EpsImagePlugin.gs_windows_binary = r'C:\Program Files\gs\gs9.56.1\bin\gswin64c'
file_name = "img"
canvas.postscript(file=f"images\ps\{file_name}.ps", colormode='color')
psimage=Image.open(f'images\ps\{file_name}.ps')
psimage.save(f'images\png\{file_name}.png', "png")
psimage.close()
Keep in mind the pixels are not the size of a 'real' pixel, they are much bigger and changing the format to 'png' or 'jpg' didn't solve the problem.
If someone knows the solution to this problem, I will greatly appreciate it.
Sorry for the missing information, hopefully this is enough.
Img.ps file as text
And this is how the code generates the .ps file
import tkinter as tk
import random
from save import saveFile
pixels = []
FIELD_SIZE = (1280, 720)
PIXEL_SIZE = 10
class random_pixels():
def draw_full_screen(canvas):
height = round(FIELD_SIZE[1] / PIXEL_SIZE)
width = round(FIELD_SIZE[0] / PIXEL_SIZE)
for y in range(height):
for x in range(width):
color = ["#"+''.join([random.choice('ABCDEF0123456789') for i in range(6)])]
# color = "#444"
x_top_left = x * PIXEL_SIZE + 1
y_top_left = y * PIXEL_SIZE + 1
x_bottom_right = x_top_left + PIXEL_SIZE - 1
y_bottom_right = y_top_left + PIXEL_SIZE - 1
resolution = width * height
util.draw_pixel(canvas, color, x_top_left, x_bottom_right, y_top_left, y_bottom_right, resolution)
canvas.update()
canvas.update()
print("\nPixels drawn!")
print("\nSaving image...")
saveFile(canvas)
canvas.focus_set()
print('\nImage saved!')
class util:
def draw_pixel(canvas:tk.Canvas, color, x0, x, y0, y, pixels_to_draw=1):
pixel = canvas.create_rectangle(x0, y0, x, y, fill=color, outline=color)
pixels.append(pixel)
print(f"{len(pixels)}/{pixels_to_draw} | { round(len(pixels) / pixels_to_draw * 100, 2)}%")
return None
def get_theme(e, canvas, root):
if len(pixels) != 0:
canvas.delete('all')
pixels.clear()
root.focus_set()
if e.char == "g":
random_pixels.draw_full_screen(canvas)
canvas.focus_set()
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()
The goal is to convert the current image in GUI window to black and white
Below is my code:
def BlackAndWhite(self):
from images import Image
LoadAFile = self.inputText.getText()
CurrentImage = open(LoadAFile)
image = self.image = PhotoImage(file = LoadAFile)
image.draw()
BlackAndWhite(image)
image.draw()
self.imageLabel["image"] = self.image
blackPixel = (0,0,0)
whitePixel = (255,255,255)
for y in range(image.getHeight()):
for x in range(image.getWidth()):
(r,g,b) = image.getPixel(x,y)
average = (r+b+g) /3
if average < 128:
image.setPixel(x,y,blackPixel)
else:
image.setPixel(x,y, whitePixel)
I am getting this error message:
image.draw()
AttributeError: 'PhotoImage' object has no attribute 'draw'
Here's working code, you should be able to tweak it to work with your work:
from tkinter import Tk, Canvas, NW
from PIL import ImageTk, Image
root = Tk()
canvas = Canvas(root, width=1000, height=1000)
canvas.pack()
img = Image.open("PATH_TO_AN_IMAGE")
blackPixel = (0, 0, 0)
whitePixel = (255, 255, 255)
for y in range(img.height):
for x in range(img.width):
pixelVal = img.getpixel((x, y))
# Unpacking in this way in case the pixel contains more than R, G, B (ex: a png)
r, g, b = pixelVal[0:3]
average = (r + b + g) / 3
if average < 128:
img.putpixel((x, y), blackPixel)
else:
img.putpixel((x, y), whitePixel)
photoimage = ImageTk.PhotoImage(img)
canvas.create_image((20, 20), anchor=NW, image=photoimage, state="normal")
root.mainloop()
I want to be able to make my self a graphical api in Tkinter for a project
using Python 2.7
import Tkinter as tk
# import tkinter as tk # if you are using python 3
class Graphics(tk.Tk):
def __init__(self, width=60, height=60, pixel_width=10):
#super().__init__()
tk.Tk.__init__(self)
self.width, self.height = width, height
self.pixel_width = pixel_width
# self.geometry(f'{self.width*self.pixel_width}x{self.height*self.pixel_width}')
self.geometry('600x600')
self.my_canvas = tk.Canvas(
self,
width = self.width * self.pixel_width,
height = self.height * self.pixel_width,
)
self.pixels = [[None for x in range(self.width)] for y in range(self.height)]
self.fill_area((0, 0), (self.width, self.height), 'white')
self.fill_point((30, 30),'red')
self.fill_area((10, 10), (15, 20), 'yellow')
self.my_canvas.pack()
self.run()
def fill_point(self, point, color):
pixel = self.pixels[point[0]][point[1]]
if pixel is None:
cx0 = self.pixel_width * point[0]
cy0 = self.pixel_width * point[1]
cx1 = self.pixel_width * (point[0] + 1)
cy1 = self.pixel_width * (point[1] + 1)
self.pixels[point[0]][point[1]] = self.my_canvas.create_rectangle(cx0, cy0, cx1, cy1, fill=color) # outline=''
else:
self.my_canvas.itemconfig(pixel, fill=color)
def fill_area(self, pointA, pointB, color):
for x in range(pointA[0], pointB[0]):
for y in range(pointA[1], pointB[1]):
self.fill_point((x, y), color)
def run(self):
self.mainloop()
g = Graphics()
g.fill_point((9,9),'blue')
As I've understood the mainloop method is blocking any further actions on the canvas and I need a solution for an updatable screen.
I tried using threads with mainloop and update methods but it would just immediately exit the window.
Thank you for helping and have a nice day
You probably need to read a little bit about the tkinter canvas, and its immense capabilities: unlike pygame, or other graphics canvasses, it does not need to refresh at a given frequency; canvas items can be addressed individually, and their attributes set precisely.
I reworked your code to display a canvas made of 'scaled pixels': here 60 x 60 pixels wide, with each pixels scaled up by a factor 10.
The 2D list self.pixels contains canvas items; the updates directly change the attributes of these items.
You can remove the black lines around each pixel by setting their attribute outline to the empty string (see comment in the code).
import Tkinter as tk
# import tkinter as tk # if you are using python 3
class Graphics(tk.Tk):
def __init__(self, width=60, height=60, pixel_width=10):
super().__init__(self)
self.width, self.height = width, height
self.pixel_width = pixel_width
# self.geometry(f'{self.width*self.pixel_width}x{self.height*self.pixel_width}')
self.geometry('600x600')
self.my_canvas = tk.Canvas(
self,
width = self.width * self.pixel_width,
height = self.height * self.pixel_width,
)
self.pixels = [[None for x in range(self.width)] for y in range(self.height)]
self.fill_area((0, 0), (self.width, self.height), 'white')
self.fill_point((30, 30),'red')
self.fill_area((10, 10), (15, 20), 'yellow')
self.my_canvas.pack()
self.run()
def fill_point(self, point, color):
pixel = self.pixels[point[0]][point[1]]
if pixel is None:
cx0 = self.pixel_width * point[0]
cy0 = self.pixel_width * point[1]
cx1 = self.pixel_width * (point[0] + 1)
cy1 = self.pixel_width * (point[1] + 1)
self.pixels[point[0]][point[1]] = self.my_canvas.create_rectangle(cx0, cy0, cx1, cy1, fill=color) # outline=''
else:
self.my_canvas.itemconfig(pixel, fill=color)
def fill_area(self, pointA, pointB, color):
for x in range(pointA[0], pointB[0]):
for y in range(pointA[1], pointB[1]):
self.fill_point((x, y), color)
def run(self):
self.mainloop()
g = Graphics()
My question is about displaying and updating text, in order to display the score on screen.
I would like to create a score like the real game that would appear on the screen. But after researching Google, I have not found anyone wishing to increase a score on the screen ...
Indeed, I would like the score to increase each time the bird passes between the pipes and therefore whenever the pipes have an X of 67 pixels. So does anyone know how to do this?
from tkinter import *
import random
from random import randint
def sauter(event):
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
if y1 < 416:
canvas.move(image_oiseau, 0, DY)
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-241)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(256,505)
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-241)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(256,505)
canvas.after(40,deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
tuyx=316
tuyx2=488
h=randint(256,505)
H=randint(256,505)
oisx=67
oisy=244
solx=144
sol2x=432
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
deplacement()
canvas.pack()
canvas.focus_set()
canvas.bind("<space>",sauter)
fenetre.mainloop()
Could someone explain the problem to me because I thought it would be easy :(
Here are the pictures of the game :)
Here are the pictures of the game
Here is one approach to display the scores: It uses a tk.Label, that is updated at the same time the score increases.
The trigger that increases the score is currently a random call to on_change; you can modify this to be a test if a pipe x coordinates becomes lower than the bird x coordinates (the bird successfully crossed the obstacle)
You can, if you want relocate the score label on the canvas.
import random
import tkinter as tk
WIDTH, HEIGHT = 500, 500
def create_pipes():
pipes = []
for x in range(0, WIDTH, 40):
y1 = random.randrange(50, HEIGHT - 50)
y0 = y1 + 50
pipes.append(canvas.create_line(x, 0, x, y1))
pipes.append(canvas.create_line(x, y0, x, HEIGHT))
return pipes
def move_pipes():
for pipe in pipes:
canvas.move(pipe, -2, 0)
x, y0, _, y1 = canvas.coords(pipe)
if x < 0:
canvas.coords(pipe, WIDTH+20, y0, WIDTH+20, y1)
if random.randrange(0, 20) == 10:
on_change()
root.after(40, move_pipes)
def on_change():
global score
score += 1
score_variable.set(f'score: {score}')
root = tk.Tk()
tk.Button(root, text='start', command=move_pipes).pack()
score = 0
score_variable = tk.StringVar(root, f'score: {score}')
score_lbl = tk.Label(root, textvariable=score_variable)
score_lbl.pack()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="cyan")
canvas.pack()
pipes = create_pipes()
root.mainloop()