Unintuitive difference between 'Canvas.add()' and 'Canvas.insert()'
Software Versions
- Python: 3.8.12
- OS: Linux Mint 20.2 Mate Edition
- Kivy: master 714f4935085d4ac0c220fdf7183fb35175f8bcad
- Kivy installation method:
git clonethenpython -m pip install -e ".[dev,full]"
Issue
There was a canvas that holds a before group and an after group.
from kivy.uix.widget import Widget
c = Widget().canvas
def print_children(c):
print([i.__class__.__name__ for i in c.children])
print_children(c)
c.before
c.after
print_children(c)
[]
['CanvasBase', 'CanvasBase']
When I called c.insert(0, PushMatrix()) and c.add(PopMatrix()), I was expecting the c.children to be either of
['CanvasBase', 'PushMatrix', 'PopMatrix', 'CanvasBase']or['PushMatrix', 'CanvasBase', 'CanvasBase', 'PopMatrix']
but neither was an actual result.
c.insert(0, PushMatrix())
c.add(PopMatrix())
print_children(c)
['PushMatrix', 'CanvasBase', 'PopMatrix', 'CanvasBase']
Cause
The cause of the issue is that Canvas overrides InstructionGroup.add() but doesn't override InstructionGroup.insert().
https://github.com/kivy/kivy/blob/714f4935085d4ac0c220fdf7183fb35175f8bcad/kivy/graphics/instructions.pyx#L656-L662
As you can see, Canvas.add() does some check so that the after group always remains the last one, which is why I couldn't add a PopMatrix behind the after group. On the other hand, InstructionGroup.insert() doesn't do that (Of course, InstructionGroup is not even aware of the existance of the before group and the after group),
https://github.com/kivy/kivy/blob/714f4935085d4ac0c220fdf7183fb35175f8bcad/kivy/graphics/instructions.pyx#L191-L195
which is why I was able to insert a PushMatrix in font of the before group.
What should we do?
I think we need to do either of
- document about this unintuitive behavior or
- have
Canvasoverrideinsert()so that it works in the same way asadd()
For those who want to confirm the issue
from kivy.graphics import Canvas, PushMatrix, PopMatrix
from kivy.uix.widget import Widget
def print_children(c: Canvas):
print([i.__class__.__name__ for i in c.children])
c = Widget().canvas
print("\n---- initial state ----")
print_children(c)
c.before
c.after
print("\n---- added 'after' and 'before' ----")
print_children(c)
c.add(PopMatrix())
print("\n---- after c.add(PopMatrix()) ----")
print_children(c)
c.insert(0, PushMatrix())
print("\n---- after c.insert(0, PushMatrix()) ----")
print_children(c)