Tkinter animation without canvas.move() - animation

I would like to animate a canvas item WITHOUT (!!!) the canvas.move() function.
For example, I tried this:
see below:
coords is known
def getCoords(i):
....
return coords #a list
for i in range(4):
id=canvas.create_oval(getCoords(i))
canvas.after(1000)
canvas.delete(id)
canvas.update()
It does NOT work this way.
What is wrong? and/or
Where do I find an example?

You can both get and change the coordinates of a canvas object with the coords method.
Here's a complete example that grows an oval in the x direction by one pixel every 100ms:
import Tkinter as tk
def grow(canvas, item):
(x1,y1,x2,y2) = canvas.coords(item)
new_coords = (x1, y1, x2+1, y2)
canvas.coords(item, new_coords)
root.after(100, grow, canvas, item)
root = tk.Tk()
canvas = tk.Canvas(root)
canvas.pack(side="top", fill="both", expand=True)
item = canvas.create_oval(10, 10, 100, 100, outline="black", fill="red")
grow(canvas, item)
root.mainloop()

Related

Problem with images overlapping in pygame

Im having problems with blitting images to rect objects in pygame. i have a background image blitted to my main pygame window, and also an image blitted to a rect object on the screen which moves. the problem i am having is the rect object is overlapping my background image when its moving around. i was looking to only be able to see the green helicopter shape and not the black outline around it. sorry if i havent explained this very well. will try to include all files im using.
Thanks for any help
import pygame as pg
import random as r
import time
pg.init()
MAX_X = 1190
MAX_Y = 590
MIN_X = 10
MIN_Y = 10
SIZE = 100
SPEED = 1
COLOR = (0,255,0)
move_amount = 0
wn = pg.display.set_mode((1200, 600))
BG_IMG = pg.image.load('bg.png').convert()
BG_IMG = pg.transform.scale(BG_IMG, (1200, 600))
class Wall (pg.Rect):
def __init__(self, posX, posY):
self.xcor = posX
self.ycor = posY
self.rect = None
class Heli (pg.Rect):
def __init__(self, posX, posY):
self.image = pg.image.load('art.png').convert()
self.rect = self.image.get_rect()
self.xcor = posX
self.ycor = posY
# top and bottom constant walls
TOP = pg.Rect(MIN_X, MIN_Y, MAX_X, 3)
BOTTOM = pg.Rect(MIN_X, MAX_Y, MAX_X, 3)
heli = Heli(MIN_X, MAX_Y //2)
# keep moving walls in a list
moving_walls = [Wall(MAX_X, r.randint((MIN_Y + 10), (MAX_Y - 10)))]
# main loop
while True:
# fill screen
wn.fill('black')
# editing objects to move
# blitting must happen before everything else
pg.draw.rect(wn,COLOR, heli.rect)
wn.blit(BG_IMG, (0,0))
wn.blit(heli.image, heli.rect)
heli.rect.y += move_amount
heli.rect.y += 1
# use a variable to control how much movement is happening
# movement happens continuosly
# if key down it oves if key up it doesnt
for wall in moving_walls :
wall.rect = pg.Rect(wall.xcor, wall.ycor, 3, SIZE)
pg.draw.rect(wn, COLOR, wall.rect)
wall.xcor -= SPEED
if wall.xcor < MIN_X + 10:
wall.xcor = MAX_X
wall.ycor = r.randint((MIN_Y), (MAX_Y - SIZE))
# drawing all objects back to the screen
pg.draw.rect(wn, COLOR, TOP)
pg.draw.rect(wn, COLOR, BOTTOM)
# update window
pg.display.update()
# event handling
for ev in pg.event.get():
if ev.type == pg.KEYDOWN:
if ev.key == pg.K_UP:
move_amount = -3
if ev.type == pg.KEYUP:
move_amount = 0
if ev.type == pg.QUIT:
pg.quit()
time.sleep(0.01)
You discard the transparency information of the image. You have to use convert_alpha instead of convert:
self.image = pg.image.load('art.png').convert()
self.image = pg.image.load('art.png').convert_alpha()
The pygame documentation notes that:
The returned Surface will contain the same color format, colorkey and alpha transparency as the file it came from. You will often want to call convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images, use the convert_alpha() method after loading so that the image has per pixel transparency.
See also How can I make an Image with a transparent Backround in Pygame?

How to color the mask image obtained out of a polygon

I am getting a mask out of a polygon and below is the image. Now I see that there is a white boundary but I want not just the boundary but inside boundary as white too.
Here is my code:
sar_polygon = Image.new('L', (int(range_samples), int(azimuth_lines)), 0)
draw = ImageDraw.Draw(sar_polygon)
for vertex in range(len(sar_ver)):
st = sar_ver[vertex]
try:
end = sar_ver[vertex + 1]
except IndexError:
end = sar_ver[0]
draw.line((st[0], st[1], end[0], end[1]), fill=1)
sar_polygon.save('polygon.jpg', 'JPEG')
You are currently drawing lines along the edges. You are interested in the polygon method, or perhaps the rectangle method - http://pillow.readthedocs.io/en/5.2.x/reference/ImageDraw.html#PIL.ImageDraw.PIL.ImageDraw.ImageDraw.polygon.
from PIL import Image, ImageDraw
sar_polygon = Image.new('RGB', (500, 500))
draw = ImageDraw.Draw(sar_polygon)
sar_ver = ((100,100),(200,100),(200,200),(100,200))
draw.polygon(sar_ver, fill='#f00')
sar_polygon.save('polygon.jpg', 'JPEG')

Tkinter - Creating a square that follows my mouse location

I have to create a square around my pointer on the canvas. And I want that square to follow my pointer as I move it around.
from tkinter import *
root = Tk()
f = Frame(root)
f.pack()
c = Canvas(f,bg = "black")
while root:
x = c.winfo_pointerx()
y = c.winfo_pointery()
c.create_rectangle(x,y,(x+10),(y+10),fill = "red")
root.mainloop()
root.mainloop()
Now when I run this the rectangle doesn't load.
Your method won't work, because once you call mainloop, it waits for the window to close, so it will never get past the first iteration of the loop. And if you remove the mainloop from the loop, it will never reach the mainloop after the (infinite) loop.
The proper way to do this is to use callback events. Also, you should move the rectangle, instead of creating a bunch of new ones. Try something like this:
def callback(event):
x, y = event.x, event.y
c.coords(rect, x - 10, y - 10, x + 10, y + 10)
root = Tk()
c = Canvas(root)
rect = c.create_rectangle(0, 0, 0, 0)
c.bind('<Motion>', callback)
c.pack()
root.mainloop()

Pygame: How to tile subsurfaces on an image

I'm completely new to Python/Pygame and I'm trying to slice an image up into subsurfaces and then blit them in order to the screen (so that later on I can modify it to call upon a certain tile and then blit it).
E1: I think I've finally managed to slice it the right way but there's a little hitch with the blitting.
E2: Updated the code and error (been getting this one on most of the code I tried before as well).
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((640, 480))
screen.fill((255, 255, 255))
def load_tileset(filename, width, height):
image = pygame.image.load(filename).convert()
image_width, image_height = image.get_size()
tileset = []
for tile_x in range(0, image_width//width):
line = []
tileset.append(line)
for tile_y in range(0, image_height//height):
rect = (tile_x*width, tile_y*height, width, height)
line.append(image.subsurface(rect))
return tileset
def draw_background(screen, tile_img, field_rect):
img_rect = tile_img.get_rect()
for y in range(0,400,32):
for x in range(0,600,32):
screen.blit(tile_img, (x,y))
Field_Rect = Rect(50,50, 300,300)
tileset = load_tileset("platform1.bmp", 17, 17)
bg_tile_img = tileset[0]
draw_background(screen, bg_tile_img, Field_Rect)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
pygame.display.flip()
The error it gives atm is:
img_rect = tile_img.get_rect()
AttributeError: 'list' object has no attribute 'get_rect'
In this function here where you create the tileset:
def load_tileset(filename, width, height):
image = pygame.image.load(filename).convert()
image_width, image_height = image.get_size()
tileset = []
for tile_x in range(0, image_width//width):
line = []
tileset.append(line)
for tile_y in range(0, image_height//height):
rect = (tile_x*width, tile_y*height, width, height)
line.append(image.subsurface(rect))
return tileset
...you will notice that tileset is a list, and each element of tileset (labeled as line), is also a list.
So in this code here:
tileset = load_tileset("platform1.bmp", 17, 17)
bg_tile_img = tileset[0]
draw_background(screen, bg_tile_img, Field_Rect)
...you are passing a list, called bg_tile_img, to the draw_background function.
Your draw_background function should instead look more like this:
def draw_background(screen, tile_img, field_rect):
for img in tile_img:
img_rect = img.get_rect()
for y in range(0,400,32):
for x in range(0,600,32):
screen.blit(img, (x,y))
Alternatively, instead of passing bg_tile_img as a list, pop an element off of it to pass to the draw_background function.
You are not assigning the tileset:
load_tileset("platform1.bmp", 17, 17)
should be:
tileset = load_tileset("platform1.bmp", 17, 17)
Update:
set_mode creates the window, so it should be outside the main loop (load_tileset too):
screen = pygame.display.set_mode((640, 480))
tileset = load_tileset("platform1.bmp", 17, 17)
while True:
for event in pygame.event.get():
...
Also, I can't see any call to draw_background.
Update2:
As Haz said, tileset is a list of lines (a matrix), you could use coordinates to retrieve a specific tile:
bg_tile_img = tileset[0][0]
Or make the tileset a plain list, adding everything directly to the tileset instead of using lines.

tkinter: displaying a square grid

I'm trying to use tkinter to periodically refresh a square grid. Each square in the grid is painted a certain color (say obtained from a given function); in the center of each square, a smaller circle is drawn (the color is also obtained from a function).
If I have to do it from scratch, perhaps there's an example or a standard recipe I can use?
Here's a quick hack showing how to draw a grid of rectangles and circles, and how to update the display once a second. If you've never programmed in tk before, it uses concepts that are no doubt foreign to you. You'll need to read up on canvas object ids and tags, as well as what the after command does.
import Tkinter as tk
import random
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.canvas = tk.Canvas(self, width=500, height=500, borderwidth=0, highlightthickness=0)
self.canvas.pack(side="top", fill="both", expand="true")
self.rows = 100
self.columns = 100
self.cellwidth = 25
self.cellheight = 25
self.rect = {}
self.oval = {}
for column in range(20):
for row in range(20):
x1 = column*self.cellwidth
y1 = row * self.cellheight
x2 = x1 + self.cellwidth
y2 = y1 + self.cellheight
self.rect[row,column] = self.canvas.create_rectangle(x1,y1,x2,y2, fill="blue", tags="rect")
self.oval[row,column] = self.canvas.create_oval(x1+2,y1+2,x2-2,y2-2, fill="blue", tags="oval")
self.redraw(1000)
def redraw(self, delay):
self.canvas.itemconfig("rect", fill="blue")
self.canvas.itemconfig("oval", fill="blue")
for i in range(10):
row = random.randint(0,19)
col = random.randint(0,19)
item_id = self.oval[row,col]
self.canvas.itemconfig(item_id, fill="green")
self.after(delay, lambda: self.redraw(delay))
if __name__ == "__main__":
app = App()
app.mainloop()

Resources