Exception IndexError: 'list index out of range' in 'kivy.graphics.instructions.RenderContext.apply'

1,155 views
Skip to first unread message

Ur. Kr.

unread,
May 19, 2014, 10:27:03 AM5/19/14
to kivy-...@googlegroups.com
Hello!

I'm making a game in kivy. Up until now I've been working with only one screen - the game. Today I've been trying to implement the ScreenManager. The main game class on which everything is drawn was previously a widget. I now changed it into a Screen.

All the methods of the 'Board' class are working, but it only draws for a moment, after which the screen goes black, throwing out: "Exception IndexError: 'list index out of range' in 'kivy.graphics.instructions.RenderContext.apply'" each frame.

Here are my files reduced to only what is necessary for demonstrating the error, so that it's easier to navigate

.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.graphics import Rectangle
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.clock import Clock

TILE_PIXEL_SIZE = 51


Builder.load_file('C:\\Users\\user\\PycharmProjects\\untitled2\\mainKv')


class GameScreenManager(ScreenManager):
pass


class Player(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)

self.coords = (500 // 2, 500 // 2)
# calculate pos for bliting
self.pos = (self.coords[0] * TILE_PIXEL_SIZE, self.coords[1] * TILE_PIXEL_SIZE)


class Board(Screen):
thing = ObjectProperty(None)

def __init__(self, **kwargs):
super().__init__(**kwargs)

def on_touch_down(self, touch):
print(touch.pos)

def update(self, dt):

self.canvas.before.clear()

self.canvas.before.add(Rectangle(size=(51, 51),
pos=(5 * TILE_PIXEL_SIZE, 5 * TILE_PIXEL_SIZE)))



class Game(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)

def build(self):

sm = ScreenManager()
board = Board(name='game')
sm.add_widget(board)

Clock.schedule_interval(board.update, 1.0 / 60.0)

return sm


Game().run()

.kv
<Player>:

size:51,51
canvas.after:
Color:
rgba: 0,0,0,1

Rectangle:
pos:self.pos
size:self.size




<Board>:

thing:player
Widget:


Player:
id:player


Any help would be greatly appreciated.

 

Amirouche Boubekki

unread,
May 19, 2014, 6:49:51 PM5/19/14
to kivy-...@googlegroups.com


On Monday, May 19, 2014 4:27:03 PM UTC+2, Ur. Kr. wrote:
Hello!

I'm making a game in kivy. Up until now I've been working with only one screen - the game. Today I've been trying to implement the ScreenManager. The main game class on which everything is drawn was previously a widget. I now changed it into a Screen.

All the methods of the 'Board' class are working, but it only draws for a moment, after which the screen goes black, throwing out: "Exception IndexError: 'list index out of range' in 'kivy.graphics.instructions.RenderContext.apply'" each frame.

Here are my files reduced to only what is necessary for demonstrating the error, so that it's easier to navigate

I copy pasted your code offline. At the end is what remains in my editor, wrapped with "Highlight code syntax" of Google groups wysiwyg toolbar (last icon with curly braces).

It doesn't seem related to Screen or ScreenManager.

I have the exact same error on Ubuntu Python 3 kivy 1.8.0.  If the self.canvas.before.clear() is commented it will not "crash" but it's still not valid code, I couldn't dig deeper in the code, as I don't know how to run kivy from source... but basicly the error says that some instruction is missing, obviously since the clear meant to wipe the instructions from this Canvas, but it's not, at least it reference empty lists... It doesn't happen when you use self.canvas instead of self.canvas.before. it might be a bug in Kivy.

# main.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.graphics import Rectangle
from kivy.graphics import Rectangle
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.clock import Clock


TILE_PIXEL_SIZE
= 51


class GameScreenManager(ScreenManager):
   
pass


class Player(Widget):
   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)

       
self.coords = (500 // 2, 500 // 2)
       
# calculate pos for bliting
       
self.pos = (self.coords[0] * TILE_PIXEL_SIZE, self.coords[1] * TILE_PIXEL_SIZE)


class Board(Screen):
    thing
= ObjectProperty(None)

   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)
           
   
def on_touch_down(self, touch):
       
print(touch.pos)


   
def update(self, dt):  #! you are calling this *very* often
       
print('board.canvas.before *before* add rectangle', len(self.canvas.before.children))
       
# self.canvas.before.clear() #! I commented that line and it "works"
       
self.canvas.before.add(
           
Rectangle(

                size
=(51, 51),
                pos
=(5 * TILE_PIXEL_SIZE, 5 * TILE_PIXEL_SIZE)
           
)
       
)

       
print('board.canvas.before *after* add rectangle', len(self.canvas.before.children))



class Game(App):
   
def __init__(self, **kwargs):
       
super().__init__(**kwargs)

   
def build(self):
        sm
= ScreenManager()
        board
= Board(name='game')

       
#sm.add_widget(board)
       
Clock.schedule_interval(board.update, 1.0 / 60.0)
       
return board


Game().run()

# This file use tabs :/
#
# if the the Kivy app class is named Game, Kivy will try load game.kv (default behavior)
# So you don't need to load it manually, at least if: main.py where Game class is defined
# and Game().run() is called is in the same directory as ``game.kv``
#
#  ./main.py
#  ./game.kv
#
<Player>:
    size
: 51, 51

Alexander Taylor

unread,
May 19, 2014, 7:02:41 PM5/19/14
to kivy-...@googlegroups.com
Nice sleuthing, the problem may well be that clearing canvas.before clears *everything*, including all the stuff that's part of the normal widget drawing and that they expect to be there. That includes, for instance, translation instructions for the Screen as it's a RelativeLayout.

Even without knowing the details, the real solution is to not clear the canvas, but simply store a reference to the Rectangle (self.whatever = Rectangle(...)) and update it later instead of deleting and re-adding it (e.g. self.canvas.pos = ...). This is vastly more efficient and you should always interact with canvas instructions this way if you can.
Message has been deleted
Message has been deleted

Ur. Kr.

unread,
May 21, 2014, 2:57:17 PM5/21/14
to kivy-...@googlegroups.com
hank you very much for taking the time! Your diagnosis has already helped.

However, this problem does NOT persist if there is no Screen Manager even with canvas.clear. Like so:

.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder

from kivy.properties import ObjectProperty
from kivy.graphics import Rectangle
from kivy.uix.screenmanager import Screen,ScreenManager

from kivy.clock import Clock


TILE_PIXEL_SIZE
= 51




Builder.load_file('C:\\Users\\user\\PycharmProjects\\untitled2\\mainKv')




class GameScreenManager(ScreenManager):
 
pass




class Player(Widget):
 
def __init__(self, **kwargs):
 
super().__init__(**kwargs)


 
self.coords = (500 // 2, 500 // 2)
 
# calculate pos for bliting
 
self.pos = (self.coords[0] * TILE_PIXEL_SIZE, self.coords[1] * TILE_PIXEL_SIZE)




class Board(Widget):

 thing
= ObjectProperty(None)


 
def __init__(self, **kwargs):
 
super().__init__(**kwargs)


 
def on_touch_down(self, touch):
 
print(touch.pos)


 
def update(self, dt):



 
self.canvas.before.clear()


 
self.canvas.before.add(Rectangle(size=(51, 51),

 pos
=(5 * TILE_PIXEL_SIZE, 5 * TILE_PIXEL_SIZE)))






class Game(App):
 
def __init__(self, **kwargs):
 
super().__init__(**kwargs)


 
def build(self):





 board
= Board()





 
Clock.schedule_interval(board.update, 1.0 / 60.0)


 
return board




Game().run()




.kv


<Player>:


 size:51,51

 canvas.after:
 Color:
 rgba: 0,0,0,1


 Rectangle:
 pos:self.pos
 size:self.size








<Board>:


 thing:player






 Player:
 id:player








Message has been deleted

Ur. Kr.

unread,
May 21, 2014, 2:59:27 PM5/21/14
to kivy-...@googlegroups.com
@ Alexander
I think I might have removed to much of the code. I was not clearing the canvas simply to move a rectangle, but to update the board which changes quite frequently. I need to clear, otherwise Kivy has to draw more and more layers of the Board, which slows it down.
Reply all
Reply to author
Forward
0 new messages