Why does resizing in kivy brings back widgets in old positions? - python

I'm making a game in kivy and when I close the screen (android) or try to resize the window (linux) some widgets that I've moved away from the screen return to their starting position.
I created a minimal reproducible example for that:
example.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class GameCanvas(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def start(self):
# move the label widget far away
self.children[0].pos = 10000, 10000
class Example(App):
pass
if __name__ == '__main__':
Example().run()
example.kv
GameCanvas:
<GameCanvas>:
Button:
text: 'Start Game'
size: root.width, root.height / 2
on_release: root.start()
Label:
text: 'Game Title'
size: root.width, root.height / 2
Before pressing the button:
After pressing the button:
After resizing:
As the attached images show after I press the button the label 'disappears' (moves position) but when I try to resize the Window it comes back.
Why does this happen? Also why does it happen on android as well? Does it have to do with some sort of events in kivy?
Thanks in advance!

The behavior you see is the intended behavior of the BoxLayout. The BoxLayout positions its children. So, you can move the Label, but as soon as the BoxLayout gets a chance (as in a size change event), it positions its children as intended. If you want to control the position of the children widgets, then you should use a Layout that does not position its children, perhaps a FloatLayout or RelativeLayout

Related

Having problems using ScrollView

I'm having a very hard time trying to understand Kivi's ScrollView, is there anyone who can help me? The thing is that I have a page with a label, multiple switches and a button to confirm the choices of those switches, how do I make a working ScrollView for this?
check this tuto: https://www.youtube.com/watch?v=lPaeoSv0rjo&t=23s
but instead of a Label in the ScrollView use a layout like GridLayout or something and remember that the scrollView accepts one widget as a child so u have to add a layout scrollview and layout it self can accept many widgets
ScrollView:
size: # whatever u need
pos: # whatever u need
GridLayout:
size_hint_y: None ## this is mandatory
id: my_scroll
height: self.minimum_height
cols: 1
and in ur main.py u can just:
self.ur_widget = Button(text='btn', size_hint_y=None, size_hint_x= 1.0)
self.ids.my_scroll.add_widget(self.ur_widget)
u can play with this and play with layouts to find whats best suited for u:

Static background in kivy

I was wondering if there is a way to get a static background in a Kivy application with a screen manager. With static, I mean that the background remains as it is, even when switching screens. I am using a .kv file for the layout. I'd guess it has something to do with the placement order within a .kv file.
Thanks!
You can use a float layout as the root widget in every screen and add to that float layout the image and the other layout in your screen, here is a code example in kv:
Screen: # Screen 1
id: Home
FloatLayout:
Image:
source: "path to the image"
BoxLayout: # Here you put your other layout
# And here the code you had
Screen: # Screen 2
id: Another Screen
FloatLayout:
Image:
source: "path to the image"
BoxLayout: # Here you put your other layout
# And here the code you had
This is the solution that I know, it might not be perfect for you, but I will leave the other options to others...
FloatLayout:
Image:
ScreenManager:
would be enough I think but be careful, if the transition of ScreenManager is ShaderTransition(or a sub-class thereof), it doesn't respect the pixels in the background, so the animation during the transition may not work properly.

Flip a widget in kivy

How do I flip a (custom) kivy Widget from Python code? I've tried setting its height or width to negative value, but it actually rotates widget, ie flips both axes. The Rotate instruction obviously does the same. I'm aware there is a flip_horizontal() method of the Texture, but I don't have a slightest idea how to actually cause it to affect an existing widget rather than in-memory texture from eg Atlas.
If it helps, I use kivy 1.9.2 with Python 3.4.3.
UPD I tried Scale() to no effect.
event.actor.widget.img.canvas.before.add(PushMatrix())
event.actor.widget.canvas.before.add(Scale(x=2.0, origin=event.actor.widget.center))
event.actor.widget.img.canvas.after.add(PopMatrix())
event.actor.widget.canvas.ask_update()
It doesn't work either with or without PushMatrix/PopMatrix. The widget in question is a simple Widget subclass with stretchable image and a couple callbacks for size and position.
An easy way to achieve your goal is using a Scatter. Here is a full working example
from kivy.lang import Builder
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
import random
from kivy.graphics.transformation import Matrix
class MyBox(BoxLayout):
def later_(self, dt=None):
self.t.apply_transform(Matrix().scale(-1, 1.0, 1.0),
post_multiply=True,
anchor=self.to_local(*self.center))
Clock.schedule_once(self.later_, 1.0)
class TestApp(App):
def build(self):
Builder.load_string("""\
<MyBox>:
t: s
orientation: 'vertical'
Scatter:
id: s
do_scale: 0
do_rotate: 0
do_translation: 0,0
Label:
pos: s.pos
size: s.size
text: "The text below will keep changing using a delayed function..."
"""
)
mybox = MyBox()
Clock.schedule_once(mybox.later_, 1.0)
return mybox
if __name__ == '__main__':
TestApp().run()
How about a Scale instruction with a negative scaling?
If the widget also needs to take touch events, you'll need to modify its on_touch_down to transform them appropriately.

ActionPrevious getting an unexpected background

I've made a rule in my kv for the Button class to have a specific background (provided in an image). When I created an ActionBar that contains ActionPrevious and ActionButton widgets, they both seemed to get the same background.
And I would understand that ActionButton instance got that background, since it inherits from Button and ActionItem classes, but why did the ActionPrevious get the same background? It inherits from BoxLayout and ActionItem, neither of which have anything to do with the Button class. What's the reason behind it?
Also, a side question
The ActionPrevious has a property with_previous which, when set to True, adds a clickable arrow. However, the title of the widget remains unclickable. But the docs say that this property would make the whole widget clickable. While it's not a big deal, I'd rather want the entire ActionPrevious widget background to change on press. Is it possible to achieve this?
So what I mean is that when you press the Back arrow, only the space around it and the app icon turns blue, but the text doesn't, as if it's part of a different widget.
Here is the code to visualize the question:
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.actionbar import ActionBar
Builder.load_string('''
<Button>:
background_normal: 'some_file.png'
<MenuBar>:
ActionView:
ActionPrevious:
title: "Log out"
with_previous: True
ActionButton:
text: "Settings"
''')
class MenuBar(ActionBar):
pass
runTouchApp(MenuBar())
Okay, so the truth was in the style.kv file. Basically, ActionPrevious widget has the following structure:
<ActionPrevious>:
GridLayout:
ActionPreviousButton:
GridLayout:
ActionPreviousImage:
id: prev_icon_image # the "back" arrow
ActionPreviousImage:
id: app_icon_image # the app icon
Widget:
# perhaps something to split the title from the GridLayout
Label:
id: title # the title
And the ActionPreviousButton inherits from Button, so that's why the ActionPrevious arrow part was getting that background.
Here also lies the answer to the side question. Since the clickable part is the ActionPreviousButton, and the title is kept in a Label, that is not a child of it, the text is unclickable. So to fix that, one has to create a custom class and put the Label as a child of the ActionPreviousButton.

Image is the wrong size in Kivy

I'm trying to use Kivy to display a background image, so I want the root widget to be the same size as the image. Currently, when I load the image from the kv file it appears as a small thumbnail in the bottom-left corner. The app window appears to be about the correct (full-scale) size, but hard to tell. Code below, any thoughts?
.kv file:
#:kivy 1.8.0
<BarBot_splash>:
BoxLayout:
size: image.size
Image:
id: image
source: 'MainMenu.png'
.py file:
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.uix.widget import Widget
class BarBot_splash(Widget):
def on_touch_up(self, touch):
if touch.x < self.width/3:
#we touched the menu button
pass
elif touch.x > self.width/3*2:
#we touched the custom button
pass
else:
return False
class BarBot(App):
def build(self):
return BarBot_splash()
if __name__=='__main__':
BarBot().run()
In main.py:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class BarBot_splash(BoxLayout): # subclass BoxLayout.. now it's inherently a Boxlayout
kv:
#:kivy 1.8.0
<BarBot_splash>: # I'm a BoxLayout...
Image:
id: image
source: 'MainMenu.png'
That should do the trick. The BoxLayout should take up the window. It's one child, the Image, should in turn take up the full size of the BoxLayout. Why? Well, someone correct me if I'm wrong, but I think it's because the size_hint property of the BoxLayout and the Image both default to (1, 1), which translates to: "Take up as much space in your parent as you can" or (100% width, 100% height). Though it may not be possible for a child to take up all of it's parents area if there are also other children in the parent, like if you had a few Images in the BoxLayout, or more than one BoxLayout in your app etc.. Setting a size_hint to (.5, .3) would mean take up (50% the width, 30% the height) of your parent/container, or available space.
BarBot_splash is just a widget, so it doesn't apply any position or size to its children, therefore the boxlayout (and thus its child image) have only the default position of (0, 0) and size of (100, 100).
Change BarBot_splash to a BoxLayout or other resizing layout and this will propagate correctly. You also don't need the size: image.size line, this does nothing.
Obviously very old question but I'm new to Kivy and just had to work through a very similar problem and there was very little help on the topic, so this is for future people like me:
I had the same problem but the image was also in a scatter widget. Solution is really simple, just frustrating to find. Hierarchy has to be as follows:
BoxLayout:
Scatter:
size: image.size
Image:
id: image
size: root.size

Categories

Resources