I'm trying to write a program in python that takes parts of an image and renders them in kivy using a GridLayout.
Using an XML file with my map data, I'm reading each tile from their Global ID (or "GID", see here for an explanation of how it works and the process my program's using), converting the source tileset image into a texture, and taking a region of that texture with the .get_region() method. I then create an Image widget with that part of the texture to go in the GridLayout. But I get this error:
Traceback (most recent call last): File "test_parse_xml.py", line 92, in Ground = Layer(root[1].attrib['name'], int(root[1].attrib['width']), int(root[1].attrib['height'])) File "test_parse_xml.py", line 85, in init self.add_widget(Image(current_texture)) #returns an error. What am I doing wrong?
File "/Applications/Kivy.app/Contents/Resources/kivy/kivy/uix/layout.py", line 85, in add_widget size=self._trigger_layout, AttributeError: 'Layer' object has no attribute '_trigger_layout'
Any ideas on how I can fix this? The point of interest is the line that says
self.add_widget(Image(current_texture))
Here's my complete program.
import xml.etree.ElementTree as ET
from kivy.uix.gridlayout import GridLayout
from kivy.graphics.texture import Texture
from kivy.core.image import Image
from kivy.core.window import Window
tree = ET.parse('test_tileset.tmx')
root = tree.getroot()
#import general elements from <map> tag
mapWidth = int(root.attrib['width'])
mapHeight = int(root.attrib['height'])
tileWidth = int(root.attrib['tilewidth'])
tileHeight = int(root.attrib['tileheight'])
class TileSet(object):
"""Stores data about tilesets to be accessed by tiles from that tileset"""
def __init__(self, imagePath, imageWidth, imageHeight, #creating instance attributes of the class,
tilesetFirstGid, tilesetTileWidth, tilesetTileHeight): #change values with each instance
self.imagePath = imagePath
self.imageWidth = imageWidth
self.imageHeight = imageHeight
self.tilesetFirstGid = tilesetFirstGid
self.tilesetTileWidth = tilesetTileWidth
self.tilesetTileHeight = tilesetTileHeight
self.tilesetLastGid = (imageHeight//tilesetTileHeight)*(imageWidth//tilesetTileWidth)
#make a list of all the tilesets
tilesetList = []
#import data for each tileset from each <tileset> tag
Test = TileSet(root[0][0].attrib['source'], int(root[0][0].attrib['width']),
int(root[0][0].attrib['height']), int(root[0].attrib['firstgid']),
int(root[0].attrib['tilewidth']), int(root[0].attrib['tileheight'])
)
tilesetList.append(Test)
def get_tileset(gid):
"""takes an integer, the gid. Returns an instance of the tileset that has that gid"""
for i in tilesetList:
if gid <= i.tilesetLastGid:
return i
class Layer(GridLayout):
"""creates a grid of tiles based on information from a layer."""
def __init__(self, name, width, height, **kwargs):
self.name = str(name)
self.width = width
self.height = height
#set the number of columns of the gridlayout
self.cols = width
#get the layer for ease of iteration below
#using XPath to find all 'data' nodes that are children of nodes
#with the name of the instance of the class
self.layer = root.find(".//*[@name='"+self.name+"']/data")
prevgid = 1
current_texture = Image(Test.imagePath).texture
current_texture = current_texture.get_region(0, 0, 32, 32)
for gid in self.layer:
gid = int(gid.attrib['gid'])
if gid > 0:
if gid != prevgid:
ts = get_tileset(gid)
current_texture = Image(ts.imagePath).texture
#getting a region of the texture based off the Global ID (GID)
current_texture = current_texture.get_region((ts.imageWidth//ts.tilesetTileWidth-1)*ts.tilesetTileWidth,
(ts.imageHeight//ts.tilesetTileHeight-1)*ts.tilesetTileHeight,
ts.tilesetTileWidth, ts.tilesetTileHeight)
prevgid = gid
self.add_widget(Image(current_texture)) #returns an error. What am I doing wrong?
#Is there a better way to add the texture to the GridLayout?
else:
self.add_widget() #something will go here once I figure out my main problem
Ground = Layer(root[1].attrib['name'], int(root[1].attrib['width']), int(root[1].attrib['height']))
if __name__ == '__main__':
Ground().run()
you need to call your super constructor in your __init__
method
def __init__(self, name, width, height, **kwargs):
GridLayout.__init__(self, cols=2, row_force_default=True, row_default_height=40, spacing=[0, 1],**kwargs)
self.name = str(name)
self.width = width
self.height = height