Kivy-Add custom widget definded in py file via kv file - python

I have a custom button. I cannot put it on screen via kv file. I studied a lot topics. No useful info can find. Here is a simple example:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<MyGrid>:
Button
text: 'hello'
''')
class MyGrid(BoxLayout):
pass
class DropApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
DropApp().run()
Note that the Button in this kv file is native Kivy button. I run this code, I can see this button on screen. But now I have a custom Button:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<MyGrid>:
customButton:
''')
class MyGrid(BoxLayout):
pass
class customButton(Button):
def __init__(self, **kwargs):
self.text = 'hi'
super(Button, self).__init__(**kwargs)
class DropApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
DropApp().run()
I run this code, I cannot see this customButton on screen. Note that the custom widget is complex. I have to define it in py file. For example:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class DropApp(App):
def build(self):
layout = GridLayout(cols=1, spacing=10)
for i in range(100):
btn = Button(text=str(i), size_hint_y=None, height=40)
layout.add_widget(btn)
return layout
if __name__ == '__main__':
DropApp().run()
Building this layout need for loop. I cannot find a way to build it in kv file. So I define it in Py file. But if I define it in py file, I cannot work with it in kv file.
Question 1: What's wrong with the second code?
Question 2: If I can't make it work, can I achieve the third code in kvlang(in kv file not in python)?
I am new to Kivy, hope someone can help.

When I run the code on Kivy v1.11.1 , Python v3.7.5:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class DropApp(App):
def build(self):
layout = GridLayout(cols=1, spacing=10)
for i in range(100):
btn = Button(text=str(i), size_hint_y=None, height=40)
layout.add_widget(btn)
return layout
if __name__ == '__main__':
DropApp().run()
The output is:
If this is the output you require, try running:
pip install --upgrade kivy
For the second code, python is case-sensitive. So instead of customButton, use CustomButton.
If you want to do the 3rd code using .kv file, here is an example:
from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.lang import Builder
Builder.load_string('''
<ExampleRV>:
viewclass: 'Button'
RecycleBoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class ExampleRV(RecycleView):
def __init__(self, **kwargs):
super(ExampleRV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(20)]
class RecycleApp(App):
def build(self):
return ExampleRV()
RecycleApp().run()
For reference, go HERE.

Related

Can't create only widget with kv variable in .py file

I always see people creating big structures in .kv files. Sometimes they write the kv in a variable in the .py file.
Here I'm trying to create a widget with kv language in .py file but it doesn't work.
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivymd.uix.textfield import MDTextFieldRect
class App(MDApp):
def build(self):
print("building app")
self.screen = Screen()
return self.screen
def on_start(self):
print("starting app")
kv = '''
MDTextFieldRect:
'''
e = Builder.load_string(kv)
self.screen.add_widget(e)
if __name__ == "__main__":
app = App()
app.run()
And I know that I can do it another way but I want to learn how to do it like this because I have troubles creating some widget with python when it works in kv language.
Here is a working example of how I could do it but I need to learn another way.
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivymd.uix.textfield import MDTextFieldRect
class App(MDApp):
def build(self):
print("building app")
self.screen = Screen()
return self.screen
def on_start(self):
print("starting app")
self.screen.add_widget(MDTextFieldRect())
if __name__ == "__main__":
app = App()
app.run()

Kivy: How to refernce kv ID in Python?

I'm new to Kivy and I would have to think this is possible, but I can't figure it out - How can I update a Kivy label when a button is pressed, but only by referencing that Kivy id within Python? (The reason I'm trying to do it this way is because in my actual application, I would like several labels to update at once, which I was hoping I could do all within the button_pressed equivalent button I have in my app).
In the simple example below, I'm just trying to have the button pressed and then have the label update to 'Updated!'
Thanks very much!
My Python code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
import random
class TestingWidget(BoxLayout):
# This is the kv id of the Label I would like to update
label_to_update = StringProperty('')
# This is the action I would like to happen when the button is pressed
def button_pressed(self):
label_to_update.text = 'Updated!'
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
My kv file:
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
Button:
text: 'test'
on_press: root.button_pressed()
Label:
id: label_to_update
text: 'Trying to get this to update'
You definitely update all label when you press the button. Just crate a StringProperty for each and do what you are doing now.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
from kivy.lang import Builder #used because I didn't want to create two files
import random
Builder.load_string('''
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
Button:
text: 'test'
on_press: root.button_pressed()
Label:
id: label_to_update
text: root.label_to_update
''')
class TestingWidget(BoxLayout):
# This is the kv id of the Label I would like to update
label_to_update = StringProperty('Trying to get this to update')
#default text set
# This is the action I would like to happen when the button is pressed
def button_pressed(self):
self.label_to_update = 'Updated!'
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()

Scrollview not scrolling button

I would like to create a scrollable Floatlayout with dynamically created buttons (kind of paint where I can scroll the drawing board). Unfortunately, the code that I come out doesn't work and the buttons don't move while scrolling the FloatLayout. How can I attach the button to the FloatLayout?
import kivy
kivy.require('1.0.7')
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
Builder.load_string('''
<Scroller>
view: glayout
FloatLayout:
id: glayout
width: 2000
height: 2000
size_hint: (None, None)
''')
class Main(App):
def build(self):
self.root=Scroller()
return self.root
class Scroller(ScrollView):
def __init__(self, **kwargs):
super(Scroller, self).__init__(**kwargs)
a = Button(size_hint=(None,None), width=200, height=200)
self.ids.glayout.add_widget(a)
a.bind(pos=self.ids.glayout.pos)
if __name__ in ('__main__'):
app = Main()
app.run()
The FloatLayout handle the size, not the position. The most simple solution is to replace your FloatLayout with a RelativeLayout, which handle size, and position is relative to the RelativeLayout origin.

Kivy Event on carousel when slide change

I don't have any idea to find a solution of my problem.
I have make an application with Carousel Widget. In this Carousel I have 4 slides.
welcomeSlide -> DVDSlide -> DVDPretSlide --> CategorySlide
I have make a class for each slides.
I use a ListAdpter to display the data extracted from an Sqlite3 Database.
My pblem is about the refresh of the list, when I modify a DVD (add name to pret) in the DVDSlide, when I slide to the DVDPret, the DVD do not appears because the List is not refresh.
Like the doccumentation for the carousel I don't find the event when slide change. It will be the best if an event exist to get the current slide index.
Have you got any Idea ?
Thanks,
You can observe index property:
from kivy.uix.carousel import Carousel
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
Builder.load_string('''
<Page>:
Label:
text: str(id(root))
<Carousel>
on_index: print("slide #{}".format(args[1]))
''')
class Page(BoxLayout):
pass
class TestApp(App):
def build(self):
root = Carousel()
for x in range(10):
root.add_widget(Page())
return root
if __name__ == '__main__':
TestApp().run()
Or you can observe current_slide property:
from kivy.uix.carousel import Carousel
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
Builder.load_string('''
<Page>:
label_id: label_id
Label:
id: label_id
text: str(id(root))
<Carousel>
on_current_slide: print(args[1].label_id.text)
''')
class Page(BoxLayout):
pass
class TestApp(App):
def build(self):
root = Carousel()
for x in range(10):
root.add_widget(Page())
return root
if __name__ == '__main__':
TestApp().run()
If you want a pure python solution rather than a Kivy Language solution you can create your own carousel that inherits from the Kivy carousel as follows.
import kivy
from kivy.uix.carousel import Carousel
class MyCarousel(Carousel):
# This class is a carousel that runs script
# when a slide gets focus (except first load).
def on_index(self, *args):
print('the slide is', self.index)
# YOUR CODE GOES HERE
Carousel.on_index(self, *args)

Python kivy text input

I'm a newbie at python, and now doing a dictionary with kivy. Issue is when I type text, it's not working. Below there I just want to check if it's working or not, so I put some popup, and if input text is 'a' then print true. It's just checking it's working or not, hope you guys help me, thank you.
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.bubble import Bubble
class CustomPopup(Popup):
pass
class Diction(GridLayout):
def __init__(self, **kwargs):
super(Diction, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text="Search"))
self.search=TextInput(multiline=False)
self.add_widget(self.search)
if self.search=='A':
print 'True'
else:
print 'False'
self.add_widget(Button(text="click",on_press=self.show_popup))
def show_popup(self, b):
p = CustomPopup()
p.open()
class MyApp(App):
def build(self):
return LoginScreen()
if __name__=="__main__":
MyApp().run()
There are two reasons why is not working:
The if should be in the method that handles the events, i.e. show_popup
You should compare the text in the Label, not the Label itself. Instead of self.search=='A', you should use self.search.text=='A'
Here is the corrected __init__ and show_popup code:
class Diction(GridLayout):
def __init__(self, **kwargs):
super(Diction, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text="Search"))
self.search=TextInput(multiline=False)
self.add_widget(self.search)
self.add_widget(Button(text="click",on_press=self.show_popup))
def show_popup(self, b):
if self.search.text=='A':
print 'True'
else:
print 'False'
p = CustomPopup()
p.open()
An alternative approach using the Kivy Language
The Kivy Language could help you to have a cleaner code. Your code could look like this:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
# DON'T forget to import Label!!!
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.lang import Builder
Builder.load_string("""
<CustomPopup#Popup>:
title: "My Custom Poput"
<Diction#GridLayout>:
cols: 2
search: id_search
Label:
text: "Search"
TextInput:
id: id_search
Button:
text: "click"
on_press: root.show_popup(self)
""")
class CustomPopup(Popup):
pass
class Diction(GridLayout):
def show_popup(self, b):
if self.search.text=='A':
print 'True'
else:
print 'False'
# You can send any content to the popup with the content attribute
CustomPopup(content=Label(text=self.search.text)).open()
class MyApp(App):
def build(self):
return Diction()
It helps to keep the logic separated from the interface. You can even keep in separate files if you use the load_file function instead of the load_string.

Categories

Resources