Is there a way to zoom into an image on a desktop kivy app (e.g. zoom with a mouse scroll-wheel)? It appears to be discussed here: https://github.com/kivy/kivy/issues/3563 but I do not see if there was a work around given.
I began with a static image in my kivy app. I want to add the ability to zoom / pan into the image. I do not want the actual size of the image frame to change, just add zoom/pan functionality, like you might expect from interacting with google maps for example.
Possible Programming Directions
From what I've read, I should be using Scatter(?), and I see that I can manually set the Scatter scale to size up/down the image.
My initial thoughts are that I will have to add a separate widget with a scrollview to contain the scatter layout and that will keep the image frame a consistent size. Then I will need to add an event that dynamically changes the scale value.
The problems
on_motion
event seemed promising. My app can update values with a slider, but when I try a similar approach using on_motion,
I get AttributeError: motion
errors I am struggling to create the widget. Most documentation seems to use .add_widget(...)
in the python file. Is it possible to do this from the kv file? I imagine this process is similar to screens and the screen manager, but I am struggling to find an approach that works.
Is there a more straight forward way to do this?
Is there a way I can use on_motion
type event in my kv file to adjust this value using the mouse scroll-wheel?
I give a watered down example of the structure of my kivy app - along with what I tried to do to add Scatter. I think I will need to put it into it's own widget to keep the image the same size?
Toy Example
import kivy
from kivy.lang import Builder
from kivy.core.window import Window
kivy.require('1.1.0')
from kivy.app import App
presentation = Builder.load_file("scatter.kv")
class TestApp(App):
def build(self):
Window.clearcolor = (1, 1, 1, 1)
return presentation
# def foo():
# print("You've reached foo")
if __name__ == '__main__':
TestApp().run()
and
#:kivy 1.10.0
GridLayout:
cols: 2
Scatter:
scale: 5
# on_motion: root.foo()
Image :
source: 'foo.png'
allow_stretch: True
keep_ratio: True
Button:
text: 'Hello World'
Kind of Related:
To achieve my goal, I used a combination of the information in kivy python3 detect mousewheel, as pointed out ikolim, and the code given here: https://github.com/kivy/kivy/wiki/Draggable-Scalable-Button
To keep my answer brief, here's the minimalistic object that extends Scatter object.
class ResizableDraggablePicture(Scatter):
def on_touch_down(self, touch):
# Override Scatter's `on_touch_down` behavior for mouse scroll
if touch.is_mouse_scrolling:
if touch.button == 'scrolldown':
if self.scale < 10:
self.scale = self.scale * 1.1
elif touch.button == 'scrollup':
if self.scale > 1:
self.scale = self.scale * 0.8
# If some other kind of "touch": Fall back on Scatter's behavior
else:
super(ResizableDraggablePicture, self).on_touch_down(touch)
The layout is slightly different and I changed the text on the button, but the functionality of my code can be seen in the following gif:
For anybody wanting to see my entire toy project to adapt for their own purposes, the entire code is on my github: https://github.com/melissadale/Learning-Kivy/tree/master/ZoomPanning
UPDATE my code has been edited to be far more correct from an object-orientated approach, and so I could not reject the edits with a clear conscience. However, when I was first starting with kivy, I would have found this code confusing. If you want to just see the simple version that you can apply directly to verify the relevant, this is my original code:
if touch.is_mouse_scrolling:
if touch.button == 'scrolldown':
print('down')
## zoom in
if self.scale < 10:
self.scale = self.scale * 1.1
elif touch.button == 'scrollup':
## zoom out
print('up')
if self.scale > 1:
self.scale = self.scale * 0.8