Python KIvy: Why layout not changed? - python

From the below code I expect layout to be changed from BoxLayout to GridLayout in show_buttons() method but it is not happening and I am still seeing BoxLayout. I would appreciate an explanation, thank you.
class MainScreen(BoxLayout):
def show_buttons(self, button):
self.clear_widgets()
self.layout = GridLayout(cols=2)
if button.text == 'Button 1':
for x in range (100, 110):
t = ('Button %s' % x)
self.add_widget(Button(text=t))
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.orientation='vertical'
self.add_widget(Label(text='Select Button', size_hint = (1, 0.2)))
self.button1=Button(text="Button 1")
self.button1.bind(on_press=self.show_buttons)
self.add_widget(self.button1)
self.button2=Button(text="Button 2")
self.button2.bind(on_press=self.show_buttons)
self.add_widget(self.button2)
class MyApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
MyApp().run()

You forgot to add the GridLayout to the parent...
def show_buttons(self, button):
self.clear_widgets()
self.layout = GridLayout(cols=2, size_hint=(1.0, 1.0))
if button.text == 'Button 1':
for x in range (100, 110):
t = ('Button %s' % x)
self.add_widget(Button(text=t))
self.add_widget(self.layout) # <-----------------
That said, You might want to rethink about clearing the widgets and just move to a diffent screen using a ScreenManager

Related

How do I set Id of widget inside python file?

I am trying to change the text of an MDLabel inside one screen from another screen. I can reference the screen itself but since I'm not using kv language I can't set Id of the MDLabel I'm trying to reference.
Is there a way to set Id of an MDLabel from within Python and add it to self.ids of the screen it's part of?
-Or. Is there another way to reference widgets of another screen?
My code:
def main():
class HomeScreen(Screen, GridLayout, MDApp):
def __init__(self, **kwargs):
super(HomeScreen, self).__init__(**kwargs)
self.add_widget(MDRaisedButton(text='Read', size_hint=(.3, .2), font_size='30sp', on_press=lambda x:self.changerReadMail()))
def changerReadMail(self, *args):
self.manager.transition.direction = 'right'
# It's here I want to change the text of label inside the ReadMail class.
self.manager.current = 'read'
class ReadMail(Screen, FloatLayout, MDApp):
def __init__(self, **kwargs):
super(ReadMail, self).__init__(**kwargs)
label = (MDLabel(text='hej'))
self.add_widget(label)
self.add_widget(MDFillRoundFlatButton(text='Back', font_size='20sp', size_hint=(.1,.1), pos_hint={'x':.01, 'y':.02}, on_press=lambda x:self.changerInbox()))
def changerInbox(self, *args):
self.manager.transition.direction = 'left'
self.manager.current = 'home'
class KivyApp(MDApp):
def build(self):
Window.size = (1000, 600)
self.sm = ScreenManager()
self.sm.add_widget(HomeScreen(name='home'))
self.sm.add_widget(ReadMail(name='read'))
self.sm.current = 'home'
return self.sm
KivyApp().run()
if __name__ == '__main__':
main()
I solved this one!
Here's my solution (See the line commented with "This is a new line"):
import weakref #This is a new line
def main():
class HomeScreen(Screen, GridLayout, MDApp):
def __init__(self, **kwargs):
super(HomeScreen, self).__init__(**kwargs)
self.add_widget(MDRaisedButton(text='Read', size_hint=(.3, .2), font_size='30sp', on_press=lambda x:self.changerReadMail()))
def changerReadMail(self, *args):
self.manager.transition.direction = 'right'
self.manager.get_screen('read').ids.test.text = 'test' #This is a new line
self.manager.current = 'read'
class ReadMail(Screen, FloatLayout, MDApp):
def __init__(self, **kwargs):
super(ReadMail, self).__init__(**kwargs)
label = (MDLabel())
self.ids['test'] = weakref.ref(label) #This is a new line
self.add_widget(label)
self.add_widget(MDFillRoundFlatButton(text='Back', font_size='20sp', size_hint=(.1,.1), pos_hint={'x':.01, 'y':.02}, on_press=lambda x:self.changerInbox()))
def changerInbox(self, *args):
self.manager.transition.direction = 'left'
self.manager.current = 'home'
class KivyApp(MDApp):
def build(self):
Window.size = (1000, 600)
self.sm = ScreenManager()
self.sm.add_widget(HomeScreen(name='home'))
self.sm.add_widget(ReadMail(name='read'))
self.sm.current = 'home'
return self.sm
KivyApp().run()
if __name__ == '__main__':
main()

Use button in screen 1 to change label text in screen 2 in Kivy

I've been trying to change the label2 text in layout 2, with button1 in layout1, but it doesn't seem to work, when I press the button nothing happens
Here's the code:
class layout1(GridLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.cols = 2
self.button1 = Button(text = "Button 1 changes screen 2", on_press = self.change_label)
self.add_widget(self.button1)
self.change_button = Button(text = "move to screen 2", on_press = self.change_screen)
self.add_widget(self.change_button)
def change_screen(self, instance):
practice_app.sm.current = "screen2"
def change_label(self,instance):
func_layout = layout2()
func_layout.label2.text = "changed"
class layout2(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.label2 = Label(text = "this should change")
self.add_widget(self.label2)
class TestApp(App):
def build(self):
self.sm = ScreenManager()
screen1 = Screen(name = "screen1")
screen1.add_widget(layout1())
self.sm.add_widget(screen1)
screen2 = Screen(name = "screen2")
screen2.add_widget(layout2())
self.sm.add_widget(screen2)
return self.sm
if __name__ == "__main__":
practice_app = TestApp()
practice_app.run()
There are many ways to do what you want. Since you are not using kv, perhaps the easiest way is to save a reference to layout2. Here is a modified version of your build() method that does that:
class TestApp(App):
def build(self):
self.sm = ScreenManager()
screen1 = Screen(name = "screen1")
screen1.add_widget(layout1())
self.sm.add_widget(screen1)
screen2 = Screen(name = "screen2")
self.layout2 = layout2() # save reference to layout2
screen2.add_widget(self.layout2)
self.sm.add_widget(screen2)
return self.sm
And then, use that reference in the change_label() method:
def change_label(self,instance):
# func_layout = layout2() # creates a new instance of layout2 (not the one in the GUI)
func_layout = App.get_running_app().layout2
func_layout.label2.text = "changed"

How to access a widget from one screen inside another screen in kivy

How do I access a widget from screen1 and make changes to it in screen2 and also return screen1 back to it initial state(like I just run the code).
I have commented out some code that is not working.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
class Screen2(Screen):
def __init__(self, **kwargs):
super(Screen2, self).__init__(**kwargs)
self.retry = Button(text='retry', font_size=15, size_hint=(.26, .26),
pos_hint={'center_x': .5, 'center_y': .32}, on_press=self.retrying,
background_color=(0, 0, 1, 1))
self.add_widget(self.retry)
def retrying(self, *args):
self.manager.current = 'screen1'
# it should change the text in screen1 to "i am back to screen1, thanks you"
#self.welc.text=" i am back to screen1, thank you"
# it should change the button color back to it normal state
#self.goto.background_color='normal state'
class Screen1(Screen):
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
self.welc = Label(text='hi there welcome to my first screen', font_size=15, size_hint=(.26, .26),
pos_hint={'center_x': .5, 'center_y': .7})
self.add_widget(self.welc)
self.goto = Button(text='next screen', font_size=15, size_hint=(.2, .2),
pos_hint={'center_x': .5, 'center_y': .32}, on_press=self.going, background_color=(0, 0, 1, 1))
self.add_widget(self.goto)
def going(self, *args):
self.goto.background_color=(1,0,0,1)
self.manager.current = 'screen2'
class Application(App):
def build(self):
sm = ScreenManagement(transition=FadeTransition())
sm.add_widget(Screen1(name='screen1'))
sm.add_widget(Screen2(name='screen2'))
return sm
if __name__ == "__main__":
Application().run()
My question is this:
how do i change the text in screen1 when the retry button is pressed.
How do i return screen1 back to it initial state after the retry button is pressed so that the "next screen" button color changes back to blue
Once you have changed the current screen to screen1, then you can access that Screen as self.manager.current_screen, so your retrying() method can be:
def retrying(self, *args):
self.manager.current = 'screen1'
self.manager.current_screen.welc.text = "i am back to screen1, thanks you"
self.manager.current_screen.goto.background_color = background_color=(0, 0, 1, 1)
To set the screen1 back to its original state, you could write another method that just sets all the values back to the original value one by one. Or you could recreate screen1 by doing something like this in a method of your Application.:
def reset_screen1(self):
sm = self.root
scr1 = sm.get_screen('screen1`)
sm.remove_widget(scr1)
sm.add_widget(Screen1(name='screen1'))

How can I make my horizontal scroll view in Kivy centered on the screen?

I'm trying to make a horizontal scroll view in Kivy (Python) which would display an image (buttons for now) that you can scroll through. I've been able to figure out how to do the horizontal scroll view, but I just can't seem to figure out what I'm doing wrong to center on the screen. Here is what I have so far:
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
self.spacing=50
self.size_hint=(None, .5)
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
scroll = ScrollView( size_hint=(1,1), do_scroll_x=True, do_scroll_y=False )
grid = MyGrid()
for i in range(60):
grid.add_widget(Button(text='#00' + str(i),size=(100,100), size_hint=(1,1)))
scroll.add_widget(grid)
return scroll
if __name__ == '__main__':
ScrollViewApp().run()
I looked through the documentation and I think it has something to do with pos_hint: https://kivy.org/doc/stable/api-kivy.uix.widget.html?highlight=pos_hint#kivy.uix.widget.Widget.pos_hint
I tried adding:
self.pos={'center_y':1}
in my MyGrid() class, but nothing is being changed. No matter what value is I put after center_y, my the ScrollView contained in the grid still appears at the top of the screen, rather than centered halfway down. I've also tried adjusting things in the ScrollViewApp() class too, using
scroll.pos={'center_y':1}
as well. I can't seem to figure out what I'm doing wrong. Does anyone have any ideas on what I'm not understanding here? I feel like I'm so close!!!
Edit:
I'm trying to have the buttons centered, but also be able to scroll by dragging the black space around the buttons. In the picture below, you can see there is a bunch of black space beneath the buttons. I can click a button or this black space and scroll horizontally. I just need this to happen with the buttons centered!
The layout is behaving as you told it to.
You have a ScrollView as a root widget, and inside it you put your Grid that has size_hint_y = 0.5.
It takes half of the parent widget height, that takes the full window height.
One way to center your Grid is to put some spacers over and under it, but this is not possible, since ScrollView accepts onle one widget as content.
So, one solution is to..
let the Grid have the full height of the ScrollView
add the ScrollView inside another Layout (e.g. a BoxLayout)
and finally, add two widgets to force the ScrollView in the center.
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
self.spacing = 50
self.size_hint = (None, 1)
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
base = BoxLayout(orientation="vertical", size_hint=(1, 1))
base.add_widget(Widget(size_hint_y=.3))
scroll = ScrollView(size_hint=(1, .4), do_scroll_x=True, do_scroll_y=False)
grid = MyGrid()
for i in range(60):
grid.add_widget(
Button(text='#00' + str(i), size=(100, 100), size_hint=(1, 1)))
scroll.add_widget(grid)
base.add_widget(scroll)
base.add_widget(Widget(size_hint_y=.3))
return base
if __name__ == '__main__':
ScrollViewApp().run()
Another approach would be to..
put the ScrollView inside a FloatLayout
and position it using pos_hint
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
# self.spacing = 50
self.size_hint = (None, 1)
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
base = FloatLayout(size_hint=(1, 1))
scroll = ScrollView(size_hint=(1, .4), do_scroll_x=True, do_scroll_y=False,
pos_hint={"center_y": .5})
grid = MyGrid()
for i in range(60):
grid.add_widget(
Button(text='#00' + str(i), width=100, size_hint=(None, 1)))
scroll.add_widget(grid)
base.add_widget(scroll)
return base
if __name__ == '__main__':
ScrollViewApp().run()
Edit:
OK. After the update of the question, this is a way to to center and use the black bars to scroll:
Builder.load_string("""
<BaseWidget>:
ScrollView:
do_scroll_y: False
BoxLayout:
orientation: "vertical"
size_hint_x: None
width: self.minimum_width
Widget:
size_hint_y: .3
GridLayout:
id: grid
rows: 1
size_hint_y: .4
size_hint_x: None
width: self.minimum_width
Widget:
size_hint_y: .3
""")
class BaseWidget(FloatLayout):
pass
class ScrollViewApp(App):
def build(self):
base = BaseWidget()
for i in range(60):
base.ids.grid.add_widget(Button(text='#00' + str(i),
width=100, size_hint_x=None))
return base
if __name__ == '__main__':
ScrollViewApp().run()
It uses kv_lng to create the layout because I find it easier to the eye.
For a "Python only" code you can use this:
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.rows = 1
self.size_hint = None, .4
self.bind(minimum_width=self.setter('width'))
class MyBox(BoxLayout):
def __init__(self, **kwargs):
super(MyBox, self).__init__(**kwargs)
self.orientation = "vertical"
self.size_hint_x = None
self.bind(minimum_width=self.setter('width'))
class ScrollViewApp(App):
def build(self):
base = FloatLayout()
grid = MyGrid()
box = MyBox()
box.add_widget(Widget(size_hint_y=.3))
box.add_widget(grid)
box.add_widget(Widget(size_hint_y=.3))
for i in range(60):
grid.add_widget(Button(text='#00' + str(i), width=100, size_hint_x=None))
scroll = ScrollView(do_scroll_y=False, pos_hint={"center_y": .5})
scroll.add_widget(box)
base.add_widget(scroll)
return base
if __name__ == '__main__':
ScrollViewApp().run()

How can I change an attribute of a parents class instance

I'm trying to make a custom layout using kivy, I need to generate a gridlayout with scrollview buttons and put it inside of another layout.
Everytime I click the button that generates the buttons it pushes the "get links" button up and if I click it a second time instead of adding buttons to the existing gridlayout it creates a new gridlayout.
This is the app before I press the 'get links' button
The first time I press the 'get links' button
The second time the button is pressed
class RootWidget(BoxLayout):
pass
class Scrollbox(BoxLayout):
def __init__(self, **kwargs):
super(Scrollbox, self).__init__(**kwargs)
self.orientation = 'vertical'
class CustomLayout(FloatLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(CustomLayout, self).__init__(**kwargs)
with self.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 instead of 0-255
self.rect = Rectangle(size=self.size, pos=self.pos)
self.bind(size=self._update_rect, pos=self._update_rect)
def _update_rect(self, instance, value):
self.rect.pos = instance.pos
self.rect.size = instance.size
class MainApp(App):
link_buttons = 0
def build(self):
root = RootWidget()
c = CustomLayout()
s = Scrollbox()
root.add_widget(c)
root.add_widget(s)
def on_enter(self):
func = Function()
buttons = func.buttons()
s.add_widget(buttons)
get_buttons = Button(
text='Get links',
size_hint=(1, 0),
pos=(20, 20))
s.add_widget(get_buttons)
get_buttons.bind(on_press=on_enter)
return root
class Function(MainApp):
def buttons(self):
if self.link_buttons == 0:
layout = GridLayout(cols=1, padding=1, spacing=10,
size_hint=(None, None), width=10)
layout.bind(minimum_height=layout.setter('height'))
self.link_buttons += 1
for buttn in range(20):
btn = Button(text='test', size=(200, 50),
size_hint=(None, None))
try:
self.layout.add_widget(btn)
except:
layout.add_widget(btn)
# create a scroll view, with a size < size of the grid
root = ScrollView(size_hint=(None, None), size=(200, 400),
pos_hint={'center_x': .5, 'center_y': .5}, do_scroll_x=False)
root.add_widget(layout)
return root
if __name__=='__main__':
MainApp().run()
You have a few problems:
1) Function inherts from MainApp - don't do that - its weird!!
2) You are recreating the ScrollView on each click
Here is a modified (part of) the source code that worked for me
class MainApp(App):
link_buttons = 0
def build(self):
layout = GridLayout(cols=1, padding=1, spacing=10,
size_hint=(None, None), width=10)
layout.bind(minimum_height=layout.setter('height'))
sv = ScrollView(size_hint=(None, None), size=(200, 400),
pos_hint={'center_x': .5, 'center_y': .5}, do_scroll_x=False)
sv.add_widget(layout)
self.layout = layout
root = RootWidget()
c = CustomLayout()
s = Scrollbox()
root.add_widget(c)
root.add_widget(s)
s.add_widget(sv)
get_buttons = Button(
text='Get links',
size_hint=(1, 0),
pos=(20, 20))
s.add_widget(get_buttons)
get_buttons.bind(on_press=self.buttons)
return root
def buttons(self, btn):
layout = self.layout
self.link_buttons += 1
for buttn in range(20):
btn = Button(text='test', size=(200, 50),
size_hint=(None, None))
self.layout.add_widget(btn)

Categories