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
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
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.