kivy - how to prepare app for touch events - python

I am developing an app using Kivy for Android and IOS, i haven't tried it on any mobile yet but i have been noticing that i need to add some sort off 'touch' events so the buttons, swipes and etc will work on a mobile touchscreen.
i've tried searching around for examples but i didn't exactly get the concept, and i was wondering what exactly do i need to do for it to be ready for touch events, i am thinking of making a 'swiping touch' to move between pages on both sides, what do i need to do to make this happen?

When you want to swipe through pages as you have mentioned. It could be a nice option to use PageLayout. Check the docs:https://kivy.org/doc/stable/api-kivy.uix.pagelayout.html
PageLayout is where you have several pages and you can swipe through them. It could be suitable for you.
Here is a very simple example.Where you will have three pages
PageLayout:
Button:
text: 'page1'
Button:
text: 'page2'
Button:
text: 'page3'
Here is a advanced pagelayout example from kv file. The pagelyout then consists of floatlayouts. But you could use other layouts too
<Example>:
PageLayout:
FloatLayout:
id: string1
canvas:
Color:
rgb:utils.get_color_from_hex("#2BFFFA")
Rectangle:
size: self.size
pos:self.pos
Image:
size_hint:.9,1
pos_hint:{"top": 1,"right": 1}
source: "string/str1.png"
FloatLayout:
canvas:
Color:
rgb:utils.get_color_from_hex("#39FF22")
Rectangle:
size: self.size
pos:self.pos
Image:
size_hint:.9,1
pos_hint:{"top": 1,"right": 1}
source: "string/str1_sol.png"
FloatLayout:
canvas:
Color:
rgb:utils.get_color_from_hex("#2BFFFA")
Rectangle:
size: self.size
pos:self.pos
Image:
size_hint:.9,1
pos_hint:{"top": 1,"right": 1}
source: "string/str2.png"
Notice that my main layout is a pagelayout and in the floatlayout I have floatlayouts. So now I have three pages to swipe through
Or you could use a screemanger to manage your screens. . And then you just have to click on a button and then you will automatically switch screens. Here is a example. This is better than pagelayouts.
import kivy
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class ScreenOne(Screen):
def __init__ (self,**kwargs):
super (ScreenOne, self).__init__(**kwargs)
my_box1 = BoxLayout(orientation='vertical')
my_label1 = Label(text="BlaBlaBla on screen 1", font_size='24dp')
my_button1 = Button(text="Go to screen 2",size_hint_y=None, size_y=100)
my_button1.bind(on_press=self.changer)
my_box1.add_widget(my_label1)
my_box1.add_widget(my_button1)
self.add_widget(my_box1)
def changer(self,*args):
self.manager.current = 'screen2'
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super (ScreenTwo,self).__init__(**kwargs)
my_box1 = BoxLayout(orientation='vertical')
my_label1 = Label(text="BlaBlaBla on screen 2",font_size='24dp')
my_button1 = Button(text="Go to screen 1",size_hint_y=None, size_y=100)
my_button1.bind(on_press=self.changer)
my_box1.add_widget(my_label1)
my_box1.add_widget(my_button1)
self.add_widget(my_box1)
def changer(self,*args):
self.manager.current = 'screen1'
class TestApp(App):
def build(self):
my_screenmanager = ScreenManager()
screen1 = ScreenOne(name='screen1')
screen2 = ScreenTwo(name='screen2')
my_screenmanager.add_widget(screen1)
my_screenmanager.add_widget(screen2)
return my_screenmanager
if __name__ == '__main__':
TestApp().run()

Related

Python kivy position of the Label moves when resizing screen

I'm making an app in python kivy and in my tacscreen I have a draggable Image and an Label, whenever I drag my draggable Image my label drags with it as well. The position of the label is just below the draggable Image. The problem that I'm facing is whenever I resize my window and drag the image the label goes from this to this the space between the Image and the label is too much. How can I fix this? I want the label to always be like how it is in the first screenshot even after I resize the screen and drag. Below is my code. I have been trying to find a solution to this for a months now. I really appreciate any help. Thanks!
main.py
from kivy.properties import BooleanProperty
from kivy.properties import ObjectProperty
from kivy.metrics import dp
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
class DragImage1(DragBehavior, Image):
dragging = BooleanProperty(False)
drag_label = ObjectProperty()
def on_touch_move(self, touch):
if touch.grab_current is self:
self.drag_label.pos = self.x, self.y - dp(300)
return super().on_touch_move(touch)
def on_touch_up(self, touch):
uid = self._get_uid()
if uid in touch.ud:
# do something on drop
print(self.source, 'dropped at', touch.x, touch.y)
return super(DragImage1, self).on_touch_up(touch)
class TacScreen(Screen):
Pass
GUI = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
return GUI
def change_screen(self, screen_name, *args):
self.root.current = screen_name
MainApp().run()
main.kv
#:include tacscreen.kv
ScreenManager:
id: screen_manager
TacScreen:
name: "tac_screen"
id: tac_screen
tacscreen.kv
<tacscreen>:
#:import utils kivy.utils
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: "Background.png"
DragImage1:
drag_label: per
pos: root.center_x - self.width/2, root.center_y - self.height/0.3
size_hint: 1, .1
source: "Image.png"
Label:
id: per
text: "Hello"
font_size: "20dp"
pos: root.center_x - self.width/2, root.center_y - self.height/1.2
You can modify your kv to do the positioning for you:
DragImage1:
id: di # added id for use by the Label
drag_label: per
pos: root.center_x - self.width/2, root.center_y - self.height/0.3
size_hint: 1, .1
source: "Image.png"
Label:
id: per
text: "Hello"
font_size: "20dp"
size_hint: None, None
size: self.texture_size # set size to just contain the text
pos: di.center_x - self.width/2, di.y - self.height # adjust postion based on the DragImage1
And, since kv is now handling the positioning, you should remove the on_touch_move() method from the DragImage1 class.
Note that this will handle positioning when resizing before dragging. But if you drag before resizing, then the resizing will revert the position to that defined in the kv.

(Kivy) How do I display new content on my canvas in Kivy?

This should be a pretty easy fix. I'm new to Kivy. I'm trying to have the canvas cleared on a button press, and then display a new widget to essentially move to another page. When I run it and press the button, the canvas is cleared, but I get nothing from IntroPage.
Python Script:
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
class ABT(App):
def build(self):
return WelcomePage()
class WelcomePage(Widget):
def btn(self):
self.canvas.clear()
print('pressed')
return IntroPage()
class IntroPage(Widget):
def __init__(self):
pass
if __name__ == '__main__':
ABT().run()
KV File:
<WelcomePage>
canvas.before:
Color:
rgba: (.43,.51,.92,.26)
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols:1
size: root.width, root.height
Image:
source: 'abt1t.png'
size_hint: (1,.8)
Button:
text:"begin"
background_color: (.43,.51,.92,.26)
size_hint: (1,.2)
on_press: root.btn()
<IntroPage>
GridLayout:
cols:1
Label:
text:"This won't show up!"
Returning IntroPage from btn won't work, as btn isn't a build method.
The best way to implement multiple pages is probably to use ScreenManager:
from kivy.uix.screenmanager import ScreenManager, Screen
class ABT(App):
def build(self):
sm = ScreenManager()
sm.add_widget(WelcomePage(name="welcome"))
sm.add_widget(IntroPage(name="intro"))
return sm
class WelcomePage(Screen):
def btn(self):
print('pressed')
self.manager.current = "intro"
class IntroPage(Screen):
def __init__(self):
pass

Kivy Filechooser overlapping text on scroll with screens

Versions
Python: 3.7
OS: Windows 10
Kivy: 1.11.1
Kivy installation method: pip
Description
FileChooser overlaps text on scrolling through files list. Looks like the 1st content stays and on scroll the content of scrolled data is getting displayed on top of 1st content.
Code and Logs
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty, BoundedNumericProperty, StringProperty
from kivy.lang import Builder
class FirstWindow(Screen):
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def dismiss_popup(self):
self._popup.dismiss()
def cancel(self):
pass
def load(path, selection):
print(path, selection)
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
class EditorApp(App):
def build(self):
kv = Builder.load_file("editor.kv")
self.screen_manager = ScreenManager()
screen = FirstWindow(name="first")
self.screen_manager.add_widget(screen)
self.screen_manager.current = "first"
return self.screen_manager
if __name__ == "__main__":
editor_app = EditorApp()
editor_app.run()
KV File
<FirstWindow>:
BoxLayout:
orientation: "vertical"
Button:
text: "Select Folder"
on_release: root.show_load()
<LoadDialog>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
Screenshots
For people who are facing the same issue, I resolved by replacing FileChooser with Plyer's native filechooser. Disussion on Github can be found here
Just remove the line
kv = Builder.load_file("editor.kv")
your code will work fine

Scrollview to display different videos in kivy lags after a while

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

adding widget dynamically into kivy screen object

As far as I am aware, kv language is useful when making a static display, e.g. not when making game which need much widget's positioning during runtime. Here I try to make a simple game, but still need much positioning, so kv language is out of context for the widget, but not for the screen. I use screen to differentiate the main menu and game screen. But when I try to use 'add_widget' to insert my image, it always positioned at the middle of the window. Later I found out that the screen size is only 100x100.
Below are the only way that I can thought of, but still with no luck:
class HomeScreen(Screen):
pass
class GameScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation=Builder.load_file('ProjectSD.kv')
class ProjectSDApp(App):
def build(self):
A=presentation
A.screens[0].size=(Window.size)
A.screens[0].add_widget(Label(text='hello',font_Size=80,pos=(0,0)))
return A
if __name__=='__main__':
print(Window.size)
ProjectSDApp().run()
and my ProjectSD.kv file:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
HomeScreen:
GameScreen:
<Button>:
font_name:'attackofthecucumbers.ttf'
<HomeScreen>:
name:'home'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'nature.jpg'
Label:
text: 'Monopoly GX'
font_name:'KBDunkTank.ttf'
font_size:100
size_hint:0.7,0.2
pos:root.width*0.15,root.height*0.70
Button:
on_release: app.root.current = "game"
text: 'Play Game'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.45
Button:
on_release: app.stop()
text: 'exit'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.20
<GameScreen>:
name:'game'
Button:
on_release: app.root.current = "home"
background_color: (1,0.15,0.2,0.8)
text: 'X'
font_size:root.width/40
size_hint:0.05,0.05
pos:root.width*0.95,root.height*0.95
Since there is no 'pos' method in screen object, I put my widget to position (0,0) manually.
The only way I found is just below:
https://kivyspacegame.wordpress.com/2014/08/10/tutorial-flappy-ship-part-2-build-simple-menus-and-animate-your-games-using-clock/
So my question is, if I use screen object from kivy's build in, how to achieve the same result? So I can still adding and remove widget as I want it later?
I'm not entirely clear on what you're asking; I don't see anywhere in your code where you are trying to add an Image to a Layout. You add a label to the middle and that works fine.
I think because you are creating screens without any layouts and not setting a default window size, that the screens just take up their minimum default size. You need to add a layout and fill it with stuff that has a defined size, or set a window size at the beginning e.g:
# window import
from kivy.core.window import Window
from kivy.utils import get_color_from_hex
Window.size = (1920/2,1080/2)
Window.clearcolor = get_color_from_hex('#000000') # black
Here is some code that creates your two Screens, and adds a draggable and a static Image at a position to a FloatLayout.
from kivy.app import App
from kivy.uix.screenmanager import (ScreenManager, Screen)
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
class HomeScreen(Screen):
pass
class GameScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class MovingImage(DragBehavior,Image):
pass
class DragLabel(DragBehavior, Label):
pass
class StaticImage(Image):
pass
class MyApp(App):
def build(self):
A = ScreenManagement()
A.current = 'game'
A.screens[1].ids.gamefloat.add_widget(MovingImage(size_hint = [0.3,0.3]))
A.screens[1].ids.gamefloat.add_widget(StaticImage(pos = (150,300)))
return A
if __name__=='__main__':
MyApp().run()
and the .kv file called myapp.kv
<ScreenManagement>:
HomeScreen:
GameScreen:
<Button>:
<HomeScreen>:
name:'home'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image.jpg'
Label:
text: 'Monopoly GX'
font_size:100
size_hint:0.7,0.2
pos:root.width*0.15,root.height*0.70
Button:
on_release: app.root.current = "game"
text: 'Play Game'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.45
Button:
on_release: app.stop()
text: 'exit'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.20
<GameScreen>:
name:'game'
FloatLayout:
id: gamefloat
Button:
on_release: app.root.current = "home"
background_color: (1,0.15,0.2,0.8)
text: 'X'
font_size:root.width/40
size_hint:0.05,0.05
pos:root.width*0.95,root.height*0.95
<MovingImage>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 1000000
drag_distance: 0
source: 'image.jpg'
<StaticImage>:
source: 'image.jpg'

Categories