I am learning Kivy and having trouble connecting my objects declared in the .kv file to the python class in order to update their properties. No matter which way I try I get this error:
self.kbCompressionLabel.text = 'Hello World'
AttributeError: 'NoneType' object has no attribute 'text'
The app loads all the kivy files fine, and only breaks when I try to update from the Class.
I've dumbed down the current code to the minimum to illustrate how it is setup. Any help is much appreciated.
Main app entry
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager
Builder.load_file('appscreenmanager.kv')
Builder.load_file('compressorscreen.kv')
Builder.load_file('slidersview.kv')
class AppScreenManager(ScreenManager):
pass
class AppManager(App):
def build(self):
return AppScreenManager()
if __name__ == "__main__":
AppManager().run()
Dumed down appscreenmanager.kv
#:kivy 1.10.0
<AppScreenManager>:
CompressorScreen:
...
compressorscreen.kv
<CompressorScreen>:
name: 'compressor'
GridLayout:
rows: 4
cols: 1
SlidersView:
...
This is where the issue is happening: simplified slidersview.kv
#:kivy 1.10.0
#:import slidersview slidersview
<slidersView>:
cols: 4
rows: 2
id: sliders
kbCompressionLabel: kbCompressionLabel
Label:
id: kbCompressionLabel
text: 'test'
slidersview.py
import kivy
kivy.require('1.10.0')
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
class SlidersView(GridLayout):
# properties
sliders = ObjectProperty(None)
kbCompressionLabel = ObjectProperty(None)
def __init__(self, **kwargs):
self.kbCompressionLabel.text = 'Hello World'
super(SlidersView, self).__init__(**kwargs)
UPDATE
I had to add a delay into the init function then things worked. However, this feels pretty wonky to me. Is this the expected behavior?
Updated slidersview.py
import kivy
kivy.require('1.10.0')
from kivy.clock import mainthread
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
class SlidersView(GridLayout):
# properties
kbCompressionLabel = ObjectProperty(None)
def __init__(self, **kwargs):
super(SlidersView, self).__init__(**kwargs)
#mainthread
def delayed():
self.kbCompressionLabel.text = 'Hello World'
delayed()
The KV definitions are applied only when you call __init__ of the base classes...
reverse the order and you'll be fine...
class SlidersView(GridLayout):
# properties
sliders = ObjectProperty(None)
kbCompressionLabel = ObjectProperty(None)
def __init__(self, **kwargs):
super(SlidersView, self).__init__(**kwargs) #first!
self.kbCompressionLabel.text = 'Hello World' #2nd!
Related
as far as I know, my codes are good because there is no error from the IDE, and I used the same code on another project and it works. I read from SO posts that say, maybe the object don't have the .text() method, that would give me an error, but I don't have an error. Another post that say could come from not set the table to an object. As far as I know, I referenced everything correctly.
does it have to do with the data type? thanks again for some insight.
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.lang.builder import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.graphics import Rectangle, Color
import socket
class Screen_Manager(ScreenManager):
screen_1 = ObjectProperty()
class Screen_1(Screen):
main_display = ObjectProperty()
def __init__(self, **kwargs):
super(Screen_1, self).__init__(**kwargs)
def get_host(self):
self.s = socket.gethostbyname(socket.gethostname())
return self.s
class MainApp(App):
def build(self):
self.title = "Number Display App"
self.sm = Screen_Manager()
return self.sm
if __name__=="__main__":
MainApp().run()
main.ky
<Screen_Manager>:
Screen_1:
<Screen_1>:
id: screen_1
name: "first"
main_display: display_1
Label:
id: display_1
text: root.get_host()
font_size: "30sp"
It looks like you don't set the text of your label to anything but " ".
i want to access an id from class main to class fahim2_pop. want to access to word from textinput(in main class) to the popup widget which will appear when someone press the search button. when someone search "hello" and press search button the the popup widget will appear and in that popup widget the text of the label will be "hello" same as from the textinput. but the label and the id remains in different class. how to do it?
python code
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.properties import *
class fahim2_pop(Popup):
pass
class main(BoxLayout):
def word(self):
pop=fahim2_pop()
pop.open()
class go(BoxLayout):
def main(self):
self.clear_widgets()
self.add_widget(main())
class CallApp(App):
def build(self):
return go()
CallApp().run()
kv code
Builder.load_string('''
<main>:
BoxLayout:
orientation:"vertical"
TextInput:
id:word
Button:
text:"search"
on_press:root.word()
<go>:
Button:
text:"go"
on_press:root.go()
<fahim2_pop>:
id:pop
title:"result"
BoxLayout:
Label:
text:app.root.ids.word.text
''')
i know app.root.ids.word.text if that id remain in root of my app. but here go is the root of app. how to access id from class main in class fahim2_pop?
There are a couple of ways of solving this problem. One of the solution is as follow:
py file
Rename method main() in class go() to go() because in your kv file, you have binded on_press: root.go()
Instantiate main() and store it in a class attribute, main
Snippets - py file
from kivy.properties import ObjectProperty
...
class go(BoxLayout):
main = ObjectProperty(None) # declare class attribute
def go(self):
self.clear_widgets()
self.main = main()
self.add_widget(self.main)
kv file
Replace text:app.root.ids.word.text with text:app.root.main.ids.word.text
Snippets - kv file
<fahim2_pop>:
...
Label:
text:app.root.main.ids.word.text
Example - main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
Builder.load_string('''
<main>:
BoxLayout:
orientation:"vertical"
TextInput:
id:word
Button:
text:"search"
on_press:root.word()
<go>:
Button:
text:"go"
on_press:root.go()
<fahim2_pop>:
id:pop
title:"result"
BoxLayout:
Label:
text:app.root.main.ids.word.text
''')
class fahim2_pop(Popup):
pass
class main(BoxLayout):
def word(self):
pop = fahim2_pop()
pop.open()
class go(BoxLayout):
main = ObjectProperty(None)
def go(self):
self.clear_widgets()
self.main = main()
self.add_widget(self.main)
class CallApp(App):
def build(self):
return go()
CallApp().run()
Output
I've got a python file(root.py) and another python file(button.py). When I define a button with an attribute (such as size_hint:0.1,1) in button.py using the kv language, root.py doesnt seem to be able to access that information.
When I define the same information using python in button.py, root.py seems to be able to access it.
ROOT.PY
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang.builder import Builder
from buttons import *
Builder.load_string("""
<Root>:
ButtonBar:
""")
class BtnBar(ButtonBar):
print(self.size_hint) # prints [1,1] instead of [0.1,1]
class Root(FloatLayout):
pass
class AppDev(App):
def build(self):
return Root()
BUTTON.PY
from kivy.uix.floatlayout import FloatLayout
from kivy.lang.builder import Builder
Builder.load_string("""
<ButtonBar>:
size_hint: 0.1,1
""")
class ButtonBar(FloatLayout):
pass
Root.py should be able to access any information I declare in button.py's Builder.load_string
After the object is instantiated, the right data will be accessable. You can use clock to make sure you get it after instantiation.
Also in your kv string, you probably want BtnBar instead of ButtonBar
Here is your root.py rewritten to do this.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang.builder import Builder
from kivy.clock import Clock
from buttons import *
Builder.load_string("""
<Root>:
BtnBar: # corrected to BtnBar
""")
class BtnBar(ButtonBar):
def __init__(self, **kwargs):
super(BtnBar, self).__init__(**kwargs)
Clock.schedule_once(self.get_data)
def get_data(self, dt):
print(self.size_hint) # prints on second frame
class Root(FloatLayout):
pass
class AppDev(App):
def build(self):
return Root()
AppDev().run()
i want popup effect for references in kivy reStructuredText renderer, in default when we press any reference it scrolls to the reference, here i want to replace scroll with a popup, i have been searching for this for a long time but in vain, today i found goto(ref, *largs)in kivy docs, is it possible to call a reference popup using this? is it possible to redefine RstDocument fuctions in another class and use it?? i am a beginner.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<RSTGUI>:
RstDocument:
text: root.doc
goto: # Any Popup aur bubble with reference
""")
class RSTGUI(BoxLayout):
doc="""
.. _top:
Hello world
===========
This is an **emphased text**, some ``interpreted text``.
And this is a reference to top_::
$ print("Hello world")
"""
class RST(App):
def build(self):
return RSTGUI()
if __name__=='__main__':
RST().run()
After little research i have figured it out, Alhumdolillah.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.rst import RstDocument
from kivy.uix.popup import Popup
Builder.load_string("""
<RSTGUI>:
MyRST:
text: root.doc
<about_us_popup>:
size_hint: 0.8,0.6
title: "About Us"
BoxLayout:
orientation:'vertical'
RstDocument:
text: "MyText"
Button:
text:"OK"
size_hint: 1,0.1
on_press:root.dismiss()
""")
class RSTGUI(BoxLayout):
doc="""
.. _top:
Hello world
===========
This is an **emphased text**, some ``interpreted text``.
And this is a reference to top_::
$ print("Hello world")
"""
class MyRST(RstDocument):
def __init__(self, **kwargs):
super(MyRST, self).__init__(**kwargs)
self.about_us_popup = about_us_popup()
def goto(self, ref, *largs):
self.about_us_popup.open()
class RST(App):
def build(self):
return RSTGUI()
class about_us_popup(Popup):
pass
if __name__=='__main__':
RST().run()
This is my t.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class Simple(BoxLayout):
def __init__(self, **kwargs):
super(Simple, self).__init__(**kwargs)
# THIS IS SIMLE EXAMPLE, IN PRACTICE I AM READING VALUE FROM TEXT FILE
self.sometext = 'Hello from Init.'
def set_text(self):
return self.sometext # error: 'Simple' object has no attribute 'sometext'
#return "Hello World from Simple(BoxLayout)" # this is working
class TApp(App):
def build(self):
return Simple()
TApp().run()
My t.kv
<Simple>:
Label:
#text: 'Hello World' # THIS IS WORKING
text: root.set_text()
So this is not working
What need to be done to get it working ?
I hope that it is posible...
Thanks
Here is a copy of my reply from the kivy mailing list:
It looks like the kv language ends up calling set_text before
__init__, which seems weird but does explain your problem.
You could fix it in various ways, but I would do the whole thing using
a kivy property to keep things simple. I made an example at
https://gist.github.com/inclement/8268019 . Although the default is
set to '', the kv lang can automatically detect that it's a property
and make a binding so that when __init__ changes it the text is
updated.
The linked example code is:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import StringProperty
Builder.load_string('''
<Simple>:
Label:
#text: 'Hello World' # THIS IS WORKING
text: root.sometext
''')
class Simple(BoxLayout):
sometext = StringProperty('')
def __init__(self, **kwargs):
super(Simple, self).__init__(**kwargs)
self.sometext = 'Hello from Init.'
class TApp(App):
def build(self):
return Simple()
TApp().run()