community icon indicating copy to clipboard operation
community copied to clipboard

Unintuitive difference between 'Canvas.add()' and 'Canvas.insert()'

Open gottadiveintopython opened this issue 3 years ago • 0 comments

Software Versions

  • Python: 3.8.12
  • OS: Linux Mint 20.2 Mate Edition
  • Kivy: master 714f4935085d4ac0c220fdf7183fb35175f8bcad
  • Kivy installation method: git clone then python -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 Canvas override insert() so that it works in the same way as add()

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)

gottadiveintopython avatar Aug 07 '22 23:08 gottadiveintopython