How to load a complex SVG into pygame correctly? - python

I am making a chess game in Python with PyGame using the python-chess library.
When I implement my program in PyGame the window only shows the empty board without the pieces. However, if I manually open the svg file that my code generates it shows correctly, with the pieces on the board. The problem is - when using blit to display the same image in PyGame I get an empty board.
To be clear, the issue is not a general 'loading SVG in PyGame' question. I am successfully displaying my SVG in this example. The question is a PyGame/python-chess question:
Why is PyGame loading the image that has the pieces in it as an empty board if it's the same file?
Code:
import pygame as pg
import chess
import chess.svg
import chess.pgn
WIDTH, HEIGHT = 900, 500
WINDOW = pg.display.set_mode((WIDTH,HEIGHT))
pg.display.set_caption('Opening mastery')
FPS = 60
board = chess.BaseBoard()
def get_board_img(brd):
SVG = chess.svg.board(board=brd)
f = open("image.svg", "w")
f.write(SVG)
f.close()
image = ('image.svg')
return image
def main():
clock = pg.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
image = get_board_img(board)
WINDOW.blit(pg.image.load(image),(0,0))
pg.display.update()
pg.quit()
if __name__ == '__main__':
main()
This is the generated SVG file opened manually (it has the pieces)
This is the result (no pieces on the board)

It appears that pygame's SVG loading library (nanosvg) can't handle that SVG.
I found a workaround using cairosvg to convert the SVG to a PNG and then using pygame to load that.
I made a new get_board_image function that returns a surface directly
def get_board_img(brd):
svg = chess.svg.board(board=brd)
png_io = io.BytesIO()
cairosvg.svg2png(bytestring=bytes(svg, "utf8"), write_to=png_io)
png_io.seek(0)
surf = pg.image.load(png_io, "png")
return surf
It uses cairosvg to get the svg bytes into a png BytesIO object that pygame can load.
^ To get that snippet working in your code you need to add the imports for io and cairosvg (and install cairosvg) ofc, as well as changing your main loop a bit:
while run:
clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
WINDOW.blit(get_board_img(board),(0,0))
pg.display.update()

Related

image not displaying nicely in pygame

i am trying to display this image in a fullscreen window in pygame as the texture for my floor
but when i try to actually display the image inside the pygame window, it comes up as this
for some reason there are random blobs of blue coming up in the image. i used this code to try and put in my image
width,height=pygame.display.get_surface().get_size()
floorimg=pygame.image.load("assets//image.png")
win.blit(floorimg,(0,height-100))
how can i fix this?
Seems like the problem is not the code but the image itself...
Attached is the link of the "floor" image with the transparent area colored using Windows Paint3D.
The code works perfectly fine, but you might need to get some clean pictures in the future.
I saved your image and wrote this script to draw it:
import pygame, sys
pygame.init()
window = pygame.display.set_mode((1300, 200), 0 , 32)
width,height=pygame.display.get_surface().get_size()
floorimg = pygame.image.load("floor.png")
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
window.fill((0))
window.blit(floorimg,(0,height-100))
pygame.display.flip()
It seems to have similar problem to yours if the image is converted to pygame format(which I am assuming you do somewhere else in your code?)
floorimg = pygame.image.load("floor.png").convert()
But this can easily be solved by taking transparency into account while converting, which is done by convert_alpha.
floorimg = pygame.image.load("floor.png").convert_alpha()
This results in an image similar to the first one.

pygame.error: File is not a Windows BMP file on m1 Mac running Mac os Monteray

I've looked around but haven't found any solutions that work.
I'm just learning pygame using the tutorial: https://www.youtube.com/watch?v=jO6qQDNa2UY&list=WL&index=44&t=1379s
but when I try to link the image I get the error: pygame.error: File is not a Windows BMP file.
I've tried everything short of reinstalling pygame again (previous issue), or reinstalling python.
Here is my code
import pygame
import os
pygame.init()
WIDTH, HEIGHT = 900, 500
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("First Game")
WHITE = (255,255,255)
RED = (255,0,0)
FPS = 60
YELLOW_SPACESHIP_IMAGE = pygame.image.load(
os.path.join('Assets', 'spaceship_yellow.png')).convert()
RED_SPACESHIP_IMAGE = pygame.image.load(
os.path.join('Assets', 'spaceship_red.png')).convert()
def draw_window():
WIN.fill((RED))
WIN.blit(YELLOW_SPACESHIP_IMAGE, (300,100))
pygame.display.update()
def main():
clock = pygame.time.Clock()
run = True
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
draw_window()
pygame.QUIT()
if __name__ == "__main__":
main()
You try to load spaceship_yellow.png and spaceship_red.png which judging from extensions are PNGs not BMPs, your error
pygame.error: File is not a Windows BMP file
suggest that pygame is expecting BMP file. pygame.image docs says
The image module is a required dependency of pygame, but it only
optionally supports any extended file formats. By default it can only
load uncompressed BMP images. When built with full image support, the
pygame.image.load() function can support the following formats.
then enumarate formats, apparently you do not have full image support, I think simplest solution is to convert PNGs you have to uncompressed BMP image format.
Turning the image to a BMP does work but I would rather still have the full functionality of pygame. What I did to finally make it work was from a thread here. I put this in the terminal to get it working:
python3 -m pip install git+https://github.com/nelsonlove/pygame.git
This will update your pygame to version 2.0.2.dev1, modified for m1 Mac.

Read pygame window with open cv

I am very new to python and I am working on a project using pygame and cv2
My first python file code has an output image that shows up in a pygame window, but I have another python file that detects objects and it gets an input video like this:
cap = cv2.VideoCapture("video.mp4")
and then it reads the video and shows the detected objects in the video by surrounding boxes around each object.
I want the cap = cv2.VideoCapture() to get the live video from the pygame window and maybe show the boxes of object detection in a second window or the same pygame window (not sure right now what I want, but maybe showing it in the same pygame window would be better)
I am not sure if this is enough information to help me since I am not very familiar with pygame or cv2, please let me know.
In pygame you are actually drawing on a Surface object. You can get the pygame.Surface object associated with the display with pygame.display.get_surface():
screen = pygame.display.get_surface()
Use pygame.surfarray.pixels3d to create a new 3D array that directly references the pixel values in the Surface. This is equivalent to a cv2 matrix respectively NumPy array when you transpose the x and y axis and transform from RGB to BGR:
capture = pygame.surfarray.pixels3d(screen)
capture = capture.transpose([1, 0, 2])
capture_bgr = cv2.cvtColor(capture, cv2.COLOR_RGB2BGR)
Do this once per frame after updating the display:
def capture_frame():
screen = pygame.display.get_surface()
capture = pygame.surfarray.pixels3d(screen)
capture = capture.transpose([1, 0, 2])
capture_bgr = cv2.cvtColor(capture, cv2.COLOR_RGB2BGR)
return capture_bgr
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# draw
# [...]
pygame.display.flip()
frame_img = capture_frame()
# [...]

Force a fullscreen resolution that is not contained in display.list_modes()

I'm making a simulation for school and I'm trying to make pygame create a fullscreen display in my native resolution. However, I have a QHD screen (2560x1440), and it isn't working properly. As far as I can tell, pygame is rendering a screen at the correct resolution, but expanding it so it is scaled as if it were 1080p, so about 300-400 pixels are cut off around the edges. This causes, for example, a circle rendered at (200,200) to be completely invisible. After some research, I learned that this is because pygame doesn't officially support my resolution (it is not listed in pygame.display.list_modes()). Is there any way to force it to work? I would prefer if I could use my actual resolution instead of upscaled 1080p.
Here is the code that initializes the window:
import pygame
from pygame.locals import *
pygame.init()
w = pygame.display.Info().current_w
h = pygame.display.Info().current_h
S = pygame.display.set_mode((w,h), pygame.FULLSCREEN)
If you are using Windows, make sure in your display settings you have scaling set to 100%. This will make your text and everything smaller if you don't have it at that currently but I think Pygame windows get affected by this number for some reason.
See the below code snippet for making sure your window scales properly. Also see here.
import pygame
from ctypes import windll
def run():
# Force windows to ignore the display scaling value.
windll.user32.SetProcessDPIAware()
pygame.display.init()
# Make the screen the highest resolution that will fit the screen at 100% scaling.
screen = pygame.display.set_mode(pygame.display.list_modes()[0])
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
done = True
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
run()

PyGame garbles horizontal pixel line at image load

I am loading a png file in PyGame which looks fine before I load it.
If you look at the bottom of the image screenshot, a single horizontal pixel line has been distorted. I included the original, and the screenshot. I have tried, smoothscale and normal scale, but nothing seems to make a difference.
Does anyone have some hints for me?
My load code is as follows:
palmTree = 'palmTree.png'
palm = pygame.image.load(palmTree).convert_alpha()
palm = pygame.transform.smoothscale(palm, (palmwidth, palmheight))
screen.blit(palm, (x, y))
pygame.display.flip()
Thanks for the comments below. I believe the problem I have has nothing to do with my program. I ran another program a friend gave me and bingo ... the same problem. Could it have something to do with my Mac Retina display?
The following code shows both the palm tree unaltered as well as scaled down by 10% side by side. I don't see any strange bars with it implemented as I have it.
import pygame,sys,math
from pygame.locals import *
def Main():
pygame.init()
screen_dimensions = 640,480
screen = pygame.display.set_mode(screen_dimensions,0,32)
unaltered_palm = pygame.image.load("palm.png").convert_alpha()
palm_rect = unaltered_palm.get_rect()
scale = palm_rect[2:]
scale[0],scale[1] =int(math.ceil(scale[0]*.9)),int(math.ceil(scale[1]*.9))
altered_palm = pygame.transform.smoothscale(unaltered_palm,scale)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.blit(unaltered_palm,(50,125))
screen.blit (altered_palm,(375,130))
pygame.display.flip()
Main()

Categories