So I'm just working with some example Kivy file code, and I came across this code which allows the user to switch between screens:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
BoxLayout:
Button:
text: 'Build Scenario'
on_press: root.manager.current = 'settings'
Button:
text: 'Run Existing Scenerio'
<SettingsScreen>:
BoxLayout:
Button:
text: 'Run Existing Scenerio'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
""")
# Declare both screens
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
I wondered to myself if it would be possible to put the code given in the Builder.load_string() method into a separate .kv file. So I did just that. I commented the Builder part out (I admit I don't know what its role is) and copied the string into a .kv file which looks like this:
# the file name is test.kv
#:kivy 1.0.9
<MenuScreen>:
BoxLayout:
Button:
text: 'Build Scenario'
on_press: root.manager.current = 'settings'
Button:
text: 'Run Existing Scenerio'
<SettingsScreen>:
BoxLayout:
Button:
text: 'Run Existing Scenerio'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
Unfortunately when I run the code now I just get a black screen. Can anyone tell me what's wrong? Thanks!
The code creates the screenmanager (sm) in the main body of the python file. When the kv is loaded from a file this only happens later, so none of the kv rules are applied to sm. It was okay before because the load_string happens before it is instantiated.
For this reason, instantiating widgets this way is bad practice, and the sm = ScreenManager(... etc should be moved to the build method. This is run after the kv file is loaded, so everything should work.
Related
I have a seemingly pretty easy question. I am making a app with kivymd and the first .kv file is sort of long. I have three .kv files that I would like to navigate in between. Currently i'm wondering how to use Builder.load_file, or something else to read from window to window, thanks for your help.
Not sure if there is a better way to do this or not. I myself couldn't find anything but ended up doing the following.
Create your main .kv file that is a screen manager. Make sure you include your other .kv files with '#:include 'filename.kv'
#:include screen1.kv
#:include screen2.kv
ScreenManager:
Screen1:
Screen2:
Format your other .kv files however you'd like. Just make sure to include a name for referencing in your screen manager. You must also name the screens the same as you did in your main.kv file.
<Screen1>
name: 'screen1'
orientation: 'vertical'
MDLabel:
text: 'Screen1'
halign: 'center'
MDFlatButton:
text: 'Go to screen 2'
pos_hint: {'center_x': 0.5}
on_release: root.manager.current = 'screen2'
This is .kv file #2
<Screen2>
name: 'screen2'
orientation: 'vertical'
MDLabel:
text: 'Screen2'
halign: 'center'
MDFlatButton:
text: 'Go to screen 1'
pos_hint: {'center_x': 0.5}
on_release: root.manager.current = 'screen1'
Your main.py file will have to include the libraries, define the classes, add the screens to your screen manager, and then you can build your main.kv file in your MDApp.
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
class Screen1(Screen):
pass
class Screen2(Screen):
pass
sm = ScreenManager()
sm.add_widget(Screen1(name= 'screen1'))
sm.add_widget(Screen2(name= 'screen2'))
class MainApp(MDApp):
def build(self):
sm = Builder.load_file("Main.kv")
return sm
MainApp().run()
I am making a simple GUI with kivy. made twi screens but facing a problem.
PROBLEM: Previous is still visible behind the current screen.
check screenshots:
First Screen..
enter image description here
second screen where first screen is still visible..
enter image description here
main.py code
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file("design.kv")
class LoginScreen(Screen):
def sign_up(self):
self.manager.current = "signup_screen"
class SignUpScreen(LoginScreen):
pass
class RootWidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()
Here, design.kv
<LoginScreen>:
GridLayout:
cols:1
GridLayout:
cols:1
Label:
text: "User Login"
TextInput:
hint_text:"Username"
TextInput:
hint_text:"Password"
Button:
text:"Login"
GridLayout:
cols:2
Button:
text:"Forgot Password?"
Button:
text:"Sign up"
on_press:root.sign_up()
<SignUpScreen>:
GridLayout:
cols:1
Label:
text:"Welcome to Another Dimension, Bitch!"
TextInput:
hint_text:"New Username"
TextInput:
hint_text:"New Password"
GridLayout:
cols:2
Button:
text:"Submit"
Button:
text:"Cancel"
<RootWidget>:
LoginScreen:
name:"login_screen"
SignUpScreen:
name:"signup_screen"
The problem here is that you inherited your SignUpScreen from your LoginScreen, which is why the widgets of the login screen is still in the signup one. The simple solution here is to instead inherit your SignUpScreen from the Screen class:
class SignUpScreen(Screen):
pass
I see there are a lot of answers for a case similar to mine: if I add a widget in code with the ".add_widget()" function, I can later remove this widget with the ".remove_widget()" function.
My case is a little bit different: I'm trying to remove a button in a Screen that is added in the configuration file (this has not a separated kv file because I'm still doing some research to well understand the mechanics under Kivy).
I'm trying to remove the button with id "setBtn" inside the "MenuScreen" screen.
I tried several ways to do this, but no one worked for me.
I'm not even sure that there is a solution to remove a widget that has not been dynamically added to a parent widget...
I leave you my code, if anyone could help me I'd be very grateful.
Thank you.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# Create both screens. Please note the root.manager.current: this is how
# you can control the ScreenManager from kv. Each screen has by default a
# property manager that gives you the instance of the ScreenManager used.
Builder.load_string("""
<MenuScreen>:
BoxLayout:
Button:
id: 'setBtn'
text: 'Goto settings'
on_press: root.manager.current = 'settings'
Button:
text: 'Quit'
on_press: root.remove_settings()
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
""")
# Declare both screens
class MenuScreen(Screen):
def remove_settings(self):
sm.remove_widget(settings)
self.remove_widget(self.ids.setBtn)
print 'Screen "settings" removed succesfully'
class SettingsScreen(Screen):
pass
# Create the screen manager
sm = ScreenManager()
menu = MenuScreen(name='menu')
settings=SettingsScreen(name='settings')
sm.add_widget(menu)
sm.add_widget(settings)
class TestApp(App):
def build(self):
return sm
def exit(self):
self.exit()
if __name__ == '__main__':
TestApp().run()
You have 2 errors:
The ids are not strings.
If you want to remove a widget you must use it through your parent, in your case if you want to remove the "setBtn" then you must use the BoxLayout.
Considering the above, the solution is:
# ...
Builder.load_string(
"""
<MenuScreen>:
BoxLayout:
id: box_layout
Button:
id: setBtn
text: 'Goto settings'
on_press: root.manager.current = 'settings'
Button:
text: 'Quit'
on_press: root.remove_settings()
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press: root.manager.current = 'menu'
"""
)
# Declare both screens
class MenuScreen(Screen):
def remove_settings(self):
self.ids.box_layout.remove_widget(self.ids.setBtn)
print('Screen "settings" removed succesfully')
# ...
I want set the focus in textinput. at the begining the focus set correctly but when i go to the next screen and come back to initial screen the focus dont set correctly.
This is a app with a rfid lector, I want to read a code and select enter or exit
main.py
import kivy
kivy.require('1.10.0') # replace with your current kivy version !
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class MyScreenManager(ScreenManager):
def __init__(self,**kwargs):
super(MyScreenManager,self).__init__()
class Menu1(Screen):
def __init__(self, **kwargs):
super(Menu1, self).__init__()
class Menu2(Screen):
def __init__(self, **kwargs):
super(Menu2, self).__init__()
Builder.load_file("main.kv")
class Fichaje(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
Fichaje().run()
main.kv
#:kivy 1.10.0
#:import WipeTransition kivy.uix.screenmanager.WipeTransition
<MyScreenManager>:
#transition: WipeTransition()
Menu1:
id: menu1
Menu2:
id: menu2
<Menu1>:
name: "screen1"
BoxLayout:
orientation: 'vertical'
TextInput:
id: input1
size_hint_y: .1
multiline: False
focus: True
on_text_validate:
root.manager.current = "screen2"
BoxLayout:
<Menu2>:
name: "screen2"
BoxLayout:
Button:
text:"Entrada"
on_press:
root.manager.current = "screen1"
Button:
text:"Salida"
on_press:
root.manager.current = "screen1"
No error messages but the focus is not in the right site,
Thanks
I change the code to simplify the error
In the example, there is not an attempt to change the focus. But I assume this was tried but it lost focus again.
The reason the text input looses focus again, is because it gets focused before the mouse or tap is released. The on_press method, is followed by on_release where the text input looses focus again.
To fix this you can just set the focus in the on_release method instead.
The quickest is to only add one line of code to the kv file and change on_press to on_release.
root.manager.get_screen("screen1").ids["input1"].focus
This line can be different by using object property in screen1, for example. Or if you cannot use the on_release method, maybe use clock to schedule a focus in some amount of time, and if the touch is still down, reschedule it.
But here is the quick fix.
<Menu2>:
name: "screen2"
BoxLayout:
Button:
text:"Entrada"
on_release:
root.manager.current = "screen1"
root.manager.get_screen("screen1").ids["input1"].focus = True
I just started learning Kivy today. The book i am referring to uses kv files. The problem is when i create more than one .kv file, and when i import the other one, it doesn't work. Please any detailed help will be appreciated as i am a complete beginner. Thanks
I already searched alot and tried from places, but nothing seems to work.
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
class AddLocationForm(BoxLayout):
pass
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
AddLocationForm.kv
AddLocationForm:
<AddLocationForm#BoxLayout>:
orientation: "vertical"
BoxLayout:
TextInput:
Button:
text: "Search"
Button:
text: "Current Location"
weather.kv
BoxLayout:
Label:
text: "Hello"
Label:
text: "Awesome"
Label:
text: "World"
I want it to print the strings, and show the button as well.
You can also use Builder to load kv files or strings.
from kivy.lang import Builder
Builder.load_file("kvfile1.kv")
Builder.load_file("kvfile2.kv")
Use Kv Language Directives, include <file>
Snippets - weather.kv
#:include AddLocationForm.kv
AddLocationForm:
BoxLayout:
Label:
text: "Hello"
Label:
text: "Awesome"
Label:
text: "World"
Snippets - AddLocationForm.kv
<AddLocationForm#BoxLayout>:
orientation: "vertical"
BoxLayout:
TextInput:
Button:
text: "Search"
Button:
text: "Current Location"
Output
Kv language ยป Lang Directives
include <file>
Syntax:
#:include [force] <file>
Includes an external kivy file. This allows you to split complex
widgets into their own files. If the include is forced, the file will
first be unloaded and then reloaded again. For example:
# Test.kv
#:include mycomponent.kv
#:include force mybutton.kv
<Rule>:
state: 'normal'
MyButton:
MyComponent:
# mycomponent.kv
#:include mybutton.kv
<MyComponent>:
MyButton:
# mybutton.kv
<MyButton>:
canvas:
Color:
rgb: (1.0, 0.0, 0.0)
Rectangle:
pos: self.pos
size: (self.size[0]/4, self.size[1]/4)