Why do I get "missing 1 required positional argument: 'self'"? - python

When I use in the program querauto_group.add(QuerWagen.create_querauto()) then I get the report
missing 1 required positional argument: 'self'. When I use querauto_group.add(player.create_querauto()) then the program works.
import pygame, sys
import random
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
def create_querauto(self):
ii=random.randrange(0,8)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
img = pygame.transform.scale(img,(100,25))
return QuerWagen(200,300,img)
class QuerWagen(pygame.sprite.Sprite):
def __init__(self, pos_x,pos_y,img):
super().__init__()
self.image=img
self.rect = self.image.get_rect(center = (pos_x,pos_y))
def update(self):
self.rect.x+=5
if self.rect.x > screen_width + 200:
self.kill()
def create_querauto(self):
ii=random.randrange(0,8)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
img = pygame.transform.scale(img,(100,25))
return QuerWagen(200,300,img)
pygame.init()
clock = pygame.time.Clock()
screen_width,screen_hight = (1200,800)
screen = pygame.display.set_mode((screen_width,screen_hight))
player = Player()
querauto_group = pygame.sprite.Group()
WECHSELBILD = pygame.USEREVENT + 1
pygame.time.set_timer(WECHSELBILD,1800)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type ==WECHSELBILD:
#querauto_group.add(QuerWagen.create_querauto())
querauto_group.add(player.create_querauto())
screen.fill((30,30,30))
querauto_group.draw(screen)
querauto_group.update()
pygame.display.flip()
clock.tick(30)
pygame.quit()
quit()

On both classes create_querauto is an instance method, meaning that the class needs to be instantiated in order to call the method.
That's why it is working with the player instance.
i.e.
class Player:
def create_querauto(self):
ii=random.randrange(0,8)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
img = pygame.transform.scale(img,(100,25))
return QuerWagen(200,300,img)
player_instance = Player()
player_instance.create_querauto() # This will work
Player.create_querauto() # This will throw the error you see above
If you would like to method to work without instantiating the class first you can define it as a static method, using the #staticmethod decorator
This would look like:
class QuerWagen(pygame.sprite.Sprite):
#staticmethod
def create_querauto():
ii=random.randrange(0,8)
img = pygame.image.load(f"Bilder/Gegenstaende/geg{ii}.png")
img = pygame.transform.scale(img,(100,25))
return QuerWagen(200,300,img)
Now you can call that function on the class directly like you were trying to do.
querwagen = QuerWagen.create_querauto() # works fine

Related

How to not initialize the def init every time

this code is by tech with tim but i am trying to tweak it, i am trying to make the rotation velocity vary everytime.
import pygame
import time
import math
from utility import scale_image, blit_rotate_center
rotation_speed=2
Joystickx=0
GRASS = scale_image(pygame.image.load("Sprites/grass.png"),2.5)
TRACK = scale_image(pygame.image.load("Sprites/track.png"),0.7)
TRACK_BORDER = scale_image(pygame.image.load("Sprites/track-border.png"),0.7)
Car = scale_image(pygame.image.load("Sprites/F1.xcf"),0.1)
FINISH=pygame.image.load("Sprites/finish.png")
WIDTH, HEIGHT= TRACK.get_width(), TRACK.get_height()
WIN = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("F1")
FPS = 60
class AbstractCar:
def __init__(self,max_vel,rotation_vel):
self.img=self.IMG
self.max_vel=max_vel
self.vel = 0
self.rotation_vel = rotation_vel
self.angle = 90
self.x, self.y = self.START_POS
def rotate(self, left=False, right=False):
if left:
self.angle += self.rotation_vel
print(self.angle)
elif right:
self.angle -= self.rotation_vel
print(self.angle)
def draw(self):
blit_rotate_center(WIN, self.img,(self.x, self.y), self.angle)
class PlayerCar(AbstractCar):
IMG = Car
START_POS = (180,200)
def draw(win,images, player_car):
for img, pos in images:
win.blit(img,pos)
player_car.draw()
pygame.display.update()
run=True
clock = pygame.time.Clock()
images = [(GRASS, (0,0)),(TRACK,(0,0))]
def rotate():
global rotation_speed
global Joystickx
rotation_speed+=2*Joystickx
rotation_speed*=0.9
while run:
clock.tick(FPS)
rotate()
player_car = PlayerCar(4, rotation_speed)
draw(WIN, images, player_car)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
break
keys = pygame.key.get_pressed()
if keys[pygame.K_a]:
player_car.rotate(left=True)
Joystickx=-1
elif keys[pygame.K_d]:
player_car.rotate(right=True)
Joystickx=1
else:
Joystickx=0
pygame.quit()
however, every time the class is ran, the code will set self.angle to 90, i do not want that to happen, what can i do to not let the def init run as that will set the self.angle to 94 instead remembering the last self.angle
This is OOP (Object Oriented Programming) thing. I really suggest you to look it up, it's not an easy topic at first, but it's also not that hard to understand everything in it, it's just the first time is hard.
So, the __init__ is the constructor of your AbstractCar class. This will always run when you make a new object from this class.
The simple way to fix this is to place the line where you define your car a bit above, outside the while loop and to keep the rotation_vel updated, we make a new method in the AbstractCar class and call it instead:
class AbstractCar:
def set_rotation_vel(self, new_rotation_vel):
self.rotation_vel = new_rotation_vel
[...]
player_car = PlayerCar(4, rotation_speed)
while run:
clock.tick(FPS)
rotate()
player_car.set_rotation_vel(rotation_speed)
draw(WIN, images, player_car)

How to call a particular object's method and have affect only on that object which is a value of a key of a dictionary in pygame?

I am a new learner of programming. I am practiceing python and writing a litle game using pygame. I have ten circle on the pygame window and each have a counter. I want to increase the counter by 1 when it's circle is clicked. I first created group using .sprite.Group(), but I could not get the desired result. Because then the counter does not even get update. So I create two list, one for the circle and one for the counter. And for each circle in the circle list I created a dictionary taking the circle as the key and each counter in the circle list is the value of the circle. Now when the circle got clicked then the all counter gets updated, not the counter that the circle holds. But goal is to get the specific counter updated for it's circle.
(hole == circle)
dig_hole.py(This is the main file.)
import pygame
import sys
from pygame.sprite import Group
from counter import Counter
from hole import Hole
from settings import Settings
class DigHole:
def __init__(self):
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Dig Hole")
pygame.display.set_icon(Hole(self).image)
self.count = Counter(self)
self.counter_group = list()
self.holes = list()
self.dict = dict()
self._create_holes()
self.hole = Hole(self)
self.mouse_pos = (0, 0)
def run_dig_hole(self):
while True:
self._check_events()
self._update_screen()
def _check_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
self.mouse_pos = pygame.mouse.get_pos()
self._check_hole_clicked_events(self.mouse_pos)
def _check_hole_clicked_events(self, mouse_pos):
for key in self.dict:
if key.rect.collidepoint(mouse_pos):
self.dict[key].count_clock += 1
self.dict[key].prep_counter()
self.count.prep_counter()
def _create_holes(self):
for row_number in range(2):
for hole_number in range(5):
self._create_hole(row_number, hole_number)
for hole in self.holes:
counter = Counter(self)
counter.counter_rect.midbottom = hole.rect.midtop
self.counter_group.append(counter)
for hole in self.holes:
for counter in self.counter_group:
self.dict[hole] = counter
def _create_hole(self, row_number, hole_number):
hole = Hole(self)
hole_width, hole_height = hole.rect.size
available_space_x = self.settings.screen_width - (2 * hole_width)
available_space_y = self.settings.screen_height - (2 * hole_height)
hole.x =(((available_space_x // 5) - hole_width) // 2) + (available_space_x // 5) * hole_number
hole.rect.x = hole.x
hole.rect.y = 2 * hole.rect.height + (available_space_y - (4 * hole_height)) * row_number
self.holes.append(hole)
def _update_screen(self):
self.screen.fill(self.settings.bg_color)
for key in self.dict:
key.draw()
for key in self.dict:
self.dict[key].counter_rect.midbottom = key.rect.midtop
self.dict[key].show_counter()
self.count.show_counter()
pygame.display.flip()
if __name__ == '__main__':
dh = DigHole()
dh.run_dig_hole()
hole.py
import pygame
from pygame.sprite import Sprite
class Hole():
def __init__(self, dh):
# super().__init__()
self.screen = dh.screen
self.image = pygame.image.load("images/circle.bmp")
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.x = float(self.rect.x)
def draw(self):
self.screen.blit(self.image, self.rect)
counter.py
import pygame.font
from pygame.sprite import Sprite
class Counter():
def __init__(self, dh):
# super().__init__()
self.screen = dh.screen
self.screen_rect = self.screen.get_rect()
self.settings = dh.settings
self.count_clock = 0
self.text_color = (30, 30, 30)
self.font = pygame.font.SysFont(None, 48)
self.prep_counter()
def prep_counter(self):
counter_str = str(self.count_clock)
self.counter_image = self.font.render(counter_str, True, self.text_color, self.settings.bg_color)
self.counter_rect = self.counter_image.get_rect()
self.counter_rect.right = self.screen_rect.right - 20
self.counter_rect.top = 20
def show_counter(self):
self.screen.blit(self.counter_image, self.counter_rect)
Thank you.This is the window of the progeam. Here all circles are gets update but one is clicked.
The issue is that, in _create_holes, you set the counters of each circle to be the same Counter object.
for hole in self.holes:
for counter in self.counter_group:
self.dict[hole] = counter
Unrolling the inner loop, this is the same as
for hole in self.holes:
self.dict[hole] = self.counter_group[0]
self.dict[hole] = self.counter_group[1]
self.dict[hole] = self.counter_group[2]
...
self.dict[hole] = self.counter_group[-1]
The first assignments are all immediately overwritten, so this code is setting every self.dict value to self.counter_group[-1]. What you want to do instead is
for hole, counter in zip(self.holes, self.counter_group):
self.dict[hole] = counter
which iterates over both self.holes and self.counter_group simultaneously. In your case, you can actually rewrite this as
self.dict = dict(zip(self.holes, self.counter_group))
which is nicer.
I’m not sure, but I think you intend self.count to be a total. If this is the case, it won’t quite work: you’re missing a line from _check_hole_clicked_events. It should look like this:
def _check_hole_clicked_events(self, mouse_pos):
for key in self.dict:
if key.rect.collidepoint(mouse_pos):
self.dict[key].count_clock += 1
self.dict[key].prep_counter()
self.count.count_clock += 1
self.count.prep_counter()
As a side note, I noticed you also wrote list() and dict() to create empty lists and dicts. It’s more efficient and idiomatic just to write literals ([] and {}).

Python Pygame deliver parameter when use call by sharing

I am the first time use call by sharing, search the result but there's no answer
i want to deliver the parameter into call by sharing
here's my codes:
#!/usr/bin/python
import sys,os
import pygame
class Setting():
'''how to deliver self.w and self.h into pic'''
pic = pygame.transform.smoothscale(pygame.image.load("pic.png"),(self.w,self.h)) #how to deliver self.w and self.h in here?
def __init__(self,width,height):
self.w=width
self.h=height
self.flag=pygame.RESIZABLE
self.screen=pygame.display.set_mode((self.w,self.h),self.flag)
self.screen_rect=self.screen.get_rect()
self.bkg=Setting.pic.convert()
pygame.display.set_caption("Muhaha")
def game():
pygame.init()
setting=Setting(1200,800)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
setting.screen.blit(setting.bkg,(0,0))
pygame.display.flip()
game()
Class-level variables are evaluated when the class is created, i.e. BEFORE the game() function is started.
You should either make pic a regular instance member (i.e. using self.pic) or you should pre-initialize it to None and only really initialize it lazily when calling the constructor the first time
class Setting:
pic = None
def __init__(self, width, height):
if Setting.pic is None:
# This code will execute only once
Setting.pic = ...
...

How to remove sprites out of group, outside of the class: Pygame

#Importing Modules
import pygame as pg
import sys
import random
#All pygame stuff under here
pg.init()
#Font definitions
backFont = pg.font.SysFont("monospace",40)
titleFont = pg.font.SysFont("garamond", 100)
cipherFont = pg.font.SysFont("garamond", 50)
buttonFont = pg.font.SysFont("garamond", 25)
bigFont = pg.font.SysFont("garamond",100)
Font = pg.font.SysFont(None,32)
inputFont = pg.font.SysFont('consola', 35)
errorFont = pg.font.SysFont('tahoma',20)
diagramFont = pg.font.SysFont('courier new',25)
#Colour definitions
BackGray = pg.Color('gray60')
screenGray = pg.Color('gray80')
buttonGray2 = pg.Color('gray50')
textColour = pg.Color('navy')
#Screen size set
screen = pg.display.set_mode((400, 400))
clock = pg.time.Clock()
class Button(pg.sprite.Sprite):
def __init__(self, text, x, y, width, height, colour, enabled):
super().__init__()
self.image = pg.Surface((width, height))
self.image.fill(colour)
self.rect = self.image.get_rect()
txt = buttonFont.render(text, True, textColour)
txtRect = txt.get_rect(center = self.rect.center)
self.image.blit(txt, txtRect)
self.rect.topleft = x, y
self.enabled = enabled
def isPressed(self, event):
if self.enabled == True:
if event.type == pg.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
return True
return False
def Function():
background = pg.Surface(screen.get_size())
background.fill(screenGray)
Button1 = Button('Encrypt',100,100,125,50,buttonGray2,True)
Button2 = Button('Decrypt',100,200,125,50,buttonGray2,True)
buttonsGroup = pg.sprite.Group(Button1,Button2)
ACTIONPRINT = False
Active1 = False
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
elif Button1.isPressed(event):
print("1")
Active1 = True
elif Button2.isPressed(event):
print("2")
if Active1 == True:
ACTIONPRINT = True
buttonsGroup = pg.sprite.Sprite.remove(Button2)
screen.blit(background,(0,0))
buttonsGroup.draw(screen)
pg.display.flip()
clock.tick(60)
Function()
Above is the code for the class of Buttons, and a simple function that runs two buttons. What I'd like to do is remove one of the buttons, when one is pressed, which are set as sprites. When the button is removed from the group, I believe it should disappear from the screen, after one is pressed.
The above code at the minute returns an error saying that there is an AttributeError: 'NoneType' object has no attribute 'draw'. However, in another program, when it did work, it said that in the Sprite.remove method, the parameters must be a sequence and not a button - what does this mean?
I have looked online, and all the removing sprites examples are inside a class. Does that mean that the only way for this to work is by changing the class?
Or can sprites still be removed from outside the class, and if so how is it done?
Any other methods are welcome to
Thanks in advance!
sprite.Group.remove doesn't return anything, it removes the sprite from the group that calls it, so instead of:
buttonsGroup = pg.sprite.Sprite.remove(Button2)
Try:
buttonsGroup.remove(Button2)

PyGame Sprites Occasionally Not Drawing

I am trying to make a basic card game using PyGame. I am currently just trying to draw a single card to the screen. The weird thing is, occasionally it will draw and occasionally it won't. Below is my code:
import pygame
from pygame.locals import *
from socket import *
import sys
import os
import math
import getopt
import random
def load_png(name) :
# Loads an image and returns the image object
fullname = os.path.join('/home/edge/Downloads/Playing Cards/PNG-cards-1.3', name)
image = pygame.image.load(fullname)
if image.get_alpha is None :
image = image.convert()
else :
image = image.convert_alpha()
return image, image.get_rect()
class Card(pygame.sprite.Sprite) :
def __init__(self, suit, val) :
pygame.sprite.Sprite.__init__(self)
self.suit = suit
self.val = val
self.image, self.rect = load_png(val + '_of_' + suit + '.png')
screen = pygame.display.get_surface()
self.area = screen.get_rect()
#self.rect.inflate(-.5, -.5)
def main() :
pygame.init()
pygame.display.set_caption('Card Game Thingy')
screen = pygame.display.set_mode( (1250, 650) )
background = pygame.Surface(screen.get_size() )
background = background.convert()
background.fill( (0, 0, 0) )
x = Card('diamonds', '2')
cardSprite = pygame.sprite.RenderPlain(x)
screen.blit(background, (0, 0) )
cardSprite.draw(screen)
clock = pygame.time.Clock()
# Game Loop
while True :
clock.tick(60)
for event in pygame.event.get() :
if event.type == QUIT :
return
elif event.type == KEYDOWN :
if event.key == K_DOWN :
return
cardSprite.draw(screen)
if __name__ == '__main__' :
main()
You have to update the display in every loop in the while loop with
pygame.display.update()

Categories