I want to use a StackLayout but it only display one button and not two as I expected. The code is:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
Builder.load_string("""
<ScreenUI>:
orientation: 'lr-bt'
Button:
text: 'Button 1'
Button:
text: 'Button 2'
""")
class ScreenUI(StackLayout):
pass
class WidgetApp(App):
def build(self):
app = ScreenUI()
return app
if __name__ == '__main__':
WidgetApp().run()
How do I use a StackLayout and add a List of Button?
Update (to includes comments): To obtain the resize behaviour I used
Builder.load_string("""
<ScreenUI>:
input1: input1
button1: button1
height: self.input1.height
spacing: 5
orientation: 'horizontal'
id: layout1
Label:
text: 'Button 1'
id: button1
size: len(self.text) * root.input1.font_size, 2 * root.input1.font_size
size_hint: None, None
TextInput:
id: input1
text: 'Button 2'
size: root.width - root.button1.width - root.spacing, 2 * self.font_size
size_hint: None, None
""")
Does a more elegant method exists?
It looks like this is a size_hint problem. The buttons have no size specified, so the StackLayout automatically sizes them with their size hints. However, its layout doesn't depend on any sensible way of doing that, so it just makes them 100% of its own size...meaning only one fits in the window. That's the one you see.
Here's a slightly modified version of your example where sizes are specified. It works fine for me, with two differently sized buttons placed in the bottom left:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.textinput import TextInput
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
Builder.load_string("""
<ScreenUI>:
orientation: 'lr-bt'
Button:
text: 'Button 1'
size: 100, 100
size_hint: None, None
Button:
text: 'Button 2'
size_hint: None, None
size: 200, 100""")
class ScreenUI(StackLayout):
pass
class WidgetApp(App):
def build(self):
app = ScreenUI()
return app
if __name__ == '__main__':
WidgetApp().run()
In general, the way to fix this is to give them a sensible size hint for whatever your particular goal is, or to set their sizes manually as I did. If you want a certain number to be sized properly to fill a space or similar, a GridLayout or BoxLayout may be more suitable.
Related
I am trying to add buttons dynamically when some text is typed on a text input. In order to achieve search select combo box in KIvy.
Even though i am able to add widgets when some text is typed , not able to remove those added widgets when the text typed is removed.
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix import dropdown
from kivy.uix.textinput import TextInput
# from kivymd.app import MDApp
kivy.require('1.9.0')
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.base import runTouchApp
from kivy.uix.screenmanager import ScreenManager,Screen
class ScreenOne(Screen):
def change_text(self,root,val):
pass
def create_dropdown(self,root,value):
print("value is :" + value)
if value == "":
result = ""
print(root.ids)
else:
lang = ["aa","aaa", "bsa","cds","dds", "ddyus"]
result = list(filter(lambda x: x.startswith(value), lang))
for i in range (0,len(result)):
self.textinput = Button()
self.textinput.id = 'textinput'+ str(i)
self.textinput.text = result[i]
self.textinput.size_hint= (.3, .06)
self.textinput.font_size= '14sp'
self.textinput.pos_hint= {'center_x': 0.5, 'center_y': 0.8-(i+1)/10, }
root.ids.screen1.add_widget(self.textinput)
sm = Builder.load_string("""
ScreenManager:
ScreenOne:
id: screen1
name: "screen_one"
TextInput:
id: input1
hint_text:'Select input Language'
size_hint: (.3,.06)
font_size: '14sp'
pos_hint:{'center_x':0.5,'center_y':0.8,}
on_text: root.ids.screen1.create_dropdown(root,input1.text)
""")
class demo(App):
def build(self):
return sm
app=demo()
# app.run()
runTouchApp(sm)
in UI when a is typed cahn see the below image
but when i clear the input text , i am still able to see the widgets added previously
I neet some logic to remove the added widgets when the text typed in input box is cleared.
Try adding this:
result = list(filter(lambda x: x.startswith(value), #from your cod
lang)) #from your code
if result == [] :
for widget in root.ids.screen1.children:
root.ids.screen1.remove_widget(widget)
I've been trying to use scrollview in a screen, that will be used as part of an app and I managed to find the following code that creates an app with scrollview. But I'm not able to change it into screen class.
Here is the python code:
from kivy.app import App
from kivy.uix.button import Button
class ScrollButton(Button):
pass
class TestApp(App):
def build(self):
super(TestApp, self).build()
container = self.root.ids.container
for i in range(30):
container.add_widget(ScrollButton(text=str(i)))
return self.root
if __name__ == '__main__':
TestApp().run()
.kv file
ScreenManager:
Screen:
ScrollView:
size_hint: None, None
size: 600, 320
pos_hint: {'center_x': .5, 'center_y': .5}
GridLayout:
cols: 1
padding: 10
spacing: 10
height: self.minimum_height
size_hint: None, None
do_scroll_x: False
id: container
<ScrollButton>
size_hint: None, None
size: 600, 40
I would appreciate if anybody could show how to do it/give me some directions on how to change this to fit my purpose. Any alternative way of doing this is also welcome.
Many people recommend making the ScreenManager in the build method in the python file instead, here's how to do that:
Python
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
Builder.load_file("styling.kv")
class ScrollButton(Button):
pass
class MainScreen(Screen):
def on_kv_post(self, instance):
container = self.ids['container']
for i in range(30):
container.add_widget(ScrollButton(text=str(i)))
class TestApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainScreen())
return sm
if __name__ == '__main__':
TestApp().run()
Kivy
<ScrollButton>:
size_hint: None, None
size: 600, 40
<MainScreen>:
name: 'MainScreen'
ScrollView:
size_hint: None, None
size: 600, 320
pos_hint: {'center_x': .5, 'center_y': .5}
GridLayout:
cols: 1
padding: 10
spacing: 10
height: self.minimum_height
size_hint: None, None
do_scroll_x: False
id: container
Changes made
ScreenManager made in .py instead of .kv
Created Screen class in .py, so we can use python methods from the Screen
Widget adding moved to the Screen's class, using on_kv_post (this method is fired only one time when the Screen is ready)
Made the screen a template class instead of an object in .kv which the Screen in the python file uses to make the final Screen
PS: This is how it's usually done
I want to create a simple app that has GUI with a button that allows me to vibrate an android phone. Im using a .kv file for the layout and the Builder in my .yp file
.py file:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from plyer import vibrator
class AndroidApp(GridLayout):
def vibrate(self):
vibrator.vibrate()
kv = Builder.load_file("android_app.kv")
class MainApp(App):
def build(self):
return kv
if __name__ == "__main__":
MainApp().run()
.kv file:
#:import utils kivy.utils
<AndroidApp>:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
cols: 2
Label:
text:"Android Vibrate"
Button:
text:"Android Vibrate"
on_press:
root.vibrate()
When I try to run the app, I get the following error:
enter image description here
This should be straightforward app but somehow I find a way to make it crash. The android_app.py & .kv files are in the same folder. Any ideas why the window wont be created? I aprreciate any help.
Thanks,
Alex
You have to return the main class, not the result from building. Here I'm using Builder.load_string() for convenience, and not importing player, but otherwise the only substantive change is that I changed return kv to return AndroidApp()
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
KV = '''
#:import utils kivy.utils
<AndroidApp>:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex("#ffffff")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
cols: 2
Label:
text:"Android Vibrate"
Button:
text:"Android Vibrate"
on_press:
root.vibrate()
'''
class AndroidApp(GridLayout):
def vibrate(self):
vibrator.vibrate()
Builder.load_string(KV)
class MainApp(App):
def build(self):
return AndroidApp()
if __name__ == "__main__":
MainApp().run()
What's the preferred way to combine an image/icon and text within a button? For example, how would you create a button with text = 'my button', and a graphical icon to the left of that text?
Regarding to question #2.
The way Kivy works is embedding Widget instances. Since Image and Button are subclasses of Widget, then all you have to do is embed an Image inside a the Button. Notice that the positioning inside a widget is fixed. You have to give explicit coordinates.
That said, you can always embed a Layout to organize the stuff you are putting inside the Button.
Here is the simple ex
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<ButtonsApp>:
orientation: "vertical"
Button:
text: "B1"
Image:
source: 'kivy.png'
y: self.parent.y + self.parent.height - 200
x: self.parent.x
Label:
text: "A label"
""")
class ButtonsApp(App, BoxLayout):
def build(self):
return self
if __name__ == "__main__":
ButtonsApp().run()
EDIT: An example of how a relative layout can be embedded inside a button
In this case I am using a StackLayout to organize an Image and a Label inside. As I said, Button is a Widget and Kivy works embedding widgets inside widgets. It doesn't matter if they are labels, buttons or layouts.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<ButtonsApp>:
orientation: "vertical"
Button:
StackLayout:
pos: self.parent.pos
size: self.parent.size
orientation: 'lr-tb'
Image:
source: 'kivy.png'
size_hint_x: None
width: 74
Label:
size_hint_x: None
width: 100
text: "The text"
Label:
text: "A label"
""")
class ButtonsApp(App, BoxLayout):
def build(self):
return self
if __name__ == "__main__":
ButtonsApp().run()
Meanwhile, there is another way.
You can use icon fonts such as Font Awesome and combine them with text.
Either import the font directly and wrap the text in their font tag, or simply use some of the libraries that take care of that.
#: import icon ...
Button:
markup: True
text: "%s Comment" % icon('comment', 32)
size_hint_x: None
width: 100
Kivy-iconfonts converts css/tff combinations that are distributed for web sites into a json format that it loads during runtime using the import statement as shown in the example above.
I extended this in my fork to fetch Font Awesome icons during runtime and put them into the working directory of your application. That gives you the advantage of not having to distribute the fonts with the application.
Define a new Button class or modify the one in Kivy.uix.button
class MyButton(Button):
#add these three properties in the class
icon = ObjectProperty(None)
icon_size = (0,0)
icon_padding = NumericProperty(0) #Enter any default value like 50 if you will
#always use an icon, or specify this field
#while creating the button
def __init__(self, **kwargs):
#no changes here, just for reference
return super(MyButton, self).__init__(**kwargs)
KV file:
<MyButton>:
state_image: self.background_normal if self.state == 'normal' else self.background_down
disabled_image: self.background_disabled_normal if self.state == 'normal' else self.background_disabled_down
canvas:
Color:
rgba: self.background_color
BorderImage:
border: self.border
pos: self.pos
size: self.size
source: self.disabled_image if self.disabled else self.state_image
Color:
rgba: (1, 1, 1, 1) if root.icon != None else (1,1,1,0)
Rectangle:
source: root.icon
size: (self.texture_size[1],self.texture_size[1]) if self.icon_size == (0,0) else self.icon_size
pos: int(self.center_x - self.texture_size[0] / 2.)-dp(root.icon_padding), int(self.center_y - self.texture_size[1] / 2.)
Color:
rgba: 1, 1, 1, 1
Rectangle:
texture: self.texture
size: self.texture_size
pos: int(self.center_x - self.texture_size[0] / 2.)+dp(root.icon_padding), int(self.center_y - self.texture_size[1] / 2.)
Now just create a button widget
MyButton(text = 'Hello',icon = 'icon.png', icon_padding = 50)
How to create simple Kivy app?
If user type text into field "Name" using keyboard like on android
phone, this name is display
I need this to learn
Here's as simple an example as i can figure out.
Here's the KVLANG code.
<LblTxt#BoxLayout>:
orientation: 'horizontal'
lblTxtIn: 'default'
theTxt: iAmTxt
Label:
text: root.lblTxtIn
TextInput:
id: iAmTxt
text: 'txt'
<MyLayout#BoxLayout>:
orientation: 'vertical'
LblTxt:
id: lt0
lblTxtIn: 'LblTxtInput0'
LblTxt:
id: lt1
lblTxtIn: 'LblTxtInput1'
LblTxt:
id: lt2
lblTxtIn: 'LblTxtInput2'
Button:
text: 'print LblTxtInput [0, 1, 2]'
on_release: print lt0.theTxt.text, lt1.theTxt.text, lt2.theTxt.text
MyLayout
Here's the Python code.
import kivy
kivy.require('1.8.0') # replace with your current kivy version !
from kivy.app import App
from kivy.lang import Builder
from kivy.config import Config
from kivy.core.window import Window
Window.size = (400,130)
from kivy.uix.boxlayout import BoxLayout
class LblTxt(BoxLayout):
from kivy.properties import ObjectProperty
theTxt = ObjectProperty(None)
class MyApp(App):
def build(self):
self.root = Builder.load_file('simpleForm.kv')
return self.root
if __name__ == '__main__':
MyApp().run()
Here's a run screenshot. It will print a b c to the command line when the 'print LblTxtInput [0, 1, 2]' button is released.
I hope this helps you out.