Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kivy: scroll to zoom

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

  • I cannot find the appropriate event to use to change the scale value. the 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'

Produces: screenshot

Kind of Related:

  • Scatter Covers whole display kivy (Unanswered)
  • https://github.com/kivy/kivy/issues/3563
like image 606
mdoc-2011 Avatar asked Apr 12 '18 23:04

mdoc-2011


People also ask

How do I use scroll view in Kivymd?

You must carefully specify the size of your content to get the desired scroll/pan effect. By default, the size_hint is (1, 1), so the content size will fit your ScrollView exactly (you will have nothing to scroll). You must deactivate at least one of the size_hint instructions (x or y) of the child to enable scrolling.

What is carousel in KIVY?

The Carousel widget provides the classic mobile-friendly carousel view where you can swipe between slides. You can add any content to the carousel and have it move horizontally or vertically. The carousel can display pages in a sequence or a loop.


2 Answers

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:

demo of zooming and panning

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
like image 113
mdoc-2011 Avatar answered Oct 22 '22 00:10

mdoc-2011


The other answer by @mdoc-2011 has the problem that it doesn't anchor onto the mouse pointer, meaning that it is quite annoying to use, since the content will scroll around a lot. This can be fixed by not using the .scale property and instead directly calling apply_transform:

if touch.is_mouse_scrolling:
    factor = None
    if touch.button == 'scrolldown':
        if self.scale < self.scale_max:
            factor = 1.1
    elif touch.button == 'scrollup':
        if self.scale > self.scale_min:
            factor = 1 / 1.1
    if factor is not None:
        self.apply_transform(Matrix().scale(factor, factor, factor),
                             anchor=touch.pos)

This version also uses the.scale_(min/max) properties, and instead of 1.1 and 0.8 uses 1/1.1 which will make it more intuitive to scroll in an out.

like image 3
MegaIng Avatar answered Oct 21 '22 23:10

MegaIng