Hi there I am a rookie in Python and Kivy. To practice I am trying to generate a new screen automatically when clicking a button of my new Kivy app. In the new screen, I generate two buttons. They should send me back to the home page, but when I click on them the following happens:
File "~/screenmanager.py", line 1064, in get_screen
raise ScreenManagerException('No Screen with name "%s".' % name)
kivy.uix.screenmanager.ScreenManagerException: No Screen with name "home_screen"
.
Can you check the code and explain what I am doing wrong, please?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import ButtonBehavior, Button
from kivy.uix.image import Image
from kivy.uix.gridlayout import GridLayout
class MyButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.source = "img/spot.png"
def on_release(self):
sm = MyScreenManager()
on_release = sm.change_screen(name="home_screen")
self.source = "img/settings.png"
class ImageButton(ButtonBehavior, Image):
pass
class MyGrids(GridLayout):
def Grid_1x2():
obj = GridLayout(cols=1)
obj.add_widget(MyButton(
#on_release = sm.change_screen(name="home_screen")
))
obj.add_widget(Button(
text="World 4"
#on_release = sm.change_screen(name="home_screen")
))
return obj
class Homescreen(Screen):
pass
class Newscreen(Screen):
pass
class Settingscreen(Screen):
pass
class MyScreenManager(ScreenManager):
# def __init__(self, **kwargs):
# super(MyScreenManager, self).__init__(**kwargs)
def change_screen(self, name):
self.current = name
def new_screen(self):
name="new_screen"
mynewscreen = Newscreen(name=name)
layout = MyGrids.Grid_1x2()
mynewscreen.add_widget(layout)
self.add_widget(mynewscreen)
self.current = name
GUI = Builder.load_file("main.kv")
class Main(App):
def build(self):
#print(self.root.ids)
return GUI
Main().run()
My Main.kv is
#: include kv/homescreen.kv
#: include kv/settingscreen.kv
MyScreenManager:
Homescreen:
name: "home_screen"
id: home_screen
Settingscreen:
name: "setting_screen"
id: setting_screen
And my HomeScreen is:
<Homescreen>:
FloatLayout:
GridLayout:
canvas.before:
Color:
rgba: 0.99, 0.99, 0.99, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
cols: 1
pos_hint: {"top":1, "left": 1}
size_hint: 1, .05
GridLayout:
canvas.before:
Color:
rgba: 0.99, 0.99, 0.99, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
cols: 1
pos_hint: {"top":0.95, "left": 1}
size_hint: 1, .15
GridLayout:
rows: 1
cols: 5
pos_hint: {'left': .2}
size_hint: 0.6, .15
Label:
canvas.before:
Color:
rgba: 0.99, 0.99, 0.99, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
ImageButton:
id: left
source: "img/arrow-left.png"
center_x: self.parent.center_x
center_y: self.parent.center_y
on_release:
app.root.change_screen("setting_screen")
Image:
source: "img/spot.png"
ImageButton:
id: right
source: "img/arrow-right.png"
center_x: self.parent.center_x
center_y: self.parent.center_y
on_release:
app.root.change_screen("setting_screen")
Label:
canvas.before:
Color:
rgba: 0.99, 0.99, 0.99, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
GridLayout:
canvas.before:
Color:
rgba: 0.99, 0.99, 0.99, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
cols: 1
pos_hint: {"top":0.8, "left": 1}
size_hint:1, .2
GridLayout:
canvas.before:
Color:
rgba: 0.99, 0.99, 0.99, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
cols: 1
pos_hint: {"top":0.6, "left": 1}
size_hint:1, .5
Button:
text: "New Screen"
pos_hint: {"top":0.1, "left": 1}
id: test
size_hint:1, .1
on_release:
app.root.new_screen()
When I run the app, everything seems fine initially. It generates the new screen. However, I cannot go back to the homepage because the buttons generate a "No Screen with Name" error.
Problem
File "~/screenmanager.py", line 1064, in get_screen
raise ScreenManagerException('No Screen with name "%s".' % name)
kivy.uix.screenmanager.ScreenManagerException: No Screen with name "home_screen"
Root Cause
In the method on_release(), it instantiated another instance of MyScreenManager which is different from the one instantiated in kv file. The screens, "home_screen" and "settings_screen" are not defined in this new instance of MyScreenManager. Due to this, your Kivy app threw the ScreenManagerException.
Solution
Use the following in Python script:
App.get_running_app() function to get an instance of your application
root which refers to the base widget/template in the current rule
Snippets - py file
class MyButton(ButtonBehavior, Image):
...
def on_release(self):
App.get_running_app().root.current = "home_screen"
self.source = "img/settings.png"
...
class MyGrids(GridLayout):
def __init__(self, **kwargs):
super(MyGrids, self).__init__(**kwargs)
self.cols = 1
self.add_widget(MyButton(
# on_release = sm.change_screen(name="home_screen")
))
self.add_widget(Button(
text="World 4"
# on_release = sm.change_screen(name="home_screen")
))
...
class MyScreenManager(ScreenManager):
...
def new_screen(self):
name = "new_screen"
# if new_screen does not exist
if not (name in self.screen_names):
mynewscreen = Newscreen(name=name)
layout = MyGrids()
mynewscreen.add_widget(layout)
self.add_widget(mynewscreen)
# switch screen
self.current = name
Output
Related
I'm trying to get this kv. file to run, but it throws the error "Invalid indentation (too many levels)". For the life of me, I don't understand why. Im suspicious that its the way I am nesting the layouts within the kv. file, but I'm not sure. Any help/critique would be appreciated!
.py file:
from kivy.app import App
import tarot
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
class selectCard(BoxLayout):
def __init__(self, *args, **kwargs):
super(selectCard,self).__init__(*args,**kwargs)
self.my_text = "Background.png"
def on_click(self):
if self.my_text == "Background.png":
self.my_text = tarot.read().one()
self.ids.card_image.source = "Cards\\" + self.my_text + ".png"
print(self.my_text)
else:
self.my_text = "Background.png"
self.ids.card_image.source = "Background.png"
print(self.my_text)
class layout(FloatLayout):
pass
class TarotApp(App):
def build(self):
return selectCard()
TarotApp().run()
.kv file:
<layout>
canvas.before:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
<seletCard>
layout:
Button:
normal: root.my_text
on_release: root.on_click()
background_color: 1,1,1,1
size_hint: (1,1)
pos_hint: {'center_x':.5,'center_y':.5}
Image:
source: root.my_text
id: card_image
pos: self.parent.pos
size: self.parent.size
#background_color: self.parent.background_color
stretch: True
Button:
normal: root.my_text
on_release: root.on_click()
background_color: 1,1,1,1
#size_hint: (1,1)
pos_hint: {'center_x':.5,'center_y':.5}
Image:
source: root.my_text
id: card_image
pos: self.parent.pos
size: self.parent.size
background_color: self.parent.background_color
stretch: True
I don't think this is an indentation problem. Am I referencing something incorrectly?
I´m learning kivy, and it has passed 3 weeks since i face this problem without encounter a solution, so i hope any of u guys could help me, i would appreciate it.
I have a main file:
from app import MyProgramApp
if __name__ == "__main__":
winapp = MyProgramApp()
winapp.run()
from where i start my app. Then i have a directory called "app", inside there is the following "init.py" file.
from kivy.app import App
from kivy.utils import QueryDict, rgba
from kivy.core.window import Window
from .view import MainWindow
Window.minimum_width = 500
Window.minimum_height = 650
Window.maximize()
class MyProgramApp(App):
colors = QueryDict()
colors.primary = rgba('#2D9CDB')
colors.secondary = rgba('#16213E')
colors.succes = rgba('#1FC98E')
colors.warning = rgba('#F2C94C')
colors.danger = rgba('#E85757')
colors.grey_dark = rgba('#C4C4C4')
colors.grey_light = rgba('#F5F5F5')
colors.black = rgba('#A1A1A1')
colors.white = rgba('#FFFFFF')
def build(self):
return MainWindow()
Same folder app, i have the following "view.py".
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager
class MainWindow(BoxLayout):
username = StringProperty("Usuario")
def __init__(self, **kw):
super().__init__(**kw)
def on_press_home(self):
self.ids.scrn_mngr.current = "scrn_home"
class ViewManager(ScreenManager):
def __init__(self, **kw):
super().__init__(**kw)
class NavTab(ToggleButtonBehavior, BoxLayout):
text = StringProperty('')
icon = StringProperty('')
def __init__(self, **kw):
super().__init__(**kw)
and finally for that folder i have "myprogram.kv"
#:kivy 2.1.0
#:import Home views.home.Home
<MainWindow>:
spacing: dp(8)
canvas.before:
Color:
rgba: app.colors.white
Rectangle:
pos: self.pos
size: self.size
# NAVIGATION BAR
BoxLayout:
id: nav_menu
size_hint_x: .2
orientation: "vertical"
size_hint_min_x: dp(100)
# LOGO
BoxLayout:
id: logo_nbox
size_hint_y: .1
size_hint_min_y: dp(70)
padding: dp(16)
AnchorLayout:
anchor_x: "right"
size_hint_x: None
width: dp(52)
halign: "left"
Label:
text: "COMPANY"
halign: "center"
BoxLayout:
orientation: "vertical"
Label:
text: "COMPANY"
halign: "center"
Label:
text: "Phrase"
halign: "center"
# OPTIONS
GridLayout:
id: tabs_box
cols: 1
spacing: dp(4)
size_hint_y: .5
canvas.before:
Color:
rgba: app.colors.grey_dark
Rectangle:
pos: self.pos
size: [self.size[0], dp(1)]
NavTab:
text: "Home"
state: "down"
on_press: root.on_press_home()
# BODY
BoxLayout:
size_hint_x: .8
spacing: dp(8)
orientation: "vertical"
padding: [dp(16), dp(8), dp(12), dp(8)]
canvas.before:
Color:
rgba: app.colors.grey_light
Rectangle:
pos: self.pos
size: self.size
# SCREENS
BoxLayout:
ViewManager:
id: scrn_mngr
<ViewManager>:
Screen:
name: "scrn_home"
Home:
id: home
<NavTab>:
background_normal: ""
background_down: ""
background_color: [0,0,0,0]
group: "tabs"
size_hint_y: None
height: dp(45)
spacing: dp(4)
canvas.before:
Color:
rgba: [0,0,0,0] if self.state == "normal" else rgba("#E1F1FF")
Rectangle:
pos: self.pos
size: self.size
Color:
rgba: [0,0,0,0] if self.state == "normal" else app.colors.primary
Rectangle:
pos: [self.pos[0]+self.size[0]-dp(1), self.pos[1]]
size: [dp(8), self.size[1]]
Label:
halign: "left"
valign: "middle"
text: root.text
color: app.colors.grey_dark if root.state == "normal" else app.colors.primary
Then i got another folder called "views" inside i have another folder called "home", inside home we encounter 3 files "init.py", "home.kv", "home.py". the first one "init.py" is the following.
from .home import Home
then we got "home.kv".
#:kivy 2.1.0
<Home>:
orientation: "vertical"
Label:
size_hint_y: .1
text: "Other"
Button:
size_hint_y: .1
text: "popup"
on_press: root.open_popup()
BoxLayout:
size_hint_y: .8
id: home_box
<SomePopup>:
title: "SOME TITLE"
title_align: "center"
title_color: app.colors.primary
size_hint: .25, .8
size_hint_min_x: dp(200)
pos_hint: {"x": .1, "top": .9}
BoxLayout:
orientation: "vertical"
padding: [dp(0), dp(12), dp(0), dp(12)]
Label:
text: "Some text"
Button:
text: "create buttons on box"
on_press: root.modify_home_box()
and finally and the problem that i´m facing is whit the following file "home.py"
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.factory import Factory
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.app import App
Builder.load_file('views/home/home.kv')
class Home(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def open_popup(self):
Factory.SomePopup().open()
class SomePopup(Popup):
def __init__(self, **kw):
super().__init__(**kw)
def modify_home_box(self):
my_app = App.get_running_app().root
my_box = my_app.ids.home.ids.home_box
custom_button = Button(
text = "something"
)
my_box.add_widget(custom_button)
That i´m trying to do is actually modify "home_box" with is store on ids Home dictionary, i also try with ObjectProperty (but that gives and Attribute Error, since i only could read propertys but doesnt modify it), instance of a new Home class doesnt work... searching for current app in App appear doesnt work since Home is store i think on screenManager...
I need add a button or some widget to "home_box" from outside class "SomePopup". I also drop here a repository on github with the whole code. github_repo
I don't know how to solve my issue, and i try with the resources available here on stack as well other net places... any help would be appreciate.
Just a very complicated path to the widget of interest. In your modify_home_box() method, try replacing:
my_app = App.get_running_app().root
my_box = my_app.ids.home.ids.home_box
with:
my_app = App.get_running_app().root
my_box = my_app.ids.scrn_mngr.ids.home.ids.home_box
I need to create an object of class GameScreen and call it's init and schedule interval to it's update method when the on_release event of a button with text as "New Game" happens
Here is the shortened code:
class Snake(Widget):
def init(self, bool, list):
pass
def update(self):
pass
class Food(Widget):
def init():
pass
def draw(snake_instance):
pass
def check(snake_instance):
pass
class WindowManager(ScreenManager):
pass
class MainMenuScreen(Screen):
pass
class GameScreen(Screen):
snk = ObjectProperty(None)
fd = ObjectProperty(None)
def init(self):
self.snk.init(False, (0,1,0,1))
self.fd.init()
Clock.schedule_interval(self.update, 1/20)
def update(self, deltaTime):
self.snk.update()
self.fd.draw(self.snk)
self.fd.check(self.snk)
class GameOverScreen(Screen):
pass
class OptionsScreen(Screen):
pass
class HighScoreScreen(Screen):
pass
kivyFile = Builder.load_string("""
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
WindowManager:
transition: FadeTransition()
MainMenuScreen:
GameScreen:
GameOverScreen
OptionsScreen:
HighScoreScreen:
<Button>:
font_size: 50
size_hint: None, None
background_color: 0,0,0,0
color: self.parent.red if self.state=="normal" else self.parent.black
canvas.before:
Color:
rgba: self.parent.black if self.state=="normal" else self.parent.red
RoundedRectangle:
size: self.size
pos: self.pos
radius: [self.height*0.4]
Color:
rgba: self.parent.red if self.state=="normal" else self.parent.black
Line:
width: 2
rounded_rectangle: self.x, self.y, self.width, self.height, self.height*0.4
<MainMenuScreen>:
name: "mainmenuscreen"
red: (1,0,0,1)
black: (0,0,0,1)
canvas.before:
Color:
rgba: 0.15,0,0,1
Rectangle:
size: root.size
pos: root.pos
Widget:
Label:
text: "Snake Game"
bold: True
color: (0,0,0,1)
font_size: 100
outline_width: 3
outline_color: (1,0,0,1)
pos: 0, 1050
center_x: root.center_x
Button:
text: "New Game"
size: 350, 90
pos: 0, 800
center_x: root.center_x
on_release:
root.manager.current = "gamescreen"
Button:
text: "Options"
size: 300, 90
pos: 0, 650
center_x: root.center_x
Button:
text: "High Scores"
size: 350, 90
pos: 0, 500
center_x: root.center_x
Button:
text: "Exit"
size: 200, 90
pos: 0, 350
center_x: root.center_x
on_release: app.stop()
<GameScreen>:
name: "gamescreen"
snk: snake_id
fd: fd_id
canvas.before:
Color:
rgba: .2, .2, .2, 1
Rectangle:
pos: self.pos
size: self.size
Widget:
Snake:
id: snake_id
Label:
font_size: 50
color: self.parent.color
text: self.parent.disScore
size: self.texture_size
size_hint: None, None
pos: root.width-self.width-20, root.height-self.height-10
Food:
id: fd_id
""")
runTouchApp(kivyFile)
Everything works fine, no errors.
I need some way to create an instance of the GameScreen class and call it's init() and also schedule an interval to call its update() method.
Something Like this if i can guess:
def someMethod():
gs = GameScreen()
gs.init()
Clock.schedule_interval(gs.update,1/20)
Can anybody help this noob kivy developer ??
One problem with your code is that you seem to be using init() where you should be using __init__(). The __init__() method is automatically called when a Widget is created, but init() is just another method that won't get called unless you explicitly call it. And when you do write your own __init__() method, you should call the base class __init__(). See this question.
So, if you replace the init() method of GameScreen, with __init__(), then your update() method should get scheduled.
Perhaps a better approach might be to start the updating only when the GameScreen is displayed, using the on_enter() method:
def on_enter(self, *args):
self.snk.init(False, (0, 1, 0, 1))
self.fd.init()
self.update_event = Clock.schedule_interval(self.update, 1 / 20)
def on_leave(self, *args):
self.update_event.cancel()
This avoids having the GameScreen updating when it isn't even visible. And now you don't even need an __init__() over ride, since the default one will be called.
Also. the above comments regarding init() vs __init__() apply to your Snake and Food Widgets as well.
Note that the GameScreen is created when your kv string is evaluated, so you should never actually call GameScreen().
I am trying to print an input given by a user but all I get is: < ObjectProperty name=input >
I can't use Text Input in my py because python quits if I try to run a program with it installed. I have tried putting the popup in the 'test' class but it just comes up with a different error.
Any help is appreciated,
Thank you.
Here is my code:
In my py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
class MyPopup(Popup):
pass
class Test(Widget):
pass
class TestApp(App):
def build(self):
return Test()
def Process(self):
text = MyPopup.input
print(text)
if __name__ == '__main__':
TestApp().run()
and in my kv:
#:import Factory kivy.factory.Factory
<MyPopup#Popup>
input: text
auto_dismiss: True
size_hint: 0.4, 0.4
TextInput:
id: text
hint_text: 'insert text'
multiline: False
on_text_validate: app.Process()
<Test>:
canvas:
Color:
rgba: 0, 0, 0, 0
Rectangle:
pos: 0, 0
size: self.width, self.height - self.height/6
Color:
rgba: 1, 1, 1, 0.1
Rectangle:
pos: 0, 0
size: self.width/2 + self.width/10, self.height - self.height/6
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: 0, self.height - self.height/6
size: self.width, self.height/6
Button:
id: GL
text: 'In here'
pos: 0 , root.height - root.height/6
on_parent: GLdropdown.dismiss()
on_release: GLdropdown.open(self)
size_hint_y: None
height: root.height/6
width: root.width/7
DropDown:
id: GLdropdown
on_select: btn.text = '{}'.format(args[1])
Button:
id: 'AV'
text: 'press me'
size_hint_y: None
height: 50
on_release: Factory.MyPopup().open()
If you change:
on_text_validate: app.Process()
to:
on_text_validate: app.Process(self.text)
And change the Process() method to:
def Process(self, text):
print(text)
I think it will work as you want.
The problem is that the line:
text = MyPopup.input
makes text a reference to the ObjectProperty of the class MyPopup. What you really want is a reference to the text of the TextInput, which is simply self.text in the TextInput.
I'm still getting the hang of both kivy and python so bear with me. I have two layouts, my Graph layout is overriding the properties of my TopBar layout. Is there any way to fix this?
Here is my .kv file:
Root:
orientation: "vertical"
TopBar:
size_hint: 1,0.08
size_hint_max_y: 100
pos_hint: {"top":1}
TextInput:
text: "Search"
right: True
size_hint_max_x: 200
Button:
text: str(self.state)
Graph:
size_hint_max_y: 100
Button:
text: "test"
Here's the python file:
class Root(BoxLayout):
def __init__(self, **kwargs):
super(Root, self).__init__(**kwargs)
class TopBar(BoxLayout):
pass
class Graph(FloatLayout):
pass
class Gui(App):
def build(self):
return Root()
Gui().run()
Thanks!
kv file
Move size_hint_max_y: 100 from under Graph: to under Button:.
In the snippet, color was added to illustrates the Graph's canvas.
Snippet
Graph:
canvas.before:
Color:
rgba: 0, 0, 1, 0.5 # 50% blue
Rectangle:
pos: self.pos
size: self.size
Button:
size_hint_max_y: 100
text: "test"
Output