I'm currently working to update a bunch of labels with a button press, which I was able to get an answer with through Kivy: How to refernce kv ID in Python?
However, now that I'm in my actual app, I need the functionality to be able to take a user input value and update existing labels. I've modified the example slightly where I'm just taking a user input for a starting number, adding one with each button click and displaying the running total to the right of all of this.
The code I have now is shown below, but the addition of 1 doesn't seem to be processing (it's just staying with the same total after a click). Any ideas why? Thanks very much!
This is the display before the button is clicked (running total is just the user input):
My Python file:
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
starting_number = StringProperty('Put your Starting Number here')
running_total = StringProperty(str(0))
#default text set
# This is the action I would like to happen when the button is pressed
def button_pressed(self):
self.running_total = str(int(self.running_total) + 1)
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
My kv file:
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
TextInput:
id: starting_number
hint_text: root.starting_number
Button:
id: add_one_button
text: 'Add 1 to Starting Number'
on_press: root.button_pressed()
Label:
id: running_total
text: starting_number.text
the problem is that Label is bound with TextInput, and if you change Label as you are doing it the TextInput updates it again giving the feeling that it does not change. What you should do is change the text with the on_text event as shown below:
*.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
class TestingWidget(BoxLayout):
starting_number = StringProperty("")
running_total = StringProperty(str(0))
def button_pressed(self):
if self.running_total != "":
self.running_total = str(int(self.running_total) + 1)
def text_changed(self, instance, value):
self.running_total = value
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
*.kv
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
TextInput:
id: starting_number
hint_text: "Put your Starting Number here"
on_text: root.text_changed(*args)
text: root.starting_number
Button:
id: add_one_button
text: 'Add 1 to Starting Number'
on_press: root.button_pressed()
Label:
id: running_total
text: root.running_total
The solution is to only change your kv File and no changes required to your Python Script. The changes are as follow:
kv File
1. TextInput: Add on_text Event
The textinput’s text is stored in its TextInput.text property. To run a callback when the text changes, do the following.
on_text:
root.running_total = self.text
2. Label: Reference StringProperty, root.running_total
Replace:
text: starting_number.text
with:
text: root.running_total
testbutton.kv
#:kivy 1.10.0
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
TextInput:
id: starting_number
hint_text: root.starting_number
on_text:
root.running_total = self.text
Button:
id: add_one_button
text: 'Add 1 to Starting Number'
on_press: root.button_pressed()
Label:
id: running_total
text: root.running_total
Output
Related
Sorry my english.
How can I send some text from popup to label in another class which is inherited from the Screen class?
I tried different options for accessing this object, but nothing happens.
It looks like text sends to another object, because these objects has different memory adresses. I checked it.
Comment indicating the problem is in the code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.popup import Popup
Builder.load_string("""
#:import Factory kivy.factory.Factory
<Keyboard>:
text_input: text_input
BoxLayout:
orientation: 'vertical'
TextInput:
id: text_input
Button:
text: 'Send text'
on_release: root.send()
<Container>:
label: label
BoxLayout:
orientation: 'vertical'
Label:
id: label
text: 'Here must some be text from popup'
Button:
text: 'My popup'
on_release: Factory.Keyboard().open()
Button:
text: 'Goto options'
on_press: root.manager.current = 'Options'
<Options>:
BoxLayout:
Button:
text: 'Back to time'
on_press: root.manager.current = 'Time'
""")
class Options(Screen):
def show_kv(self, instance, value):
self.manager.current = value
class Container(Screen):
pass
class Keyboard(Popup):
def send(self):
try:
time = self.text_input.text
except:
time = ''
# Here is some problem
Container().label.text = time # Nothing happens
self.dismiss()
class KivyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(Container(name='Time'))
sm.add_widget(Options(name='Options'))
sm.current = "Time"
return sm
if __name__ == '__main__':
KivyApp().run()
The line:
Container().label.text = time
is creating a new instance of Container, and sets the text of a Label in that new instance. However, that instance of Container is not the one that appears in your GUI. You must access the instance that is actually in your GUI. You can do that using the get_screen() method of the ScreenManager that is the root of your GUI. Like this:
container_instance = App.get_running_app().root.get_screen('Time')
container_instance.label.text = time
Hy! I have this app here, a kind of "To Do List". When I press "Add Item" Button, it opens a PopUp Window and I want when "Up" Button is pressed to add "ItemTemplate" in "ItemsList". I am new in Kivy and I tried do that in last few days. How can I do that?
Thanks a lot!
Python code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
class FirstBox(BoxLayout):
def btn(self):
Pu().open()
class ItemsList(GridLayout):
pass
class ItemTemplate(BoxLayout):
pass
class Pu(Popup):
pass
class MyAppApp(App):
pass
MyAppApp().run()
Kv code:
FirstBox:
<FirstBox>:
orientation: "vertical"
BoxLayout:
ScrollView:
ItemsList:
size_hint: 1, None
height: self.minimum_height
Button:
size_hint: 1,0.2
text: "Add Item"
on_release: root.btn()
<ItemsList>:
cols: 1
size_hint_y: None
ItemTemplate:
ItemTemplate:
ItemTemplate:
<ItemTemplate>:
size_hint: 1, None
CheckBox:
size_hint: 0.15, 1
Button:
text: "Task Name"
Button:
text: "Some action"
size_hint: 0.15,1
<Pu>:
size_hint: 1, 0.3
BoxLayout:
orientation: "vertical"
TextInput:
Button:
text: "Up"
Give id to your ItemList for add widget and TextInput for get text from.
ItemsList:
id: viewlist
and
TextInput:
id: new_task_input_id
Use StringProperty for dinamicly change text of ItemTemplate:
from kivy.properties import StringProperty
class ItemTemplate(BoxLayout):
task_text = StringProperty()
Edit .kv side dinamic: ( ItemTemplate's Button )
Button:
text: root.task_text
Trigger Up Button in .kv side: on_release: root.add_item()
Create this function in Pu class and add this operations:
def add_item(self,*args):
firstbox = App.get_running_app().root #access correctly main layout
itemlist = firstbox.ids.viewlist #find viewlist by id
new_task_text = self.ids.new_task_input_id.text #get textinput text
item = ItemTemplate() #create custom item
item.task_text = new_task_text #set item text
itemlist.add_widget(item) #add item to layout
Short function:
App.get_running_app().root.ids.viewlist.add_widget(ItemTemplate(task_text=self.ids.new_task_input_id.text))
Lets close popup after add new item:
self.dismiss()
Also if you want to add this widget to top, need to give index in add_widget :
itemlist.add_widget(item,len(itemlist.children))
I'm trying to make a program that puts x amount of TextInputs based on the user's input. I can do that in the py file, but how do I do this in the kv file? I also need to be able to reference these TextInputs later on.
This is what I have so far:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.textinput import TextInput
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [TextInput(multiline=False) for i in range(int(self.count_input.text))]
for text_input in text_inputs:
self.manager.ids.get_input.ids.grid.add_widget(text_input)
# Switch to next screen
self.manager.current = "get_input"
class GetInput(Screen):
pass
kv_file = Builder.load_string("""
ScreenManager:
GetCount:
name: "get_count"
id: get_count
GetInput:
name: "get_input"
id: get_input
<GetCount>:
count_input: count_input
FloatLayout:
Label:
text: "count"
size_hint: 1, 0.05
pos_hint: {"top":0.9}
TextInput:
id: count_input
size_hint: 0.8, 0.05
pos_hint: {"top":0.7, "x":0.1}
multiline: False
Button:
text: "Next"
on_release: root.next()
size_hint: 0.8, 0.05
pos_hint: {"top":0.5, "x":0.1}
<GetInput>:
FloatLayout:
GridLayout:
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
The problem is, the TextInputs are being added and created by the py code, how can I create the widget in kv and then add it in py?
I'm beginner in kivy module. I want to put 8 textboxes in screen to get input from user and then, save this inputs in a list in order to use them later!
I searched in the internet but a didn't find any thing useful.
I think I should do sth like this code:
Save text input to a variable in a kivy app
But don't want to show the inputs in shell, I wanna save them in a list!
Py file
Use a for loop to traverse through a container of all widgets e.g. TextInput.
Snippets
for child in reversed(self.container.children):
if isinstance(child, TextInput):
self.data_list.append(child.text)
kv file
Use a container e.g. GridLayout
Add an id for the container
Add all those Label and TextInput widgets as child of GridLayout
Snippets
GridLayout:
id: container
cols: 2
Label:
text: "Last Name:"
TextInput:
id: last_name
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty, ListProperty
from kivy.lang import Builder
Builder.load_file('main.kv')
class MyScreen(Screen):
container = ObjectProperty(None)
data_list = ListProperty([])
def save_data(self):
for child in reversed(self.container.children):
if isinstance(child, TextInput):
self.data_list.append(child.text)
print(self.data_list)
class TestApp(App):
def build(self):
return MyScreen()
if __name__ == "__main__":
TestApp().run()
main.kv
#:kivy 1.11.0
<MyScreen>:
container: container
BoxLayout:
orientation: 'vertical'
GridLayout:
id: container
cols: 2
row_force_default: True
row_default_height: 30
col_force_default: True
col_default_width: dp(100)
Label:
text: "Last Name:"
TextInput:
id: last_name
Label:
text: "First Name:"
TextInput:
id: first_name
Label:
text: "Age:"
TextInput:
id: age
Label:
text: "City:"
TextInput:
id: city
Label:
text: "Country:"
TextInput:
id: country
Button:
text: "Save Data"
size_hint_y: None
height: '48dp'
on_release: root.save_data()
Output
You need to give your text inputs ids, then reference the id of them and get their text using .text. self.root in the TestApp class refers to the root widget of your kv file, which is the one that doesn't have brackets (< >) around it, in this case the GridLayout.
main.py
from kivy.app import App
class MainApp(App):
def get_text_inputs(self):
my_list = [self.root.ids.first_input_id.text, self.root.ids.second_input_id.text]
print(my_list)
pass
MainApp().run()
main.kv
GridLayout:
cols: 1
TextInput:
id: first_input_id
TextInput:
id: second_input_id
Button:
text: "Get the inputs"
on_release:
app.get_text_inputs()
I am attempting to dynamically add and remove widgets from a GridLayout with Python-Kivy, and I am running into an issue with weak references. When the Screen containing the GridLayout is initialized, I am placing a Label inside of the GridLayout that contains text notifying the user that the container has no items (technically, it has the Label in there so it is not necessarily empty). Then I have a Button that allows the user to add individual GridLayout widgets to the GridLayout that contain a Label, a TextInput and a CheckBox, as well as a Button that allows the user to remove those individual GridLayout widgets that they created. If all of these widgets (dynamically added by the user) are removed, then the original Label is added back to the GridLayout.
When I attempt to construct this logic in Python to pair with Kivy, I run into an issue where the original Label never seems to be fully removed from the stack.
I was under the impression that self.ids.widget_list.remove_widget(self.ids.empty) would remove the Label widget with id empty, however this is not the case. It is clear that the widget still exists, when I call print(self.ids):
{'widget_list': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x0451F030>>, 'empty': <WeakProxy to <kivy.uix.gridlayout.GridLayout object at 0x0451F9D0>>}
Any help is much appreciated.
EDIT
When checking for i in self.layouts: print(i.children) every time the remove() method is called, shows that the references to the added widgets are never being completely removed. This may be where my issues resides, but unsure how to resolve it.
Python
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
#Load kv file
Builder.load_file('test.kv')
#First Screen
class Screen1(Screen):
layouts = []
def remove(self):
for i in self.layouts:
if i.children[0].active:
self.ids.widget_list.remove_widget(i)
if len(self.layouts)==0:
layout = GridLayout(rows=1, id=empty)
layout.add_widget(Label(text='Nothing Here'))
self.ids.widget_list.add_widget(layout)
else:
self.update_hints()
def add(self):
if len(self.ids.widget_list.children)<5:
print(self.ids)
self.ids.widget_list.remove_widget(self.ids.empty)
print(self.ids)
layout = GridLayout(cols=3)
layout.add_widget(Label(text='Test ' + str(len(self.ids.widget_list.children)+1)))
layout.add_widget(TextInput())
layout.add_widget(CheckBox())
self.ids.widget_list.add_widget(layout)
self.layouts.append(layout)
self.update_hints()
else:
layout = GridLayout(cols=1)
layout.add_widget(Label(text='Only five allowed at once.\nRemove at least one to add another.'))
button = Button(text='Acknowledge'); layout.add_widget(button)
popup = Popup(content=layout, title='Limit Reached', size_hint=(.5,.5), auto_dismiss=False)
button.bind(on_release=popup.dismiss)
popup.open()
def update_hints(self):
for i in self.layouts:
i.children[1].hint_text = 'Ex. ' + str(round(100/len(self.ids.widget_list.children),2)) + '%'
#Initialize Screens and Start App
class MyScreenManager(ScreenManager):
pass
#Main application
class SampleApp(App):
def build(self):
self.sm = MyScreenManager()
return self.sm
if __name__ == '__main__':
SampleApp().run()
kv
<MyScreenManager>:
Screen1:
name: 'screen1'
<Screen1>:
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .1
Label:
text: 'List of Widgets'
underline: True
Label:
text: 'Percentage'
underline: True
Label:
text: 'Remove (Y/N)'
underline: True
ScrollView:
size_hint: 1, .5
do_scroll_x: False
padding: 20
spacing: 20
GridLayout:
id: widget_list
cols: 1
spacing: 5
GridLayout:
id: empty
rows: 1
Label:
text: 'Nothing Here'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .2
Button:
text: 'Add Widget'
on_release: root.add()
Label:
text: ''
Button:
text: 'Remove Widget'
on_release: root.remove()
Ok, I seem to have found a simpler means of implementation that avoids specifying any id for the initial Label (the same one that shows if self.layouts==[]):
Python
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
#Load kv file
Builder.load_file('test.kv')
#First Screen
class Screen1(Screen):
count = 0
layouts = []
def remove(self):
for i in self.layouts:
if i.children[0].active:
self.ids.widget_list.remove_widget(i)
self.layouts = [i for i in self.layouts if not i.children[0].active]
if self.layouts!=[]:
self.update_hints()
else:
layout = GridLayout(rows=1)
layout.add_widget(Label(text='Nothing Here'))
self.ids.widget_list.add_widget(layout)
def add(self):
if self.layouts==[]:
self.ids.widget_list.clear_widgets()
if len(self.ids.widget_list.children)<5:
self.count+=1
layout = GridLayout(cols=3)
layout.add_widget(Label(text='Test ' + str(self.count)))
layout.add_widget(TextInput())
layout.add_widget(CheckBox())
self.ids.widget_list.add_widget(layout)
self.layouts.append(layout)
self.update_hints()
else:
layout = GridLayout(cols=1)
layout.add_widget(Label(text='Only five allowed at once.\nRemove at least one to add another.'))
button = Button(text='Acknowledge'); layout.add_widget(button)
popup = Popup(content=layout, title='Limit Reached', size_hint=(.5,.5), auto_dismiss=False)
button.bind(on_release=popup.dismiss)
popup.open()
def update_hints(self):
for i in self.layouts:
i.children[1].hint_text = 'Ex. ' + str(round(100/len(self.ids.widget_list.children),2)) + '%'
#Initialize Screens and Start App
class MyScreenManager(ScreenManager):
pass
#Main application
class SampleApp(App):
def build(self):
self.sm = MyScreenManager()
return self.sm
if __name__ == '__main__':
SampleApp().run()
kv
<MyScreenManager>:
Screen1:
name: 'screen1'
<Screen1>:
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .1
Label:
text: 'List of Widgets'
underline: True
Label:
text: 'Percentage'
underline: True
Label:
text: 'Remove (Y/N)'
underline: True
ScrollView:
size_hint: 1, .5
do_scroll_x: False
padding: 20
spacing: 20
GridLayout:
id: widget_list
cols: 1
spacing: 5
GridLayout:
rows: 1
Label:
text: 'Nothing Here'
GridLayout:
cols: 3
padding: 20
spacing: 20
size_hint: 1, .2
Button:
text: 'Add Widget'
on_release: root.add()
Label:
text: ''
Button:
text: 'Remove Widget'
on_release: root.remove()