I am trying to create my own version of a pygame.Rect rectangle, but with the added feature that when a square is out of certain worldbounds, it appears on the other side.
This means I had to rewrite a lot of functions of pygame.Rect in my extension, and i succeeded in that. No problems here.
The problems start when I try to change __getattr__ and __setattr__. pygame.Rect makes heavy use of these functions, so that for example asking 'top' or 'bottom' refer to 'y' and 'y'+'width' respectively. I have changed these functions to accomodate my feature, but there is one more thing i need to do: In the __init__ function I need to create the worldwidth and worldheight variables. But I can't, since the __setattr__ function does not allow it.
Here is what I have right now:
class Rectinworld(pygame.Rect):
'''hides the fact that the world is round, when it comes to collisions and such'''
__slots__ = ['_r']
def __init__(self,(worldwidth,worldheight),*args):
object.__setattr__(self, "worldwidth",worldwidth)
if len(args)==0:
super(Rectinworld, self).__init__((0,0,0,0))
else:
super(Rectinworld, self).__init__(*args)
def __getattr__(self, name):
if name == 'top':
return self._r.y
#etc etc etc
elif name == 'height':
return self._r.h
else:
raise AttributeError(name)
def __setattr__(self, name, value):
if name == 'top' or name == 'y':
self._r.y = value % worldheight
#etc etc etc
elif name == 'height':
if int(value) < 0:
self._ensure_proxy()
self._r.h = int(value)
else:
raise AttributeError(name)
I left some code out at the #etc etc etc
comments for clarity. The code for pygame.Rect is similar: the __setattr__ and __getattr__ do not reference worldwidth or worldheight, but are otherwise the same, the pygame.Rect.__init__ function is extremely long, but i think the following snippet covers the most important:
def __init__(self, *args):
#etc etc etc
if len(args) == 4:
if args[2] < 0 or args[3] < 0:
object.__setattr__(self, '_r', _RectProxy((int(args[0]),
int(args[1]),
int(args[2]),
int(args[3]))))
The complete code can be found at https://github.com/brython-dev/brython-pygame/blob/master/pygame/rect.py
The error I get now is:
line 10, in __init__
object.__setattr__(self, "worldwidth",0)
AttributeError: 'Rectinworld' object has no attribute 'worldwidth'
An obvious fix seems to be adding worldwidth and worldheight to __slots__ and go from there. That gave even weirder errors. It would give the following message when trying to set any variable:
line 65, in __getattr__
raise AttributeError(name)
AttributeError: _r
So in short my question boils down to: why can I not create new variables, and what do I need to do, so that I can?
I'm not sure if it is correct but it works for me (on Python 2 and 3)
and it uses slots
as you wish.
import pygame
class Rectinworld(object):
'''hides the fact that the world is round, when it comes to collisions and such'''
__slots__ = ['_r', '_worldwidth', '_worldheight']
def __init__(self, worldwidth, worldheight, *args):
if not args:
args = (0,0,0,0)
super(Rectinworld, self).__setattr__("_r", pygame.Rect(args))
super(Rectinworld, self).__setattr__("_worldwidth", worldwidth)
super(Rectinworld, self).__setattr__("_worldheight", worldheight)
def __getattr__(self, name):
if name == 'top':
return self._r.y
#etc etc etc
elif name == 'height':
return self._r.h
else:
raise AttributeError(name)
def __setattr__(self, name, value):
if name == 'top' or name == 'y':
self._r.y = value % self._worldwidth
#etc etc etc
elif name == 'height':
if int(value) < 0:
self._ensure_proxy()
self._r.h = int(value)
else:
raise AttributeError(name)
# --- test ---
r = Rectinworld(100, 100)
r.top = 120
print(r.top) # gives 20