Search code examples
pythonwidgettextureskivyattributeerror

AttributeError in Kivy: GridLayout object has no attribute _trigger_layout


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()

Solution

  • 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