I don't understand what I'm doing wrong. I'm learning Kivy but when I want to load the new screen it tells me that it doesn't exist.
edited : [ how to work with screen in kivy correctly ]
Error :
raise ScreenManagerException('No Screen with name "%s".' % name)
kivy.uix.screenmanager.ScreenManagerException: No Screen with name "startup_screen".
main.py
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.core.window import Window
from startup_screen import StartupScreen
class MyApp(MDApp):
def build(self):
Window.size = (300, 500)
sm = ScreenManager()
self.title = "App Test"
return sm
def on_start(self):
self.root.current = "startup_screen"
MyApp().run()
startup_screen.py
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
kv = """
<StartupScreen>
name: 'startup_screen'
canvas:
Color:
rgb: (.93, .93, .93)
Rectangle:
size: self.size
pos: self.pos
Image:
id: img_logo
source: "resources/img/logo.png"
size_hint: (.8,.8)
pos_hint: {'center_x': .5, 'center_y': .6}
"""
class StartupScreen(Screen):
Builder.load_string(kv)
def __init__(self):
super(StartupScreen, self).__init__()
def on_enter(self, *args):
from kivy.clock import Clock
Clock.schedule_once(lambda dt: self.load_navigation(), 5)
#staticmethod
def load_navigation():
from kivymd.theming import ThemeManager
app = MDApp.get_running_app()
app.theme_cls = ThemeManager()
app.theme_cls.primary_hue = "600"
app.theme_cls.accent_palette = "Teal"
app.theme_cls.theme_style = "Light"
from navigation import NavigationScreen
app.root.current = "navigation"
The only problem with your code is that you are not creating a StartupScreen. Your kv string defines a rule for how a StartupScreen should be built, but it does not build one. A simple fix is to just add a line in your build() method to create the StartupScreen:
def build(self):
Window.size = (300, 500)
sm = ScreenManager()
sm.add_widget(StartupScreen()) # createe the StartupScreen
self.title = "App Test"
return sm
Related
I made a recycleview list of custom cards. For each card i want to pass a custom object called "show", its type is "Content".
The class of custom card is "StreamingShowCard". I can't create the card by KVlang because its class contains some pytho methods.
This is the code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
from kivy.properties import StringProperty
from kivy.uix.behaviors import ButtonBehavior
from kivymd.uix.list import OneLineIconListItem
from kivymd.uix.tab import MDTabsBase
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.label import MDLabel
from kivymd.uix.gridlayout import MDGridLayout
from kivymd.uix.fitimage import FitImage
from kivy.uix.image import AsyncImage
from kivymd.uix.button import MDFlatButton
#import datetime
from datetime import datetime
from kivy.uix.image import Image
from kivymd.uix.dialog import MDDialog
from kivymd.uix.screen import MDScreen
from kivymd.uix.responsivelayout import MDResponsiveLayout
from kivymd.uix.progressbar import MDProgressBar
from kivy.clock import Clock
from kivymd.uix.button import MDIconButton
from kivymd.uix.list import IconLeftWidget
from kivymd.uix.recycleview import MDRecycleView
from kivy import properties
KV = '''
<Cover>:
<StreamingShowCard>:
show: root.show
MDScreen:
MDRecycleView:
size_hint_y: 1
size_hint_x: 1
viewclass: 'StreamingShowCard'
id: rv
MDRecycleGridLayout:
cols: 2
height: self.minimum_height
size_hint_y: None
row_default_height: '250dp'
row_force_default: True
padding: dp(10)
spacing: dp(10)
MDFloatingActionButton:
id: fab
icon: "plus"
pos_hint: {"center_x": .5, "center_y": .1}
on_release: app.add_item()
'''
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
'''Implements a material design v3 card.'''
text = StringProperty()
class Content: #eg: film found
name = ""
url = ""
platform = ""
imageUrl = ""
Image = False
#free = False
def __init__(self, name, url, platform, imageUrl):
self.name = name
self.url = url
self.platform = platform
self.imageUrl = imageUrl
class StreamingShowCard(MD3Card):
#show = None #the object of Content class attached to the
show = properties.ObjectProperty()
def __init__(self, **kwargs):
super(StreamingShowCard, self).__init__(**kwargs)
self.__set_cardGraphic()
self.image = Cover(size_hint_min_y=0.7, source="https://kivy.org/doc/stable/_static/logo-kivy.png", opacity= 100 if True else 0, radius= ["10dp", "10dp", "0dp", "0dp"])
grid1 = MDGridLayout(cols=1, size_hint_x=1, size_hint_y=1, rows=3)
grid1.add_widget(self.image)
grid1.add_widget(MDLabel(text=self.show, size_hint_y = 0.3, font_style='Subtitle2', halign='left', valign='middle'))
box1 = MDBoxLayout(orientation = "horizontal", size_hint_x=1, size_hint_y=0.2)
box1.add_widget(MDLabel(text="self.show.platform", size_hint_y = 1, size_hint_x=0.8, theme_text_color="Secondary", font_style='Caption', halign='left', valign='middle'))
grid1.add_widget(box1)
self.add_widget(grid1)
def __set_cardGraphic(self):
self.md_bg_color = "#18222c"
#self.size_hint_y = None
self.elevation = 10
self.padding = "1dp"
self.size_hint_x = 1
self.radius = "10dp"
self.ripple_behavior = True
self.on_press = self.cardPress
def cardPress(self, *args):
dialog = MDDialog(
title = "Aprire la pagina in un browser?",
text = "Text",#self.show.name,
size_hint = (0.8, 0.8),
auto_dismiss = False,
buttons=[
MDFlatButton(text="No", text_color=self.theme_cls.primary_color, on_release=lambda x: dialog.dismiss()),
MDFlatButton(text="Si", text_color=self.theme_cls.primary_color, on_release=lambda x: yesClick())
]
)
dialog.open()
def yesClick():
#openUrl(self.show.url)
dialog.dismiss(force=True)
def loadImage(self, *args):
if not self.show.Image:
self.show.loadImage(True)
self.image.source = self.show.imageUrl
self.image.opacity = 100
self.image.reload()
class Cover(AsyncImage, FitImage):
pass
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def add_item(self):
for i in range(100):
self.root.ids.rv.data.append({
"show": Content("name", "www.google.com", "platform", "https://kivy.org/doc/stable/_static/logo-kivy.png")
})
MainApp().run()
If the recycleview's viewclass is a Label i can pass the text directly from main by appending dict to RV data, but whit this custom class of recycle view i cannot pass Content to show property.
Is it possible to populate the RecycleView by the main?
Couldn't get your code to run, but if you want to use an ObjectProperty in the data of a RecycleView, you need to handle it a bit differently. Here is an example of using an ObjectProperty in the data:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.uix.recycleview import RecycleView
Builder.load_string('''
<MyObject>:
size_hint_y: None
height: 100
Label:
id: label
text: root.text # uses the text StringProperty
size_hint: None, None
size: 200, 100
<RV>:
viewclass: 'MyObject'
RecycleBoxLayout:
default_size: None, 100
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class MyObject(BoxLayout):
show = ObjectProperty(None)
text = StringProperty('Abba')
def on_show(self, instance, new_obj):
# handle the ObjectProperty named show
if new_obj.parent:
# remove this obj from any other MyObject instance
new_obj.parent.remove_widget(new_obj)
for ch in self.children:
if isinstance(ch, Image):
# remove any previous obj instances
self.remove_widget(ch)
break
# add the new obj to this MyObject instance
self.add_widget(new_obj)
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x),
'show': Image(source='tester.png', size_hint=(None, None), size=(100, 100),
allow_stretch=True, keep_ratio=True)}
for x in range(100)]
class TestApp(App):
def build(self):
return RV()
if __name__ == '__main__':
TestApp().run()
I am working on a mobile app and I want to create multiple of scoll items by using for loop but whenever I use for loop, gives me some error It has already parent widget. how can I make list of MDCard?
My App:
Click here
My Code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.boxlayout import BoxLayout
from kivy.uix.image import AsyncImage
from kivymd.uix.card import MDCard
from kivymd.uix.label import MDLabel
Window.size = (450, 740)
kv = '''
ScreenManager:
Main:
<main>:
name: 'main'
video_list: video_list
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Video downloader'
ScrollView:
Screen:
id: video_list
'''
class Main(Screen):
pass
sm = ScreenManager()
sm.add_widget(Main(name='main'))
class Ytube(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_cls.colors = 'Red'
self.theme_cls.primary_palette = "Red"
self.root = Builder.load_string(kv)
image = AsyncImage(
source='https://static.hub.91mobiles.com/wp-content/uploads/2020/05/How-to-download-youtube-videos.jpg', size_hint=(1, .7), )
screen_id = self.root.get_screen('main').ids.video_list
for i in range(1):
card = MDCard(orientation='vertical', pos_hint={
'center_x': .5, 'center_y': .7}, size_hint=(.9, .4))
card.add_widget(image)
card.add_widget(MDLabel(
text='Phishing attacks are SCARY easy to do!! (let me show you!)', size_hint=(.6, .2), ))
screen_id.add_widget(card)
def build(self):
return self.root
if __name__ == "__main__":
Ytube().run()
Is there any way to make it
look like this.
The problem is that you are trying to use the same image widget for every MDCard. Any widget can only have one parent. You can only use that image widget once. You can fix that by moving the creation of the image widget inside the loop.
Also, a BoxLayout is a better choice for the child of a ScrollView, since it has a minimum_height property that you can use. Here is a modified version of your code that applies both those suggestions:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.image import AsyncImage
from kivymd.uix.card import MDCard
from kivymd.uix.label import MDLabel
Window.size = (450, 740)
kv = '''
ScreenManager:
Main:
<main>:
name: 'main'
video_list: video_list
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Video downloader'
ScrollView:
do_scroll_x: False
BoxLayout:
id: video_list
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
'''
class Main(Screen):
pass
sm = ScreenManager()
sm.add_widget(Main(name='main'))
class Ytube(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_cls.colors = 'Red'
self.theme_cls.primary_palette = "Red"
self.root = Builder.load_string(kv)
screen_id = self.root.get_screen('main').ids.video_list
for i in range(5):
# Make a new image widget for each MDCard
image = AsyncImage(
source='https://static.hub.91mobiles.com/wp-content/uploads/2020/05/How-to-download-youtube-videos.jpg', size_hint=(1, .7), )
card = MDCard(orientation='vertical', pos_hint={
'center_x': .5, 'center_y': .7}, size_hint=(.9, None), height=200)
card.add_widget(image)
card.add_widget(MDLabel(
text='Phishing attacks are SCARY easy to do!! (let me show you!)', size_hint=(.6, .2), ))
screen_id.add_widget(card)
def build(self):
return self.root
if __name__ == "__main__":
Ytube().run()
I'm trying to build an app using kivy. I have added close button and then I added on_release. However, pressing the button does not work.
python code:
import kivy
kivy.require('1.11.0')
from kivy.lang import Builder
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.window import Window
Window.size = (350 * 1.5 , 600 * 1.5)
with open("./template.kv", encoding='utf8') as f:
Builder.load_string(f.read())
class CloseButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(CloseButton, self).__init__(**kwargs)
self.source = './close_btn#2x.png'
always_release = True
def on_press(self):
App.get_running_app().stop()
class Background(Screen):
def __init__(self, **kwargs):
super(Background, self).__init__(**kwargs)
class TemplateApp(App):
def build(self):
# title bar remove
# Window.borderless = True
sm = ScreenManager()
sm.add_widget(Background(name='back'))
return sm
if __name__ == '__main__':
TemplateApp().run()
kivy code:
<Background>:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: "background.png"
Label:
font_size: 12 * 1.5
text: 'Template'
font_name: './NotoSans-hinted/NotoSans-Regular.ttf'
size_hint: (1.0, 1.0)
halign: "left"
valign: "top"
color: 0.43568, 0.43568, 0.43568, 1
text_size: root.width - (40 * 1.5), 583 * 1.5
BoxLayout:
size_hint: 1.9, 1.938
CloseButton:
id: close_btn
Instead of
def on_press(self):
App.get_running_app().stop()
try this
self.on_press = App.get_running_app().stop()
Ok, let's try this:
from kivy.clock import Clock
...
class CloseButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(CloseButton, self).__init__(**kwargs)
self.source = './close_btn#2x.png'
# no parentheses after method's name!
self.on_press = self.closeapp
def closeapp(self):
Clock.schedule_once(App.get_running_app().stop())
I am using python-2.7 and kivy.When i run test.py then it gives error AttributeError: 'NoneType' object has no attribute 'text' in python?
Someone tell me what is mistake?
test.py
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.properties import ObjectProperty
Window.size = (500, 230)
class GroupScreen(Screen):
groupName = ObjectProperty(None)
def __init__(self, **kwargs):
super(GroupScreen, self).__init__(**kwargs)
self.groupName.text = "Test"
class Group(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
Group().run()
test.kv
GroupScreen:
groupName:groupName
GridLayout:
cols: 2
padding : 30,30
spacing: 10, 10
row_default_height: '40dp'
Label:
text: 'Test'
SingleLineTextInput:
id: groupName
GreenButton:
text: 'Ok'
GreenButton:
text: 'Cancel'
Label:
Label:
<SingleLineTextInput#TextInput>:
multiline: False
<GreenButton#Button>:
background_color: 1, 1, 1, 1
size_hint_y: None
height: self.parent.height * 0.150
If you are going to create the object in the .kv:
GroupScreen:
groupName:groupName
...
Then it is not necessary to declare it in the .py.
On the other hand the addition of children to a widget is not instantaneous so it is always recommended in these cases to use Clock.
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.clock import Clock
Window.size = (500, 230)
class GroupScreen(Screen):
def __init__(self, **kwargs):
super(GroupScreen, self).__init__(**kwargs)
Clock.schedule_once(lambda dt: setattr(self.groupName, 'text', "Test"))
class Group(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
Group().run()
Following is the main.py:
from kivy.app import App
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.screenmanager import (ScreenManager, Screen)
from kivy.uix.button import Button
from kivy.properties import *
class EmotionsScreen(Screen):
def button_press(self, emotion_str):
print (self.ids[emotion_str].emotion)
class ScreenManagement(ScreenManager):
pass
class HappyButton(ButtonBehavior, Image):
def on_press(self):
print(self.emotion)
class SadButton(ButtonBehavior, Image):
def on_press(self):
print(self.emotion)
class TiredButton(ButtonBehavior, Image):
def on_press(self):
print(self.emotion)
class MyApp(App):
def build(self):
sm = ScreenManagement()
sm.current = 'Emotions'
return sm
if __name__=='__main__':
MyApp().run()
Following is the contents of myapp.kv file:
:
EmotionsScreen:
<EmotionsScreen>:
name:'Emotions'
HappyButton:
id: happy
source: "happy.png"
pos: (-200, 100)
emotion: "Happy"
SadButton:
id: sad
source: "sad.png"
pos: (0, 100)
emotion: "Sad"
TiredButton:
id: tired
source: "tired.png"
pos: (200, 100)
emotion: "Tired"
Upon running the application, and clicking on the three buttons, I am getting the following behavior:
Clicking the happy button prints "Sad" :)
Clicking the sad button prints "Tired"
Clicking the tired button prints "Tired"
The above happens even if I call a single callback (inside app, with arguments supplied), as follows:
main.py:
from kivy.app import App
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.screenmanager import (ScreenManager, Screen)
from kivy.uix.button import Button
from kivy.properties import *
class EmotionsScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class ImageButton(ButtonBehavior, Image):
pass
class MyApp(App):
def build(self):
sm = ScreenManagement()
sm.current = 'Emotions'
return sm
def button_press(self, *args):
print (args)
if __name__=='__main__':
MyApp().run()
myapp.kv:
<ScreenManagement>:
EmotionsScreen:
<EmotionsScreen>:
name:'Emotions'
ImageButton:
source: "happy.png"
pos: (-200, 100)
on_press: app.button_press("Happy")
ImageButton:
source: "sad.png"
pos: (0, 100)
on_press: app.button_press("Sad")
ImageButton:
source: "tired.png"
pos: (200, 100)
on_press: app.button_press("Tired")
I expect it to print, "Happy", "Sad" and "Tired" upon clicking the three buttons respectively. May I know where I have gone wrong?
These images seem to be placed randomly. Example using buttons as mockups:
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.screenmanager import (ScreenManager, Screen)
from kivy.uix.button import Button
from kivy.properties import *
from kivy.lang import Builder
Builder.load_string('''
<EmotionsScreen>:
name:'Emotions'
HappyButton:
id: happy
pos: (-200, 100)
text: "Happy"
emotion: "Happy"
SadButton:
id: sad
pos: (0, 100)
text: "Sad"
emotion: "Sad"
TiredButton:
id: tired
pos: (200, 100)
text: "Tired"
emotion: "Tired"
''')
class EmotionsScreen(Screen):
def button_press(self, emotion_str):
print (self.ids[emotion_str].emotion)
class ScreenManagement(ScreenManager):
pass
class HappyButton(Button):
def on_press(self):
print(self.emotion)
class SadButton(Button):
def on_press(self):
print(self.emotion)
class TiredButton(Button):
def on_press(self):
print(self.emotion)
class MyApp(App):
def build(self):
sm = ScreenManagement()
sm.add_widget(EmotionsScreen())
sm.current = 'Emotions'
return sm
if __name__=='__main__':
MyApp().run()
You should rather put a layout inside your screen to manage its widgets:
Builder.load_string('''
<EmotionsScreen>:
name:'Emotions'
BoxLayout:
HappyButton:
id: happy
text: "Happy"
emotion: "Happy"
SadButton:
id: sad
text: "Sad"
emotion: "Sad"
TiredButton:
id: tired
text: "Tired"
emotion: "Tired"
''')