This is a sample program to display "Hello" or "Good-bye" when you push the button 1 or 2, simplified from my real program.
What I want to do is to execute function btn1() and display "Hello" when the program starts, without pushing the button 1.
What lines of code and where should I add?
When I put "Greetings().btn1()" in class Greetings, error: "name Greetings is not defined" occured. Then I put the same command after def build(self) in class GreetingApp, but nothing happened.
In main.py,
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class Greetings(BoxLayout):
greeting=StringProperty()
def btn1(self):
self.greeting='Hello.'
def btn2(self):
self.greeting='Good-bye.'
class GreetingApp(App):
def build(self):
return Greetings()
GreetingApp().run()
And in greeting.kv,
#: import main main
Greetings:
<Greetings>:
orientation: "vertical"
Label:
text: root.greeting
BoxLayout:
orientation: "horizontal"
Button:
text: '1'
on_press: root.btn1()
Button:
text: '2'
on_press: root.btn2()
Just call btn1 before returning root widget:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
Builder.load_string('''
<Greetings>:
orientation: "vertical"
Label:
text: root.greeting
BoxLayout:
orientation: "horizontal"
Button:
text: '1'
on_press: root.btn1()
Button:
text: '2'
on_press: root.btn2()
''')
class Greetings(BoxLayout):
greeting = StringProperty()
def btn1(self):
self.greeting = 'Hello.'
def btn2(self):
self.greeting = 'Good-bye.'
class GreetingApp(App):
def build(self):
root = Greetings()
root.btn1()
return root
GreetingApp().run()
The __init__ method runs every time you create an instance of a class.
So what ever you want to do in creation time of the object, you can put in __init__. Initiate the object.
Put this in your Greetings class.
def __init__(self, **kwargs):
super(Greetings, self).__init__(**kwargs)
self.btn1()
Then you call it when you create the object.
Related
I want to make a variable that can be modified by one screen, then the variable can change the text of different screen.
Here is the python file
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.properties import NumericProperty
import random
value = NumericProperty(0)
class MainWindow(Screen):
def vval(self, root):
root.value = 1
def vvval(self, root):
root.value = 2
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("testing.kv")
class testing(App):
def build(self):
return kv
if __name__ == "__main__":
testing().run()
and here is the kivy file
WindowManager:
MainWindow:
SecondWindow:
<MainWindow>:
name: "main"
GridLayout:
size: root.width, root.height
rows: 2
Button:
text: "print 1"
on_release:
root.vval(root)
app.root.current = "second"
Button:
text: "print 2"
on_release:
root.vvval(root)
app.root.current = "second"
<SecondWindow>:
name: "second"
Label:
text: "successfully printed ", root.value
What I expected to happens is when I click one of the button in the the MainWindow, the variable, which I named it "value" will be modified to 1 or 2 depending on what button i click, then the screen changed to the SecondWindow and display the text "successfully printed x", the value of x is depends to the "value" variable.
I'm still new on kivy, if there is some error or ambiguity, I am sorry. Please share me your knowledge about this, it will be appreciated.
Generally, one would do this with a StringProperty object. The code below shows a working example, although I did take liberties in restructuring some things about your code. I admit I had some challenges with .bind of the StringProperty which I ended up resolving by building the ScreenManager object in the Python code instead of the .kv lines.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty, StringProperty
value = NumericProperty(0)
Builder.load_string('''
<MainWindow>
name: "main"
GridLayout:
size: root.width, root.height
rows: 2
Button:
text: "print 1"
on_release:
# arguments can be passed, including a 'self' of the button object
app.kv_button("1", "second", self)
# app.root.current = "second"
Button:
text: "print 2"
on_release:
# arguments can be passed, including a 'self' of the button object
app.kv_button("2", "second", self)
# app.root.current = "second"
<SecondWindow>
name: "second"
Label:
# maps to a StringProperty, root. would be the widget, this is app. which is the App()
text: app.result_text
''')
class MainWindow(Screen):
pass
class SecondWindow(Screen):
value: str
pass
class WindowManager(ScreenManager):
pass
# Builder.load_file("testing.kv")
class testing(App):
result_text = StringProperty()
def kv_button(self, _value: str, next_screen: str, *args):
print(f"{self} {_value} {args}")
self.result_text = _value
# change the screen using the WindowManager object that has been kept as a reference
self._main.current=next_screen
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._main = WindowManager()
self._main.add_widget(MainWindow())
self._main.add_widget(SecondWindow())
def build(self):
return self._main
if __name__ == "__main__":
testing().run()
I would like to have code in KV and PY files that will display a YES/NO popup that I can instantiate anywhere in my app to pop the question.
Will a good kindly person help me to convert code I found on internet to KV file and PY file content.
I would like to be able to create an instance of this popup at any point in my app for display.
This is the sample I am working from:
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty()
def __init__(self,**kwargs):
self.register_event_type('on_answer')
super(ConfirmPopup,self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupTest(App):
def build(self):
content = ConfirmPopup(text='Do You Love Kivy?')
content.bind(on_answer=self._on_answer)
self.popup = Popup(title="Answer Question",
content=content,
size_hint=(None, None),
size=(480,400),
auto_dismiss= False)
self.popup.open()
def _on_answer(self, instance, answer):
print "USER ANSWER: " , repr(answer)
self.popup.dismiss()
if __name__ == '__main__':
PopupTest().run()
I have tried the following:
KV file:
<YesNoPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
and PY file:
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
class YesNoPopup(GridLayout):
text = StringProperty()
def __init__(self, **kwargs):
self.register_event_type('on_answer')
super(YesNoPopup, self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupTest:
def build(self):
content = YesNoPopup(text='Are you sure?')
content.bind(on_answer=self._on_answer)
self.popup = Popup(title='PIN',
content=content,
size_hint=(0.6, 0.4),
auto_dismiss=False)
self.popup.open()
def _on_answer(self, instance, answer):
print
"USER ANSWER: ", repr(answer)
self.dismiss()
Then in the App
def pin_action(self):
pop = PopupTest()
pop.build()
But this only shows popup with Title and separator bar.
I will appreciate explanations of why you do what you do so that I as an 82 year old can learn and understand since my OO education is 22 years old and my Python Kivy is still in the learning stage.
Thanks a million
You can simplify your code by just calling the _on_answer() method directly from the on_release: of the Button. Here is a modified version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: app._on_answer(root, 'yes')
Button:
text: 'No'
on_release: app._on_answer(root, 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty()
class PopupTest(App):
def build(self):
content = ConfirmPopup(text='Do You Love Kivy?')
self.popup = Popup(title="Answer Question",
content=content,
size_hint=(None, None),
size=(480, 400),
auto_dismiss=False)
self.popup.open()
def _on_answer(self, instance, answer):
print("USER ANSWER: ", repr(answer))
self.popup.dismiss()
if __name__ == '__main__':
PopupTest().run()
Now that I understand your question, the main problem is just that the bind() method creates a WeakMethod for the bound method. In your code:
def pin_action(self):
pop = PopupTest()
pop.build()
An instance of PopupTest is created (which contains the bound _on_answer() method). But after the build() method of PopupTest is executed, the Popup is opened and the pop variable gets garbage collected. So the _on_answer() method is also garbage collected as a result. Since bind() creates a WeakMethod, there is no method left for the dispatched event to trigger.
There are two ways (that I know of) to fix this issue. One is to just modify the pin_action() method to retain a reference to PopTest so that is does not get garbage collected:
def pin_action(self):
self.pop = PopupTest()
self.pop.build()
Another way is to use fbind() instead of bind(), since fbind() keeps a reference to the bound method, so it cannot be garbage collected:
class PopupTest:
def build(self):
content = YesNoPopup(text='Are you sure?')
# content.bind(on_answer=self._on_answer)
content.fbind('on_answer', self._on_answer)
I made this to code to show what I want but fail to do. I get the message: "AttributeError: 'super' object has no attribute 'getattr'" when I try to acces a widget.
Someone please explain how to use widget id:s so that you can acces them from anywhere using python code, i'm sure I'm doing something fundamentally wrong.
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager, SlideTransition
from kivy.lang.builder import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
import random
Builder.load_string('''
<Manager>:
id: screen_manager
SettingScreen:
id: settings
name: 'settings'
manager: 'screen_manager'
MainScreen:
id: mainscreen
name: 'mainscreen'
manager: 'screen_manager'
ThirdScreen:
id: thirdscreen
name: 'thirdscreen'
manager: 'screen_manager'
<SettingScreen>:
BoxLayout:
id: settingbox
orientation: "vertical"
TextInput:
id: getthis
text: "this is the data"
font_size: 40
size_hint: 1,0.1
Button:
text: "NEXT SCREEN"
font_size: 40
size_hint: 1,0.1
on_release:
app.root.current = "mainscreen"
app.root.transition.direction="left"
<MainScreen>:
BoxLayout:
orientation: 'vertical'
Label:
id: changethis
text: "to be changed"
Button:
text: "Push this to make above into the text from TextInput on last screen"
on_release:
app.root.ids.changethis.text = app.root.ids.getthis.text #<<<<<
Button:
text: "Go to third Screen"
on_release:
app.root.current = "thirdscreen"
app.root.transition.direction="left"
<ThirdScreen>
put_label_here: put_label_here
BoxLayout:
orientation: 'horizontal'
Button:
text: 'Make make label'
on_release:
self.parent.parent.makelabel()
BoxLayout:
orientation: 'vertical'
id: put_label_here
''')
class MainScreen(Screen):
def __init__(self, **kwargs):
Screen.__init__(self, **kwargs)
class SettingScreen(Screen):
def __init__(self, **kwargs):
super(SettingScreen, self).__init__(**kwargs)
class ThirdScreen(Screen):
def __init__(self, **kwargs):
super(ThirdScreen, self).__init__(**kwargs)
def makelabel(self): #this should make a Label with text from TextInput on SettingScreen
print('Runs function makelabel')
thelabel=Label(text=self.parent.settings.settingbox.getthis.text)
self.put_label_here.add_widget(thelabel)
class Manager(ScreenManager):
pass
sm = ScreenManager(transition=SlideTransition())
sm.add_widget(MainScreen(name='mainscreen'))
sm.add_widget(SettingScreen(name='settings'))
sm.add_widget(SettingScreen(name='thirdscreen'))
class testApp(App):
def build(self):
return Manager()
if __name__ == "__main__":
testApp().run()
Well, I can fix this for you, but honestly I don't think you are making the right use of kv templates. But it will work anyway.
A new template is a rule by itself, it has its own root, so in return the application root (app.root) won't have direct access to children's ids defined inside their own templates. So you have to reach its direct child which makes a template containing the child you want to access then you reach that child by its id.
For instance, in your kv code, you will need to change line 63 (kv) from:
app.root.ids.changethis.text = app.root.ids.getthis.text
to:
app.root.ids.mainscreen.ids.changethis.text = app.root.ids.settings.ids.getthis.text
and line 99 (python) from:
thelabel=Label(text=self.parent.settings.settingbox.getthis.text)
to:
thelabel=Label(text=self.parent.ids.settings.ids.getthis.text)
Why shouldn't root (or any parent widget) access the ids defined inside separate templates ?
Templates, or Dynamic classes are made for re-usability, just like any none-static class.
Consider the following scenario:
BoxLayout:
CustomWidget:
id: wdg1
CustomWidget:
id: wdg2
Button:
text: 'change text of first label'
on_press: root.ids.lbl.text = 'new text'
<CustomWidget#Widget>:
Label:
text: 'some text'
id: lbl
Now The root widget have two instances of CustomWidget as children, thus two children Labels having the id 'lbl'. Which one should be called if the root called lbl directly?
To handle this, you can call lbl from its direct template instance. Thus: root.ids.wdg1.ids.lbl.text for the label inside the first widget, or root.ids.wdg2.ids.lbl.text for the label inside the second one.
In python2.7 + kivy1.9, I use AsyncImage like:
class Foo(BoxLayout):
..def bar(self):
....file_name=StringProperty()
..
..
....self.file_name="/../../image.png"
..
and in kivy,
BoxLayout:
..AsyncImage:
....source: root.file_name
Before the second call of function bar I change the content of image.png.
But the image displayed doesn't change.
I tried "nocache: True" after source command in kivy but it doesn't work.
How can I get the correct display everytime I call the func bar.
The problem is that you declare file_name property in each bar call. The file_name property must be a class attribute:
main.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class RootWidget(BoxLayout):
image_path = StringProperty('image1.png') # <<<<<<<<
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
def change_image(self, path):
self.image_path = path
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
test.kv:
<RootWidget>:
orientation: "vertical"
AsyncImage:
id: image
source: root.image_path
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Image 1'
on_press: root.change_image("image1.png")
Button:
text: 'Image 2'
on_press: root.change_image("image2.png")
EDIT
If you would like to use only one file name and change the content of the image, you need call reload method (with nocache property True):
main.py:
import os
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class AsyncTest(BoxLayout):
img = ObjectProperty()
def show_icon(self):
self.img.source = "/sdcard/archive/icon_0.png"
def switch(self):
os.system("mv /sdcard/archive/icon_1.png /sdcard/archive/icon_0.png")
self.img.reload()
class TestApp(App):
def build(self):
return AsyncTest()
if __name__ == '__main__':
TestApp().run()
test.ḱv:
<AsyncTest>:
orientation: "vertical"
img: asyn_image
AsyncImage:
id: asyn_image
nocache: True
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Show'
on_press: root.show_icon()
Button:
text: 'Switch'
on_press: root.switch()
Output:
I need a way that, a class which has a button(go to for advanced options), at release a pop up opens(has own class) inside it there is texet inputand a button whatever user typing in it i need at clicking (write button) to send it back to the first class (that will print the texet input values)
***********python file***********
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class btn_(BoxLayout):
pass
class Var():
def PP_(self):
xx = self.textinput1.text
print (xx)
class Editor(App):
zozo = Var()
def build(self):
return btn_()
if __name__ == '__main__':
Editor().run()
****KV file *********
#:import Factory kivy.factory.Factory
<btn_>:
Button:
text: "Open PopUp"
on_release: Factory.Popup().open()
<Popup>:
textinput1: textinput1
BoxLayout:
TextInput:
id: textinput1
hint_text: "Write something"
Button:
text: "Write"
on_release: app.zozo.PP_()
The solution for passing the TextInput.text from class rule, <Popup>: in kv file to class, Var() in Python code is as follow:
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class btn_(BoxLayout):
pass
class Var():
def PP_(self, txt):
print(txt)
class Editor(App):
zozo = Var()
def build(self):
return btn_()
if __name__ == '__main__':
Editor().run()
editor.kv
#:import Factory kivy.factory.Factory
<btn_>:
Button:
text: "Open PopUp"
on_release: Factory.Popup().open()
<Popup>:
BoxLayout:
TextInput:
id: textinput1
hint_text: "Write something"
Button:
text: "Write"
on_release: app.zozo.PP_(textinput1.text)