Q:

Modified kivy scatter widget does not update transformation

I try to create a kivy widget from Scatter, which is freely zoomable, but once the mouse button is lifted falls back to the closest zoom level. 
It works, but it does not update the zoom until the next click. I think I need to bind some event here, but I'm pretty new to kivy and can not figure it out. Here is my current code:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.scatter import Scatter
from kivy.graphics.transformation import Matrix

ZOOM_LEVELS = [0.25, 0.5, 1, 2, 4]

class PPMap(Scatter):
    def __init__(self, **kwargs):
        super(PPMap, self).__init__(**kwargs)
        self.bind(on_touch_up=self.adjust_zoom)

    def adjust_zoom(self, *args):
        old_zoom = self.scale
        new_zoom = min(ZOOM_LEVELS, key=lambda x:abs(x-old_zoom))
        self.set_zoom(new_zoom)

    def set_zoom(self, zoom):
        self.transform.set(array=[[zoom, 0, 0, 0],
                                  [0, zoom, 0, 0],
                                  [0, 0, zoom, 0],
                                  self.transform.tolist()[3]])

class PPApp(App):
    def build(self):
        pp_map = PPMap(do_rotation=False, scale_min=ZOOM_LEVELS[0], scale_max=ZOOM_LEVELS[-1])
        label = Label(text="Hello!", font_size=300, pos=(0, 0))
        pp_map.add_widget(label)
        return pp_map

if __name__ == "__main__":
    PPApp().run()

A:

self.transform.set( # ...

Problem here is that you modify existing ObjectProperty instance and Kivy doesn't know it changed:

Warning To mark the property as changed, you must reassign a new
  python object.

You can either do it or, for example, dispatch manually:
def set_zoom(self, zoom):
    self.transform.set(array=[[zoom, 0, 0, 0],
                              [0, zoom, 0, 0],
                              [0, 0, zoom, 0],
                              self.transform.tolist()[3]])
    self.property('transform').dispatch(self)  # transform has changed

