Hi all my question is a basic one. I'm using a .kv file in which I have defined a button that changes screens. I would like to use that button to load .kv file. I can use builder.load(kv2.kv) ?
I've tried a bunch of different ways writing it out. The stats.kv file loads all my RPG stats. If I remove the builder statement, I just get the next screen. All my widgets are in a different .kv files for ease of debugging purposes. I just need to load multiple .kv files within a .kv file.
However this error always occurs:
AttributeError: 'RevengeApp' object has no attribute 'builder'
Button in .kv file:
Button:
text: "Confirm"
on_press: app.builder.load(stats.kv)
on_release: app.root.current = "AStats"
Did you assign the Builder object to the builder attribute of your app? because that's what your kv code seems to expect.
from kivy.lang import Builder
[…]
class RevengeApp(App):
def build(self):
self.builder = Builder
should do it.
But you could also just import Builder directly in your kv code by doing
#:import builder kivy.lang.Builder
at the top of your kv file, and then replace your app.builder.load with builder.load_file in your on_press binding.
Related
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.
So I want to do the styling of my Kivy app in a external .kv file, but when I run the main code, nothing appears to the black Kivy window.
Here's my main file called main.py
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
class MyGrid(Widget):
pass
class MyApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
MyApp().run()
And here's the .kv file located in the same directory and called my.kv
#:kivy 2.0.0
<MyGrid>:
Label:
text: "example example"
So I'm not getting any error, just nothing appearing in the Kivy GUI when I run the main code.
Why is this and how to fix it?
In order to load widgets from a separate kivy file, you need to import Builder:
from kivy.lang.builder import Builder
Builder.load_file('my.kv')
or in .py file
Builder.load_string("""
<MyGrid>:
Label:
text: "example example"
""")
The kivy file name should always be the App class name, in your case you should save file with MyApp.kv
else you need to use Builder to import
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
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.
I have defined my screens in .kv file, and I can access them without any problem when using ie. on_release: app.root.current = 'main' in .kv.
But I have a problem when Buttons are created dynamically in python, what is equivalent to on_release: app.root.current = 'main' in python? Because I cant make it recognize app.root.
You can access the current App in Python by calling App.get_running_app(). So, in other words:
from kivy.app import App
def change_screen(*args):
App.get_running_app().root.current = 'main'
from kivy.uix.button import Button
btn = Button(text='abc')
btn.bind(on_press=change_screen)