Label not updating in real time in Kivy

653 views Asked by At

I am trying to build graph of Real Time Data and at same time to print it on Label but label not updating till i hit start button.

Here is the .py code:

// import


def get_microphone_level(label_level, stream, store_bool, dt):
    global levels
    data = stream.read(chunk)
    mx = audioop.rms(data, 2)

    if store_bool:
        if len(levels) >= 100:
            levels = []
        levels.append(mx)

    label_level.text = str(mx)
    # print mx
    print label_level.text


class Logic(BoxLayout):
    label_level = ObjectProperty(Spinner)
    select_timer = ObjectProperty(Spinner)
    timer = NumericProperty('0.0001')
    stream = None

    def __init__(self):
        super(Logic, self).__init__()
        self.plot = SmoothLinePlot(color=[1, 0, 0, 1])
        self.stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=chunk)
        Clock.schedule_interval(partial(get_microphone_level, self.label_level, self.stream, False), self.timer)

    def start(self):
        self.ids.graph.add_plot(self.plot)
        Clock.schedule_interval(partial(get_microphone_level, self.label_level, self.stream, True), self.timer)
        Clock.schedule_interval(self.update_graph, self.timer)

    def stop(self):
        Clock.unschedule(self.update_graph)
        Clock.unschedule(get_microphone_level)

    def update_graph(self, dt):
        self.plot.points = [(i, j / 5) for i, j in enumerate(levels)]

    def update_timer(self, text):
        self.timer = float(text)
        self.stop()
        self.start()


class RealTimeMicrophone(App):
    def build(self):
        return Builder.load_file("look.kv")


if __name__ == "__main__":
    chunk = 2048
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 44100
    p = pyaudio.PyAudio()

    levels = []
    RealTimeMicrophone().run()

Here is .kv:

#:import SmoothLinePlot kivy.garden.graph.SmoothLinePlot
Logic:
    select_timer: select_timer
    label_level: label_level
    BoxLayout:
        orientation: "vertical"
        BoxLayout:
            size_hint_y: .8
            Graph:
                id: graph
                xlabel: "Time (in Second)"
                ylabel: "Level"
                ymin: 0
                ymax: 5000
                y_ticks_major: 1
                x_ticks_minor: 5
                x_ticks_major: 25
        BoxLayout:
            size_hint_y: .2
            orientation: "horizontal"
            spacing: 10
            BoxLayout:
                orientation: "vertical"
                spacing: 10
                Button:
                    text: "START"
                    bold: True
                    on_press: root.start()
                Button:
                    text: "STOP"
                    bold: True
                    on_press: root.stop()
            BoxLayout:
                orientation: "vertical"
                spacing: 10
                BoxLayout:
                    orientation: "horizontal"
                    Label:
                        text: 'Current Level:'
                    Label:
                        id: label_level
                        text: '0'

                Spinner:
                    id: select_timer
                    text: 'Select Timer (in Seconds)'
                    values: ['0.1', '0.5', '1', '2', '5']
                    on_text: root.update_timer(self.text)

I created three clock schedule one for Label data, levels collection after set timer, one for graph update.

Is there any problem with calling Clock.schedule_interval from init.

1

There are 1 answers

0
Yoav Glazner On

label_level is not set yet in __init__ so you would be better of send self to your function:

  def __init__(self, **kw):
    ...
    Clock.schedule_interval(partial(get_microphone_level, self, False), self.timer)

self.label_level, self.stream

def get_microphone_level(logic, store_bool, dt):
    stream = logic.stream  #now get your  stuff back...
    label_level = logic.label_level
    ... # continue as usual ... 

This way you don't fix on the wrong value, since each time the function called it will fetch label_level from the Logic instance