[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Extending Surface Class


Sorry to not have read your example exactly.

I went curious and tried to extend pygame.Surface by my own. I made a SurfaceObject that has all the draw, transform and gfxdraw functions as its own methods (see attachment). This only works because the functions take a surface as first argument! It was a little experiment and I had to use some trickery to get it to work. Not sure if it is useful at all.


ERName schrieb:
Is it possible to extend the surface class? Every time I do the
surface ends up dieing somehow right after it is created... I may just
be doing it in the wrong way, though. The main point is to override
the __str__() method, so if you know another way besides extending
pygame.Surface, I'd be happy to hear it.

class ExtendedSurface(pygame.Surface):

    def __init__(self, surface, string):
        pygame.Surface.__init__(surface, surface.get_size()) #The docs
say to include a size, etc. but that just gives me "too many values to
unpack" errors.
        self.string = string

    def __str__(self):
        return self.string


# -*- coding: utf-8 -*-
TODO: doc

__version__ = '$Id$'

import pygame
    import pygame.gfxdraw
import new

# save a pointer to the original pygame.Surface
pygame_surface = pygame.Surface

class SurfaceObject(pygame_surface):
    SurfaceObject is a Surface that has all all functions from pygame.draw, pygame.gfxdraw, 
    pygame.transform as a own method. So instead of calling
        s = pygame.Surface((10,10))
    with this SurfaceObject one can do:
        s = SurfaceObject((10,10))
    The SurfaceObject might be slower than using the native functions.
    At the end, the SurfaceObject needs to be blitted to the screen.

    def __init__(self, size, flags=0, depth=32):
        super(SurfaceObject, self).__init__(size, flags, depth)

        # add methods from pygame.draw
        for name in dir(pygame.draw):
            if not name.startswith('__'):
                #new.instancemethod(function, instance, class)
                attr = getattr(pygame.draw, name)
                setattr(self, 'draw_'+name, new.instancemethod(attr, self, self.__class__))
            # add methods from gfxdraw, new in pygame 1.9
            for name in dir(pygame.gfxdraw):
                if not name.startswith('__'):
                    setattr(self, "gfxdraw_"+name, new.instancemethod(getattr(pygame.gfxdraw, name), self, self.__class__))
        # wrapper for functions that return a pygame.Surface
        # wrapper returns a SurfaceObject instaed of a pygame.Surface
        class Wrapper(object):
            def __init__(self, func):
                self._func = func
            def __call__(self, *args, **kwargs):
                s = self._func(*args, **kwargs)
                # not sure if this creates a surface that is the same a converted surface
                so = SurfaceObject(s.get_size(), 0, s)
                # transfer the content
                so.blit(s, (0,0), )
                return so

        for name in dir(pygame.transform):
            if not name.startswith('__'):
                if name in ['threshold', 'average_color']:
                    # this functions take an surf, return a non surf
                    setattr(self, name, getattr(pygame.transform, name))
                elif name in ['set_smoothscale_backend', 'get_smoothscale_backend']:
                    # this functions take not a surface and return not a surface
                    # this wrapper is there to just filter out the self argument of the methods
                    class Wrapper1(object):
                        def __init__(self, func):
                            self._func = func
                        def __call__(self, *args, **kwargs):
                            return self._func(*args, **kwargs)
                    w = Wrapper1(getattr(pygame.transform, name))
                    inst = new.instancemethod(w, self, self.__class__)
                    setattr(self, name, inst)
                    # return a surface
                    w = Wrapper(getattr(pygame.transform, name))
                    inst = new.instancemethod(w, self, self.__class__)
                    setattr(self, name, inst)
        # some methods from pygame.Surface return a Surface, they need to 
        # be wrapped to return a SurfaceObject
        for name in ['convert', 'convert_alpha', 'subsurface']:
            orig_name = '_orig_'+name
            orig_func = getattr(self, name)
            setattr(self, name, Wrapper(orig_func))
            setattr(self, orig_name, orig_func)

# only allow SurfaceObject to be instanciated, even using pygame.Surface
pygame.Surface = SurfaceObject
pygame.surface.Surface= SurfaceObject

if __name__ == '__main__':
    # usage

    s = SurfaceObject((400,300))
    s.draw_line((255,255,0), (100,100), (400,300), 1)
    s.draw_circle((0,0,255), (200,200), 50)
    s.draw_line((255,255,255), (100,100), (300,300), 5)
    #s = s.rotate(90)
    s2 = pygame.Surface((50,50))
    s2.draw_line((100,100,100), (0,0), (25,25), 3)

    # print all attributes from a SurfaceObject
    for i in dir(s): print i, '\t\t\t', getattr(s, i)

    screen = pygame.display.set_mode((800, 600))
    s2 = s2.convert()

    # update display
    screen.blit(s, (0,0))
    screen.blit(s2, (400,0))

    # wait for an event