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())
Related
This app shows a screen with a video, logo, user input field, and submit button. It uses FloatLayout and GridLayout. How can I use this to switch screens when a valid input is given to the user input? I want to import ScreenManager but it looks like that FloatLayout and ScreenManager are not very compatible.
'''
from kivy.app import App
from kivy.uix.video import Video
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class iaScreen(Screen):
pass
class rtScreen(Screen):
pass
class MZ_Invest(App):
def build(self):
self.root_layout = FloatLayout()
self.window = GridLayout()
self.window.cols = 1
self.window.size_hint = (0.6,0.7)
self.window.pos_hint = {"center_x":0.5, "center_y":0.5}
#add widgets
#Video
video = Video(source='birds.mp4', state='play', volume = 0)
video.allow_stretch = False
video.options = {'eos': 'loop'}
video.opacity = 0.5
#Image
self.window.add_widget(Image(
source="mzi.png",
size_hint = (1.5,1.5)
))
#Label
self.greeting = Label(
text="How much would you like to invest?",
font_size = 18,
color='90EE90'
)
self.window.add_widget(self.greeting)
#User Input
self.user = TextInput(
multiline=False,
padding_y= (20,20),
size_hint = (1, 0.5)
)
self.window.add_widget(self.user)
#Button
self.button = Button(
text="Submit",
size_hint = (1,0.5),
bold = True,
background_color = '90EE90',
)
self.button.bind(on_press=self.callback)
self.window.add_widget(self.button)
self.root_layout.add_widget(video)
self.root_layout.add_widget(self.window)
return self.root_layout
def callback(self, instance):
if self.user.text.isnumeric() and int(self.user.text) >= 10000:
self.greeting.text = "Calculating: $" + self.user.text
self.greeting.color = '90EE90'
else:
self.greeting.text = "Invalid"
self.greeting.color = "#FF0000"
if __name__ == "__main__":
MZ_Invest().run()
'''
mz_invest.py:
from kivy.app import App
from kivy.uix.video import Video
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class SM(ScreenManager):
def __init__(self, **kwargs):
super(SM, self).__init__(**kwargs)
class Screen1(Screen):
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
def callback(self, instance):
entry = self.manager.ids['screen1'].ids.my_input.text
greeting = self.manager.ids['screen1'].ids.my_label
if entry.isnumeric() and int(entry) >= 10000:
greeting.text = "Calculating: $" + entry
greeting.color = '90EE90'
self.manager.current = 'screen2'
else:
greeting.text = "Invalid"
greeting.color = "#FF0000"
class Screen2(Screen):
def __init__(self, **kwargs):
super(Screen2, self).__init__(**kwargs)
class MZ_Invest(App):
def build(self):
sm = SM()
return sm
if __name__ == "__main__":
MZ_Invest().run()
mz_invest.kv:
<SM>:
Screen1:
id: screen1
name: 'screen1'
Screen2
id: screen2
name: 'screen2'
<Screen1>:
FloatLayout:
Video:
source: 'birds.mp4'
state: 'play'
volume: 0
allow_stretch: False
options: {'eos': 'loop'}
opacity: 0.5
GridLayout:
cols: 1
size_hint: (0.6,0.7)
pos_hint: {"center_x":0.5, "center_y":0.5}
Image:
size_hint: (1.5,1.5)
Label:
id: my_label
name: 'my_label'
text: "How much would you like to invest?"
font_size: 18
color: '90EE90'
TextInput:
id: my_input
name: 'my_input'
multiline: False
padding_y: (20,20)
size_hint: (1, 0.5)
Button:
text:"Submit"
size_hint: (1,0.5)
bold: True
background_color: '90EE90'
on_press: root.parent.ids['screen1'].callback(self)
<Screen2>:
Label:
text:"Welcome to Screen2"
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
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 want to make a custom mouse cursor in kivy.
This is what I have at the moment:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
#Window.show_cursor = False
KV = """
FloatLayout
BoxLayout
MyTextInput
MyMouse
<MyTextInput>:
font_size: 40
text: 'Some text'
<MyMouse>:
mouse_im_size: mouse_im.size
auto_bring_to_front: True
do_rotation:False
do_scale:False
do_translation_y:False
Image
id: mouse_im
size: 100, 100 / self.image_ratio
source: 'cursor-pink.png'
"""
class MyTextInput(TextInput):
pass
class MyMouse(Scatter):
def __init__(self, **kwargs):
Window.bind(mouse_pos=self.on_mouse_pos)
super(MyMouse, self).__init__(**kwargs)
def on_touch_down(self, *touch):
return
def on_mouse_pos(self, *args):
x,y = args[1]
self.pos = [x,y-self.mouse_im_size[1]]
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
MyApp().run()
the problem is that when I move the mouse beyond the left or upper edge of application, the cursor image remains within the app, and I want the mouse image to disappear just like when I move the mouse beyond the right or lower edge.
It seems the problem is that on_mouse_pos() only works when the mouse is inside the window.
I found a way to get the position of the mouse when it is outside the window, but I do not know how this can be used in my task. And maybe there is a better way to do this.
You can accomplish this by using the Window events on_cursor_enter and on_cursor_leave and making the cursor visible/invisible by using the opacity property:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
#Window.show_cursor = False
KV = """
FloatLayout
BoxLayout
MyTextInput
MyMouse
id: themouse
<MyTextInput>:
font_size: 40
text: 'Some text'
<MyMouse>:
mouse_im_size: mouse_im.size
auto_bring_to_front: True
do_rotation:False
do_scale:False
do_translation_y:False
Image
id: mouse_im
size: 100, 100 / self.image_ratio
source: 'cursor-pink.png'
"""
class MyTextInput(TextInput):
pass
class MyMouse(Scatter):
def __init__(self, **kwargs):
Window.bind(mouse_pos=self.on_mouse_pos)
Window.bind(on_cursor_leave=self.on_cursor_leave)
Window.bind(on_cursor_enter=self.on_cursor_enter)
super(MyMouse, self).__init__(**kwargs)
def on_touch_down(self, *touch):
return
def on_mouse_pos(self, *args):
x,y = args[1]
self.pos = [x,y-self.mouse_im_size[1]]
def on_cursor_leave(self, *args):
App.get_running_app().root.ids.themouse.opacity = 0
def on_cursor_enter(self, *args):
App.get_running_app().root.ids.themouse.opacity = 1
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
MyApp().run()
I added the themouse id to the MyMouse widget to accomplish this.
Here is another approach, but it requires a border around your basic layout:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
#Window.show_cursor = False
KV = """
FloatLayout
canvas.before:
Color:
rgba: (1, 0, 0, 1)
Rectangle:
size: self.size
pos: self.pos
FloatLayout
id: mainlayout
size_hint: (None, None)
#pos: (50, 50)
#pos_hint: {'center_x': 0.5, 'center_y': 0.5}
canvas.before:
Color:
rgba: (0, 1, 0, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout
size_hint: (1.0, 0.2)
pos_hint: {'center_x': 0.5, 'top': 1.0}
MyTextInput
StencilView:
size_hint: (1.0, 1.0)
pos_hint: {'x': 0, 'y': 0}
MyMouse
id: themouse
<MyTextInput>:
font_size: 40
text: 'Some text'
<MyMouse>:
mouse_im_size: mouse_im.size
auto_bring_to_front: True
do_rotation:False
do_scale:False
do_translation_y:False
Image
id: mouse_im
size: 100, 100 / self.image_ratio
source: 'cursor-pink.png'
"""
class MyTextInput(TextInput):
pass
class MyMouse(Scatter):
def __init__(self, **kwargs):
Window.bind(mouse_pos=self.on_mouse_pos)
super(MyMouse, self).__init__(**kwargs)
def on_touch_down(self, *touch):
return
def on_mouse_pos(self, *args):
x,y = args[1]
self.pos = [x,y-self.mouse_im_size[1]]
class MyApp(App):
def build(self):
self.mainlayout = None
self.mymouse = None
self.root = Builder.load_string(KV)
Window.bind(size=self.do_size)
Clock.schedule_once(self.do_size)
def do_size(self, *args):
if self.mainlayout is None:
self.mainlayout = self.root.ids.mainlayout
if self.mymouse is None:
self.mymouse = self.root.ids.themouse
self.mainlayout.size = (self.root.width - 2.0 * self.mymouse.mouse_im_size[0], self.root.height - 2.0 * self.mymouse.mouse_im_size[1])
self.mainlayout.pos = self.mymouse.mouse_im_size
MyApp().run()
This uses a StencilView to clip the drawing of the cursor to the inside of the man layout.
I'm a beginner in kivy and python. I've been trying to create a scrollview in a page that displays selected video on the screen. But after a while when i selected 5-6 videos to display even though i delete the video widget it starts to lag. And i'm open to any suggestions to better way to handle this kind of application.
from kivy.config import Config
Config.set('graphics', 'width', '1920')
Config.set('graphics', 'height', '1080')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.properties import StringProperty
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.button import Button
import os
from kivy.uix.video import Video
from kivy.uix.videoplayer import VideoPlayer
from kivy.clock import mainthread
from functools import partial
from kivy.core.window import Window
Window.clearcolor = (0, 0, 0, 0)
isThereVideo=False
picture_path="/home/linux/kivyFiles/kivyLogin/assets"
video_path="/home/linux/kivyFiles/kivyLogin/videoAssets/"
class Image(Image):
pass
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class Selfie(Screen):
pass
class RootWidget(Widget):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
Clock.schedule_once(self.createMultipleButton)
#mainthread
def createMultipleButton(self, dt):
self.root = Widget()
size_y=150;
size_x=150;
for i in range(1):
folderList = os.listdir(picture_path)
if len(folderList)==0:
time.sleep(1)
break
fileList = os.listdir(picture_path)
print fileList
for file in fileList:
x = (picture_path+"/"+file)
button = Button(id=str(file),text="" + str(file),size_hint=(None, None),height=size_y,width=size_x, pos_hint={'x': 0, 'y': 1},background_normal=x)
button.bind(on_release=partial(self.VideoContainer, file))
print file
self.scrollview.content_layout.add_widget(button)
print button.id
print("Parent of ScreenTwo: {}".format(self.parent))
#print(button.pos)
def VideoContainer(self,name,btn):
global isThereVideo
if isThereVideo==True:
#self.videocontainer.video_layout.unload()
self.videocontainer.clear_widgets()
mylist=name.split('.')
emailButton = Button(id='email')
video = Video(source="/home/linux/kivyFiles/kivyLogin/videoAssets/"+mylist[0]+".mp4", state='play',options={'eos': 'loop'})
video.size=(self.parent.width,self.parent.height)
video_pos=(self.parent.x,self.parent.y)
#video.pos_hint={'x': self.parent.width /2, 'y': self.parent.height/2}
video.play=True
#video.pos=(self.parent.width /2 , self.parent.height/2)
#self.videocontainer.video_layout.add_widget(emailButton)
self.videocontainer.add_widget(emailButton)
emailButton.add_widget(video)
isThereVideo=True
print("Parent of ScreenTwo: {}".format(self.parent))
return 0
class ScreenManagement(ScreenManager):
pass
class VideoContain(Widget):
pass
class ScrollableContainer(ScrollView):
pass
presentation = Builder.load_file("login.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == '__main__':
Window.fullscreen = True
app=MainApp()
app.run()
And my Kv file
ScreenManagement:
MainScreen:
Selfie:
<MainScreen>:
name: 'main'
Button:
on_release: app.root.current = 'selfie'
text: 'Another Screen'
font_size: 50
<Selfie>:
name: 'selfie'
Button:
on_release: app.root.current = 'login'
text: 'Selfie Screen'
font_size: 10
pos_hint: {"right": 1, 'top':1}
size_hint: (0.1, 0.1)
RootWidget:
<RootWidget>
size_hint: (0.1, None)
scrollview: scrollview
videocontainer:videocontainer
size:(self.parent.width, self.parent.height)
VideoContain:
id:videocontainer
##size:(self.parent.width, self.parent.height)
size:(root.width, root.height)
ScrollableContainer:
id: scrollview
size: root.size
pos: root.pos
<VideoContain>
video_layout:video_layout
FloatLayout:
cols:1
id: video_layout
<ScrollableContainer>:
scroll_timeout: 75
scroll_distance: 10
app: app
content_layout: content_layout
GridLayout:
id: content_layout
cols: 1
size_hint: (0.1, None)
pos: root.pos
height: self.minimum_height
padding: 25
spacing: 25
I posted all my code since i don't know which part may causing the problem i'm facing.
Solved it.
def VideoContainer(self,name,btn):
global isThereVideo
if isThereVideo:
# self.videocontainer.video_layout.unload()
self.videocontainer.clear_widgets()
In this part i'm clearing the widgets but i also need to unload the video somehow.
self.video.unload() solved my problem