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...