I've been working with this code for a days now.
I cant make it work, my goal is to pass an arguments from a function from a different CLASS to another function in a different CLASS without affecting the 'self'
Please see sample code.
PY CODE:
`
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.button import Button
#Custom Button
class PassdataButton(Button):
def on_release(self):
ThisScreen.getters(datas=['5','3'])
class ThisScreen(Screen):
def getters(self,datas):
self.ids.gettersBox.text = f"There is {datas[0]} Apple's and {datas[0]} Banana's"
class SM(ScreenManager):
pass
kv_file = Builder.load_file('getData.kv')
class projectApps(App):
def build(self):
return kv_file
if __name__ == "__main__":
projectApps().run()
`
KV CODE:
SM:
ThisScreen:
name: 'ThisScreen'
<ThisScreen>:
canvas.before:
canvas:
Rectangle:
size: self.size
source: 'im-502378444.jpg'
BoxLayout:
padding: 10
orientation: 'vertical'
TextInput:
id: gettersBox
BoxLayout:
size_hint_y: .3
orientation: 'horizontal'
Button:
text: 'GetterButton'
on_release: root.getters(datas = 'this data is from GetterButton')
#CustomButton
PassdataButton:
text: "Apple's & Banana's"
Everytime I run the code and click the Custom Button I created, it's getting me an error
ThisScreen.getters(datas=['5','3'])
TypeError: getters() missing 1 required positional argument: 'self'
When I add a value for the missing positional argument for self like ThisScreen.getters(self=True, datas=['5','3']) its giving me different error and I cant access/call anymore other elements under the the getters() function.
self.ids.gettersBox.text = f"There is {datas[0]} Apple's and {datas[0]} Banana's"
AttributeError: 'bool' object has no attribute 'ids'
Hope you can help me with this simple code.
Thanks a bunch!
You have two options.
1. You can pass it direct from your kv file. Since you're already on the ThisScreen screen, you can just delete your on_relase method in python and call the function inside .kv from root like this:
PassdataButton:
text: "Apple's & Banana's"
on_release: root.getters(datas=['5', '3'])
2. Or if you want to use python for this, you will have to get your running app, the screen manager, then jump to the screen with the desired function, and only there you can call your getter:
class PassdataButton(Button):
def on_release(self):
App.get_running_app().root.get_screen('ThisScreen').getters(datas=['5', '3'])
# App -> App instance
# get_running_app() -> current running
# root -> Screen Manager
# get_screen('ThisScreen') -> Desired screen
Related
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
#MADE A CLASS FOR MAKING THE GUI
class Main_GUI(App):
pass
def ind(self):
print("hi")
kv = Main_GUI()
kv.run()
The above is the Python code which i have named sample.py.
Below is the Main_GUI.kv
BoxLayout:
orientation: "vertical"
Button:
text: "Hello"
on_press: ind()
Button:
text: "Btn2"
problem is that the function ind is not called up
Please help in rectifying the error.
ERROR
NameError: name 'ind' is not defined
Please Intimate me if any errors. I am new to kivy.
Your ind() method is defined outside of any class. Probably not what you want, but you can access it from within kv by adding:
#: import ind __main__.ind
to the start of your kv file.
A better approach is to just move that method inside your Main_GUI class:
class Main_GUI(App):
def ind(self):
print("hi")
and access it in kv using the app keyword:
Button:
text: "Hello"
on_press: app.ind()
The app refers to your application instance. See the documentation.
I am trying to make an app out of different .py files. But I don't know how to add them together, I have one main file, and one login file with plans to add a lot more, but with these I'm experimenting right now. They are pretty basic for now until I figure out this "bonding" between them and then I will start adding some more complex stuff. I tried couple of things and they didn't work, but I left them in code for you to see (I tried to make the app to start with MainWindow, and on press of the first button it goes to login page*). Here's the code and please help me.
*Right now when I press the button it gives me this error: OSError: exception: access violation writing 0x0000000080006010
this is main.py:
from kivy.lang import Builder
from kivy.app import App
import login
from kivy.uix.screenmanager import Screen
kv = Builder.load_string('''
<MainWindow>:
GridLayout:
cols:1
GridLayout:
rows:5
Button:
text:"NOVA ROBA"
on_release:
root.call_login()
Button:
text:"KUPCI"
Button:
text:"PRODATO"
Button:
text: "AGRONOMI"
Button:
text: "STANJE U MAGACINU"
''')
class MainWindow(Screen):
def call_login(self):
login.app().run()
pass
class main_app(App):
def build(self):
return MainWindow()
if __name__ == '__main__':
main_app().run()
this is login.py:
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.app import App
class Login(Screen, App):
def build(self):
return
pass
kv = Builder.load_string('''
<Login>:
name:"login"
GridLayout:
rows:2
GridLayout:
cols:2
Label:
text:"Password: "
TextInput:
id:passwd
multiline: False
Button:
text: "Submit"
on_release:
passwd.text = ""
''')
class app(App):
def build(self):
return Login()
if __name__ == "__main__":
app().run()
You are creating 2 apps, which is not needed. Instead of inheriting from both Screen and App in the Loginscreen, inherit only from Screen. Then create a ScreenManager in your main.py's build method and then add the imported loginscreen as a widget, to switch to the new screen, use self.manager.current = "login" in the call_login method of MainWindow
class app(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainWindow())
sm.add_widget(Login())
return sm
My problem is probably mainly because of lack of skills but i couldnt find any similar posts. So I have textinputs on mainscreen. I need to have button in secondscreen which clear these textinputs.
I couldnt figure out how to can i call the clear_inputs method and pass textinput as arguments. I think with this clear_inputs method i could empty those textfields, but how to bind it to that button in another page?
Py.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import StringProperty, BooleanProperty
class MainScreen(Screen):
pass
class SecondScreen(Screen):
def clear_inputs(self, text_inputs):
for text_input in text_inputs:
text_input.text = ''
class ScreenManagement(ScreenManager):
def changescreen(self, value):
try:
if value !='main':
self.current = value
except:
print('No Screen named'+ value)
class testiApp(App):
def build(self):
self.title = 'Hello'
testiApp().run()
KV.
ScreenManagement:
MainScreen:
name:'Main'
SecondScreen:
name:'Page2'
<MainScreen>:
name:'Main'
BoxLayout:
orientation:'vertical'
GridLayout:
cols:2
Label:
text:'testfield1'
TextInput:
id: textfield1
Label:
text:'testfield2'
TextInput:
id: textfield2
Button:
text:'Next Page'
on_release: app.root.current ='Page2'
<SecondScreen>:
name:'Page2'
Button:
text:'Clear textfields'
on_release:
The following enhancements (kv file & Python script) are required to clear the TextInput's text in another screen.
kv file
In order to access the TextInput widgets, add an id: container to the instantiated object, GridLayout:
Each screen has by default a property manager that gives you the instance of the ScreenManager used.
Bind the on_release event to method, clear_inputs() without any argument
Snippets - kv file
<MainScreen>:
name:'Main'
BoxLayout:
orientation:'vertical'
GridLayout:
id: container
...
Button:
text:'Next Page'
on_release: root.manager.current ='Page2'
<SecondScreen>:
name:'Page2'
Button:
text:'Clear textfields'
on_release: root.clear_inputs()
Py file
Add import statement, from kivy.uix.textinput import TextInput
Use ScreenManager's get_screen('Main') function to get the instantiated object, MainScreen
Use for loop to traverse the children of GridLayout: via ids.container
Use isinstance() function to check for TextInput widget
Snippets - Py file
from kivy.uix.textinput import TextInput
...
class SecondScreen(Screen):
def clear_inputs(self):
main = self.manager.get_screen('Main')
for child in reversed(main.ids.container.children):
if isinstance(child, TextInput):
child.text = ''
If I'm undestanding correctly, what you want to do is use a button in page X (Main?) to change the text in page Y (Page2?). I'm not an expert on Kivy, so there might be a better way, but here are a few thoughts:
1) I tried giving a class attribute parent to all screens, which turned out to be a bad idea because the name was already in used by Kivy. You could simply change it to parent_ or something and give it a go yourself. What you want is to pass the "parent" as a parameter to __init__ on creation:
class ScreenManagement(ScreenManager):
def __init__(self, children_, **kwargs):
# you might need to save the children too
self.children_ = children_
def add_child(self, child):
# maybe as dict
self.children_[child.name] = child
class SecondScreen(Screen):
def __init__(self, parent_, **kwargs):
super().__init__(**kwargs)
# maybe the screen manager or the main app?
self.parent_ = parent_
self.name_ = "Second"
....
def clear_inputs(self, text_inputs):
....
class MainScreen(Screen):
def __init__(self, parent_, **kwargs):
super().__init__(**kwargs)
# maybe the screen manager or the main app?
self.parent_ = parent_
# you may want to
....
# Get the appropriate screen from the parent
self.parent_.children_["Second"].clear_inputs(...)
2) I also saw another way from a youtube tutorial. Instead of running your app directly, assign it to a variable and reference that variable. This might still need tampering for advanced usecases:
# Use the global variable within your classes/methods
class Whatever:
def whatever2(self, params):
app.something()
....
app = testiApp()
app.run()
>> BACKGROUND :
I want to update/change the text of a Button in the SecondScreen with a press of a Button in the MainScreen. Well I did some research and did what I want, and when I checked in the terminal the text did change. BUUT, the text shown on the SecondScreen did not.
>> THIS IS WHAT I DID :
((Mind you that I'm only using snippets of code for example, I'm going to post the whole code below.))
Button:
text:"PRESS TO CHANGE TEXT"
on_press:
root.funcself()
## on press it goes to it's root and do the "funcself" function in it
which is :
class MainScreen(Screen):
def funcself(self):
app.second.funcscreen()
## it re-directs to the SecondScreen and do the "funcscreen" function
which is :
class SecondScreen(Screen):
def funcscreen(self):
self.ids["button"].text = "SUPPOSED TO CHANGE TO THIS"
and then I checked if I did it successfully by doing print(self.ids["button"].text), and yes!
It did change, but when I navigated to the next screen, the text shown still didn't change.
Anyone mind helping and explaining?
FULL CODE :
python file :
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
def funcself(self):
app.second.funcscreen()
class SecondScreen(Screen):
def funcscreen(self):
value = self.ids["button"]
self.ids["button"].text = "SUPPOSED TO CHANGE TO THIS"
kv = Builder.load_file("reproduce.kv")
class reproduce(App):
second = SecondScreen()
def build(self):
return kv
def change_screen(self, x):
scrnmngr = self.root.ids["sm"]
scrnmngr.current = x
def check(self):
print(self.second.ids["button"].text)
if __name__ == "__main__":
app = reproduce()
app.run()
kivy file :
<MainScreen>:
GridLayout:
rows:2
Label:
text: "PRESS TO GO TO THE NEXT PAGE"
GridLayout:
cols:2
Button:
text:"PRESS TO CHANGE TEXT"
on_press:
root.funcself()
Button:
text:">>>"
on_press:
app.change_screen("second")
root.manager.transition.direction = "left"
<SecondScreen>:
GridLayout:
rows:2
Label:
id:label
text: "PRESS TO CHECK AND RETURN TO PREV PAGE"
Button:
id:button
text:"TEXT BEFORE CHANGE"
on_press:
app.change_screen("first")
root.manager.transition.direction = "right"
app.check()
GridLayout:
cols: 1
ScreenManager:
id:sm
MainScreen:
id:main
name:"first"
SecondScreen:
id:second
name:"second"
Root Cause
It did not change because there are two instances of SecondScreen() i.e. one instantiated in the kv file and the other one instantiated in the App class, reproduce(). The view presented is created from the kv file and the second instance does not has a view associated to it.
Solution
There are two solutions to the problem, and remove second = SecondScreen() from the App class.
Kivy Screen ยป default property manager
Each screen has by default a property manager that gives you the
instance of the ScreenManager used.
Using get_screen()
class MainScreen(Screen):
def funcself(self):
self.manager.get_screen('second').funcscreen()
Using App.get_running_app() & ids
class MainScreen(Screen):
def funcself(self):
App.get_running_app().root.ids.second.funcscreen()
Example
In the following example, there are two solutions provided but one of it is commented off.
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
class MainScreen(Screen):
def funcself(self):
self.manager.get_screen('second').funcscreen()
# App.get_running_app().root.ids.second.funcscreen()
class SecondScreen(Screen):
def funcscreen(self):
value = self.ids["button"]
self.ids["button"].text = "SUPPOSED TO CHANGE TO THIS"
kv = Builder.load_file("reproduce.kv")
class reproduce(App):
def build(self):
return kv
def change_screen(self, x):
scrnmngr = self.root.ids["sm"]
scrnmngr.current = x
def check(self):
print(self.second.ids["button"].text)
if __name__ == "__main__":
reproduce().run()
Output
The second attribute you define in your app class, is a new instantiation of the screen, and not really the instance you got in your screenmanager, which you add in kv. This is why when you check, you see its changed, but not on the right instance. And again when you call app.second.func, from mainscreen, again its the wrong instance.
But your app always has a root. In your case its the gridlayout. And every screen has a manager. There are a couple of ways to acces it. But you can do like this.
In your mainscreen class in kv:
Button:
text:"PRESS TO CHANGE TEXT"
on_press:
root.manager.get_screen("second").ids["button"].text = "Something"
Here it gets the screenmanager, and uses its get_screen() method to get the screen named second, and then the id's of that kv rule.
I've defined two buttons: one in kv and one in Python. They are located in different screens and are used to navigate between them. What I found strange is that the button that was defined in Python successfully switched the screen, while the one defined in kv did not. Perhaps I'm not accessing the App class method properly?
Here is the code of the issue:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
Builder.load_string('''
<MyScreen1>:
Button:
id: my_bt
text: "back"
on_release: app.back
''')
class MyScreen1(Screen):
pass
class TestApp(App):
def here(self, btn):
self.sm.current = "back"
def back(self, btn):
self.sm.current = "here"
def build(self):
self.sm = ScreenManager()
s1 = Screen(name = "here")
bt = Button(text = "here",
on_release = self.here)
s2 = MyScreen1(name = "back")
#s2.ids['my_bt'].bind(on_release = self.back)
self.sm.add_widget(s1)
s1.add_widget(bt)
self.sm.add_widget(s2)
return self.sm
TestApp().run()
So if I define the switching function in kv (on_release), I can't go to the "here" screen. But if I uncomment that line in Python and comment the on_release: app.back instead, everything works fine.
I'm pretty sure that this is the correct way to access the current app, since it doesn't give me any errors (which means that the method was successfully located)
That's a subtle difference between kv and python: In kv you actually have to write the callback as a function call (a python expression), in this case:
on_release: app.back(self)