Home > Mobile >  Simultaneous placement of camera images and buttons in GUI applications using Kivy
Simultaneous placement of camera images and buttons in GUI applications using Kivy

Time:10-27

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.

CodePudding user response:

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