From 0745b184e2100049c337d0ce5c8632b08516a031 Mon Sep 17 00:00:00 2001 From: Jaskrendix Date: Mon, 22 Jan 2024 21:23:54 +0100 Subject: [PATCH] typehints --- pytmx/pytmx.py | 66 +++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/pytmx/pytmx.py b/pytmx/pytmx.py index 64f9c5c..4e8277e 100644 --- a/pytmx/pytmx.py +++ b/pytmx/pytmx.py @@ -32,7 +32,7 @@ from itertools import chain, product from math import cos, radians, sin from operator import attrgetter -from typing import Optional, Union +from typing import DefaultDict, Optional, Union from xml.etree import ElementTree # for type hinting @@ -74,11 +74,11 @@ 'Cannot set user {} property on {} "{}"; Tiled property already exists.' ) -flag_names = ("flipped_horizontally", "flipped_vertically", "flipped_diagonally") - AnimationFrame = namedtuple("AnimationFrame", ["gid", "duration"]) Point = namedtuple("Point", ["x", "y"]) -TileFlags = namedtuple("TileFlags", flag_names) +TileFlags = namedtuple( + "TileFlags", ("flipped_horizontally", "flipped_vertically", "flipped_diagonally") +) empty_flags = TileFlags(False, False, False) ColorLike = Union[tuple[int, int, int, int], tuple[int, int, int], int, str] MapPoint = tuple[int, int, int] @@ -505,19 +505,24 @@ def __init__( # allow duplicate names to be parsed and loaded TiledElement.allow_duplicate_names = kwargs.get("allow_duplicate_names", False) - self.layers: list[TiledLayer] = [] # all layers in proper order - self.tilesets: list[TiledTileset] = [] # TiledTileset objects - self.tile_properties = dict() # tiles that have properties - self.layernames = dict() - self.objects_by_id = dict() - self.objects_by_name = dict() + # all layers in proper order + self.layers: list[TiledLayer] = [] + # TiledTileset objects + self.tilesets: list[TiledTileset] = [] + # tiles that have properties + self.tile_properties: dict[int, dict[str, str]] = {} + self.layernames: dict[str, TiledLayer] = {} + self.objects_by_id: dict[str, TiledObject] = {} + self.objects_by_name: dict[int, TiledObject] = {} # only used tiles are actually loaded, so there will be a difference # between the GIDs in the Tiled map data (tmx) and the data in this # object and the layers. This dictionary keeps track of that. - self.gidmap = defaultdict(list) - self.imagemap = dict() # mapping of gid and trans flags to real gids - self.tiledgidmap = dict() # mapping of tiledgid to pytmx gid + self.gidmap: DefaultDict[int, list[tuple[int, TileFlags]]] = defaultdict() + # mapping of gid and trans flags to real gids + self.imagemap: dict[tuple[int, TileFlags], tuple[int, TileFlags]] = {} + # mapping of tiledgid to pytmx gid + self.tiledgidmap: dict[int, int] = {} self.maxgid = 1 # should be filled in by a loader function @@ -541,7 +546,7 @@ def __init__( self.custom_types = dict() # initialize the gid mapping - self.imagemap[(0, 0)] = 0 + self.imagemap[(0, TileFlags(0, 0, 0))] = (0, TileFlags(0, 0, 0)) if custom_property_filename: self.parse_json(json.load(open(custom_property_filename))) @@ -811,7 +816,9 @@ def get_tile_gid(self, x: int, y: int, layer: int) -> int: logger.debug(msg.format(x, y, layer)) raise ValueError(msg.format(x, y, layer)) - def get_tile_properties(self, x: int, y: int, layer: int) -> Optional[dict]: + def get_tile_properties( + self, x: int, y: int, layer: int + ) -> Optional[dict[str, str]]: """Return the tile image GID for this location. Args: @@ -866,7 +873,7 @@ def get_tile_locations_by_gid(self, gid: int) -> Iterable[MapPoint]: for x, y, _gid in [i for i in self.layers[l].iter_data() if i[2] == gid]: yield x, y, l - def get_tile_properties_by_gid(self, gid: int) -> Optional[dict]: + def get_tile_properties_by_gid(self, gid: int) -> Optional[dict[str, str]]: """Get the tile properties of a tile GID. Args: @@ -881,7 +888,7 @@ def get_tile_properties_by_gid(self, gid: int) -> Optional[dict]: except KeyError: return None - def set_tile_properties(self, gid: int, properties: dict) -> None: + def set_tile_properties(self, gid: int, properties: dict[str, str]) -> None: """Set the tile properties of a tile GID. Args: @@ -933,6 +940,7 @@ def add_layer( ) self.layers.append(layer) + assert layer.name self.layernames[layer.name] = layer def add_tileset(self, tileset: TiledTileset) -> None: @@ -1011,7 +1019,7 @@ def get_tileset_from_gid(self, gid: int) -> TiledTileset: raise ValueError("Tileset not found") - def get_tile_colliders(self) -> Iterable[tuple[int, list[dict]]]: + def get_tile_colliders(self) -> Iterable[tuple[int, str]]: """Return iterator of (gid, dict) pairs of tiles with colliders. Returns: @@ -1140,7 +1148,7 @@ def register_gid_check_flags( else: return self.register_gid(*decode_gid(tiled_gid)) - def map_gid(self, tiled_gid: int) -> Optional[list[int]]: + def map_gid(self, tiled_gid: int) -> Optional[list[tuple[int, TileFlags]]]: """Used to lookup a GID read from a TMX file's data. Args: @@ -1151,7 +1159,7 @@ def map_gid(self, tiled_gid: int) -> Optional[list[int]]: """ try: - return self.gidmap[int(tiled_gid)] + return self.gidmap[tiled_gid] except KeyError: return None except TypeError: @@ -1159,10 +1167,8 @@ def map_gid(self, tiled_gid: int) -> Optional[list[int]]: logger.debug(msg) raise TypeError(msg) - def map_gid2(self, tiled_gid: int) -> list[tuple[int, Optional[int]]]: + def map_gid2(self, tiled_gid: int) -> list[tuple[int, Optional[TileFlags]]]: """WIP. need to refactor the gid code""" - tiled_gid = int(tiled_gid) - # gidmap is a default dict, so cannot trust to raise KeyError if tiled_gid in self.gidmap: return self.gidmap[tiled_gid] @@ -1227,7 +1233,7 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: if source: if source[-4:].lower() == ".tsx": # external tilesets don't save this, store it for later - self.firstgid = int(node.get("firstgid")) + self.firstgid = int(node.get("firstgid", 0)) # we need to mangle the path - tiled stores relative paths dirname = os.path.dirname(self.parent.filename) @@ -1257,7 +1263,7 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: # we store it separately in the parent (a TiledMap instance) register_gid = self.parent.register_gid for child in node.iter("tile"): - tiled_gid = int(child.get("id")) + tiled_gid = int(child.get("id", 0)) p = {k: types[k](v) for k, v in child.items()} p.update(parse_properties(child)) @@ -1269,8 +1275,8 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: # handle tiles that have their own image image = child.find("image") if image is None: - p["width"] = self.tilewidth - p["height"] = self.tileheight + p["width"] = str(self.tilewidth) + p["height"] = str(self.tileheight) else: tile_source = image.get("source") # images are listed as relative to the .tsx file, not the .tmx file: @@ -1278,8 +1284,8 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: tile_source = os.path.join(os.path.dirname(source), tile_source) p["source"] = tile_source p["trans"] = image.get("trans", None) - p["width"] = image.get("width", None) - p["height"] = image.get("height", None) + p["width"] = image.get("width", "0") + p["height"] = image.get("height", "0") # handle tiles with animations anim = child.find("animation") @@ -1288,7 +1294,7 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: if anim is not None: for frame in anim.findall("frame"): duration = int(frame.get("duration", 0)) - gid = register_gid(int(frame.get("tileid")) + self.firstgid) + gid = register_gid(int(frame.get("tileid", 0)) + self.firstgid) frames.append(AnimationFrame(gid, duration)) for objgrp_node in child.findall("objectgroup"):