Search code examples
pythonkivy

AttributeError: 'float' object has no attribute 'ids' when running kivy app


I am trying to make a app that captures 30 images a second from the webcam in kivy.

But when I run it, it give me this error:

AttributeError: 'float' object has no attribute 'ids'

Here is the code to reproduce the problem:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock

Builder.load_string('''
<CameraClick>:
    orientation: 'vertical'
    Camera:
        id: camera
        resolution: (640, 480)
        play: True
''')


class CameraClick(BoxLayout):
    def capture(self):
        '''
        Function to capture the images from the camera
        '''

        camera = self.ids['camera']
        camera.export_to_png("IMG.png")
        print("Captured")

    event = Clock.schedule_interval(capture, 1 / 30.)


class TestCamera(App):

    def build(self):
        return CameraClick()


TestCamera().run()

This code brings up the error but deleting event = Clock.schedule_interval(capture, 1 / 30.) fixes that error but I need that line of code.

Question:

So, how can I fix the error so that I can capture images from the webcam and store them?


Solution

  • With the following line

    event = Clock.schedule_interval(capture, 1 / 30.)
    

    you are actually creating a class attr. assigning a ClockEvent. As it takes a callback function/method with default arg. dt (delta-time, a float), in your case of

    def capture(self):
    

    the arg. self is supposed to represent that (as being defined in class level).

    So think for a moment, you want self to represent the current class instance and due to the construction it is supposed to be the dt arg., and that raises the AttributeError.

    The solution could be like the following,

    Declare that clock event from some method and call that method accordingly. Or define it within __init__ as follows,

        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.event = Clock.schedule_interval(self.capture, 1 / 30.)
            # Or just,
            # Clock.schedule_interval(self.capture, 1 / 30.)
    
    
        def capture(self, dt): # Note the added arg.
            '''
            Function to capture the images from the camera
            '''
            ...