karlitos
karlitos

Reputation: 1656

Placing and moving a group of images with kivy relative to the layout

I am trying to create some sort of interactive "wall" with raspberry pi and the kivy library.

I would like to display images (maybe also text later) vertically one below the other and move them up/down on a keypress. Kind of scroll behavior but I would like to display many images so I do not want to load all of them at one go and use a scroll view.

So I use a FloatLayout and I change the position of the images after a keypress. My first issue is, that I have to operate with Window.width / Window.height instead of size_hint / pos_hint. I tried to use the examples from the kivy FloatLayout documentation but it did not work.

My problem is, that after hitting the keys the images are moving, but they also change the size. Here is the code I am using.

EDIT:

I realized that adding size_hint=(None, None) will keep the image size. This only shows that it would be probably better to use the relative positioning and sizing of the images in the layout. As mentioned earlier the example from the FloatLayout documentation did not work for me.

from kivy.app import App
    from kivy.uix.widget import Widget
    from kivy.uix.button import Button
    from kivy.uix.label import Label
    from kivy.uix.floatlayout import FloatLayout
    from kivy.uix.image import Image
    from kivy.core.window import Window


    class Wall(FloatLayout):
        def __init__(self, **kwargs):
        super(FloatLayout, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        if not self._keyboard:
            return
        self._keyboard.bind(on_key_down=self.on_keyboard_down)

        self.posts = list()
        self.orientation = 'vertical'

        spacing = 25

        post = Image(source='test.png',
                 allow_stretch=True,
                 keep_ratio=True,
                 size=(Window.width * 0.2z, Window.height * 0.2),
                 center=(Window.width * 0.6, Window.height * 0.5))

        print '#####\nOriginal size and position: width/height', post.width, post.height, ' position x/y', post.x, post.y

        self.posts.append(post)
        self.add_widget(post)
        while self.posts[-1].y + self.posts[-1].height <= Window.height:
            post = Image(source='test.jpg',
                     allow_stretch=True,
                     keep_ratio=True,
                     size=(Window.width * 0.2, Window.height * 0.2),
                     pos=(self.posts[-1].x, self.posts[-1].y + self.posts[-1].height + spacing))

            print '#####\nOriginal size and position: width/height', post.width, post.height, ' position x/y', post.x, post.y

            self.posts.append(post)
            self.add_widget(post)

        def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None

        def on_keyboard_down(self, keyboard, keycode, text, modifiers):

        key = keycode[1]
        for post in self.children:
            if key == 'up':
            post.y -= 50
            print '#####\nNew size and position: width/height', post.width, post.height, ' position x/y', post.x, post.y
            elif keycode[1] == 'down':
            post.y += 50
            elif key == 'q':
            App.get_running_app().stop()
            #else:
            #    return False
            #return True
        return True

class MessageBoard(App):
    def build(self):
    return Wall()


if __name__ == '__main__':
    MessageBoard().run()

Upvotes: 0

Views: 1887

Answers (1)

karlitos
karlitos

Reputation: 1656

I found a solution using RelativeLayout and working with the pos_hint / size_hint properties. The positive side efect is, that the elements keep their size/position even when the window resizes.

I still need to compute absolute coordinates to determine the position of the elements. The drawback is, that in the init() method, the size of the parrent element is not knownw, so I have to work with Window size and coorinates. This will fail if my wall would not be the root element.

The moving of the elements need to be done by setting of the pos_hint property, changing the absolute coordinates would not take any effects.

class ImageAndText(RelativeLayout):
    pass


class Wall(FloatLayout):
    def __init__(self, **kwargs):
        super(Wall, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        if not self._keyboard:
            return
        self._keyboard.bind(on_key_down=self.on_keyboard_down)

        self.posts = list()
        post = ImageAndText()
        post.size_hint = 1, 0.3
        post.size = (Window.width, Window.height * 0.3)  # needs to be set in order to determine the absolute size
        post.center_y = Window.height * 0.5  # needs to be set in order to determine the absolute coordinates
        post.pos_hint = {'center_y': 0.5}
        self.add_widget(post)
        self.posts.append(post)

        while self.posts[-1].top <= Window.height:
            #print 'Old post top:', self.posts[-1].top
            post = ImageAndText()
            post.size_hint = 1, 0.25
            post.size = (Window.width, Window.height * 0.25)
            post.y = self.posts[-1].top  # just above the first one
            post.pos_hint = {'center_y': post.center_y/Window.height}
            self.add_widget(post)
            self.posts.append(post)

    def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None

    def on_keyboard_down(self, keyboard, keycode, text, modifiers):

        key = keycode[1]
        for post in self.posts:
            if key == 'up':
                post.pos_hint = {'center_y': post.pos_hint['center_y']+0.1}
            elif keycode[1] == 'down':
                post.pos_hint = {'center_y': post.pos_hint['center_y']-0.1}
            elif key == 'q':
                App.get_running_app().stop()
        return True

class MyTestApp(App):
    def build(self):
        return Wall()

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

Here is the .kv file describing the ImageAndText element.

<ImageAndText>:
    Label:
        font_size: '14sp'
        text: 'Lorem ipsum dolor sit'
        color: (1, 1, 1, 1)
        text_size: (self.parent.width*0.25, None)
        pos_hint: {'center_x': 1.0/6} # set the center to 1/6 of parents width   

    Image:
        source: 'test.png'
        pos_hint: {'center_x': 2.0/3, 'center_y': .5}
        size_hint: 0.5, 0.95 # the height will be the same as the parent, the width 0.5 because of the label
        allow_stretch: True

Upvotes: 1

Related Questions