I would like to position my TextInput (i.e. the field) to the left of the but I cannot do this:
I recommend rearranging your gui in kv scrpit.
Replace
Label:
...
TextInput:
...
with
BoxLayout:
orientation:'horizontal'
Label:
...
TextInput:
...
Answering the second question with code sample and help link (https://www.geeksforgeeks.org/python-textinput-in-kivy-using-kv-file/)
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
class MyApp(App):
def build(self):
title = "Sample Layout not using kv file"
layout = BoxLayout(orientation='horizontal')
self.textinput = TextInput(text="")
self.textinput.bind(text=self.get_text)
self.btn1 = Button(text='Move text to label')
self.btn1.bind(on_press=self.get_text)
self.btn2 = Button(text='World')
self.lbl1 = Label(text="text goes here")
layout.add_widget(self.textinput)
layout.add_widget(self.btn1)
layout.add_widget(self.btn2)
layout.add_widget(self.lbl1)
return layout
def get_text(self, *args):
print(self.textinput.text)
self.lbl1.text = "You wrote " + self.textinput.text
if __name__ == '__main__':
MyApp().run()
Related
I'm making an app in python kivy, and I have 2 draggable Images in my "TacScreen" and whenever I double tap on either of them It opens up a popup, inside the popup I have a Textinput field where I can input text. Whatever text I put in there I want that to show inside a Label below my Draggable Image. The problem that I'm facing now is when I type anything in the text input field and press the close button (you can see it below in my code) It closes the popup and I don't see any text. How can I fix this? Below is my code!
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.clock import Clock
from functools import partial
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
from kivy.vector import Vector
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
class DragImage(DragBehavior, Image):
def on_touch_up(self, touch):
uid = self._get_uid()
if uid in touch.ud:
print(self.source, 'dropped at', touch.x, touch.y)
return super(DragImage, self).on_touch_up(touch)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and touch.is_double_tap:
print('this is a double tap')
self.layout = GridLayout(cols=1, row_force_default=True,
row_default_height=30,
spacing=80)
self.layout2 = GridLayout(cols=1,
row_force_default=True,
row_default_height=30,
spacing=80)
self.popuptextinput = TextInput(hint_text='Person Name',
multiline=False,
background_color='red',
halign='left',
cursor_blink=True,
cursor_color='white',
foreground_color='white',
font_size='14')
self.closebutton = Button(text="SAVE",
background_color="black",
font_size="14",
font_name='font1.otf')
self.output = Label(text='',)
self.layout.add_widget(self.popuptextinput)
self.layout.add_widget(self.closebutton)
self.layout2.add_widget(self.output)
self.popup = Popup(title='PERSON',
title_color="white",
separator_color="black",
title_align="center",
separator_height="0dp",
background_color="black",
title_font="font.otf",
title_size="12",
content=self.layout,
size_hint_y=.4,
size_hint_x=.5)
self.popup.open()
self.closebutton.bind(on_release=self.updttext)
return self.layout2
return super(DragImage, self).on_touch_down(touch)
def updttext(self, other):
self.output.text = self.popuptextinput.text
self.popup.dismiss()
class StartScreen(Screen):
pass
class TacScreen(Screen):
pass
class MyApp(App):
def build(self):
return Builder.load_string(kv)
MyApp().run()
my .kv file
kv = '''
<DragImage>:
drag_rectangle: self.center[0] - self.norm_image_size[0]/6,
self.center[1] -
self.norm_image_size[1]/6, \
self.norm_image_size[0]/3, self.norm_image_size[1]/3
drag_timeout: 10000000
drag_distance: 0
<TacScreen>:
#:import utils kivy.utils
DragImage
id: person1
pos: 0, 102
size_hint: 1, .1
source: "person1.png"
DragImage:
id: person2
pos: 50, 160
size_hint: 1, .1
source: "person2.png"
...
Im trying to add labels for each item in OneLineAvatarListItem, but item adding only for last OneLineListItem, can i do it using python only?
My code:
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivymd.uix.screen import Screen
from kivymd.uix.list import MDList, OneLineAvatarListItem
class MyApp(MDApp):
def build(self):
Window.clearcolor = (100, 100, 100, 1)
window = BoxLayout()
screen = Screen()
scroll = ScrollView()
list_view = MDList()
scroll.add_widget(list_view)
for i in range(10):
items = OneLineAvatarListItem(text=str(i))
label = Label(text='www', color=[.1, .1, .1, 1])
items.add_widget(label)
list_view.add_widget(items)
screen.add_widget(scroll)
window.add_widget(screen)
return window
MyApp().run()
Im getting that
Using example from documentation - CustomItem - I created custom ListItem with label which use ILeftBody to display on left side of standard text.
from kivymd.app import MDApp
from kivymd.uix.list import OneLineAvatarListItem, ILeftBody
from kivymd.uix.label import MDLabel
from kivy.lang import Builder
KV = '''
<MyItemList>:
LeftLabel:
id: left_label
BoxLayout:
ScrollView:
MDList:
id: scroll
'''
class MyItemList(OneLineAvatarListItem):
'''Custom list item.'''
class LeftLabel(ILeftBody, MDLabel):
'''Custom left container.'''
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(30):
item = MyItemList(text=f"Item {i}", on_release=self.on_click_item)
#print(item.ids)
item.ids.left_label.text = str(i)
self.root.ids.scroll.add_widget(item)
def on_click_item(self, widget):
print('text:', widget.text, 'left_label.text:', widget.ids.left_label.text)
MainApp().run()
Result:
I tried to add other label with IRightBody but it didn't work for me with OneLineAvatarListItem but work with OneLineAvatarIconListItem (with Icon in name).
from kivymd.app import MDApp
from kivymd.uix.list import OneLineAvatarIconListItem, ILeftBody, IRightBody
from kivymd.uix.label import MDLabel
from kivy.lang import Builder
KV = '''
<MyItemList>:
LeftLabel:
id: left_label
RightLabel:
id: right_label
BoxLayout:
ScrollView:
MDList:
id: scroll
'''
class MyItemList(OneLineAvatarIconListItem):
'''Custom list item.'''
class LeftLabel(ILeftBody, MDLabel):
'''Custom left container.'''
class RightLabel(IRightBody, MDLabel):
'''Custom right container.'''
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(30):
item = MyItemList(text=f"Item {i}", on_release=self.on_click_item)
#print(item.ids)
item.ids.left_label.text = str(i)
item.ids.right_label.text = str(100+i)
self.root.ids.scroll.add_widget(item)
def on_click_item(self, widget):
print('text:', widget.text, 'left_label.text:', widget.ids.left_label.text, 'right_label.text:', widget.ids.right_label.text)
MainApp().run()
Result:
For Label you can use ILeftBody/IRightBody without Touch and it will run function assigned to ListItem. But if you want to add Button, CheckButton and assign function to this widget then it may need ILeftBodyTouch/IRightBodyTouch without Touch
from kivymd.app import MDApp
from kivymd.uix.list import OneLineAvatarIconListItem, ILeftBody, IRightBodyTouch
from kivymd.uix.label import MDLabel
from kivymd.uix.button import MDTextButton
from kivy.lang import Builder
KV = '''
<MyItemList>:
LeftLabel:
id: left_label
RightButton:
id: right_button
# on_release: app.on_click_right_button(self)
BoxLayout:
ScrollView:
MDList:
id: scroll
'''
class MyItemList(OneLineAvatarIconListItem):
'''Custom list item.'''
class LeftLabel(ILeftBody, MDLabel):
'''Custom left container.'''
class RightButton(IRightBodyTouch, MDTextButton):
'''Custom right container.'''
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(30):
item = MyItemList(text=f'Item {i}', on_release=self.on_click_item)
#print(item.ids)
item.ids.left_label.text = str(i)
item.ids.right_button.text = f'More {i}'
item.ids.right_button.on_release = lambda widget=item.ids.right_button:self.on_click_right_button(widget) # it needs `widget=...` because created in `for`-loop
self.root.ids.scroll.add_widget(item)
def on_click_item(self, widget):
print('--- on_click_item ---')
print('wdiget.text:', widget.text, 'left_label.text:', widget.ids.left_label.text, 'right_button.text:', widget.ids.right_button.text)
def on_click_right_button(self, widget):
print('--- on_click_right_button ---')
print('wdiget.text:', widget.text)
print('widget.parent.parent:', widget.parent.parent)
print('widget.parent.parent.text:', widget.parent.parent.text)
MainApp().run()
I am just trying to get code working where I have two screens in a Python Kivy app that can switch back and forth, without using the .kv file stuff.
On this page: https://kivy.org/docs/api-kivy.uix.screenmanager.html, the second block of code from the top is what I am trying to accomplish, except I want to do it without the 'Builder.load_string("""' section, and instead just instantiate buttons normally.
Here is my attempt at doing so, except I can't get it to work:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class MenuScreen(Screen):
def build(self):
def switchScreen():
root.manager.current = 'settings'
f = FloatLayout()
button1 = Button(text = "My settings button")
button2 = Button(text = "Back to menu", on_press = switchScreen)
f.add_widget(button1)
f.add_widget(button2)
class SettingsScreen(Screen):
def build(self):
def switchScreen():
root.manager.current = 'menu'
f = FloatLayout()
button1 = Button(text = "My settings button")
button2 = Button(text = "Back to menu", on_press = switchScreen)
f.add_widget(button1)
f.add_widget(button2)
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class MainApp(App):
def build(self):
return sm
if __name__ == '__main__':
MainApp().run()
Running this code just creates a blank page that produces no errors.
Is there a way to designate it to draw a certain screen to begin with that I am missing? I'm not really sure where my issue is.
What you did wrong:
If you want to create Widget content from Python code you should place it inside Widget __init__ method, not build
You're creating a layout and then discarding it. You need to use self.add_widget(f) to actually use it after its creation
You're binding to your switchScreen method, so it needs to accept caller widget as an argument. Or you can simply use *args and not worry about it.
You're not in kv anymore, so there's no root. Use self instead.
Putting this all together:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class MenuScreen(Screen):
def __init__(self, **kwargs):
super(MenuScreen, self).__init__(**kwargs)
def switchScreen(*args):
self.manager.current = 'settings'
f = FloatLayout()
button1 = Button(text = "My settings button")
button2 = Button(text = "Back to menu", on_press = switchScreen)
f.add_widget(button1)
f.add_widget(button2)
self.add_widget(f)
class SettingsScreen(Screen):
def __init__(self, **kwargs):
super(SettingsScreen, self).__init__(**kwargs)
def switchScreen(*args):
self.manager.current = 'menu'
f = FloatLayout()
button1 = Button(text = "My settings button")
button2 = Button(text = "Back to menu", on_press = switchScreen)
f.add_widget(button1)
f.add_widget(button2)
self.add_widget(f)
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class MainApp(App):
def build(self):
return sm
if __name__ == '__main__':
MainApp().run()
I'm working in an app with kivy and I have an issue that involve the GridLayout. I have a screen with different rows and I want the buttons of the last row to have always the same height (11,1% of the height of the Screen). I have tried to modify the attribute height in the buttons but doesn't work properly. With size_hint_y works fine , but the fact is that i want to do with height because the screen won't have always the same number of rows (is responsive and it depends of the selections of previous screens). I attach here the code that I've done with the attribute height calculated through the command Window.height/9:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text='Subject'))
self.add_widget(Label(text=''))
self.add_widget(Label(text='1'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='2'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='3'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='4'))
self.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],height=int(Window.height)/9.0) #doesn't work properly
self.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],height=int(Window.height)/9.0) #doesn't work properly
self.add_widget(b2)
b1.bind(on_press=exit)
class SimpleKivy(App):
def build(self):
return LoginScreen()
if __name__=='__main__':
SimpleKivy().run()
I know it could be done with kivy language in a easier way but for my app is better to do in this way. If anyone knows how to fix this problem I would be very grateful.
If you want a widget in a grid/box layout to have a fixed size, you should set its size_hint to None first. And always use kivy lang at such tasks - no exceptions.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
gui = '''
LoginScreen:
GridLayout:
cols: 2
Label:
text: 'Subject'
Label:
Label:
text: '1'
SingleLineTextInput:
Label:
text: '2'
SingleLineTextInput:
Label:
text: '3'
SingleLineTextInput:
Label:
text: '4'
SingleLineTextInput:
GreenButton:
text: 'Exit'
on_press: app.stop()
GreenButton:
text: 'Run'
<SingleLineTextInput#TextInput>:
multiline: False
<GreenButton#Button>:
background_color: 0, 1, 0, 1
size_hint_y: None
height: self.parent.height * 0.111
'''
class LoginScreen(Screen):
pass
class SimpleKivy(App):
def build(self):
return Builder.load_string(gui)
if __name__ == '__main__':
SimpleKivy().run()
Try this
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text='Subject'))
self.add_widget(Label(text=''))
self.add_widget(Label(text='1'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='2'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='3'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='4'))
self.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.add_widget(b2)
b1.bind(on_press=exit)
Edited to change it to be at 11%.
And here one that keeps the button at 11% in response to the window size, where you redraw the Grid Layer whenever the window is resized (as by the bind to 'on_resize').
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
#init and add grid layer
self.cols=2
self.layout = GridLayout(cols=self.cols)
self.add_widget(self.layout)
#function to set the buttons based on the current window size
self.set_content(Window.width, Window.height)
#bind above function to get called whenever the window resizes
Window.bind(on_resize=self.set_content)
def set_content(self, width, height, *args):
#first remove the old sized grid layer
self.remove_widget(self.layout)
#now build a new grid layer with the current size
self.layout =GridLayout(cols=self.cols)
self.layout.add_widget(Label(text='Subject'))
self.layout.add_widget(Label(text=''))
self.layout.add_widget(Label(text='1'))
self.layout.add_widget(TextInput(multiline=False))
self.layout.add_widget(Label(text='2'))
self.layout.add_widget(TextInput(multiline=False))
self.layout.add_widget(Label(text='3'))
self.layout.add_widget(TextInput(multiline=False))
self.layout.add_widget(Label(text='4'))
self.layout.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.layout.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],size_hint_y=None, height=int(Window.height)/8.9)
self.layout.add_widget(b2)
b1.bind(on_press=exit)
#add the newly sized layer
self.add_widget(self.layout)
class SimpleKivy(App):
def build(self):
return LoginScreen()
if __name__=='__main__':
SimpleKivy().run()
Trying to populate the text of three buttons with the content of myList["One", "Two", "Three"]
btn1 = myList[1] etc
myList will be populated fron csv file
from kivy.app import App
from kivy.lang import Builder
kv = """
BoxLayout:
Button:
id: btn1
Button:
id: btn2
Button:
id: btn3
"""
class TestApp(App):
def build(self):
my_box = Builder.load_string(kv)
my_ShowList = ['My Way', 'Wine Drinker', 'Boots']
'''
This is where I get lost
want to use for loop to populate Button text with my_ShowList items
'''
return my_box
if __name__ == '__main__':
TestApp().run()enter code here`
You can do it entirely in python code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class TestClass(App):
def build(self):
my_box = BoxLayout()
my_show_list = ["My Way", "Wine Drinker", "Boots"]
my_box.my_buttons = [] # if you want to keep an "easy" reference to your buttons to do something with them later
#kivy doesnt crashes because it creates the property automatically
for message in my_show_list:
button = Button(text=message)
my_box.my_buttons.append(button)
my_box.add_widget(button)
return my_box
if __name__ == "__main__":
TestClass().run()
This should solve this problem:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class MyLayout(BoxLayout):
def __init__(self, **kwargs):
super(MyLayout, self).__init__(**kwargs)
self.btn_list = ['My Way', 'Wine Drinker', 'Boots']
for btn_text in self.btn_list:
self.add_widget(Button(text=btn_text))
class MyApp(App):
def build(self):
return MyLayout()
if __name__ == '__main__':
MyApp().run()