bitcraft / pytmx

Python library to read Tiled Map Editor's TMX maps.
GNU Lesser General Public License v3.0
386 stars 81 forks source link

AttributeError: 'NoneType' object has no attribute 'get' #46

Closed jfach closed 9 years ago

jfach commented 9 years ago

Upon running this code:

"""
This is tested on pygame 1.9 and python 2.7 and 3.3+.
Leif Theden "bitcraft", 2012-2014
Rendering demo for the TMXLoader.
Typically this is run to verify that any code changes do do break the loader.
Tests all Tiled features -except- terrains and object rotation.
Missing tests:
- object rotation
"""
import logging

logger = logging.getLogger(__name__)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
logger.addHandler(ch)
logger.setLevel(logging.INFO)

import pygame
from pytmx import *
from pygame.locals import *

def init_screen(width, height):
    return pygame.display.set_mode((width, height), pygame.RESIZABLE)

class TiledRenderer(object):
    """
    Super simple way to render a tiled map
    """
    def __init__(self, filename):
        tm = load_pygame(filename)
        self.size = tm.width * tm.tilewidth, tm.height * tm.tileheight
        self.tmx_data = tm

    def render(self, surface):
        # not going for efficiency here
        # for demonstration purposes only

        # deref these heavily used references for speed
        tw = self.tmx_data.tilewidth
        th = self.tmx_data.tileheight
        surface_blit = surface.blit
        draw_rect = pygame.draw.rect
        draw_lines = pygame.draw.lines

        rect_color = (255, 0, 0)
        poly_color = (0, 255, 0)

        # fill the background color
        if self.tmx_data.background_color:
            surface.fill(pygame.Color(self.tmx_data.background_color))

        # iterate over all the visible layers, then draw them
        # according to the type of layer they are.
        for layer in self.tmx_data.visible_layers:

            # draw map tile layers
            if isinstance(layer, TiledTileLayer):

                # iterate over the tiles in the layer
                for x, y, image in layer.tiles():
                    surface_blit(image, (x * tw, y * th))

            # draw object layers
            elif isinstance(layer, TiledObjectGroup):

                # iterate over all the objects in the layer
                for obj in layer:
                    logger.info(obj)

                    # objects with points are polygons or lines
                    if hasattr(obj, 'points'):
                        draw_lines(surface, poly_color,
                                   obj.closed, obj.points, 3)

                    # some object have an image
                    elif obj.image:
                        surface_blit(obj.image, (obj.x, obj.y))

                    # draw a rect for everything else
                    else:
                        draw_rect(surface, rect_color,
                                  (obj.x, obj.y, obj.width, obj.height), 3)

            # draw image layers
            elif isinstance(layer, TiledImageLayer):
                if layer.image:
                    surface.blit(layer.image, (0, 0))

class SimpleTest(object):
    def __init__(self, filename):
        self.renderer = None
        self.running = False
        self.dirty = False
        self.exit_status = 0
        self.load_map(filename)

    def load_map(self, filename):
        self.renderer = TiledRenderer(filename)

        logger.info("Objects in map:")
        for obj in self.renderer.tmx_data.objects:
            logger.info(obj)
            for k, v in obj.properties.items():
                logger.info("%s\t%s", k, v)

        logger.info("GID (tile) properties:")
        for k, v in self.renderer.tmx_data.tile_properties.items():
            logger.info("%s\t%s", k, v)

    def draw(self, surface):
        temp = pygame.Surface(self.renderer.size)
        self.renderer.render(temp)
        pygame.transform.smoothscale(temp, surface.get_size(), surface)
        f = pygame.font.Font(pygame.font.get_default_font(), 20)
        i = f.render('press any key for next map or ESC to quit',
                     1, (180, 180, 0))
        surface.blit(i, (0, 0))

    def handle_input(self):
        try:
            event = pygame.event.wait()

            if event.type == QUIT:
                self.exit_status = 0
                self.running = False

            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    self.exit_status = 0
                    self.running = False
                else:
                    self.running = False

            elif event.type == VIDEORESIZE:
                init_screen(event.w, event.h)
                self.dirty = True

        except KeyboardInterrupt:
            self.exit_status = 0
            self.running = False

    def run(self):
        self.dirty = True
        self.running = True
        self.exit_status = 1
        while self.running:
            self.handle_input()
            if self.dirty:
                self.draw(screen)
                self.dirty = False
                pygame.display.flip()

        return self.exit_status

if __name__ == '__main__':
    import os.path
    import glob

    pygame.init()
    pygame.font.init()
    screen = init_screen(600, 600)
    pygame.display.set_caption('PyTMX Map Viewer')

    SimpleTest('test_map.tmx').run()

    try:
        for filename in glob.glob(os.path.join('data', '0.9.1', '*.tmx')):
            logger.info("Testing %s", filename)
            if not SimpleTest(filename).run():
                break
    except:
        pygame.quit()
        raise

I get this error: Traceback (most recent call last): File "/Users/Jfaci794/yet_another.py", line 168, in SimpleTest('test_map.tmx').run() File "/Users/Jfaci794/yet_another.py", line 99, in init self.load_map(filename) File "/Users/Jfaci794/yet_another.py", line 102, in load_map self.renderer = TiledRenderer(filename) File "/Users/Jfaci794/yet_another.py", line 33, in init tm = load_pygame(filename) File "/Users/Jfaci794/pytmx/tmxloader.py", line 188, in load_pygame tmxdata = pytmx.TiledMap(filename) File "/Users/Jfaci794/pytmx/pytmx.py", line 75, in init self.load() File "/Users/Jfaci794/pytmx/pytmx.py", line 312, in load self.tilesets.append(TiledTileset(self, node)) File "/Users/Jfaci794/pytmx/pytmx.py", line 414, in init self.parse(node) File "/Users/Jfaci794/pytmx/pytmx.py", line 463, in parse self.source = image_node.get('source') AttributeError: 'NoneType' object has no attribute 'get'

bitcraft commented 9 years ago

Thanks for the bug report. What version of pytmx are you using? would you mind linking or uploading the map you are using, too?

bitcraft commented 9 years ago

Closed due to lack of response from OP.