I stuck at a problem of referencing app class real variable ,
I am a beginner in kivy
see this code
class Screen_mgr(ScreenManager):
pass
class MainScreen(Screen):
def func(self):
if(SimpleApp.num == 0): #this statement always returns True as num is always 0 in app class
SimpleApp.num +=10 #How can I access the num variable of app class
class SimpleApp(MDApp):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.theme_cls.theme_style = "Dark"
num = NumericProperty(0)
def build(self):
return Screen_mgr()
if __name__ == "__main__":
SimpleApp().run()
I want to use that num variable everywhere in python code only Not in KV code.
Thanks For Reading .
I think your code (at least what you posted of it) should work as you expect. Here is a version of your code, with additional code just to make it work. The Button calls the func every time you press it, and it shows the expected behavior (with num as a NumericProperty):
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
class Screen_mgr(ScreenManager):
pass
class MainScreen(Screen):
def func(self):
app = MDApp.get_running_app()
print(app.num)
if(app.num == 0): #this statement always returns True as num is always 0 in app class
app.num +=10 #How can I access the num variable of app class
Builder.load_string('''
<Screen_mgr>:
MainScreen:
id: main
name: 'main'
Button:
text: 'doit'
on_release: main.func()
''')
class SimpleApp(MDApp):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.theme_cls.theme_style = "Dark"
num = NumericProperty(0)
def build(self):
return Screen_mgr()
if __name__ == "__main__":
SimpleApp().run()
You can define it outside of your class that will make it a global variable so you can use it everywhere in your .py file or .kv file
Related
I would like to do that:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from dataclasses import dataclass
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super.(MyGrid,self).__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text="Pseudo: "))
self.pseudo = TextInput(multiline=False)
self.add_widget(self.pseudo)
class MyApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
MyApp().run()
but with dataclasses. I have made several searches but didn't find anything.
Thanks to #JohnAnderson advices, I tried that:
#dataclass
class MyGrid(GridLayout):
pass
#dataclass
class MyApp(App):
def build(self):
root = MyGrid()
root.cols = 2
pseudo = TextInput(multiline=False)
root.add_widget(Label(text="Pseudo: "))
root.add_widget(pseudo)
return root
if __name__ == "__main__":
MyApp().run()
but I get this error:
File AppData\Local\Programs\Python\Python310\lib\site-packages\kivy\app.py", line 921, in _run_prepare
if not self.built:
AttributeError: 'MyApp' object has no attribute 'built'. Did you mean: 'build'?
These add_widget methods came from GridLayout of Kivy.
Someone could help to do this with dataclasses? Thanks in advance.
Here is a simple way to do what you want:
class MyApp(App):
def build(self):
root = MyGrid()
root.cols = 2
pseudo = TextInput(multiline=False)
root.add_widget(Label(text="Pseudo: "))
root.add_widget(pseudo)
return root
as you can see in Kivy documentation, add_widget adds a new widget as a child of this widget. So as an example, parent_widget.add_widget(child_widget) adds the child to the parent's widgets. This is why you need to use self.add_widget() if you want to add a child widget to your class (which is a GridLayout).
I have the following Kivy app and I'm trying to change the text of a Label based on another widget's variable.
I mean, if the variable testing of the class TestApp changes, I want also the value of the variable text of the class TestLabel to change.
To do so, I've created a BooleanProperty in the TestLabel class that points to the testing variable of the TestApp class. The problem is that this callback is never executed despite being changing it each time I press the button.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import BooleanProperty
Builder.load_string('''
<MainApp>:
orientation: 'horizontal'
rows: 2
TestButton:
text: 'Change value'
on_release: self.change_value()
TestLabel:
''')
class TestLabel(Label):
testing = BooleanProperty()
def __init__(self, **kwargs):
super(TestLabel, self).__init__(**kwargs)
self.app = App.get_running_app()
self.testing = self.app.testing
self.bind(testing=self.changed_value)
def changed_value(self, _instance, newvalue):
self.text = str(newvalue)
class TestButton(Button):
def __init__(self, **kwargs):
super(TestButton, self).__init__(**kwargs)
self.app = App.get_running_app()
def change_value(self):
self.app.testing = not self.app.testing
class MainApp(BoxLayout):
pass
class TestApp(App):
testing = BooleanProperty(False)
def build(self):
return MainApp()
if __name__ == '__main__':
TestApp().run()
It is not necessary to create a testing property in TestLabel since when you do: self.bind(testing = self.changed_value) you are connecting the testing of TestLabel and not the testing of TestApp , so as it never changes testing after the bind then it never gets call the callback.
The bind has to be done using the object that has that property, and in your case the testing belongs to the App, so you must the App.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import BooleanProperty
Builder.load_string('''
<MainApp>:
orientation: 'horizontal'
rows: 2
TestButton:
text: 'Change value'
on_release: self.change_value()
TestLabel:
''')
class TestLabel(Label):
def __init__(self, **kwargs):
super(TestLabel, self).__init__(**kwargs)
self.app = App.get_running_app()
self.app.bind(testing=self.changed_value)
def changed_value(self, _instance, newvalue):
self.text = str(newvalue)
class TestButton(Button):
def __init__(self, **kwargs):
super(TestButton, self).__init__(**kwargs)
self.app = App.get_running_app()
def change_value(self):
self.app.testing = not self.app.testing
class MainApp(BoxLayout):
pass
class TestApp(App):
testing = BooleanProperty(False)
def build(self):
return MainApp()
if __name__ == '__main__':
TestApp().run()
Here is a basic sample that I am working with. The label displays as I would expect, but the text never changes, even though I do see the print statements in the console showing that the Clock.schedule_interval is ticking away. Any thoughts as to what have gone wrong???
Thank you and Happy New Year!
First the .kvlang file
<Demo>:
button_text: my_button
BoxLayout:
Label:
id: my_button
text: 'Initial Text!'
And my Python.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
import random
class Demo(BoxLayout):
button_text = ObjectProperty
def change_text(self, dt):
self.button_text.text = str(random.randint(1, 10))
print('Should have changed button text to {}'.format(self.button_text.text))
def start(self):
Clock.schedule_interval(self.change_text, 10)
class TutorialApp(App):
def build(self):
foo = Demo()
foo.start()
return Demo()
if __name__ == "__main__":
TutorialApp().run()
You are missing parenthesis
button_text = ObjectProperty
change to
button_text = ObjectProperty(None) # Ha! :)
Also you should return foo and not create another Demo
def build(self):
foo = Demo()
foo.start()
#return Demo() change to...
return foo
Since the later Demo will not be updated...
The class kivy.event.EventDispatcher has a method called apply_property() which allows adding a property to a class at runtime.
The docs contain a warning:
This method is not recommended for common usage because you
should declare the properties in your class instead of using this
method.
It is not clear to me why this usage is discouraged, and declaration in class preferred. Can anyone enlighten me?
apply_property() is used to add property to a single class instance but has a side effect of polluting properties of other instances as well. Consider following code:
#!/usr/bin/kivy
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
Builder.load_string('''
<MyButton>:
on_press: print('np' in self.properties())
''')
class MyButton(Button):
pass
class MainApp(App):
def build(self):
mb1 = MyButton(text="mb1")
mb2 = MyButton(text="mb2")
mb1.apply_property(np=NumericProperty(100))
layout = BoxLayout()
layout.add_widget(mb1)
layout.add_widget(mb2)
return layout
if __name__ == '__main__':
MainApp().run()
As you can see, even though I used apply_property on mb1 instance, mb2 reports that it has such property as well. This is because properties are added to the class, not the instance. However, only mb1 has an actual value:
#!/usr/bin/kivy
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
Builder.load_string('''
<MyButton>:
on_press: print(self.np)
''')
class MyButton(Button):
pass
class MainApp(App):
def build(self):
mb1 = MyButton(text="mb1")
mb2 = MyButton(text="mb2")
mb1.apply_property(np=NumericProperty(100))
layout = BoxLayout()
layout.add_widget(mb1)
layout.add_widget(mb2)
return layout
if __name__ == '__main__':
MainApp().run()
Trying to get the value of np property from mb2 instance yields an AttributeError. A safe alternative:
#!/usr/bin/kivy
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
Builder.load_string('''
<MyButton>:
on_press: print(self.np)
''')
class MyButton(Button):
np=NumericProperty(100)
class MainApp(App):
def build(self):
mb1 = MyButton(text="mb1")
mb2 = MyButton(text="mb2")
layout = BoxLayout()
layout.add_widget(mb1)
layout.add_widget(mb2)
return layout
if __name__ == '__main__':
MainApp().run()
Source: this issue.
In addition to #Nykakin's example, apply_property also interferes with inheritance in a way 'regular' class variables don't, which might therefore lead to unexpected behaviour. Here, MyButton2 is a subclass of MyButton1 and instances get attributes pp and oo. While assigning a class variable oo in MyButton1 does not interfere with instances of MyButton2, adding a Property with apply_property to an instance of MyButton1 suddenly makes pp inaccessible in MyButton2:
from kivy.properties import NumericProperty
from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
class MyButton1(Button):
pass
class MyButton2(MyButton1):
def __init__(self, **kwargs):
super(MyButton2, self).__init__(**kwargs)
self.pp = 11
self.oo = 42
bl = Builder.load_string("""BoxLayout:
MyButton1:
id: hb01
text: "MyButton1"
MyButton2:
id: hb02
text: "MyButton2"
""")
def on_press(sender):
on_press.counter += 1
print "in on_press:"
try:
print " Now pp == {}".format(sender.pp)
except:
print " no attribute 'pp'"
prop = sender.property('pp', quiet=True)
sender.apply_property(pp=NumericProperty(on_press.counter))
print " Applying property ... Now it's {}".format(sender.pp)
try:
print " Now oo == {}".format(sender.oo)
except:
print " no attribute 'oo'"
sender.__class__.oo = on_press.counter + 17
on_press.counter = 0
def on_press2(sender):
print "on_press2:"
try:
print " Now pp == {}".format(sender.pp)
except:
print " no attribute 'pp'"
try:
print " Now oo == {}".format(sender.oo)
except:
print " no attribute 'oo'"
class T01App(App):
def build(self):
return bl
def on_start(self):
self.root.ids.hb01.bind(on_press=on_press)
self.root.ids.hb02.bind(on_press=on_press2)
if __name__ == '__main__':
T01App().run()
I'm trying to execute this:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.audio import SoundLoader,Sound
from kivy.lang import Builder
Builder.load_string('''
<MenuPage>:
BoxLayout:
orientation:'vertical'
Button:
text:'play'
on_press:root.plays()
Button:
text:'stop'
on_press:root.stops()
''')
class Music(Sound):
def __init__(self):
self.sound = SoundLoader.load('/home/hosein/Music/Man.mp3')
class MenuPage(Screen):
def __init__(self):
self.M = Music()
def plays(self):
self.M.play()
def stops(self):
self.M.stop()
music = Music()
sm = ScreenManager()
menu = MenuPage(name='menu')
sm.add_widget(menu)
class TestApp(App):
def build(self):
return sm
TestApp().run()
https://gist.github.com/daryasary/f69e1d0444ae70ff5296
There should be just two buttons to play or stop a song.
But it doesn't work. What is the solution?
Also, is there any way to make the play and stop buttons into a single button, where the first touch plays the song and the second stops it?
If you want to use one button, you could try using Kivy's ToggleButton and play music when the toggle button's state is 'down' and not play music when the state is 'normal'.
<MenuPage>:
BoxLayout:
orientation:'vertical'
ToggleButton:
id: music_button
text:'play'
on_press:root.play_or_stop()
''')
class MenuPage(Screen):
def __init__(self):
self.M = Music()
def play_or_stop(self):
if self.music_button.state == 'down':
self.music_button.text = "Stop"
self.M.play()
else:
self.music_button.text = "Play"
self.M.stop()
Or, you could use a regular button that sets a variable to either True or False each time it's pressed. You can then have this value determine whether the music plays or stops.
The problem is that you're using Sound wrong. You should not be subclassing Sound or trying to create a new instance directly.
SoundLoader.load returns a Sound instance created using one of the available audio providers - this should be used instead. Try something like this:
class MenuPage(Screen):
def __init__(self):
self.M = SoundLoader.load('/home/hosein/Music/Man.mp3')
def plays(self):
self.M.play()
def stops(self):
self.M.stop()
def toggle(self):
self.M.state = 'play' if self.M.state == 'stop' else 'play'
return self.M.state
it should be something like this:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.audio import SoundLoader,Sound
from kivy.lang import Builder
Builder.load_string('''
<MenuPage>:
BoxLayout:
orientation:'vertical'
Button:
text:'song'
on_press:root.plays()
''')
class MenuPage(Screen):
M = SoundLoader.load('/home/hosein/Music/Man.mp3')
def plays(self):
if MenuPage.M.state == 'stop':
MenuPage.M.play()
else:
MenuPage.M.stop()
sm = ScreenManager()
menu = MenuPage(name='menu')
sm.add_widget(menu)
class TestApp(App):
def build(self):
return sm
TestApp().run()