Just in case you were wondering how one could go about having any child widget of a Kivy Scatter widget that would stick in a single window-relative position whilst the scatter itself was being translated and rotated, I thought I’d post this solution.

With a scatter, one can very easily implement a canvas-like object that can be rotated, translated and zoomed, whilst all of the widgets it contains are transformed along with it. This is usually great, unless you’d like, for some or other reason, to display a widget at a fixed position, for example any kind of overlay, such as a label.

The short answer is that this can be done by using a MatrixInstruction that is executed before that widget is drawn (in the canvas.before context) and that effectively reverses the parent Scatter’s transformation.

The slightly more involved answer is the minimal working example below. First we have the Python part of the ScatterOverlayApp. This only defines the MyScatter class which we’ll flesh out in the KV file, and the main app class which instantiates the top-level widget, in our case MyScatter:

from kivy.app import App
from kivy.uix.scatter import Scatter

class MyScatter(Scatter): pass

class ScatterOverlayApp(App): def build(self): return MyScatter()

if name == main: ScatterOverlayApp().run()

In the KV Language part of this example, the <MyScatter> rule defines two buttons, and then the Label which we shall configure to be the fixed position overlay. The Label stores the current matrix, and then applies the inverse transformation matrix of its parent, in other words that of the MyScatter.

This will result in the widget drawing in the space of MyScatter‘s parent, in our case the window. So when we specify the label to be in the middle of the root widget, it will actually be drawn right in the middle of the window. Even when we rotate and scale MyScatter (see screenshot below), the label will remain exactly where we put it.

<MyScatter>:
    Button:
        text: "Click me"
        pos: 100, 100
Button:
    text: "Don't click me"
    pos: 150, 200

Label:
    pos: root.width / 2.0, root.height / 2.0
    text: "I am an overlay label!"
    font_size: 32

    canvas.before:
        # store current matrix
        PushMatrix
        MatrixInstruction:
            # reverse MyScatter's transformation matrix
            matrix: self.parent.transform_inv

    canvas.after:
        # restore matrix so that other widgets draw normally
        PopMatrix

Here’s a screenshot showing the situation after the containing MyScatter widget has been rotated, translated and zoomed. Both the buttons it contains have also been transformed, but the label is exactly where we placed it initially.

scatteroverlay.jpg