Failing to get a solution to my problem from other people, I got tired of waiting and wrote this (rather rushed) Text class to deal with the text problems I've been having in Pygame. I wanted the text to update itself, so that it would be unnecessary to keep track of its original position. After playing a lot with it, I found this solution:

class Text(object):

    def __init__(self, value, size, color,
                 x=0, y=0,
                 top=None, bottom=None, left=None, right=None,
                 centerx=None, centery=None):

        self._size = size
        self._color = color
        self._value = value
        self._font = pygame.font.Font(font, self._size)
        self.width, self.height = self._font.size(self._value)
        self.left_orientation = left_orientation

        self.image = self._create_surface()
        self.rect = self.image.get_rect()
        if x: self.rect.x = x
        if y: self.rect.y = y
        if top: self.rect.top = top
        if bottom: self.rect.bottom = bottom
        if left: self.rect.left = left
        if right: self.rect.right = right
        if centerx: self.rect.centerx = centerx
        if centery: self.rect.centery = centery

    def _create_surface(self):
        return self._font.render(self._value, True, self._color)

    def set_value(self, new_value):
        if new_value != self._value:
            self._value = new_value
            self.image = self._create_surface()

            new_rect = self.image.get_rect(x = self.rect.x, y = self.rect.y)
            if self.left_orientation:
                width_diff = new_rect.width - self.rect.width
                new_rect.x = self.rect.x - width_diff
            self.rect = new_rect

    def set_position(self, x_or_x_and_y, y=None):
        if y != None:
            self.rect.x = x_or_x_and_y
            self.rect.y = y
            self.rect.x = x_or_x_and_y[0]
            self.rect.y = x_or_x_and_y[1]

So, if a text is supposed to increase to the left, all that I have to do is initialize a Text object with the left_orientation parameter set to True and, whatever the rect is, it will update itself to remain at it's original position.

Is this a good solution? If not, what would be a better one?


This is copied from my answer to your question "What about my Pong game?".

  1. The Text._size member is only used to create the font, so there is no need to store it in the instance.

  2. Why not make Text a subclass of pygame.sprite.Sprite so that you can draw it using a sprite group?

  3. Lots of code is repeated betwen Text.__init__ and Text.set_value. Why not have the former call the latter?

  4. The Text.__init__ constructor takes many keyword arguments, which you then apply to self.rect like this:

    if top: self.rect.top = top

    The first problem is that the test if top: means that you can't set top to zero. You should write something like this:

    if top is not None: self.rect.top = top

    But it would be much simpler to use Python's ** keyword argument mechanism to take any number of keyword arguments, and pass them all to Surface.get_rect.

Applying all these improvements yields the following:

class Text(pygame.sprite.Sprite):
    def __init__(self, text, size, color, font=None, **kwargs):
        super(Text, self).__init__()
        self.color = color
        self.font = pygame.font.Font(font, size)
        self.kwargs = kwargs

    def set(self, text):
        self.image = self.font.render(str(text), 1, self.color)
        self.rect = self.image.get_rect(**self.kwargs)

Not the answer you're looking for? Browse other questions tagged or ask your own question.