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)
Related
I want to have all the variables for an app defined in a .py module separate from the main.py file. Then I want to use those variables inside a kivy file.
I have tried the code below with several variations and there are always errors. The module works fine for defining for example different Fonts but, when it comes to properties(ObjectProperty) it does not work.
Project structure:
KivyFolder
modules(folder)
variables.py
screens(folder)
screen1.kv
screen2.kv
main.py
main.kv
Code:
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from os import listdir
from kivy.lang import Builder
kv_screens = './screens/'
for kv in listdir(kv_screens):
Builder.load_file(kv_screens+kv)
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file('main.kv')
class MainApp(App):
import modules.variables
def build(self):
return presentation
if __name__ == '__main__':
MainApp().run()
main.kv
#:kivy 1.10.1
ScreenManagement:
Screen1:
Screen2:
screen1.kv
#:kivy 1.10.1
<Screen1>:
name: 's1'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Screen 1'
font_size: app.font_size_one
Button:
text: 'Go To Screen 2'
on_press:
root.manager.current = 's2'
screen2.kv
#:kivy 1.10.1
<Screen2>:
name: 's2'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Screen 2'
font_size: '20dp'
Button:
text: 'Go To Screen 1'
on_press:
root.manager.current = 's1'
variables.py
from kivy.properties import ObjectProperty
font_size_one = ObjectProperty('40dp')
font_size_two = ObjectProperty('60dp')
Now doing it like this produces this error:
AttributeError: 'NoneType' object has no attribute 'bind'
I would expect to be able to call a variable since it is imported at MainApp(App), so 'app.variable name should read it.
I have also tried:
font_size: app.variables.font_size_one
and:
font_size: app.modules.variables.font_size_one
Then I tried importing the module before everything else with the same result.
I also tried putting it into a class and calling that class in .kv like this:
variables.py
from kivy.properties import ObjectProperty
class FVArs():
font_size_one = ObjectProperty('40dp')
font_size_two = ObjectProperty('60dp')
screen1.kv
#:kivy 1.10.1
<Screen2>:
name: 's2'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Screen 2'
font_size: FVars().font_size_two
Button:
text: 'Go To Screen 1'
on_press:
root.manager.current = 's1'
Error:
NameError: name 'FVars' is not defined
Thank you for your answers.
Bye
EDIT: Since the answer below works on the example code but not in my actual project I am adding the relevant project code:
Project structure:
KivyFolder
modules(folder)
global_variables.py
screens(folder)
Intro.kv
(and other screens as separate .kv files
main.py
main.kv
main.py
from kivy.app import App
from modules.global_variables import globalVariables
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.label import Label
from kivy.properties import StringProperty, NumericProperty, ObjectProperty, Property
from kivy.core.window import Window
background_color = (0.1, 0.1, 0.1, 1)
Window.clearcolor = background_color
from os import listdir
from kivy.lang import Builder
kv_screens = './screens/'
for kv in listdir(kv_screens):
Builder.load_file(kv_screens+kv)
kv_elements = './elements/'
for kv in listdir(kv_elements):
Builder.load_file(kv_elements+kv)
class IntroScreen(Screen):
user_name = StringProperty("Troll")
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("main.kv")
class MainApp(App):
title_size = globalVariables.title_size
def build(self):
self.title = 'Kivy Omnia!'
return presentation
if __name__ == "__main__":
app = MainApp()
app.run()
global_variables.py
from kivy.properties import ObjectProperty
class globalVariables():
title_size = ObjectProperty('101sp')
main.kv
#:kivy 1.10.1
ScreenManagement:
IntroScreen:
intro.kv
#:kivy 1.10.1
<IntroScreen>:
name: "intro"
BoxLayout:
orientation: 'vertical'
Label:
text: "Hello " + root.user_name + "!"
font_size: app.title_size
Error:
kivy.lang.builder.BuilderException: Parser: File
"C:\Users\Ghost\CodeProjects\phyton\kivyomnia\screens\Intro.kv", line
10: ...
8: Label:
9: text: "Hello " + root.user_name + "!"
10: font_size: app.title_size
11: font_name: "Exo-B"
12: AnchorLayout: ... BuilderException: Parser: File "C:\Users\Ghost\CodeProjects\phyton\kivyomnia\screens\Intro.kv", line
10: ...
8: Label:
9: text: "Hello " + root.user_name + "!"
10: font_size: app.title_size
11: font_name: "Exo-B"
12: AnchorLayout: ... AttributeError: 'NoneType' object has no attribute 'bind' File
"C:\Python37-32\lib\site-packages\kivy\lang\builder.py", line 249, in
create_handler
return eval(value, idmap), bound_list File "C:\Users\Ghost\CodeProjects\phyton\kivyomnia\screens\Intro.kv", line
10, in
font_size: app.title_size File "C:\Python37-32\lib\site-packages\kivy\lang\parser.py", line 75, in
getattribute
object.getattribute(self, '_ensure_app')() File "C:\Python37-32\lib\site-packages\kivy\lang\parser.py", line 70, in
_ensure_app
app.bind(on_stop=lambda instance:
File "C:\Python37-32\lib\site-packages\kivy\lang\builder.py", line
615, in _apply_rule
rctx['ids']) File "C:\Python37-32\lib\site-packages\kivy\lang\builder.py", line 254, in
create_handler
cause=tb)
So if just reposition the:
title_size = globalVariables.title_size
... inside the IntroScreen(Screen) and then inside Intro.kv:
font_size: root.title_size
... it works. But the point is to have all the global variables available across all screens.
Another question:
if app.something gets a what is inside main app class
and root.something gets what is in the root class of that file then how do you call something from another class that is not app or root. If that makes sense.
You can import your FVars class with this line:
from modules.variables import FVars
Then inside your MainApp class, you can create a class variable with the data of FVars's class variable.
class MainApp(App):
f1 = FVars.font_size_one
You can then access it on your kv files:
font_size: app.f1
Here is some basic example:
main.py
from kivy.app import App
from kivy.lang import Builder
from modules.variables import FVars
class Test(App):
f1 = FVars.font_size_one
def build(self):
return Builder.load_file("test.kv")
Test().run()
test.kv
BoxLayout:
Label:
text: "Hello"
font_size: app.f1
modules/variables.py
from kivy.properties import ObjectProperty
class FVars():
font_size_one = ObjectProperty('40dp')
font_size_two = ObjectProperty('60dp')
UPDATE
To prevent the error, you can load the kv files inside the build method.
def build(self):
self.title = 'Kivy Omnia!'
kv_screens = './screens/'
for kv in listdir(kv_screens):
Builder.load_file(kv_screens+kv)
kv_elements = './elements/'
for kv in listdir(kv_elements):
Builder.load_file(kv_elements+kv)
presentation = Builder.load_file("main.kv")
return presentation
There's a plenty of ways on how to access a variable inside a class which is neither an app nor root, you can just set the id property of that widget.
Here's a basic example:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class SecondClass(BoxLayout):
someNumber = 123 # Let's say we wanted to print the value of this variable when we press the button.
class FirstClass(BoxLayout):
pass
class TestApp(App):
def build(self):
return FirstClass()
TestApp().run()
test.kv
<SecondClass>:
<FirstClass>:
Button:
text: "Print the Numbah"
on_press: print(app.root.ids.secondclass.someNumber) # We can do something like this
SecondClass:
id: secondclass
We can also do something like this:
<SecondClass>:
<FirstClass>:
Button:
text: "Print the Numbah"
on_press: print(root.ids.secondclass.someNumber)
SecondClass:
id: secondclass
Or this:
<SecondClass>:
<FirstClass>:
Button:
text: "Print the Numbah"
on_press: print(secondclass.someNumber)
SecondClass:
id: secondclass
Or even this:
<SecondClass>:
<FirstClass>:
second: secondclass
Button:
text: "Print the Numbah"
on_press: print(app.root.second.someNumber)
SecondClass:
id: secondclass
And this:
<SecondClass>:
<FirstClass>:
second: secondclass
Button:
text: "Print the Numbah"
on_press: print(root.second.someNumber)
SecondClass:
id: secondclass
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.
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:
Hello I am facing problem when I am using a GUI in python using Kivy. I am using TabbedPanel.
TabbedPanelItem:
text: 'apple'
BoxLayout:
Label:
text: 'Label1'
Entry:
text: 'Entry1'
CheckBox:
text: 'CheckBox1'
Button:
text: 'Button1'
TabbedPanelItem:
text: 'Grape'
BoxLayout:
Label:
text: 'Label1'
Button:
text: 'Button1'
Several things:
you can have only one build method
return in the second build method, is incorrectly indented, should be same as for
you can always only have one App class not class AccordionApp(App): and class KivyGuiApp(App):
Here is a smaller version of your app from which you should be able to implement more from
'''
TabbedPanel
============
Test of the widget TabbedPanel.
'''
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem
from kivy.lang import Builder
from kivy.uix.checkbox import CheckBox
from kivy.uix.accordion import Accordion, AccordionItem
from kivy.uix.button import Button
from kivy.app import App
Builder.load_string("""
<Test>:
TabbedPanelItem:
text: 'apple'
BoxLayout:
Label:
text: 'Label1'
Label:
text: 'Entry1'
CheckBox:
text: 'CheckBox1'
Button:
text: 'Button1'
""")
class Test(TabbedPanel):
pass
class KivyGuiApp(App):
def build(self):
test = Test()
acc = Accordion()
for x in range(5):
item = AccordionItem(title='Table %d' % x)
item.add_widget(Button(text='apple\n'))
item.add_widget(Button(text='Grape\n'))
item.add_widget(Button(text='Lemon\n'))
acc.add_widget(item)
panel = TabbedPanelItem()
panel.add_widget(acc)
test.add_widget(panel)
return test
if __name__ == '__main__':
KivyGuiApp().run()
In my RootWidget I have a label and two buttons. I want to dynamically change the text of the label whenever one of the buttons is clicked.
Here's a minimal working example of how I do it at the moment.
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
w = Builder.load_string('''
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
Button:
text: '1'
on_press: label.text = self.text
Button:
text: '2'
on_press: label.text = self.text
''')
class RootWidget(BoxLayout):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()
Obviously I want to refactor the line on_press: label.text = self.text. My first tries ended in
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
MyButton:
text: '1'
MyButton:
text: '2'
<MyButton>:
on_press: label.text = self.text
But obviously the MyButton-class doesn't know the property label of the RootWidget-class. And class rules inside class rules is also not allowed.
Is there a way of accomplishing binding the on_press-action dynamically?
You can refer to the Label like this:
<MyButton#Button>:
on_press: self.parent.ids.label.text = self.text
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
MyButton:
text: '1'
MyButton:
text: '2'
It's quite simple actually, because through kv you can access parent of a widget/rule easily with self.parent, so your code would look like:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
w = Builder.load_string('''
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
But:
text: '1'
But:
text: '2'
<But>:
on_press: self.parent.label.text = self.text
''')
class RootWidget(BoxLayout):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()