Home > Net >  KivyMD- KivyCamera class is initializing without being called
KivyMD- KivyCamera class is initializing without being called

Time:12-12

This is my first time asking a question here so please bear with me. I'm trying to make a Camera App using opencv and kivy. I managed to do it but my problem is that the camera is being called right away even if its not on the right screen. I suspect this is because of my init class in KivyCamera, but I dont know any other methods to do it. What I want is that the camera will only initialize after pressing a button.

here's my main app

from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.list import OneLineIconListItem, IconLeftWidget
from kivymd.uix.list import OneLineListItem, MDList
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.graphics.texture import Texture
import cv2

import random
import string

class OneLine(OneLineListItem):
    pass

class Scr(ScrollView):
    pass

class list(MDList):
    pass

class MenuScreen(Screen):
    pass

class CamScreen(Screen):
    # def show_cam(self):
    #     self.capture = cv2.VideoCapture(0)

    def on_stop(self):
        self.capture.release()

class SecondScreen(Screen):
    # def mama(self):
    #     for i in range(20):
    #         self.parent.parent.ids.container.add_widget(
    #             OneLineListItem(text=f"Single-line item {i}")
    #         )
    def on_released(self):
        # items= 
        # self.add_widget()

        for i in range(20):
            list = OneLine(text=f"dsafsd {i}", on_release= lambda list: print(list.text))
            list.text = f"dsafsd {i}"
            # list.bind(on_release=lambda btn: self.dropdown.select(list.text))
            self.ids['container'].add_widget(list)

    # pass
class KivyCamera(Image):
    def __init__(self, capture = cv2.VideoCapture(0), fps=30, **kwargs):
        super(KivyCamera, self).__init__(**kwargs)
        self.capture = capture
        Clock.schedule_interval(self.update, 1.0/fps)

    def update(self, dt):
        ret, frame = self.capture.read()
        if ret:
            buf1 = cv2.flip(frame, 0)
            buf = buf1.tostring()
            image_texture =Texture.create(
                size = (frame.shape[1], frame.shape[0]), colorfmt='bgr'
            )
            image_texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')

            self.texture = image_texture


                    
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SecondScreen(name='second'))
sm.add_widget(CamScreen(name='cam'))


class Test(MDApp):

    def on_start(self):
        size= 0.5,0.5
        # self.help.get_screen('cam').ids['cam'].add_widget(KivyCamera(size_hint = size, pos_hint = {"x":0.1,"top":1}))
        for i in range(20):
            self.help.get_screen('second').ids['container'].add_widget(
                OneLineListItem(text=f"Single-line item {i}")
            )

    letters = string.ascii_lowercase
    random = random.choice(letters)



    def build(self):
        screen = Screen()
        self.help = Builder.load_file('tes.kv')
        screen.add_widget(self.help)
        return screen

Test().run()

and here's the kv file

ScreenManager:
    MenuScreen:
    SecondScreen:
    CamScreen:

<MenuScreen>:
    name: 'menu'
    MDRectangleFlatButton:
        text: 'to second'
        pos_hint: {'center_x':0.1,'center_y':0.2}
        on_press: 
            root.manager.current = 'second'

    MDRectangleFlatButton:
        text: 'to cam'
        pos_hint: {'center_x':0.1,'center_y':0.9}
        on_press: 
            root.manager.current = 'cam'       
    MDLabel:
        id:our_label
        text: " "
        pos_hint: {'center_x':0.5,'center_y':0.2}
        # on_parent:
        #     app.samie()

<SecondScreen>:
    name: 'second'        
    MDBoxLayout:
        orientaion: 'vertical'
        MDRectangleFlatButton:
            text: 'to menu'
            pos_hint: {'center_x':0.1,'center_y':0.95}
            on_press:
                root.manager.current = 'menu'

            # on_press:
            #     app.please()
                # ScrollView:
                # root.on_released()
                # list.text = list.text   "bobo"
                # root.manager.current = 'menu'
                # for i in range(4): print(f"bobo {i}")
        
        ScrollView:
            MDList:
                id:container
                # on_parent:
                #     app.please()     

<CamScreen>:
    name: 'cam'
    BoxLayout:
        orientation: 'vertical'

        KivyCamera:
            id: cam
            size_hint: 0.5,0.5
            pos_hint: {"x":0.1,"top":1}

            
        MDRectangleFlatButton:
            text: 'show camera'
            pos_hint: {'center_x':0.1,'center_y':0.9}
            # on_parent:
            #     app.show_cam()   

Thanks! I really think I just need to get rid of init but I dont know any alternative way

CodePudding user response:

You can trigger the camera update using the on_enter() method of Screen. Try changing the __init__() method of KivyCamera to:

class KivyCamera(Image):
    def __init__(self, capture=cv2.VideoCapture(0), fps=30, **kwargs):
        super(KivyCamera, self).__init__(**kwargs)
        self.capture = capture
        self.fps = fps
        # Clock.schedule_interval(self.update, 1.0 / fps)

And add on_enter() and on_leave() methods to CamScreen:

class CamScreen(Screen):
    # def show_cam(self):
    #     self.capture = cv2.VideoCapture(0)

    def on_stop(self):
        self.capture.release()

    def on_enter(self, *args):
        cam = self.ids.cam
        self.clock_event = Clock.schedule_interval(cam.update, 1.0 / cam.fps)

    def on_leave(self, *args):
        self.clock_event.cancel()

On an unrelated note, the lines:

sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SecondScreen(name='second'))
sm.add_widget(CamScreen(name='cam'))

are building the root of your App, but it is never used. Those lines can be eliminated.

The line:

self.help = Builder.load_file('tes.kv')

is also building the same root, and unless there is some reason for creating another Screen to contain that root, you can simplify your build() method to:

def build(self):
    self.help = Builder.load_file('tes.kv')
    return self.help

CodePudding user response:

    def __init__(self, capture = cv2.VideoCapture(0), fps=30, **kwargs):

That line causes the issue.

The VideoCapture object is constructed at the time of the definition of the class and its method.

You probably don't want that.

Use capture=None in the parameter list, and in the body of __init__ you should check if capture is None: capture = cv2.VideoCapture...

  • Related