'Accessing data from outside a running Kivy application

I'm working on a program with a GUI interface, which I've implemented using a kivy app. My end-goal is to enter a desired voltage in the kivy textbox and for it to be set using an Arduino + a DAC converter. For now I'm just trying to access the voltage from the text input from outside the kivy app in my main, so that I can separate the GUI cleanly from communication with the arduino, and other necessary calculations.

The problem is that the program doesn't continue after App.run() until the kivy app has been closed and the entered voltage has been lost. I've tried using the multiprocessing library, but the start() function also runs as long as the app is running. I'm also not sure what the best way is to access the voltage, I tried having it as a class member of the GUI, but maybe that's not a good idea.

This is my GUI.py code:

    from kivy.app import App
    from kivy.uix.label import Label
    from kivy.uix.gridlayout import GridLayout
    from kivy.uix.textinput import TextInput
    from kivy.properties import StringProperty, NumericProperty
    from kivy.uix.button import Button
    
    class GUI_GridLayout(GridLayout):
    
        voltage_label_1 = StringProperty("Voltage 1?")
        voltage_label_2 = StringProperty("Voltage 2?")
    
        voltage_input_1 = TextInput()
        voltage_input_2 = TextInput()
    
        submit_button_label_1 = StringProperty("Submit 1")
        submit_button_label_2 = StringProperty("Submit 2")
    
        voltage_output_1 = NumericProperty(0)
        voltage_output_2 = NumericProperty(0)
    
        def press_1(self):
            voltage_1 = self.voltage_input_1.text
            self.voltage_output_1  = float(voltage_1)
    
        def press_2(self):
            voltage_2 = self.voltage_input_2.text
            self.voltage_output_2 = float(voltage_2)
    
        def build(self):
            self.add_widget(Label(text=self.voltage_label_1))
            self.add_widget(self.voltage_input_1)
    
            self.add_widget(Label(text=self.voltage_label_2))
            self.add_widget(self.voltage_input_2)
    
            self.submit_button_1 = Button(text=self.submit_button_label_1)
            self.submit_button_1.bind(on_press=self.press_1)
            self.add_widget(self.submit_button_1)
    
            self.submit_button_2 = Button(text=self.submit_button_label_2)
            self.submit_button_2.bind(on_press=self.press_2)
            self.add_widget(self.submit_button_2)
    
    
    
    class apason_GUIApp(App):
        def build(self):
            return GUI_GridLayout()

The corresponding kv file:

#:kivy 1.0.9

<GUI_GridLayout>:

    cols: 3

    voltage_input_1: input_1
    voltage_input_2: input_2

    Label:
        font_size: 50
        text: str(root.voltage_label_1)

    TextInput:
        id: input_1
        font_size: 50
        multiline: False
        text: root.voltage_input_1.text

    Button:
        font_size: 50
        text: str(root.submit_button_label_1)
        on_press: root.press_1()

    Label:
        font_size: 50
        center_x: root.width / 4
        text: str(root.voltage_label_2)

    TextInput:
        id: input_2
        font_size: 50
        multiline: False
        text: root.voltage_input_2.text

    Button:
        font_size: 50
        text: str(root.submit_button_label_2)
        on_press: root.press_2()

And here's my main:

import GUI.GUI as gui
import multiprocessing as multiproc
import time

class GetVoltage:

    def __init__(self):
        self.voltage_1 = 0
        self.voltage_2 = 0

    def fetchVoltage(self, interface):
        self.voltage_1 = interface.voltage_output_1
        self.voltage_2 = interface.voltage_output_2

    def run(self, interface):
        while (True):

            self.fetchVoltage(interface)

            print(self.voltage_1)
            print(self.voltage_2)

            time.sleep(1)

if __name__ == '__main__':

    interface = gui.apason_GUIApp()
    interface_process = multiproc.Process(target=interface.run())

    checker = GetVoltage()
    checker_process = multiproc.Process(target=checker.run(interface))

    interface_process.start()
    checker_process.start()


Solution 1:[1]

Since you already have one process when you run main.py, you really only need to start the second process. And you can communicate between the processes using a Queue. Here is a modified version of your main.py that uses this approach:

import GUI as gui
import multiprocessing as multiproc


class GetVoltage:
    def run(self, queue):
        while True:
            voltage = queue.get()  # get the data from the other process by using the Queue
            print(voltage)


if __name__ == '__main__':
    q = multiproc.Queue()

    # start the GetVoltage process
    checker = GetVoltage()
    checker_process = multiproc.Process(target=checker.run, args=(q,), daemon=True)
    checker_process.start()

    # start the App
    interface = gui.apason_GUIApp()
    interface.q = q
    interface.run()  # this does not return until the App closes

    # kill the GetVoltage process after the App exits
    checker_process.kill()

And a couple minor changes in the App code:

def press_1(self):
    voltage_1 = self.voltage_input_1.text
    self.voltage_output_1 = float(voltage_1)
    App.get_running_app().q.put('voltage_1 = ' + voltage_1)  # put the data in the Queue

def press_2(self):
    voltage_2 = self.voltage_input_2.text
    self.voltage_output_2 = float(voltage_2)
    App.get_running_app().q.put('voltage_2 = ' + voltage_2)  # put the data in the Queue

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 John Anderson