Why am I getting this error: "Invalid rectstyle argument" - python

I am currently making a game in python where a ball hits a group of boxes. I have loaded an image of a grass that I wanted the boxes to stand on, the grass image is 600 by 151 pixels. So far I have all the boxes and the ball appearing perfectly however, What is not working currently is when I try to load the image and display it so that the boxes can stand on the grass image instead of falling off.
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk as pm
from pymunk import Vec2d
import sys
from random import randint
def to_pygame(p):
"""Small hack to convert pymunk to pygame coordinates"""
return int(p[0]), int(-p[1]+600)
def add_box(space, size, pos, mass=1.0):
# pos is a Vec2d object
points = [(-size, -size), (-size, size), (size,size), (size, -size)]
moment = pm.moment_for_poly(int(mass), points, (0,0))
body = pm.Body(mass, moment)
body.position = pos
shape = pm.Poly(body, points, (0,0))
shape.friction = 1
space.add(body,shape)
return shape
def draw_box(screen, box):
ps = box.get_points()
ps.append(ps[0])
ps = map(to_pygame, ps)
pygame.draw.polygon(screen, THECOLORS["blue"], ps, 3)
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Piling boxes")
clock = pygame.time.Clock()
boxes = []
x = 170
y = 120
space = pm.Space()
space.gravity = (0.0, -100.0)
### ground
body = pm.Body()
shape = pm.Segment(body, (150,100), (450,100), .0)
shape.friction = 4.4
space.add(shape)
for i in range(5):
# what is the mass of the box?
mass = randint(1,3)
# what size is the box?
size = randint(10, 35)
# calculate its position & hold it in a Vec2d
#x = x + size + 10
y = y + size
pos = Vec2d(x,y)
box = add_box(space, size, pos, mass)
boxes.append(box)
while 1:
clock.tick(15)
space.step(1/30.0)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit(0)
screen.fill(THECOLORS["white"])
test_image = pygame.image.load("grass.png")
screen.blit(test_image, to_pygame((450,100)), to_pygame((150,100)),3)
for i in range(len(boxes)):
draw_box(screen, boxes[i])
pygame.display.flip()
if __name__ == '__main__':
main()

No traceback or error log so it is a bit hard to identify the point of the error, an educated guess would be that you are passing Rectstyle arguments incorrectly somewhere. Please see the documentation for pygame.Rect on how to pass Rectstyle args correctly.
pygame.Rect(left, top, width, height): return Rect
pygame.Rect((left, top), (width, height)): return Rect
pygame.Rect(object): return Rect

The problem is
screen.blit(test_image, to_pygame((450,100)), to_pygame((150,100)),3)
To blit a surface to to screen, a valid blit is:
screen.blit(test_image, (450,100))
So you want:
screen.blit(test_image, to_pygame((450,100)))
Note: The extra args make me think maybe you're trying to use a source rect argument, to use a tileset. But in that case you wouldn't convert world to screen coords twice. Just on destination, not source.
If you do want the spritesheet/tileset, then out How do you select a sprite image from a sprite sheet in python? and Pygame.org/cookbook/SpriteSheet

The third argument in blit is the source area argument, should be a rectangle, not coordinates:
screen.blit(test_image, to_pygame((450,100)), to_pygame((150,100)),3)
Perhaps you want this:
screen.blit(test_image, to_pygame((450,100)), pygame.Rect((0,0), (150,100)), 3)

The problem is because your call to screen.blit is incorrect. See here.
The call could be
screen.blit(test_image, to_pygame((450,100)))
or perhaps if you wanted to use the area argument,
screen.blit(test_image, to_pygame((450,100)), pygame.Rect((0,0), (150,100)), 3)
Also, you are loading guess.png in every loop, instead move it out of the while loop, your code will perform better.

Related

Pygame draggable frame seems not to be working

I am working with Pygame currently, and I made a simple function to create window instances much like Windows 10 UI. the code I made doesn't give any errors or any unwanted outputs. It just seems not to be working properly, what I mean by "not working properly"; it just doesn't seem to be moving the frames that are meant to be dragged around by a master frame...
This is my code:
import pygame
from pyautogui import size
import datetime
pygame.init()
infoObject = pygame.display.Info()
surface = pygame.display.set_mode((900, 700))
run = True
clock = pygame.time.Clock()
def draw_text(text, font, text_col, x,y):
img = font.render(text, True, text_col)
rect = img.get_rect()
rect.center = (x,y)
surface.blit(img, rect)
return rect
class make_a_window():
def __init__(self,app,width=750,height=500):
self.app_name = str(app)
self.width = width
self.height = height
def run(self):
self.top_frame = pygame.draw.rect(surface, "#C0C0C0", pygame.Rect(0,0,int(self.width),40))#master frame
self.main_frame = pygame.draw.rect(surface, (255,255,255), pygame.Rect(0,40,int(self.width),int(self.height)))
self.red_frame_for_exit_btn_X = pygame.draw.rect(surface, (255,0,0), pygame.Rect(self.width-42,0,42,40))
self.exit_btn_X = draw_text("x", pygame.font.SysFont("calibri",25), "black", self.width-20, 15)
self.mouse_position = pygame.mouse.get_pos()
if pygame.mouse.get_pressed()[0] == 1:
if self.top_frame.collidepoint(self.mouse_position):
#moving the frames
self.top_frame.move(self.mouse_position[0],self.mouse_position[1])
self.main_frame.move(self.mouse_position[0]-40,self.mouse_position[1])
self.red_frame_for_exit_btn_X.move(self.mouse_position[0]-42,self.mouse_position[1])
self.exit_btn_X.move(self.mouse_position[0]-20,self.mouse_position[1])
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
run = False
app = make_a_window("hello")
app.run()
pygame.display.update()
clock.tick(60)
Sorry for my bad English. and thanks for the help, I really appreciate it 😃!
There is some logic error from line 32 to 41.
Firstly you should use the event queue by pygame.event.get() to track mouse activities(this is really important) and secondly why are you recording the mouse position before hand you are checking for its collision. Instead you should insert your
{self.mouse_position = pygame.mouse.get_pos()}
inside the collision checking if statement (rather that would not work smoothly until you use pygame.event.get())
One more thing that the function
pygame.Rect().move()
takes x and y offesets as its arguments not x and y coordinates.
So, mainly give focus on your event loop and the destination positions of your manual window. Maybe I would share the correct code later (don't wait for it.)
The method pygame.Rect.move doesn't move the rectangle itself, but it returns new rectangle that is moved. In compare, the method pygame.Rect.move_ip move the object in place.
However, these methods do not move anything that has been drawn on the screen. These methods simply move a rectangle representing an area of the screen. This rectangle can later be used to draw something on the screen at a new location.
Create the pygame.Rect objects in the class's constructor and use them to draw the objects. Use move_ip to move the rectangles:
class make_a_window():
def __init__(self,app,width=750,height=500):
self.app_name = str(app)
self.width = width
self.height = height
self.top_frame = pygame.Rect(0,0,int(self.width),40)
self.main_frame = pygame.Rect(0,40,int(self.width),int(self.height))
self.red_frame_for_exit_btn_X = pygame.Rect(self.width-42,0,42,40)
self.exit_btn_X = pygame.Rect(self.width-20, 15, 0, 0)
def run(self):
pygame.draw.rect(surface, "#C0C0C0", self.top_frame)
pygame.draw.rect(surface, (255,255,255), self.main_frame)
pygame.draw.rect(surface, (255,0,0), self.red_frame_for_exit_btn_X)
draw_text("x", pygame.font.SysFont("calibri",25), "black", self.exit_btn_X.topleft)
self.mouse_position = pygame.mouse.get_rel()
if pygame.mouse.get_pressed()[0] == 1:
if self.top_frame.collidepoint(self.mouse_position):
#moving the frames
move_rel = pygame.mouse.get_rel()
self.top_frame.move_ip(*move_rel)
self.main_frame.move_ip(*move_rel)
self.red_frame_for_exit_btn_X.move_ip(*move_rel)
self.exit_btn_X.move_ip(*move_rel)

Problem with making Circle properly appear on screen in Pygame

I think my understanding of Pygame is a little bit weak. I would appreciate any help in general about the intricacies of the code (since this was given by the teacher) or simply how I can at least make the obstacle visible.
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
Above is where I actually do the drawing and attempt to draw the circle.
The CircularObstacle class is a very simple class that looks like this:
import pygame
class CircularObstacle():
def __init__(self, x, y, radius): #MAYBE ADD A SIZE
self.x = x
self.y = y
self.radius = radius
The problem is that the circle only draws itself when the boids have went over it, which is really weird. I think it has to do with the way the pygame has been setup with and the Surfaces and everything, so below is all the code in main. Of course the obstacle does not work as intended, but I plan to fix that later, first I want to at least get a circle to show.
Below is my full code because I believe it is crucial to solving the issue:
import pygame
from pygame.locals import *
import argparse
import sys
from boid import Boid
from Obstacle import CircularObstacle
def add_boids(boids,num_boids):
for boid in range (num_boids):
boids.add(Boid())
def update(dt, boids):
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
mods = pygame.key.get_mods()
if event.key == pygame.K_q:
# quit
pygame.quit()
sys.exit(0)
elif event.key == pygame.K_UP:
# add boids
if mods & pygame.KMOD_SHIFT:
add_boids(boids, 100)
else:
add_boids(boids, 10)
elif event.key == pygame.K_DOWN:
# remove boids
if mods & pygame.KMOD_SHIFT:
boids.remove(boids.sprites()[:100])
else:
boids.remove(boids.sprites()[:10])
#ADD STUFF LIKE THE SLIDER AND STUFF
for b in boids:
b.update(dt, boids)
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
default_boids = 0
default_geometry = "1000x1000"
# Initialise pygame.
pygame.init()
pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP])
# keep a good framerate so the graphics are better
fps = 60.0
fpsClock = pygame.time.Clock()
# Set up pygamme window
window_width, window_height = 800,600
flags = DOUBLEBUF
screen = pygame.display.set_mode((window_width, window_height), flags)
screen.set_alpha(None)
background = pygame.Surface(screen.get_size()).convert()
background.fill(pygame.Color('black'))
boids = pygame.sprite.RenderUpdates()
add_boids(boids, default_boids)
obstaclearray = []
defaultcircleobstacle = CircularObstacle(200,200,13)
obstaclearray.append(defaultcircleobstacle)
#The "game loop"
dt = 1/fps # here dt means the amount of time elapsed since the last frame
#it seems like thie is a forever loop but in reality this is not since in the update method we provide functinality to quit the program
while True:
update(dt, boids)
draw(screen, background, boids, obstaclearray)
dt = fpsClock.tick(fps)
When you call pygame.display.update() you have 2 options. You can call it without any parameter. In this case the complete screen is updated.
pygame.display.update()
Or call it with a list of rectangular regions that need to be updated. In this case, only the rectangular areas will be updated.
pygame.display.update(rect_list)
You do the 2nd option, but the areas where the circles are drawn are not in the dirty list, therefore this regions are not updated.
pygame.display.update(dirty)
Either update the whole screen with pygame.display.update() or add the regions of the circles to the dirty list:
def draw(screen, background, boids, obstaclearray):
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
dirty_rect = pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
dirty.append(dirty_rect)
pygame.display.update(dirty)

Is there a way to restrict the title crawl to certain portion of screen?

I am trying do end credits like animation the code above for Title crawl, I am trying to make the following changes to it:- 1) The text should begin at the bottom of screen at certain location, such that no other text from the string should be displayed below that location on the screen. 2) The text should stop at certain location on top of screen such that the line at the top should be deleted as soon as it reaches that location making room for other lines in the string.
I am a python newbie, I am just experimenting with things, the following code doesn't belong to me either.
import pygame
from pygame.locals import *
pygame.init()
pygame.display.set_caption('Title Crawl')
screen = pygame.display.set_mode((1000, 800))
screen_r = screen.get_rect()
font = pygame.font.SysFont("franklingothicdemibold", 40)
clock = pygame.time.Clock()
def main():
crawl = ["Star Wars - The Wilds"," ","It is a dark time for the Galaxy. The evil Dark","Lord, Vitiate is rising to power. Alone, a single", "spec is on a trip, a trip that will ultimately", "rectify the wrongs of the galaxy. The keepers ", "of peace are dying out and the DARK SIDE is", "lurking, a conniving force determined to", "become the omniarch."]
texts = []
# we render the text once, since it's easier to work with surfaces
# also, font rendering is a performance killer
for i, line in enumerate(crawl):
s = font.render(line, 1, (229, 177, 58))
# we also create a Rect for each Surface.
# whenever you use rects with surfaces, it may be a good idea to use sprites instead
# we give each rect the correct starting position
r = s.get_rect(centerx=screen_r.centerx, y=screen_r.bottom + i * 45)
texts.append((r, s))
while True:
for e in pygame.event.get():
if e.type == QUIT or e.type == KEYDOWN and e.key == pygame.K_ESCAPE:
return
screen.fill((0, 0, 0))
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# if all rects have left the screen, we exit
if not screen_r.collidelistall([r for (r, _) in texts]):
return
# only call this once so the screen does not flicker
pygame.display.flip()
# cap framerate at 60 FPS
clock.tick(60)
if __name__ == '__main__':
main()
Use set_clip() to set the clipping region of the display surface.
e.g. Clip 100 rows at the top and the bottom:
# set clipping rectangle
clip_rect = (0, 100, screen.get_width(), screen.get_height()-200)
screen.set_clip(clip_rect)
for r, s in texts:
# now we just move each rect by one pixel each frame
r.move_ip(0, -1)
# and drawing is as simple as this
screen.blit(s, r)
# cancel clipping for further drawing
screen.set_clip(None)

Pygame mouse event.rel not working outside window [duplicate]

This question already has answers here:
pygame capture keyboard events when window not in focus
(2 answers)
Get Inputs without Focus in Python/Pygame?
(3 answers)
Closed 3 months ago.
I'm trying to implement a feature in Pygame where if the user moves the mouse outside the window the relative position (event.rel) can be returned. I'm trying to do this because I want to make a game where you can keep turning left or right from mouse input.
I know this is possible from the docs:
If the mouse cursor is hidden, and input is grabbed to the current display the mouse will enter a virtual input mode, where the relative movements of the mouse will never be stopped by the borders of the screen. See the functions pygame.mouse.set_visible() and pygame.event.set_grab() to get this configured.
For this reason, I've implemented these lines in my code
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
However this doesn't help. The actual behaviour is:
when the mouse is moving in the window, event.rel prints to the console (expected)
when the mouse is moving outside the window, event.rel doesn't print to the console (not expected)
Other strange behaviour:
Initially event.rel is set to (300, 300) which is the center of my 600x600 screen. Ideally initial event.rel should be (0,0)
Possible cause:
I'm running the code below in trinket.io or repl.it. Because this is a browser the demo window might cause problems. Maybe this is solved by downloading python locally but i don't want to do this as it takes up too much space (my laptop sucks) and also it would be good to have an online demo to easily show employers that are too lazy to paste code into their IDEs.
Similar issue:
This guy on reddit had a very similar issue but I think his solution is not related to my situation
This block of code was what I asked originally but it doesn't demonstrate the question as well as the last block. Please scroll down to see the last block of code instead :)
import pygame
pygame.init()
# screen
X = 600 # width
Y = 600 # height
halfX = X/2
halfY = Y/2
screen = pygame.display.set_mode((X, Y), pygame.FULLSCREEN)
clock = pygame.time.Clock()
# Colors
WHITE = (255, 255, 255)
RED = (255, 0, 0)
# From the docs:
# "If the mouse cursor is hidden, and input is grabbed to the
# current display the mouse will enter a virtual input mode, where
# the relative movements of the mouse will never be stopped by the
# borders of the screen."
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
def drawCross(point, size, color):
pygame.draw.line(screen, color, (point[0]-size,point[1]-size), (point[0]+size,point[1]+size), 2)
pygame.draw.line(screen, color, (point[0]+size,point[1]-size), (point[0]-size,point[1]+size), 2)
canvas_rel = (halfX,halfY)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
print('event.rel = ' + str(event.rel))
canvas_rel = [10 * event.rel[0] + halfX, 10 * event.rel[1] + halfY]
print(' canvas_rel = ' + str(canvas_rel))
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
print('escape')
running = False
break
screen.fill(WHITE)
# Red sight to represent pygame mouse event.rel
drawCross(canvas_rel, 10, RED)
pygame.display.flip()
clock.tick(20)
pygame.quit()
EDIT:
The above code doesn't fully demonstrate my problem. I've added a better sample below to make the the question easier to understand. This sample will draw a triangle in the centre of the screen. The triangle will rotate with relative horizontal motion of the mouse. Note the attempt to use pygame.mouse.set_pos
import pygame
from math import pi, cos, sin
pygame.init()
# screen
X = 600 # width
Y = 600 # height
halfX = X/2
halfY = Y/2
screen = pygame.display.set_mode((X, Y), pygame.FULLSCREEN)
clock = pygame.time.Clock()
# for getting relative mouse position when outside the screen
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# sensitivity
senseTheta = 0.01
# player
stand = [halfX, halfY]
t = 0 # angle in radians from positive X axis to positive x axis anticlockwise about positive Z
# draws a white cross in the screen center for when shooting is implemented :)
def drawPlayer():
p = stand
d = 50
a1 = 0.25*pi-t
a2 = 0.75*pi-t
P = (p[0],p[1])
A1 = (p[0] + d*cos(a1), p[1] + d*sin(a1)) # P + (d*cos(a1), d*sin(a1))
A2 = (p[0] + d*cos(a2), p[1] + d*sin(a2)) # P + (d*cos(a2), d*sin(a2))
pygame.draw.line(screen, BLACK, P, A1, 2)
pygame.draw.line(screen, BLACK, A1, A2, 2)
pygame.draw.line(screen, BLACK, A2, P, 2)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
mouseMove = event.rel
print('mouseMove = ' + str(mouseMove))
t -= mouseMove[0] * senseTheta
# "Fix" the mouse pointer at the center
# pygame.mouse.set_pos((screen.get_width()//2, screen.get_height()//2))
# But I think set_pos triggers another MOUSEMOTION event as moving in positive x
# seems to move backward the same amount in x (i.e. the triangle doesn't
# rotate). See the console log.
# If you uncomment this line it works ok (the triangle rotates) but still
# glitches when the mouse leaves the window.
# uncommented or not I still cant get the mouse to work outside the window
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
break
screen.fill(WHITE)
drawPlayer()
pygame.display.flip()
clock.tick(40)
pygame.quit()

Pygame Cannot Blit An Image To A List Of Rectangles

I am working with pygame, and I thought it'd be a super neat idea to make it so that the bricks were automatically generated at the beginning of the game. So I made a function that asks for how many bricks you want generated, and then creates the "bodies" for those bricks in special locations and assigns them to a list. Then, in the while loop, I try to blit those with the according image. However, I get an error I have never seen before.
Error Image
import pygame, sys
import random
import numpy
pygame.init()
def myPopulater(amountToMake, image, width, h):
myBodies = []
for i in range(amountToMake):
r1 = numpy.random.randint(0, width)
r2 = numpy.random.randint(0, h)
myBodies = myBodies + image.get_rect()
myBodies[i].x = r1
myBodies[i].x = r2
return myBodies
width = 500
h = 500
ball = pygame.image.load("c:\\python\\ball.png")
screen = pygame.display.set_mode((width, h))
myList = myPopulater(25, ball, width, h)
while (1):
#WHENEVER SOMETHING HAPPENS
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
screen.fill(black)
for i in range(0, 25):
screen.blit(ball, myList[i])
pygame.display.flip()
From what I can see, you're trying to add the result of image.get_rect() to your myBodies list.
You should use the list.append method to add an element to a list object.
Change this line:
myBodies = myBodies + image.get_rect()
To this:
myBodies.append(image.get_rect())
That will fix your error.

Categories