Question

In this Kivy code:

class MyPaintWidget(Widget):
    def on_touch_down(self, touch):
        userdata = touch.ud
        with self.canvas:
            Color(1, 1, 0)
            d = 30.
            Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d))
            userdata['line'] = Line(points=(touch.x, touch.y))

Apparently Color and d and Ellipse are within the namespace of self.canvas, but how does Python know that userdata is not within that same namespace?

Was it helpful?

Solution

Edit: This answer got a bit lengthy, so here's the summary:

  1. with self.canvas defines the currently active canvas for the following code block.
  2. All drawing instructions like Color or Ellipse draw on the active canvas.

Namespaces don't really have anything to do with it, it's the context that matters (see below).

The with statement allows you to use so called context managers.

The syntax is like this

with thing [as foo]:

where thing usually is a function decorated with the contextlib.contextmanager decorator. What exactly a context manager does depends on how thing is implemented.

But what it doesn't do is make variable magically appear in your scope. A reference to the context may be obtained by the optional as foo clause, but that's it. Color and Ellipse in your example are coming from somewhere else (probably imports?).

In order to find out what exactly the context manager in the with self.canvas line does, you should look at the API documentation or the source code for kivy.graphics.instructions.Canvas.

Here's the relevant excerpt from the tutorial:

By using the with statement with it, all successive drawing commands that are properly indented will modify this canvas. The with statement also makes sure that after our drawing, internal state can be cleaned up properly.

So the use of Color and Ellipse affects self.canvas, but they're not defined in any way by the with statement.

Looking at the source code, this is how it works:

def class CanvasBase(InstructionGroup):
    def __enter__(self):
        pushActiveCanvas(self)

    def __exit__(self, *largs):
        popActiveCanvas()

__enter__ and __exit__ define what happens if a context manager is entered (before the first line of indented code after the with statement) and exited.

In this case, the canvas simply gets pushed onto a stack that defines the currently active canvas (and popped from it if the context manager is exited).

In kivy.graphics.instructions.Instruction, the apparent base class for all drawing instructions, the parent is set to the currently active canvas:

self.parent = getActiveCanvas()

OTHER TIPS

Actually, Color and Ellipse are imported from kivy.graphics a little higher in the code:

from kivy.graphics import Color, Ellipse

To answer your question about namespaces, python doesn't need to "know" what namespace it's getting variables from at all. It has very simple namespace rules compared to languages like Java, which search function, object, class, global, and package scopes one after the other. Python has one global namespace (per module) and a stack of local namespaces (e.g. nested functions can get at the variables from outer functions). It just goes down the list of scopes until it finds the variable name in question.

The with statement above has special meaning, but I think even with can't implicitly introduce new variables into the local scope (it can introduce one variable explicitly with the as clause, though).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top