Autoresizing Canvas in a Kivy App - python

I am writing my first Kivy app in python only (I'm avoiding kv for now). I have created a custom widget called WorldviewWidget, and I'm trying to use it as a place to draw. With the button widgets, I just give a size_hint and a pos_hint, and they show up where I want them. But with my widget, I don't know how to use the size_hint and position_hint to size the rectangle I am drawing in my WorldviewWidget. Here is the code. Thanks in advance!
#! /usr/bin/python
from kivy.app import App
from kivy.graphics import *
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
class WorldviewWidget(Widget):
def __init__(self, **kwargs):
super(WorldviewWidget, self).__init__(**kwargs)
self.canvas.clear()
print self.size, self.pos
with self.canvas:
Color(1, 0, 0, 1, mode='rgba')
# HELP! I want the rectangle to be resized when the window changes size so that it always takes up the same proportion of the screen.
self.rect = Rectangle(size=???, pos=???)
class JFROCS_App(App):
def build(self):
Window.clearcolor = [1,1,1,1]
parent = FloatLayout(size=Window.size)
worldview = WorldviewWidget(size_hint=(0.4, 0.4), pos_hint = {'x':0.2, 'y':0.2})
parent.add_widget(worldview)
start_btn = Button(text='Start', size_hint=(0.1, 0.1), pos_hint={'x':.02, 'y':.7}, background_color=[0,1,0,1])
start_btn.bind(on_release=self.start_simulation)
parent.add_widget(start_btn)
pause_btn = Button(text='Pause', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.6}, background_color=[1,1,0,1])
pause_btn.bind(on_release=self.pause_simulation)
parent.add_widget(pause_btn)
stop_btn = Button(text='Stop', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.5}, background_color=[1,0,0,1])
stop_btn.bind(on_release=self.stop_simulation)
parent.add_widget(stop_btn)
return parent
def start_simulation(self, obj):
print "You pushed the start button!"
def pause_simulation(self, obj):
print "You pushed the pause button!"
def stop_simulation(self, obj):
print "You pushed the stop button!"
if __name__ == '__main__':
JFROCS_App().run()

I think that this sort of task is predestined for the kivy-language but here is a solution in Python. Basically, I have used the bind-method to make your widget listen for changes in its parent's size. Have a look at this for more information on this mechanism.
from kivy.app import App
from kivy.graphics import *
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
class WorldviewWidget(Widget):
def __init__(self, **kwargs):
super(WorldviewWidget, self).__init__(**kwargs)
self.canvas.clear()
print self.size, self.pos
with self.canvas:
Color(1, 0, 0, 1, mode='rgba')
# *changed* ##############################################
self.rect = Rectangle(size=self.size, pos=self.pos)
# *new* ##########################################################
def update_size(self, instance, new_size):
print "UPDATING SIZE", instance, new_size
self.size[0] = new_size[0] * 0.4
self.size[1] = new_size[1] * 0.4
self.rect.size = self.size
self.pos[0] = self.parent.size[0] * 0.2
self.pos[1] = self.parent.size[1] * 0.2
self.rect.pos = self.pos
class JFROCS_App(App):
def build(self):
Window.clearcolor = [1,1,1,1]
parent = FloatLayout(size=Window.size)
# *changed* ##################################################
worldview = WorldviewWidget(size=(0.4*parent.size[0], 0.4*parent.size[1]),
pos=(0.2*parent.size[0], 0.2*parent.size[1]))
# makes sure that the widget gets updated when parent's size changes:
parent.bind(size=worldview.update_size)
parent.add_widget(worldview)
start_btn = Button(text='Start', size_hint=(0.1, 0.1), pos_hint={'x':.02, 'y':.7}, background_color=[0,1,0,1])
start_btn.bind(on_release=self.start_simulation)
parent.add_widget(start_btn)
pause_btn = Button(text='Pause', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.6}, background_color=[1,1,0,1])
pause_btn.bind(on_release=self.pause_simulation)
parent.add_widget(pause_btn)
stop_btn = Button(text='Stop', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.5}, background_color=[1,0,0,1])
stop_btn.bind(on_release=self.stop_simulation)
parent.add_widget(stop_btn)
return parent
def start_simulation(self, obj):
print "You pushed the start button!"
def pause_simulation(self, obj):
print "You pushed the pause button!"
def stop_simulation(self, obj):
print "You pushed the stop button!"
if __name__ == '__main__':
JFROCS_App().run()
And have a look at the kivy-language -- it takes care of all the binding for you :)

Related

How can I successfully create an infinitely scrolling background?

import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.graphics import Rectangle
from kivy.core.image import Image as CoreImage
from kivy.core.window import Window
class Background(Widget):
def __init__(self, **kw):
super(Background, self).__init__(**kw)
with self.canvas:
texture = CoreImage('space.png').texture
texture.wrap = 'repeat'
self.rect_1 = Rectangle(texture=texture, size=self.size, pos=self.pos)
Clock.schedule_interval(self.txupdate, 0)
def txupdate(self, *l):
t = Clock.get_boottime()
self.rect_1.tex_coords = -(t * 0.001), 0, -(t * 0.001 + 10), 0, -(t * 0.001 + 10), -10, -(t * 0.001), -10
class CosmicPolygons(App):
def build(self):
return Background(size=Window.size)
if __name__ == "__main__":
CosmicPolygons().run()
I've tried many different ways and attempts in order to create a scrolling background in Kivy. This was the best method I could find as it was the only one that didn't crash. But I'm pretty sure it's still outdated as it did not work as intended, in addition to distorting my png greatly.
If anyone has any ideas on how to achieve this, let me know. Thanks in advance.
Image of current app:
Space.png:
Here is another approach that just creates a list of Images and moves them across the screen:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.core.window import Window
class BgImage(Image):
pass
Builder.load_string('''
<BgImage>:
source: 'stars.png'
allow_stretch: True
size_hint: None, 1
width: self.image_ratio * self.height
''')
class Background(FloatLayout):
def __init__(self, **kwargs):
super(Background, self).__init__(**kwargs)
self.deltax = 3 # sets speed of background movement
self.bg_images = [] # list of Images that form the background
Clock.schedule_once(self.set_up_bg)
def set_up_bg(self, dt):
# create BgImages and position them to fill the Background
self.start_x = None
pos_x = -1
while pos_x < self.width:
img = BgImage()
if self.start_x is None:
# starting position of first Image is just off screen
self.start_x = -self.height * img.image_ratio
pos_x = self.start_x
img.pos = (pos_x, 0)
self.bg_images.append(img)
self.add_widget(img)
# calculate starting position of next Image by adding its width
pos_x += self.height * img.image_ratio
# start moving the background
Clock.schedule_interval(self.update, 1.0/30.0)
def update(self, dt):
for img in self.bg_images:
img.x += self.deltax
if img.x > self.width:
# this Image is off screen, move it back to starting position
img.x = self.start_x
class CosmicPolygons(App):
def build(self):
return Background(size=Window.size)
if __name__ == "__main__":
CosmicPolygons().run()

How to update the on_touch_move and on_touch_down methods of a Widget in kivy

I am currently working on creating a paint app and I am using a Widget canvas for that purpose.
Basically I have defined methods to create straight lines and curve_line. But the problem is I can't switch between these two methods. meaning I have two buttons to trigger these methods and each method bind the widget with on_touch_down and on_touch_move methods. My problem is first time as soon as the app starts running and for example I clicked button with name f hand it works fine and after that once i clicked the next button the widget bind itself with both methods creating a mess.... now i want that the widget should bind itself with only one method at a time .. how can i achieve this..
my python code is here
import kivy
from kivy.uix.widget import Widget
from kivy.uix.widget import Canvas
from kivy.graphics import Color
from kivy.graphics import Line
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.uix.button import Button
class Main(FloatLayout):
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
self.my_widget = Widget(size_hint= (0.6,0.6),pos_hint = {'x':0.5,'top' : 0.8})
self.add_widget(self.my_widget)
self.free_hand = Button(text='f_hand',pos_hint = {'x':0.04,'top':0.2},size_hint = (0.12,0.12))
self.add_widget(self.free_hand)
self.free_hand.bind(on_press = self._free_hand)
self._hand = Button(text='s_hand', pos_hint={'x': 0.04, 'top': 0.6}, size_hint=(0.12, 0.12))
self.add_widget(self._hand)
self._hand.bind(on_press=self._straight_lines)
def _free_hand(self,instance):
def on_touch_downah(self,touch):
self.line = Line(points = (touch.x,touch.y),width = 5)
self.canvas.add(self.line)
'''self.line_list.append(self.line)
print(self.line_list)'''
def on_touch_moveah(self,touch):
self.line.points += touch.x,touch.y
self.my_widget.bind(on_touch_down=on_touch_downah)
self.my_widget.bind(on_touch_move=on_touch_moveah)
def _straight_lines(self,instance):
def on_touch_downeh(self, touch):
self.x_ = touch.x
self.y_ = touch.y
self.lines = Line(points=(touch.x,touch.y),width = 5)
self.canvas.add(self.lines)
def on_touch_moveeh(self, touch):
self.x2 = self.x_ + 0.1
self.y2 = self.y_ + 0.1
self.lines.points = (self.x_,self.y_,self.x2,self.y2,touch.x,touch.y)
self.my_widget.bind(on_touch_down=on_touch_downeh)
self.my_widget.bind(on_touch_move=on_touch_moveeh)
'''
def undo_1(self):
self.i -= 1
k = self.line_list[self.i]
self.canvas.remove(k)
'''
class Myapp(App):
def build(self):
return Main()
Myapp().run()
Any help is greatly appreciated .
Expanding on the suggestion from #inclement, here is a modified version of your code where just two methods are bound to on_touch_down and on_touch_move. The methods triggered by the Buttons just set the mode for the drawing, and the bound methods choose what to do based on the current mode:
from kivy.uix.widget import Widget
from kivy.graphics import Line
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.uix.button import Button
class Main(FloatLayout):
def __init__(self,**kwargs):
super(Main, self).__init__(**kwargs)
self.my_widget = Widget(size_hint= (0.6,0.6),pos_hint = {'x':0.5,'top' : 0.8})
self.add_widget(self.my_widget)
self.free_hand = Button(text='f_hand',pos_hint = {'x':0.04,'top':0.2},size_hint = (0.12,0.12))
self.add_widget(self.free_hand)
self.free_hand.bind(on_press = self._free_hand)
self._hand = Button(text='s_hand', pos_hint={'x': 0.04, 'top': 0.6}, size_hint=(0.12, 0.12))
self.add_widget(self._hand)
self._hand.bind(on_press=self._straight_lines)
self.mode = None # could be "free_hand" or "straight_lines"
self.my_widget.bind(on_touch_down=self.on_touch_downh)
self.my_widget.bind(on_touch_move=self.on_touch_moveh)
def _free_hand(self,instance):
self.mode = "free_hand"
def _straight_lines(self,instance):
self.mode = "straight_lines"
def on_touch_downh(self, widget, touch):
if self.mode == "free_hand":
self.free_hand_touch(touch)
elif self.mode == "straight_lines":
self.straight_lines_touch(touch)
def on_touch_moveh(self, widget, touch):
if self.mode == "free_hand":
self.free_hand_move(touch)
elif self.mode == "straight_lines":
self.straight_lines_move(touch)
def free_hand_touch(self,touch):
self.line = Line(points = (touch.x,touch.y),width = 5)
self.canvas.add(self.line)
'''self.line_list.append(self.line)
print(self.line_list)'''
def free_hand_move(self,touch):
self.line.points += touch.x,touch.y
def straight_lines_touch(self, touch):
self.x_ = touch.x
self.y_ = touch.y
self.lines = Line(points=(touch.x,touch.y),width = 5)
self.canvas.add(self.lines)
def straight_lines_move(self, touch):
self.x2 = self.x_ + 0.1
self.y2 = self.y_ + 0.1
self.lines.points = (self.x_,self.y_,self.x2,self.y2,touch.x,touch.y)
'''
def undo_1(self):
self.i -= 1
k = self.line_list[self.i]
self.canvas.remove(k)
'''
class Myapp(App):
def build(self):
return Main()
Myapp().run()
Note that in your original code, you were binding more methods every time one of the Buttons was pressed.

python- Kivy Screen Manager within .py file

I am building a multiple screen App with Kivy and I would like to use the ScreenManager to navigate between the multiple screens. I have seen examples and documentation for how to create the screens within a .kv file, but I want to know how to create them within the .py file.
Problem: When I create the screen subclasses as shown below, my app
window returns a blank screen.
Question: What is the correct way to
create the Screen subclasses within a .py file?
Right now I have two Screen subclasses defined: 'welcomeScreen', and 'functionScreen'. Each consists of a layout with some widgets.
kivy.require('1.9.1')
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
import kivy.uix.boxlayout
import kivy.uix.button
from kivy.uix.screenmanager import ScreenManager, Screen
class PanelBuilderApp(App): # display the welcome screen
def build(self):
# Create the screen manager and add widgets to the base sm widget
sm = kivy.uix.screenmanager.ScreenManager()
sm.add_widget(Screen(name='welcomeScreen'))
sm.add_widget(Screen(name='functionScreen'))
# sm.current= 'welcomeScreen'
return sm
class welcomeScreen(Screen): #welcomeScreen subclass
def __init__(self, **kwargs): #constructor method
super(welcomeScreen, self).__init__(**kwargs) #init parent
welcomePage = FloatLayout()
box = kivy.uix.boxlayout.BoxLayout(orientation='vertical', size_hint=(0.4, 0.3),
padding=8, pos_hint={'top': 0.5, 'center_x': 0.5})
welcomeLabel = Label(text='Hello and welcome to the Panel Builder version 1.0.\nApp by John Vorsten\nClick below to continue',
halign= 'center', valign= 'center', size_hint= (0.4, 0.2), pos_hint= {'top': 1, 'center_x': 0.5})
welcomeBox = kivy.uix.button.Button(text= 'Click to continue')
welcomeBox.bind(on_press= self.callback)
welcomeBox2 = kivy.uix.button.Button(text='not used')
welcomePage.add_widget(welcomeLabel)
box.add_widget(welcomeBox)
box.add_widget(welcomeBox2)
welcomePage.add_widget(box)
self.add_widget(welcomePage)
def callback(instance):
print('The button has been pressed')
sm.switch_to(Screen(name= 'functionScreen'))
# sm.current = Screen(name= 'functionScreen')
class functionScreen(Screen): #For later function navigation
def __init__(self, **kwargs): #constructor method
super(functionScreen, self).__init__(**kwargs) #init parent
functionPage = kivy.uix.floatlayout.FloatLayout()
functionLabel = Label(text='Welcome to the function page. Here you will choose what functions to use',
halign='center', valign='center', size_hint=(0.4,0.2), pox_hint={'top': 1, 'center_x': 0.5})
functionPage.add_widget(functionLabel)
self.add_widget(functionPage)
# sm.add_widget('Name') #Add more names later when you create more screens
# OR#
# for i in ScreenDirectory:
# sm.add_widget(ScreenDirectory[i])
PanelBuilderApp().run()
if __name__ == '__main__':
pass
I understand I can add the definitions to a .kv file, and I will probably do this as the app grows. However, I like being explicit as I am learning how to use kivy.
I think you think using Screen(name='welcomeScreen') you are using welcomeScreen but that is not true, if you want to use welcomeScreen you should use it directly.
On the other hand you have typographical errors so I have corrected, I recommend you follow the kivy tutorials, obviously you must have a solid OOP base (and I think you do not have it so your task is to reinforce).
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
class PanelBuilderApp(App): # display the welcome screen
def build(self):
sm = ScreenManager()
sm.add_widget(WelcomeScreen(name='welcomeScreen'))
sm.add_widget(FunctionScreen(name='functionScreen'))
return sm
class WelcomeScreen(Screen): #welcomeScreen subclass
def __init__(self, **kwargs): #constructor method
super(WelcomeScreen, self).__init__(**kwargs) #init parent
welcomePage = FloatLayout()
box = BoxLayout(orientation='vertical', size_hint=(0.4, 0.3),
padding=8, pos_hint={'top': 0.5, 'center_x': 0.5})
welcomeLabel = Label(text='Hello and welcome to the Panel Builder version 1.0.\nApp by John Vorsten\nClick below to continue',
halign= 'center', valign= 'center', size_hint= (0.4, 0.2), pos_hint= {'top': 1, 'center_x': 0.5})
welcomeBox = Button(text= 'Click to continue', on_press=self.callback)
welcomeBox2 = Button(text='not used')
welcomePage.add_widget(welcomeLabel)
box.add_widget(welcomeBox)
box.add_widget(welcomeBox2)
welcomePage.add_widget(box)
self.add_widget(welcomePage)
def callback(self, instance):
print('The button has been pressed')
self.manager.current = 'functionScreen'
class FunctionScreen(Screen): #For later function navigation
def __init__(self, **kwargs): #constructor method
super(FunctionScreen, self).__init__(**kwargs) #init parent
functionPage = FloatLayout()
functionLabel = Label(text='Welcome to the function page. Here you will choose what functions to use',
halign='center', valign='center', size_hint=(0.4,0.2), pos_hint={'top': 1, 'center_x': 0.5})
functionPage.add_widget(functionLabel)
self.add_widget(functionPage)
if __name__ == '__main__':
PanelBuilderApp().run()

Dynamic grid in Kivy with each grid element containing multiple widgets

This is my first post here, but I will try to be as detailled as I can.
So my application is in python, and involves a grid in kivy, where each element in the grid is supposed to contain 4 additional widgets and possibility for a fifth. The four additional widgets are supposed to be in a cross shape at the edges and the fifth in the middle.
Problem is, whenever I add a sub widget it lands in the bottom left corner on position 0,0 of the main window
So far so good. Right now I am just trying to get even one widget inside another widget to display correctly.
Heres what I attempted:
<GridCell#Label>
BoxLayout:
orientation:'horizontal'
Button:
text:'One'
size:10,10
size_hint:None,None
Building a .kv file for my app, where I would put a box layout inside of it and then a button.
Also I tried the following class configuration:
class GridCell(Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.root = FloatLayout()
self.button = Button(text="test")
self.button.x = self.root.x
self.button.center_y = self.root.center_y
self.root.add_widget(self.button)
self.add_widget(self.root)
Also did not work.
I am adding the grid cells by just calling .add on the grid with a newly created widget for each iteration of a for loop.
All the child widgets are apparently created, but they all land in the bottom left corner!
This is the whole code of the gui right now:
import kivy
import World
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Color, Rectangle
kivy.require('1.10.0')
class GridCell(Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.root = FloatLayout()
self.button = Button(text="blargh")
self.button.x = self.root.x
self.button.center_y = self.root.center_y
self.root.add_widget(self.button)
self.add_widget(self.root)
def on_size(self, *args):
self.canvas.before.clear()
if self.id is "cliff":
with self.canvas.before:
Color(249 / 255, 6 / 255, 6 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
if self.id is "goal":
with self.canvas.before:
Color(6 / 255, 6 / 255, 249 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
if self.id is "start":
with self.canvas.before:
Color(11 / 255, 174 / 255, 6 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
if self.id is "normal":
with self.canvas.before:
Color(119 / 255, 115 / 255, 115 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
class GameGridApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.grid = GridLayout(cols=8, rows=5)
self.load_into()
def load_into(self):
world = World.World(8, 5)
world.build_gamegrid()
for cell in world.gamegrid:
name = str(cell.name)
grid_cell = GridCell()
grid_cell.text = name
if cell.start:
grid_cell.id = "start"
if cell.goal:
grid_cell.id = "goal"
if cell.cliff:
grid_cell.id = "cliff"
if cell.field:
grid_cell.id = "normal"
self.grid.add_widget(grid_cell)
def build(self):
return self.grid
customLabel = GameGridApp()
customLabel.run()
I may give an idea , that create a 'subgrids' object and a 'main grid' object that contain the 'subgrids'. These two objects would be GridLayout objects.
Here is a simple example in python2.7 :
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
class SubGrids(GridLayout):
def __init__(self):
GridLayout.__init__(self, cols=3, rows=3);
self.add_widget(Label(text='1st'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='2nd'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='3rd'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='4th'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='5th'));
class Grids(GridLayout):
def __init__(self):
GridLayout.__init__(self, cols=2, rows = 2);
self.add_widget(SubGrids());
self.add_widget(SubGrids());
self.add_widget(SubGrids());
self.add_widget(SubGrids());
class Example(App):
def build(self):
return Grids()
if __name__ == '__main__':
x = Example();
x.run();
Hope this gives an idea.

Kivy: change layout when I change orientation

I want to create something that changes its general layout when the orientation, or size, of the window is changed. In the below example, I was trying to do this with 3 buttons. But it fails, and complains that the buttons already have a parent when I try to add them to a layout. Why do they already have a parent? I never added them to the AdaptWidget...
More importantly, how should I achieve what I want to achieve?
Code below:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class AdaptWidget(BoxLayoutWrite failed: Broken pipe
def __init__(self, **kw):
super(AdaptWidget, self).__init__(**kw)
self.but1 = Button(text='but1')
self.but2 = Button(text='but2')
self.but3 = Button(text='but3')
self.layout = None
def on_size(self, *args):
self.clear_widgets()
if self.size[0] > self.size[1]:
self.layout = BoxLayout(orientation='horizontal')
self.but1.size_hint = 0.7, 1
self.layout.add_widget(self.but1)
vert = BoxLayout(orientation='vertical')
self.layout.add_widget(vert)
self.but2.size_hint = (1,0.5)
self.but3.size_hint = (1,0.5)
vert.add_widget(self.but2)
vert.add_widget(self.but3)
else:
self.layout = BoxLayout(orientation='vertical')
self.but1.size_hint = 1, 0.7
self.layout.add_widget(self.but1)
horiz = BoxLayout(oreintation='horizontal')
self.layout.add_widget(horiz)
self.but2.size_hint = 0.5, 1
self.but3.size_hint = 0.5, 1
horiz.add_widget(self.but2)
horiz.add_widget(self.but3)
self.add_widget(self.layout)
class TestLayoutApp(App):
def build(self):
return AdaptWidget()
if __name__ == '__main__':
TestLayoutApp().run()
Every time the size of your AdaptWidget changes, your on_size callback runs - but the callback doesn't remove any widgets from their parents, so on_size will always crash the second time it is called. You can always ensure a widget is removed from its parent:
if widget.parent is not None:
widget.parent.remove_widget(widget)
EDIT:
3 days ago I wrote this "answer", although I could not make the above suggestions work.
But now someone here: https://groups.google.com/forum/#!topic/kivy-users/Z5fjt5tNys8
Showed me how to do it.
So here comes the solution:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class AdaptWidget(BoxLayout):
def __init__(self, **kw):
super(AdaptWidget, self).__init__(**kw)
# but1 = Button(id='but1', text='button1')
# but1 = ObjectProperty(None)
# self.but1 = ObjectProperty(None)
self.add_widget(Button(id='but1', text='button1'))
self.add_widget(Button(id='but2', text='button2'))
self.add_widget(Button(id='but3', text='button3'))
def on_size(self, *args):
if self.width > self.height:
self.orientation = 'horizontal'
# self.child.but1.size_hint = (0.7, 1)
# but1.size_hint = (0.7, 1)
# self.ids.but1.size_hint = (0.7, 1)
self.children[2].size_hint = (0.7, 1)
self.children[1].size_hint = (0.5, 1)
self.children[0].size_hint = (0.5, 1)
else:
self.orientation = 'vertical'
self.children[2].size_hint = (1, 0.7)
self.children[1].size_hint = (1, 0.5)
self.children[0].size_hint = (1, 0.5)
class TestLayoutApp(App):
def build(self):
return AdaptWidget()
if __name__ == '__main__':
TestLayoutApp().run()
Note that the solution above is much cleaner than the original code. No need to remove or clear any widgets. You just change their attributes.
EDIT:
Part of the problem was that the 'buildozer.spec'-file contained this:
# (str) Supported orientation (one of landscape, sensorLandscape, portrait or all)
orientation = portrait
Instead of this:
# (str) Supported orientation (one of landscape, sensorLandscape, portrait or all)
orientation = all

Categories

Resources