Moving from one screen to another generates a RecursionError - python

I want create an app with a button that will call the another app or another part of this app.
For example:
I'm on the Main screen where I can choose some options like View Report, Create Report, Manage Report, etc. I think that all of those is a app.
So, I make two subclasses from the App class of Kivy, each of which has its own .kv file. Suppose that one is the Main screen and the another is the another screen.
When I do that it looks like it works, but when I call the second screen, in the console I get the following error in the console:
RecursionError: maximum recursion depth exceeded in comparison
How can I fix this?
My Main.py file contains
from kivy.app import App
class ProbandoApp(App):
pass
class TestApp(App):
probando = ProbandoApp()
print(__name__)
if __name__ == '__main__':
TestApp().run()
and I have the .kv file for each class.
I also have a file called test.kv that contains:
Widget:
Button:
id: btnTest
text: 'Test'
width: 100
height: self.font_size * 2
on_press: app.probando.run()
and a file called probando.kv that contains
Widget:
Button:
id: btnProbando
text: 'Otra cosa mas para probar'

It sounds like you don't want multiple apps, but a single app whose interface changes. You can do this by switching widgets around, and the ScreenManager provides a convenient api for this.

Related

Python Kivy widgets duplicated on top of each other on a custom app restart method

Basically I'm working on a Kivy app that consists of multiple screens. Recently I decided to give my app a little Refresh button that refreshes the widgets and reread the savefile for sorta debugging purpose, and then I of course stole the code from this question's answer, like how every programmer would do. It worked, but there's one slight problem: every time I press the refresh button, for whatever reason the widgets got duplicated and placed on top of each other(even though the restart function cleared the former widgets beforehand). Here's my code, simplified:
test.py
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivy.config import Config
Config.set("kivy", "exit_on_escape", 0)
class First(Screen):
pass
class Second(Screen):
pass
class Test(MDApp):
def restart(self):
self.root.clear_widgets()
self.stop()
Test().run()
Test().run()
test.kv
ScreenManager:
First:
Second:
<First>:
MDFloatLayout:
MDTextField:
size_hint: 0.8, 0.2
pos_hint: {'center_x': 0.5, 'top': 0.9}
hint_text: 'owo uwu test im a furry yes'
MDRectangleFlatButton:
text: 'Restart'
size_hint: 0.15, 0.1
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
on_release: app.restart()
<Second>:
Also note that the reason to why I used screens in this example app although there's no way to access the Second screen, is because I did some tests and the result is that the "bug" only occurs with the ScreenManager being the root class, others like FloatLayout works properly. Secondly the reason why I used KivyMD in this example is that it's easier to detect the duplication of widgets using MDTextField because of the animation of the hint text.
Does anyone know what's the cause behind the problem, as well as the solution to this? Any help would be appreciated.
Edit: Here are images for demonstration:
Before restarting:
After restarting:
Th problem is that your test.kv file is getting loaded twice due to Kivy doing its automatic loading based on the file name. The fix is to control the kv file loading yourself. To do that, change the name of your kv file to anything other than test.kv, perhaps not_test.kv. Then create a build() method in your Test App:
class Test(MDApp):
def build(self):
from kivy.resources import resource_find
filename = 'not_test.kv'
filename = resource_find(filename) or filename
if filename in Builder.files:
Builder.unload_file(filename)
return Builder.load_file(filename)
This code unloads the kv file if it has already been loaded, then loads it again in order to get the root widget.

How can i get the text of an MDTextField (Or Data "Login") of an screen if im in another Screen. (Kivy / KivyMD / Python)

im new using classes and kivy/kivyMD, I have a trouble and it is that i want to get the text of an MDTextFied (Or Data "Login" of my Query) who belongs to the AScreen if im working from the class definition of the BScreen?
My minimal code is like, ScreenManager File:
<AScreen>:
name:'ascreen'
MDBoxLayout:
orientation: 'vertical'
MDTextField:
id: user
hint_text: 'User'
MDRectangleFlatButton:
text:'Login'
on_release: root.login_button()
<BScreen>:
name:'bscreen'
MDRectangleFlatButton:
text:'Registrar Equipo'
on_release: root.show_login_data()
The class file of my screens is:
class AScreen(Screen):
def login_button(self):
...
login = #User DB info
"""Here i do a query to the DB with the text of my MDTextField on AScreen
i use self.ids.user.text to get the text of the AScreen MDTextField
if the user exist in the DB i save the user's data on a variable named "login" """
class BScreen(Screen):
def show_login_data(self):
print()
For now I'm just trying to print the text from AScreen.ids.user.text or also get the login variable but I haven't got either of those.
My main file is:
sm = ScreenManager()
sm.add_widget(screensfile.AScreen(name='ascreen'))
sm.add_widget(screensfile.BScreen(name='bscreen'))
class MainApp(MDApp):
def build(self):
self.screen = Builder.load_string(screen_helper)
return self.screen
MainApp().run()
I have tried the following (I apologize for my horrible rookie mistakes):
print(AScreen.login_button().login)
print(Acreen.ids.user.text)
I know there are a similar cases here, but it doesn't help me for my screens.py file:
Kivy - Use text from TextInput of one screen in another screen in .py file
get values from a different screen (kivy)
I know that the most probable thing is that I have a bad interpretation of the work with the classes, but with the way that the screens work in kivy (screens look for the definition of a class of the same name) I would not know how to do it.
Could anyone help me?
Assuming that your screen_helper defines a ScreenManager as the Apps root widget with AScreen and BScreen as its children. In that case you can reference the MDTextField in AScreen as:
App.get_running_app().root.get_screen('ascreen').ids.user
The App.get_running_app() returns the instance of the running App. The root references the root widget (which I am assuming is a ScreenManager). The get_screen() method of the ScreenManager returns the instance of AScreen, and the ids.user gets a reference to the MDTextField.
Trying to access the ids using the class AScreen will not work as the ids are defined in the instance of AScreen.
On an unrelated note, if your build() method returns the results of Builder.load_string, then the lines:
sm = ScreenManager()
sm.add_widget(screensfile.AScreen(name='ascreen'))
sm.add_widget(screensfile.BScreen(name='bscreen'))
are building an unused sm, and can be removed.

Having difficulty in loading .kv file

I've saved both my python and kv file in the same folder but the program is unable to load it implicitly using the app name. I've tried using Builder.load_file() to load it explicitly but it results in a file not found error.
The only way it works is if i use the Builder.load_string() function but it makes the code tough to manage and also very badly organized. I'd really appreciate some help fixing this error.
maybe you can check whether your app class name match your .kv file
e.g.
.py
class MyKivyApp(App): # <-- here you should name your .kv file as mykivy , it comes from the class name "MyKivyApp" without the "App"
def build(self):
...
so the .kv file name will be mykivy.kv
or you can try kivy.lang.Builder.load_string() in .py , it just like the .kv but in the form of string ...
e.g.
.py
import kivy
from kivy.uix.widget import Widget
kivy.lang.Builder.load_string(
'''
#:kivy 2.0.0
<MyApp>
Button:
text: 'Helo World !"
size: root.size
''')
class MyApp(Widget):
pass
class mykivyApp(App):
def build(self):
main = MyApp()
return main
sth like this

How to set existing text from a variable within a function? Python Kivy

I created a function that opens a log file, and saves it to a variable named loginfo. In my kivy file, I have a TextInput widget. I tried setting the existing text: to root.loginfo.
The loginfo needs to be within a function because I am using the kivy's Clock to re-read the log file.
Python file:
class Tasks(Screen):
logginfo = ObjectProperty()
def reset_text(dt):
with open('logtest.log', 'r') as file:
loginfo = file.read()
Clock.schedule_once(reset_text, -1)
Kivy file:
<Tasks>:
name: 'task'
logginfo: logginfo
BoxLayout:
orientation: "vertical"
Label:
text: "TASKS"
TextInput:
id: logginfo
text: root.loginfo
The problem started occurring when I created the reset_text(dt) function and kivy.clock. Without the function, and just the contents of it, the textinput box displays the logtest.log file's contents correctly.
When I run the script, it gives me AttributeError: 'NoneType' object has no attribute 'replace'. I'm confused and stuck, any help would be appreciated. Thanks in advance.
Here is a complete example to do what you want to do. You'll have to modify it to integrate it with your code, but my intention here was to show you the proper way to achieve this and let your work with it yourself.
Note how I'm using Clock.schedule_interval instead of schedule once. The 1 in the schedule_interval is the time between calling the self.reset_text function in seconds. Note how in the reset_text function I can refer to my base widget in my kv file using self.root (the GridLayout), then I can get the TextInput (since I gave it an id) by doing self.root.ids['my_text_input']
main.py
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
GUI = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
Clock.schedule_interval(self.reset_text, 1) # Check if log has changed once a second
return GUI
def reset_text(self, *args):
with open("logtest.log", "r") as f:
self.root.ids['my_text_input'].text = f.read()
MainApp().run()
main.kv
GridLayout:
# This is the 'root' widget when referenced in python file
cols: 1
TextInput:
id: my_text_input

How to get a text input box to display with Kivy?

I'm currently writing an app in Kivy for a school project (I have very much had to jump in the deep end with Kivy). I have written the kv code for the text input, which you can see below:
AnswerInput:
<AnswerInput#BoxLayout>:
orientation: "vertical"
BoxLayout:
height: "40dp"
size_hint_y: None
TextInput:
size_hint_x: 20
Button:
text: "Check Answer"
size_hint_x: 25
I now need to get the text box to display in the Python file; however, I am at something of a loss as to how I would do this? My Python code is below:
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
class TextInputTest(App):
def __init__(self, *args, **kwargs):
return TextInput
if __name__ == '__main__':
TextInputTest().run()
I am almost certain that I am missing something here, probably something very simple, but I am very much a beginner with Kivy. If anyone could put me on the right track, I would be very grateful.
Firstly, this isn't clear but you need to seperate your code into a py file and a kv file. It seems like you've done this already. Your kv file also needs to be all lowercase
In your py file, you then add a class for the kivy widget. In this case:
from kivy.uix.boxlayout import BoxLayout
class AnswerInput(BoxLayout):
pass
Then in your kv file:
<AnswerInput>:
orientation: "vertical"
BoxLayout:
height: "40dp"
size_hint_y: None
TextInput:
size_hint_x: 20
Button:
text: "Check Answer"
size_hint_x: 25
AnswerInput from your py looks into your loaded kv file to see if there is a root widget with the same name as itself.
(RootWidget meaning the top widget of a bunch of kv logic encased in <>)
You have to however first know how to load a kv file, there are two ways to do this. If you're using just one kv file, you can name your app the same as your kv file.
So if your kv file is
textinputtest.kv
Your app class in py would read
TextInputTest(App):
or
TextInputTestApp(App):
You don't need to do this, you can also use the builder module to load the file itself (and in fact you will need to do this if you have more than one kv file).
To do this, you do this in your py file:
from kivy.lang.builder import Builder
Builder.load_file('textinputtest.kv')
You're also returning an object of the textinput class, what you want to do is return an object of your customized textinput class.
Your Py file would look like this:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class AnswerInput(BoxLayout):
pass
class TextInputTest(App): # If your kv file is called textinputtest.kv
def build(self):
return AnswerInput()
if __name__ == '__main__':
TextInputTest().run()
Or you can name your app anything you want and then use builder to load the relevant kv file directly into your app.

Categories

Resources