Python kivy RecursionError: maximum recursion depth exceeded in comparison - python-3.x

I am trying to create a simple app with kivy in python
but when i run this code i get following error
RecursionError: maximum recursion depth exceeded in comparison
import wikipedia
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
class GridLayout(GridLayout):
def __init__(self, **kwargs):
super(GridLayout, self).__init__()
# Number of columns
self.cols = 1
# Second grid Layout
self.second_layout = GridLayout()
self.second_layout.cols = 2
# Creating a text field to show the result of entered query
self.query_result = TextInput(text='', size_hint_y=0.8)
self.second_layout.add_widget(self.query_result) # Adding query result on the screen
# Creating a text input field to get the query from user
self.query = TextInput(text='', multiline=False, hint_text="Enter your Query", size_hint_y=0.1, font_size=20)
self.second_layout.add_widget(self.query)
# Adding Second layout on the screen
self.add_widget(second_layout)
# Creating a submit button
self.submit_button = Button(text="Submit", size_hint_y=0.1, font_size=40, on_press=self.submit)
self.add_widget(self.submit_button)
def submit(self, instance):
try:
query_result_from_wikipedia = wikipedia.page(self.query.text).summary
self.query_result.text = query_result_from_wikipedia
except:
popup = Popup(title='Query Not Found',
content=Label(text='Try to Search Anything else'),
size_hint=(None, None), size=(400, 400))
popup.open()
class MyApp(App):
def build(self):
return GridLayout()
if __name__ == '__main__':
MyApp().run()
But when i remove the second gridlayout from it it runs without errors

The first problem is the name of your class. Don't name a class the same as its subclass:
class GridLayout(GridLayout):
is likely to cause problems. Just change it to something like:
class MyGridLayout(GridLayout):

Related

Why is Kivy FocusBehavior applied to Buttons not working?

In the kivy.FocusBehavior documentation (https://kivy.org/doc/stable/api-kivy.uix.behaviors.focus.html), an example with a FocusButton(FocusBehavior, Button) is given. But using the tab key on Windows 10 to cycle between the buttons added to the GridLayout does not work. What is wrong in the code below ?
from kivy.app import App
from kivy.uix.behaviors.focus import FocusBehavior
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
class FocusButton(FocusBehavior, Button):
def _on_focus(self, instance, value, *largs):
print(self.text)
class FocusBehaviorGUI(GridLayout):
def __init__(self, **kwargs):
super().__init__( **kwargs)
self.cols = 4
self.rows = 2
for i in range(8):
self.add_widget(FocusButton(text=str(i)))
# clicking on a widget will activate focus, and tab can now be used
# to cycle through
class FocusBehaviorApp(App):
def build(self):
return FocusBehaviorGUI()
if __name__ == '__main__':
FocusBehaviorApp().run()
It is working. try changing:
def _on_focus(self, instance, value, *largs):
to:
def on_focus(self, instance, value, *largs):

Kivy - TextInput text show in Label text when Button is clicked (Not KV Lang)

How to make text that I enter in the TextInput field (textA) will show in the Label (labelA) by using a button?
Every time there is new inputs, when the button is clicked, the labels will show the latest inputs.
Problem here when I use:
self.textA = TextInput(text='ss')
When I type new text and I click the button, the label always show 'ss'. Its not updating the new input.
Hope someone can show the method - just in python, not the Kivy Languange
Thanks
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 One(BoxLayout):
def __init__(self, **kwargs):
super(One, self).__init__(**kwargs)
self.orientation = 'vertical'
# text input
self.textA = TextInput(font_size='60sp')
self.add_widget(self.textA)
self.add_widget(Two())
class Two(BoxLayout):
def __init__(self, **kwargs):
super(Two, self).__init__(**kwargs)
self.orientation = 'vertical'
# Button
self.btnA = Button(text='Show')
self.btnA.bind(on_press=show)
self.add_widget(self.btnA)
# Output
self.labelA = Label(text='Result Here From Input')
self.add_widget(self.labelA)
def show(value):
Two().labelA.text = str(One().textA.text)
x = Two().labelA.text
y = One().textA.text
print(x)
print(y)
class TestApp(App):
def build(self):
return One()
if __name__ == '__main__':
TestApp().run()
Whenever you use One() or Two() you are creating new instances of One or Two, and these new instances are unrelated to the instances that are in your GUI. If you want to access the instances in your GUI, you must arrange for that. For example, in the One class, you can save reference to the Two instance:
class One(BoxLayout):
def __init__(self, **kwargs):
super(One, self).__init__(**kwargs)
self.orientation = 'vertical'
# text input
self.textA = TextInput(font_size='60sp')
self.add_widget(self.textA)
self.two = Two() # save a reference to the Two instance
self.add_widget(self.two)
Since the instance of One is the root of the App (returned by the build() method), you can access it from the App. Then your show() method can become:
def show(value):
one = App.get_running_app().root
two = one.two
two.labelA.text = one.textA.text
x = two.labelA.text
y = one.textA.text
print(x)
print(y)

How to implement for loop in .kv file

I have written a python code in python file(main.py) to create a kivy app that contains dynamically created labels which works fine.
Here is main.py file
main.py
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
class createLabels(GridLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.cols=1
labels=[Label(text='Label '+str(i)) for i in range(5)]
[self.add_widget(label) for label in labels]
class DocApp(App):
def build(self):
return createLabels()
if __name__=="__main__":
DocApp().run()
However i would like to dynamically create labels similar to the one above using a kivy language file (.kv). I am not sure whether we can use lists and for statements in .kv file. I tried the solution mentioned in one of the similar type question but it didn't work.
You could use on_kv_post which runs when the kv class which you use on_kv_post is ready. And dynamically, like in push of buttons, is basically the same as in the example below.
from kivy.app import App
from kivy.lang import Builder
KV = '''
#: import Label kivy.uix.label.Label
GridLayout:
cols: 1
on_kv_post:
[self.add_widget(Label(text="Label " + str(i))) for i in range(9)]
'''
class TestApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
TestApp().run()

How to get nested Kivy widgets to interact between each other?

I'm playing around with the examples provided in the Kivy tutorial. I was able to create nested widgets, but can't seem to get the buttons to modify objects in the other parts of the screen.
In this example, I've tried to modify the Kivy example script to turn it into a simple login window with a numpad drawn from buttons (let's ignore the vkeyboard for this case, I'm trying to get a hold of buttons...). In short, buttons on the left, login textinput on the right. I cannot seem to have the actions on the buttons affect the text input on the right. It sounds very simple, but I can't seem to figure it out. Should I use a global variable? Am I creating my objects incorrectly for this purpose?
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
class AddNumpad(GridLayout):
def __init__(self, **kwargs):
super(AddNumpad, self).__init__(**kwargs)
self.cols = 3
self.padding = 50
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
# TODO: trying to populate the password textinput here
for i in range(1,10):
btn = Button(text=str(i))
btn.bind(on_press=callback)
self.add_widget(btn)
self.add_widget(Label(text=''))
btn = Button(text='0')
btn.bind(on_press=callback)
self.add_widget(btn)
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.password = TextInput(password=True, multiline=False)
self.cols = 2
self.numpad = AddNumpad()
self.add_widget(self.numpad)
self.entry = ''
self.password.text = self.entry
self.add_widget(self.password)
class MyApp(App):
userInput = ''
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
You can use a custom property and bind it to a callback in your LogginScreen class:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.properties import StringProperty
class AddNumpad(GridLayout):
passwIn = StringProperty() # <<<<<<<<<<<<<
def __init__(self, **kwargs):
super(AddNumpad, self).__init__(**kwargs)
self.cols = 3
self.padding = 50
for i in range(1, 10):
btn = Button(text=str(i))
btn.bind(on_press=self.callback)
self.add_widget(btn)
self.add_widget(Label(text=''))
btn = Button(text='0')
btn.bind(on_press=self.callback)
self.add_widget(btn)
def callback(self, instance):
self.passwIn += instance.text # <<<<<<<<<<<<<
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.password = TextInput(password=True, multiline=False)
self.cols = 2
self.numpad = AddNumpad()
self.numpad.bind(passwIn=self.numpad_pressed) # <<<<<<<<<<<<<
self.add_widget(self.numpad)
self.add_widget(self.password)
def numpad_pressed(self, instance, value):
self.password.text = value
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()

Kivy: Manipulating dynamically created widgets in update function

I am trying to extend the code in the answer here provided by Nykakin where widgets are defined on the fly in Kivy, dynamically assigned IDs, and then manipulated based on ID.
The 'end game' is to implement some basic 2D physics by changing position of widgets based on ID.
I am not able to 'walk' the widget tree from the update function (errors inline).
Is it possible to do such a thing?
Here is the code:
#code begins here
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.factory import Factory
class MyWidget(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text="Print IDs", id="PrintIDsButton")
button.bind(on_release=self.print_label)
self.add_widget(button)
# crate some labels with defined IDs
for i in range(5):
self.add_widget(Button(text=str(i), id="button no: " + str(i)))
# update moved as per inclement's recommendation
Clock.schedule_interval(MyApp.update, 1 / 30.0)
def print_label(self, *args):
children = self.children[:]
while children:
child = children.pop()
print("{} -> {}".format(child, child.id))
# we can change label properties from here! Woo!
children.extend(child.children)
if child.id == "PrintIDsButton":
child.text = child.text + " :)"
class MyApp(App):
def build(self):
return MyWidget()
def update(self, *args):
#ERROR# AttributeError: 'float' object has no attribute 'root'
children = self.root.children[:]
#ERROR# TypeError: 'kivy.properties.ListProperty' object is not subscriptable
#children = MyWidget.children[:]
#while children:
# child = children.pop()
# print("{} -> {}".format(child, child.id))
# children.extend(child.children)
#ERROR# TypeError: 'kivy.properties.ListProperty' object is not iterable
#for child in MyWidget.children:
# print("{} -> {}".format(child, child.id))
if __name__ == '__main__':
MyApp().run()
Windows 7 SP1
Kivy 1.8.0 / Python 3.3
My apologies if my terminology is incorrect! (and thank you for your time)
children = MyWidget.children[:]
MyWidget is the class itself, not an instance of it, and so the children is a ListProperty object and not the actual list you want.
You want instead the children of the MyWidget instance that is your root widget, self.root.children.
Also, you clock schedule the update function at class level; I think this is bad practice, and could lead to subtle bugs. It would be more normal to do it in the __init__ method of the widget instead.
So putting the update function within the 'MyWidget' class and then accessing elements within 'MyWidget' by calling self.children worked!
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.factory import Factory
class MyWidget(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text="Print IDs", id="PrintIDsButton")
button.bind(on_release=self.print_label)
self.add_widget(button)
# crate some labels with defined IDs
for i in range(5):
self.add_widget(Button(text=str(i), id="button no: " + str(i)))
# update moved as per inclement's recommendation
Clock.schedule_interval(self.update, 1 / 5.0)
def print_label(self, *args):
children = self.children[:]
while children:
child = children.pop()
print("{} -> {}".format(child, child.id))
# Add smiley by ID
children.extend(child.children)
if child.id == "PrintIDsButton":
child.text = child.text + " :)"
def update(self, *args):
children = self.children[:]
while children:
child = children.pop()
# remove smiley by ID
if child.id == "PrintIDsButton":
child.text = "Print IDs"
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
Thanks again to inclement!

Resources