I want to specify a special button so I don't have to adjust every button I use, however I want it's event to trigger a function in a different class.
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
from kivy.properties import ObjectProperty
class FancyButton(Button):
imp = ObjectProperty(None)
class Important(StackLayout):
def NoInspiration(self, smile):
print("Received: {}".format(smile))
class TestApp(App):
def build(self):
pass
if __name__ == '__main__':
TestApp().run()
test.kv (working)
#:kivy 1.9.0
<FancyButton#Button>:
on_release: self.parent.NoInspiration(':)')
<Important>:
id: imp
FancyButton:
text: "smiley"
BoxLayout:
Important
Received: :)
test.kv (not working)
#:kivy 1.9.0
<FancyButton>:
on_release: self.parent.NoInspiration(':)')
<Important>:
id: imp
BoxLayout:
FancyButton:
text: "smiley"
BoxLayout:
Important
In the 2nd test.kv I added 'BoxLayout:' in front of FancyButton and suddenly I get the error:
AttributeError: 'BoxLayout' object has no attribute 'NoInspiration'
Question
Why does self.parent only refer to the direct parent and not < Important > in the 2nd example?
How can I have on_release: in < FancyButton > point to the function NoInspiration() while keeping the function in < Important >?
Follow up questions
Kivy rule inherence with add_widget()
Kivy outside rule inherence 2
Change the kv to this
<FancyButton>:
on_release: self.imp.NoInspiration(':)')
<Important>:
id: imp
BoxLayout:
FancyButton:
text: "smiley"
imp: root
BoxLayout:
Important
Related
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
I am just learning some styles and designs of kivymd, but for some reason when the code runs
kv = Builder.load_file(MainLayout.kv), it raises the Exception(
kivy.uix.screenmanager.ScreenManagerException: ScreenManager accepts only Screen widget.
).
I just can't get it to run and any further. It has been raising this Exception no matter what changes I make.
And Just for Reference, this is the Code for the Main.py
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.app import MDApp
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.list import MDList
from kivymd.theming import ThemableBehavior
from kivy.core.window import Window
class Login(Screen):
pass
class Settings(Screen):
pass
sm = ScreenManager()
sm.add_widget(Login(name='login'))
sm.add_widget(Settings(name='settings'))
sm.current = "login"
class DemoApp(MDApp):
class ContentNavigationDrawer(BoxLayout):
pass
class DrawerList(ThemableBehavior, MDList):
pass
def build(self):
self.theme_cls.primary_palette = "Teal"
Window.size = (412, 732)
#The Error Occurs when the KV file is loaded
kv = Builder.load_file("MainLayout.kv")
return kv
if __name__ == '__main__':
DemoApp().run()
This the Kivy File and in Login File I just have ( )
#: include Login.kv
#: include Settings.kv.
NavigationLayout:
id:nav_layout
ScreenManager:
Screen:
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: "Kivy Demo App"
Widget:
ScreenManager:
id: screen_manager
Login:
id: login1
name: "login"
Settings:
id: settings1
name:"settings"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
orientation: 'vertical'
padding: "8dp"
spacing: "8dp"
MDLabel:
text: 'Username'
MDLabel:
text: 'Captions'
Okay, it was not too hard to fix this. I just Changed the name of the class/page from settings to just setting and it fixed the problem. Not really sure how it affected the code but would love to know about the bug if anyone might know.
I ran into the same problem. The class name "Settings" causes the error "ScreenManager accepts only Screen widget". I changed the class name to "Setting" and the screen widget's id and name both to "setting" and everything works fine. Frustrating.
I try to run mi program but for some reason i see this error: "Revision format must be X.Y.Z[-tag]"
I dont know what it means.
I have been trying to take some things out of my code to know what is causing it but i havent figured it out yet
Here is my python file:
#!/usr/bin/python
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
kivy.require("1.11.1")
class Inicial(BoxLayout):
def __init__(self, **kwargs):
super(Inicial, self).__init__(**kwargs)
temperaturaActual = "°C"
class MainApp(App):
def build(self):
return Inicial
if __name__=="__main__":
MainApp().run()
Here is my kv file:
#:kivy !
#:kivy !
<Inicial>:
Label:
id: "temperatura"
text: root.temperaturaActual
size_hint: (.25, .25)
pos_hint: { 'center_x': .05, 'center_y': .55}
font_size: 60sp
font_name: 'C:/Users/mateo/AppData/Local/Microsoft/Windows/Fonts/Roboto-Light'
The #:kivy kv syntax is for declaring the minimum compatible Kivy version. You are declaring a version of !. As the error says, your version must be of the form X.Y.Z[-tag], e.g. 1.11.0.
The simplest solution is to remove the #:kivy lines, they aren't necessary.
The problem that you indicate is because you are incorrectly indicating the header in the .kv as the docs points out:
Syntax of a kv File A Kivy language file must have .kv as
filename extension.
The content of the file should always start with the Kivy header,
where version must be replaced with the Kivy language version you’re
using. For now, use 1.0:
#:kivy 1.0
# content here
In your case you use #:kivy ! and you repeat it 2 times unnecessarily.
On the other hand you have other errors:
Only the Property are accessible from the .kv, in your case current temperature is not.
If you are going to indicate a measurement using the units, these should be as strings.
The build method must return an object, not a class. In your Initial class which is a class, to return an object you must instantiate it using Initial()
Considering the above, the solution is:
#!/usr/bin/python
import kivy
kivy.require("1.11.1")
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class Inicial(BoxLayout):
temperaturaActual = StringProperty()
def __init__(self, **kwargs):
super(Inicial, self).__init__(**kwargs)
self.temperaturaActual = "°C"
class MainApp(App):
def build(self):
return Inicial()
if __name__ == "__main__":
MainApp().run()
#:kivy 1.11.1
<Inicial>:
Label:
id: "temperatura"
text: root.temperaturaActual
size_hint: (.25, .25)
pos_hint: {'center_x': .05, 'center_y': .55}
font_size: "60sp"
font_name: 'C:/Users/mateo/AppData/Local/Microsoft/Windows/Fonts/Roboto-Light'
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.uix.dropdown import DropDown
class MyPracticeApp(App):
def build(self):
return Debit()
class Debit(Widget):
def debit(self):
return Hello()
class Hello(Widget):
pass
if __name__ == "__main__":
MyPracticeApp().run()
kv file...
<Debit>
Button:
text : 'popup'
size_hint : 0.2,0.2
on_press : root.debit()
<Hello>:
Button:
text : 'popup2'
size_hint : 0.2,0.2
# on_press : root.debit()
There is a button popup, and when I press this button I am calling the debit() function. Inside the debit function I am calling Hello(), but I'm unable to get anything from this class. What am I doing wrong?
Whenever I click on a buttton I want a new screen but without screen navigation. How do I do this ?
You can use ScreenManager for this.
A little example:
from kivy.app import App
from kivy.lang import Builder
KV = """
#:import NoTransition kivy.uix.screenmanager.NoTransition
BoxLayout:
orientation: "vertical"
Label:
text: "top"
ScreenManager:
id: sm
transition: NoTransition()
Screen:
name: "screen1"
Button:
text: "screen 2"
on_release: sm.current = "screen2"
Screen:
name: "screen2"
Button:
text: "screen 1"
on_release: sm.current = "screen1"
Label:
text: "bottom"
"""
class TestApp(App):
def build(self):
return Builder.load_string(KV)
TestApp().run()
Question Kivy Factory
what is the concept of Factory in kivy
Answer
When the keyword Factory is used anywhere (e.g. kv file, or Python script) in your project, it will automatically register any class or module and instantiate them.
Example
The following example illustrates the use of Factory in the kv file to register and instantiate Popup widget, Hello. There is no class definition of Hello and no definition for method debit() in Python script.
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string("""
#:import Factory kivy.factory.Factory
<Debit>:
Button:
text : 'popup'
size_hint : 0.2,0.2
on_press : Factory.Hello().open()
<Hello#Popup>:
title: 'Popup2'
auto_dismiss: False
BoxLayout:
orientation: 'vertical'
Label:
text: 'Hello Kivy'
Button:
text : 'Close Popup'
size_hint : 1,0.2
on_press : root.dismiss()
""")
class Debit(Widget):
pass
class MyPracticeApp(App):
def build(self):
return Debit()
if __name__ == "__main__":
MyPracticeApp().run()
References: Kivy » Factory object
Question 1
There is a button popup, and when I press this button I am calling the
debit() function. Inside the debit function I am calling Hello(), but
I'm unable to get anything from this class. What am I doing wrong?
Explanation
The second button is not displayed because the app did not have instructions on what to do with the instantiated object, Hello which contains a child widget, Button.
Solution
One of the solution is to add the new object using add_widget() function.
Snippets - Py
def debit(self):
return self.add_widget(Hello())
Question 2
Whenever I click on a button I want a new screen but without screen
navigation. How do I do this ?
Solution
You could use Kivy Popup widget.
Example
main.py
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_string("""
<Debit>:
Button:
text : 'popup'
size_hint : 0.2,0.2
on_press : root.debit()
<Hello>:
title: 'Hello Popup2'
auto_dismiss: False
Button:
text : 'Close Popup'
size_hint : 0.2,0.2
on_press : root.dismiss()
""")
class Debit(Widget):
def debit(self):
return Hello().open()
class Hello(Popup):
pass
class MyPracticeApp(App):
def build(self):
return Debit()
if __name__ == "__main__":
MyPracticeApp().run()
Output
i have a simple test program:
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.clock import mainthread
class TestScreen(Screen):
#mainthread
def on_pre_enter(self): #Is loaded before kv buttons etc? how make it work
pass
#mainthread
def on_enter(self): #Load after kv buttons etc?
button = Button(text="Work?")
#how now add it to display?
#how control where display it? on end or begin is just about on_pre and on?
class TestApp(App):
pass
if __name__ == '__main__':
TestApp().run()
And test.kv file
#:import NoTransition kivy.uix.screenmanager.NoTransition
<TestScreen>:
name:'try'
GridLayout:
id:'test'
cols:2
Button:
text:'Test'
on_press:app.root.current='Main'
ScreenManager:
transition: NoTransition()
Screen:
name: 'Main'
GridLayout:
cols:1
Button:
text:'1'
Button:
text:'2'
Button:
text:'Test'
on_press:root.current='try'
TestScreen:
Is simple to control kv and python widgets(but i dont know how but is more easy to writes widgets etc in kv file, but still need create some in python for automatic content) or better just create all in python withou kv file? I wanna make somehting like this: App with left menu always displayed and on the right side another screen with dynamic content based on screen(clicked from menu) is maybe another simple solution for this. Anyone can explain me step by step? :)
AttributeError
The solution to AttributeError, please replace "id: 'test'" with "id: test" in test.kv file.
Dynamic Content
It is possible to display screen with dynamic content based on clicked from menu. But remember to remove the widgets that were added when exiting the screen (TestScreen/SettingsScreen). If you do not remove the widgets, you will get duplicates/multiples of each widget added each time you enter the screen (TestScreen/SettingsScreen). I recommend using on_pre_enter and on_leave methods. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class MyScreenManager(ScreenManager):
pass
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
def on_pre_enter(self, *args):
self.ids.test.add_widget(Button(text="Work?"))
def on_leave(self, *args):
self.ids.test.remove_widget(self.ids.test.children[0])
class TestApp(App):
title = "Add & Remove Widgets Dynamically"
def build(self):
return MyScreenManager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
<MyScreenManager>:
transition: NoTransition()
MenuScreen:
SettingsScreen:
<MenuScreen>:
name: 'menu'
GridLayout:
cols: 1
Button:
text: '1'
Button:
text: '2'
Button:
text: 'Test'
on_press: root.manager.current = 'settings'
<SettingsScreen>:
name:'settings'
GridLayout:
id: test
cols: 2
Button:
text: 'Test'
on_press: root.manager.current = 'menu'
Output