Simultaneous placement of camera images and buttons in GUI applications using Kivy - python

I am currently trying to create an app using Kivy that will turn the video into grayscale when the button is pressed.
At first, I was able to display the camera image on the GUI, but I'm having trouble figuring out how to place buttons and other parts from here.
import sys
import numpy as np
import cv2
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics.texture import Texture
from kivy.clock import Clock
from kivy.core.window import Window
import datetime
import random
WINDOW_WIDTH = 1500
WINDOW_HEIGHT = 1008
### Setting of the window
Window.size = (WINDOW_WIDTH, WINDOW_HEIGHT)
class MyApp(App, Widget):
title = "opencv on kivy"
def __init__(self, **kwargs):
super(MyApp, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1.0 / 30)
self.widget = Widget()
# self.cap = cv2.VideoCapture(0)
self.cap = cv2.VideoCapture(1)
# The method by the intarval
def update(self, dt):
ret, img = self.cap.read()
### The color sequence in openCV is BGR, so fix it to RGB.
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
### The origin of Kivy's coordinates is the lower left, so flip it upside down.
img = cv2.flip(img, 0)
# if img is not None:
texture = Texture.create(size=(img.shape[1], img.shape[0]))
texture.blit_buffer(img.tostring())
with self.widget.canvas:
Rectangle(texture=texture ,pos=(0 + int(WINDOW_WIDTH/2) - int(img.shape[1]/2), WINDOW_HEIGHT - img.shape[0]), size=(img.shape[1], img.shape[0]))
return self.widget
def build(self):
return self.widget
# return MyApp()
if __name__ == '__main__':
MyApp().run()
Also, the application that displays the button works fine.
However, I have no idea how to combine these since build(self) can only return a button or a single widget for the camera image.
# import kivy module
import kivy
# this restrict the kivy version i.e
# below this kivy version you cannot
# use the app or software
kivy.require("1.9.1")
# base Class of your App inherits from the App class.
# app:always refers to the instance of your application
from kivy.app import App
# creates the button in kivy
# if not imported shows the error
from kivy.uix.button import Button
# class in which we are creating the button
class ButtonApp(App):
def build(self):
btn = Button(text ="Push Me !")
return btn
# creating the object root for ButtonApp() class
root = ButtonApp()
# run function runs the whole program
# i.e run() method which calls the
# target function passed to the constructor.
root.run()
thank you in advance.

You can use self.add_widget(..) to add other widgets inside Widget, Button, etc. but it doesn't have function to automatically arange layout.
But build() can use any widget - it doesn't have to Button or Widget.
If you use BoxLayout then you can use self.add_widget(...) to add widgets in rows or columns.
Other layout widgets can be useful to organize widgets in different way.
class MyApp(App, BoxLayout): # class MyApp(App, Widget)
def __init__(self, **kwargs):
# ... code ...
self.orientation = 'vertical' # BoxLayout
self.widget = Widget()
self.add_widget(self.widget)
self.button = Button(text='Gray', on_press=self.change_color)
self.add_widget(self.button)
Full working code.
I use BoxLayout with orientation = 'vertical' to organize in rows I put Widget in top row, and Button in bottom row.
Button runs function which switch value self.convert_to_grey - True/False - and update() uses this value to convert image to gray (and back to RGB but with gray color)
import sys
import numpy as np
import cv2
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Label
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics.texture import Texture
from kivy.clock import Clock
from kivy.core.window import Window
import datetime
import random
WINDOW_WIDTH = 1500
WINDOW_HEIGHT = 1008
### Setting of the window
Window.size = (WINDOW_WIDTH, WINDOW_HEIGHT)
class MyApp(App, BoxLayout):
title = "opencv on kivy"
def __init__(self, **kwargs):
super(MyApp, self).__init__(**kwargs)
self.orientation = 'vertical' # BoxLayout
self.convert_to_grey = False #
self.cap = cv2.VideoCapture(0)
#self.cap = cv2.VideoCapture(1)
self.widget = Widget()
self.add_widget(self.widget)
self.button = Button(text='Gray', on_press=self.change_color)
self.add_widget(self.button)
Clock.schedule_interval(self.update, 1.0 / 30)
def change_color(self, *args):
print('args:', args)
self.convert_to_grey = not self.convert_to_grey
def update(self, dt):
ret, img = self.cap.read()
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.flip(img, 0)
if self.convert_to_grey:
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
texture = Texture.create(size=(img.shape[1], img.shape[0]))
texture.blit_buffer(img.tostring())
with self.widget.canvas:
Rectangle(texture=texture, pos=(0 + int(WINDOW_WIDTH/2) - int(img.shape[1]/2), WINDOW_HEIGHT - img.shape[0]), size=(img.shape[1], img.shape[0]))
return self.widget
def build(self):
return self
if __name__ == '__main__':
MyApp().run()
More layouts in Kivy doc:
Getting Started ยป Layouts
BTW: you can nested layouts - ie. inside BoxLayout you can use one row with GridLayout and other row with different layout or widget.

Related

Play video inside kivy canvas

I have put the video inside kivy canvas, first 3 sec showing image afterthat will be play video. But only show all time image only.
Any suggestion.
Thanks
import kivy
from kivymd.app import MDApp
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle ,Color
from kivy.core.video import Video as CoreVideo
from kivy.clock import Clock
class Mycanvas(Widget):
def __init__(self,**kw):
super(Mycanvas,self).__init__(**kw)
with self.canvas:
self.bg = Rectangle(source='Flower.jpg', pos=self.pos, size=self.size)
self.video = CoreVideo()
self.video.bind(on_frame=self.set_bg_texture)
self.video.filename = 'Garden.mp4'
Clock.schedule_once(self.start_vid, 3)
self.start_vid(3)
def start_vid(self, dt):
self.video.play()
def set_bg_texture(self, *args):
self.bg.texture = self.video.texture
class mainapp(MDApp):
def build(self):
return Mycanvas()
mainapp().run()

How to insert a label on a image in kivy?

I am new to kivy. I have chosen a background image and i want to insert a label on it. But it shows like this.
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class MyApp(App):
def build(self):
self.box = BoxLayout()
self.img = Image(source = 'image4.png')
self.lbl = Label(text = "Total_Wealth")
self.box.add_widget(self.img)
self.box.add_widget(self.lbl)
return self.box
[![enter image description here][1]][1]MyApp().run()
The BoxLayout automatically stacks the widgets.
If you want to put Label on top of the image, you can use FloatLayout for more control on the placement of the widgets

Removing a widget with Kivy

I'm trying to remove an Image widget after its animation is complete.
So far I have managed to animate the widget and then call the animation_complete method after the animation ends. Unfortunately, the widget is not removed.
What am I doing wrong?
class ShootButton(Widget):
def bullet_fly(self):
def animation_complete(animation, widget):
print "removing animation"
self.remove_widget(widget=bullet1)
with self.canvas:
bullet1 = Image(source='bullet.png', pos = (100,200))
animation1 = Animation(pos=(200, 300))
animation1.start(bullet1)
animation1.bind(on_complete=animation_complete)
You do not have to use the canvas to add an animation but add the widget directly using add_widget() and then remove it with remove_widget(). In your initial case bullet1 is not the children of ShootButton.
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.animation import Animation
Window.size = (360, 640)
class ShootButton(Widget):
def bullet_fly(self):
def animation_complete(animation, widget):
self.remove_widget(widget)
bullet1 = Image(source='bullet.png', pos = (100,200))
self.add_widget(bullet1)
animation1 = Animation(pos=(200, 300))
animation1.start(bullet1)
animation1.bind(on_complete=animation_complete)
class MyApp(App):
def build(self):
button = ShootButton()
button.bullet_fly()
return button
if __name__ == '__main__':
MyApp().run()

Kivy: Coordinates

In the following code i don't explain two problems:
If I use method __init__ --> Scene.canvas = None
I don't underestand to relative position objects on image.
I wanted to obtain knowledges about coordinates system from Kivy Manual, but without success.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.graphics import Ellipse
class Scene(Widget):
"""
def __init__(self):
self.x = 0
self.y = 100
self.dir = 1
"""
def set_par(self):
self.x = 0
self.y = 100
self.dir = 1
def create_ball(self):
with self.canvas:
Ellipse(pos = (0,0), size = (40, 40))
img = Image(pos = (0,0), source = 'image1.png') # square 40x40pixels
self.add_widget(img)
class SceneApp(App):
def build(self):
scene = Scene()
scene.create_ball()
return scene
if __name__ == '__main__':
SceneApp().run()
Printscreen form my code:
You did not call super() in your __init__ method. So Widget's __init__ method just get completely overrited.
Then use FloatLayout instead of Widget
You can to do something like this:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Ellipse
Window.size = (300, 300)
class Scene(FloatLayout):
def __init__(self, **kwargs):
super(Scene,self).__init__(**kwargs)
self.size = 300,300
def create_ball(self):
img = Image(pos = (-130, -130), source = 'image1.png') # square 40x40pixels
self.add_widget(img)
with self.canvas:
Ellipse(pos = (0,0), size = (40, 40))
class SceneApp(App):
def build(self):
scene = Scene()
scene.create_ball()
return scene
if __name__ == '__main__':
SceneApp().run()

Set Background Image or Colour Rectangle to a Widget in GridLayout

Problem in hand is that i have a screen where i have created 4 widgets under gridlayout . Widget one is a custom widget which have a boxlayout and have 2 images and a button .
and other 3 widgets are simple buttons
Now i want to have a background image or coloured rectangle to the 1st widget but image is getting drawn at position 0,0 . I am trying to use canvas instructions to create a rectangle or Border Image but seems gridlayout shows the widget position as 0,0 and hence it created the rectangle at 0,0 . please see the code below :
Any ideas how to fix this and create rectangle as at the border of widget 1?
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen,FallOutTransition
from kivy.uix.image import Image
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.graphics import BorderImage
from kivy.graphics import Color, Rectangle ,Line
################# Images ######################
S = Image(source='images/Sti.png')
L = 'images/spinnin.gif'
################# Images ######################
class CButtonW(BoxLayout):
def __init__(self, **kwargs):
print "init --> CButton self.pos is ",self.pos
super(CButtonW, self).__init__(**kwargs)
self.orientation = 'vertical'
with self.canvas.before:
Color(1, 1, 0, 1, mode='rgba')
Rectangle(pos=self.pos,size=self.size)
BorderImage(
border=(10, 10, 10, 10),
source='images/tex.png')
self.add_widget(S)
self.add_widget(Button(text="Button 1"))
self.add_widget(Image(source=L))
class LevelScreen(Screen,GridLayout):
def __init__(self, **kwargs):
super(LevelScreen, self).__init__(**kwargs)
with self.canvas:
Line(points=(10, 10, 20, 30, 40, 50))
Color(1, 0, 1, 1, mode='rgba')
Rectangle(pos=self.pos, size=Window.size)
self.layout = GridLayout(cols=3,spacing=1,padding=10)
self.Button1 = CButtonW(id='1',text='Level 1')
self.Button2 = Button(id='2',text='Level 2')
self.Button3 = Button(id='3',text='Level 3')
self.Button4 = Button(id='4',text='Level 4')
self.layout.add_widget(self.Button1)
self.layout.add_widget(self.Button2)
self.layout.add_widget(self.Button3)
self.layout.add_widget(self.Button4)
LevelScreen.cols=3
LevelScreen.add_widget(self,self.layout)
print "position of 1st button is ",self.Button1.pos
print "position of 2 button is ",self.Button2.pos
# App Class
class MyJBApp(App):
def build(self):
sm = ScreenManager(transition= FallOutTransition())
sm.add_widget(LevelScreen(name='level'))
return sm
if __name__ == '__main__':
MyJBApp().run()
Your canvas instructions are drawn before the CButtonW is positioned by its layout, so at that point it's still in its default position of 0, 0.
You can fix it by adding code to reposition the instructions when the widget's position or size changes. The easiest way is to just use kv language in the first place, which will automatically create the relevant bindings, but you can also bind to pos or size in python, or use the on_propertyname events associated with their changes.

Categories