Swapping Widgets / Replacing Layouts in Kivy With on_touch_down() - python

Okay, so I've got a program that, under certain conditions, needs to remove some options and present some other ones. I originally designed it so that certain buttons would occupy the same space. However, when the on_touch_down() is triggered, the intended switch I've coded doesn't swap layouts.
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy import graphics
from kivy.uix.button import Button
from SpotChess import SCBoardWidget
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.relativelayout import RelativeLayout
promote_now = False
class Chessboard(Widget):
def __init__(self, **kwargs):
super(Chessboard, self).__init__(**kwargs)
self.bind(pos=self.drawBoardandPieces)
self.bind(size=self.drawBoardandPieces)
self.drawBoardandPieces()
def drawBoardandPieces(self, *args):
with self.canvas:
# Reset the canvas in case of redraws.
self.canvas.clear()
# Define the lengths of the edges of the squares.
edge_len = min(self.height, self.width) // 8
Config.set("graphics", "resizable", True)
for column in range(0, 8):
for row in range(0, 8):
if ((row + column) % 2) == 0:
graphics.Color(0, 0, 1)
self.dark_rect = graphics.Rectangle(pos=(column*edge_len, row*edge_len),
size=(edge_len, edge_len))
else:
graphics.Color(1, 1, 1)
self.light_rect = graphics.Rectangle(pos=(column*edge_len, row*edge_len),
size=(edge_len, edge_len))
# This provides all of the user's mouse interaction with the board.
def on_touch_down(self, touch):
global promote_now
if promote_now == False:
promote_now = True
Options().swapMenus()
else:
promote_now = False
Options().swapMenus()
game_board = Chessboard()
class PromotionOptions(GridLayout):
def __init__(self, **kwargs):
super(PromotionOptions, self).__init__(**kwargs)
self.cols = 4
self.queen_btn = Button(text="Q/q")
self.rook_btn = Button(text="R/r")
self.bishop_btn = Button(text="B/b")
self.knight_btn = Button(text="N/n")
self.add_widget(self.queen_btn)
self.add_widget(self.rook_btn)
self.add_widget(self.bishop_btn)
self.add_widget(self.knight_btn)
class Options(GridLayout):
def __init__(self, **kwargs):
super(Options, self).__init__(**kwargs)
self.cols = 1
self.swapMenus()
def swapMenus(self):
global promote_now
self.clear_widgets()
if promote_now == False:
# Present the menu layout for normal play.
self.normal_play_menu = GridLayout(cols=3)
# It contains the button to flip the board.
self.flip_board_btn = Button(text="Flip")
self.normal_play_menu.add_widget(self.flip_board_btn)
self.add_widget(self.normal_play_menu)
else:
# Present the menu layout for pawn promotions.
self.promotion_menu = GridLayout(cols=1)
self.promotion_menu.add_widget(widget=PromotionOptions())
self.add_widget(self.promotion_menu)
class SCApp(App):
def build(self):
top_portion = BoxLayout(orientation="vertical")
chessboard_portion = RelativeLayout()
chessboard_portion.add_widget(widget=game_board)
top_portion.add_widget(widget=chessboard_portion)
bottom_portion = BoxLayout(orientation="vertical")
bottom_portion.add_widget(widget=Options())
app_layout = BoxLayout(orientation="vertical")
app_layout.add_widget(top_portion)
app_layout.add_widget(bottom_portion)
return app_layout
SCApp().run()
I'm clearly calling the object to perform the replacement, but it's not completing either the self.clear_widgets() or the self.add_widget(), even though I've called the function. What am I missing?

In your on_touch_down() method the
Options().swapMenus()
lines are creating new instances of the Options class, and calling the swapMenus() method of that new instance. But that new instance of Options is not in your GUI, so it haas no effecet on your App. You need to get a reference to the Options instance that IS in your GUI, and call the swapMenus() method of that instance.
You can do that by saving a reference to the Options class in your App, by replacing this line in your build() method:
bottom_portion.add_widget(widget=Options())
with:
self.options = Options()
bottom_portion.add_widget(widget=self.options)
Then in the on_touch_down() method:
def on_touch_down(self, touch):
global promote_now
# get the instance of Options that is in the App
options = App.get_running_app().options
if promote_now == False:
promote_now = True
options.swapMenus()
else:
promote_now = False
options.swapMenus()

Related

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.

Kivy, StackLayout, object order

I have a screen with a StackLayout. The first row of the stack includes a textinput and a "+" button which is meant to add another identical row below the actual one in a loop (i.e. another textinput with another "add" button). Then there is a "Save" button, which is supposed to be always at the end of the stack.
The dictionary is supposed to later grab the input from the text field, when pressing the save button, but this should not be relevant to my problem.
There are two problems with my code:
First, when I press the "+" button in a row, the button that gets highlighted is not this button itself, but the one in the row below (e.g. if there are three rows and I press the button in the second row, the button in the third row is highlighted)
Second and most importantly, starting from the second row onwards, each press of the "+" button adds a new row above the actual one, rather than below it.
I am aware that kivy assigns reverse order to the widgets (i.e. the one added last will be drawn first) and that is why I am manually assigning indexes to the new rows. However, I cannot achieve the desired behavior.
Here is a minimal working version:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.stacklayout import StackLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class AddWindow(Screen):
def __init__(self, **kwargs):
super(AddWindow, self).__init__(**kwargs)
self.grid = StackLayout()
self.grid.pos_hint = {"x":0.05,"top":0.8}
self.grid.size_hint = (0.9,None)
self.add_widget(self.grid)
self.i = 1
self.n = 1
self.inputs = {}
self.ing1 = TextInput(size_hint=(0.9,'0.3sp'))
self.grid.add_widget(self.ing1)
self.inputs['0'] = 'ing1'
self.addIng = Button(text="+", size_hint=(0.1,'0.3sp'))
self.addIng.bind(on_press=self.addIngredient)
self.grid.add_widget(self.addIng)
self.doneButton = Button(text="Save")
self.grid.add_widget(self.doneButton, index=0)
def addIngredient (self, instance):
self.ing = TextInput(size_hint=(0.9,'0.3sp'))
self.inputs[self.i] = 'ing{}'.format(self.i+1)
self.grid.add_widget(self.ing, index=self.n+1)
self.addNext = Button(text="+", size_hint=(0.1,'0.3sp'))
self.addNext.bind(on_press=self.addIngredient)
self.grid.add_widget(self.addNext, index=self.n+2)
self.i += 1
self.n += 2
print(self.inputs)
WMan = ScreenManager()
WMan.add_widget(AddWindow(name='add'))
class RecipApp(App):
def build(self):
return WMan
if __name__ == "__main__":
RecipApp().run()
What am I missing? Here is a screenshot for better clarity:
Screenshot
Here is a brute force method to do what you want by rebuilding the StackLayout each time a + `Button is pressed:
def addIngredient(self, instance):
tmp_children_list = self.grid.children[:]
self.grid.clear_widgets()
for index in range(len(tmp_children_list)-1, -1, -1):
child = tmp_children_list[index]
self.grid.add_widget(child)
if child == instance:
# this is the pressed Button, so add new row after it
self.n += 1
self.ing = TextInput(size_hint=(0.9,'0.3sp'))
self.ing.text = str(self.n) # add text just for identification
self.grid.add_widget(self.ing)
self.addNext = Button(text="+", size_hint=(0.1,'0.3sp'))
self.addNext.bind(on_press=self.addIngredient)
self.grid.add_widget(self.addNext)
Here is a quick and dirty approach , where your problems should be fixed due to a remove and add of the save button. Between those to actions you add a new row before you finally add the save button again. Hope it helps you to adjust your code. The key part is the custom on_press method of the AddButton.
from kivy.app import App
from kivy.uix.stacklayout import StackLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.core.window import Window
class AddButton(Button):
def __init__(self, **kwargs):
super(AddButton, self).__init__(**kwargs)
def on_press(self):
self.parent.remove_widget(self.parent.save_btn)
self.parent.add_widget(TextInput(size_hint=(None, None), size=(Window.width * 0.8 - self.parent.padding[1], Window.height * 0.1)))
self.parent.add_widget(AddButton(text="+", size_hint=(None, None), size=(Window.width * 0.2, Window.height * 0.1)))
self.parent.add_widget(self.parent.save_btn)
class MyApp(App):
def build(self):
Window.size = (400, 600)
layout = StackLayout(orientation="lr-tb", padding=(20, 40))
layout.save_btn = Button(text="Save", size_hint=(None, None),
size=(Window.width - layout.padding[1], Window.height * 0.1))
layout.add_widget(TextInput(size_hint=(None, None), size=(Window.width * 0.8 - layout.padding[1], Window.height * 0.1)))
layout.add_widget(AddButton(text="+", size_hint=(None, None), size=(Window.width * 0.2, Window.height * 0.1)))
layout.add_widget(layout.save_btn)
return layout
MyApp().run()

Autoresizing Canvas in a Kivy App

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 :)

How to transfer a list of element from a screen to another using Kivy

I have some trouble to transfert a list of element, that i get from a file (in the first screen), to the second and third screen.
I have 3 Screen managed by ScreenManager, every screen is in a different file.py and have a file.kv.
This is what my ScreenManager looks like (filename : login.py):
from interface1 import Interface1App
from interface2 import Interface2App
class LoginApp(App):
def build(self):
manager = ScreenManager()
#add view login
manager.add_widget(Login(name='login'))
# add view 'interface1'
app = Interface1App()
app.load_kv()
interfacen1 = app.build()
manager.add_widget(interfacen1)
# add view 'interface2'
app2 = Interface2App()
app2.load_kv()
interfacen2 = app2.build()
manager.add_widget(interfacen2)
# add view 'interface3'
app3 = Interface3App()
app3.load_kv()
interfacen3 = app3.build()
manager.add_widget(interfacen3)
manager.transition = SlideTransition(direction="left")
return manager
In the screen manager login i import all the screens (ex: from interface1 import Interface1App)
In the second file, i put in self.list some data, and use it for that interface (filename : interface1.py):
from interface2 import Interface2App
class AllImage(Screen):
CONTAINER_PNG = os.path.join(AllImage_ROOT, 'images')
IMAGES_NAMES = [c[:-4] for c in os.listdir(CONTAINER_PNG)]
def __init__(self, **kwargs):
Screen.__init__(self, **kwargs)
for im in IMAGES_NAMES:
if IMAGES_NAMES != None :
toggleimage = ToggleWithImage(src=im+'.png')
toggleimage.bind(on_press= lambda a, im=im:self.onpress_addpage(self.listim, im))
self.layout.add_widget(toggleimage)
def change_screen(self, x, list):
self.manager.transition = SlideTransition(direction="left")
self.manager.current = 'interface2'
self.manager.get_screen('interface2')
self.manager.list1 = [1,2,3]
def backtologin(self):
self.manager.transition = SlideTransition(direction="right")
self.manager.current = 'login'
self.manager.get_screen('login')
class Interface1App(App):
'''The kivy App that runs the main root. All we do is build a AllImage
widget into the root.'''
def build(self):
screen = AllImage()
screen.name = 'interface1'
return screen
After that i want to use again the data collected in the 2nd interface (self.list) and use it in the 3rd Interface (filename: interace2.py):
from interface1 import AllImage #this isn't working
class Combinaison(Screen):
screen = ObjectProperty(None)
def __init__(self, **kwargs):
# self._previously_parsed_text = ''
super(Combinaison, self).__init__(**kwargs)
# self.list = list(self.manager.get_screen('interface1').list)
print(self.manager.list1) # The problem is here <===========
def backtointerface1(self):
self.manager.transition = SlideTransition(direction="right")
self.manager.current = 'interface1'
self.manager.get_screen('interface1')
class Interface2App(App):
'''The kivy App that runs the main root. All we do is build a Combinaison
widget into the root.'''
def build(self):
screen = Combinaison()
screen.name = 'interface2'
return screen
With this code i have this error : print(self.manager.list1)
AttributeError: 'NoneType' object has no attribute 'list1'
I think i didn't understand how i should use the self.manager.list1=[1, 2, 3]
I hope that i was clear, sorry for the long post.
Thank you for your time :)
You can use Screen.manager.get_screen(name).list = list(self.list)
A complete example:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class Screen1(Screen):
list = [1,2,3]
def __init__(self,**kwargs):
super(Screen1,self).__init__(**kwargs)
self.switch_button = Button(text="goto 2")
self.switch_button.bind(on_release=self.switch)
self.add_widget(self.switch_button)
def switch(self,*args):
self.list = [4,5,6]
self.manager.get_screen("screen2").list = list(self.list)
self.manager.current = "screen2"
class Screen2(Screen):
def __init__(self,**kwargs):
super(Screen2,self).__init__(**kwargs)
self.switch_button = Button(text="goto 1")
self.switch_button.bind(on_release=self.switch)
self.add_widget(self.switch_button)
def switch(self,*args):
self.manager.current = "screen1"
def on_enter(self):
print self.list
class MainApp(App):
def build(self):
sc1 = Screen1(name="screen1")
sc2 = Screen2(name="screen2")
self.sm = ScreenManager()
self.sm.add_widget(sc1)
self.sm.add_widget(sc2)
return self.sm
MainApp().run()
You can also use the ScreenManager to store the list or lists.
self.manager.list1 = [1,2,3] # dont call this in __init__
self.manager.list2 = [4,5,6] # call it in on_enter instead
Those lists will be available from all screens after they are added to the manager. The on_enter method on the screen would be suitable for your example.

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