Asterlux Asterlux - 24 days ago 23
Python Question

Kivy - Updating label in kv code from python side

I'm trying to make a GUI with a few screens and a button that will fetch some data from an SQLite database and assign that to a label to be displayed on the GUI screen. I'm still new to kivy, python, and object-oriented programming in general. I try to run this, and it seems to work fine for fetching the SQL data but I'm having trouble assigning it to the label I want displayed. The error I get is "attribute error: 'float' object has no attribute 'psarjvalue'", so I understand that it's looking in my float layout and trying to find psarjvalue but is not succeeding, why not? I also tried assigning an id to the float layout but I'm clearly missing something.



import kivy
import sqlite3
import sched, time
import smbus
import time
from Naked.toolshed.shell import execute_js, muterun_js
import os
import signal
import multiprocessing, signal
from kivy.uix.behaviors.button import ButtonBehavior
from kivy.uix.button import Button
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.base import runTouchApp
from kivy.clock import Clock
from kivy.properties import ListProperty
from kivy.vector import Vector
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.stacklayout import StackLayout
from kivy.core.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition, WipeTransition, SwapTransition

bus = smbus.SMBus(1)
address = 0x04

psarj = 0.00

p = multiprocessing.Process(target = muterun_js,args=('iss_telemetry.js',))

conn = sqlite3.connect('iss_telemetry.db')
c = conn.cursor()

class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)

class CalibrateScreen(Screen):
pass

class ManualControlScreen(Screen):
def __init__(self, **kwargs):
super(ManualControlScreen, self).__init__(**kwargs)

class MimicScreen(Screen):
def __init__(self, **kwargs):
super(MimicScreen, self).__init__(**kwargs)

def updatePSARJ(self, *args, **kwargs):
c.execute('SELECT two FROM telemetry where one="psarj"')
psarj = c.fetchone()
label = self.psarjvalue
label.text = psarj

# self.psarjvalue.text = psarj

class MainScreenManager(ScreenManager):
pass

#def updatePSARJ(*args):
# c.execute('SELECT two FROM telemetry where one="psarj"')
# psarj = c.fetchone()
# MimicScreen.psarjlabel.text = psarj

class MainApp(App):
def build(self):
root = ScreenManager(transition=WipeTransition())
root.add_widget(MainScreen(name = 'main'))
root.add_widget(CalibrateScreen(name = 'calibrate'))
root.add_widget(MimicScreen(name = 'mimic'))
root.add_widget(ManualControlScreen(name = 'manualcontrol'))
root.current= 'main'
return root

def startTelemetry(*kwargs):
p.start()

def stopTelemetry(*kwargs):
os.kill(p.pid,signal.SIGKILL)

#def update_values(*args):
# c.execute('SELECT two FROM telemetry where one="psarj"')
# psarj = c.fetchone()
# MimicScreen.updatePSARJ()

Clock.schedule_interval(MimicScreen.updatePSARJ, 1)

Builder.load_string('''
#:kivy 1.8
#:import kivy kivy
#:import win kivy.core.window

<MimicScreen>:
name: 'mimic'
FloatLayout:
id: mimicscreenlayout
Image:
source: 'iss1.png'
allow_stretch: True
keep_ratio: False
Label:
id: psarjvalue
pos_hint: {"center_x": 0.7, "center_y": 0.5}
text: '0.003'
markup: True
color: 1,1,1
font_size: 60
Label:
id: telemetrystatus
pos_hint: {"center_x": 0.6, "center_y": 0.8}
text: 'Telemetry'
markup: True
color: 1,0,1
font_size: 60
Button:
id: mimicstartbutton
size_hint: 0.3,0.1
pos_hint: {"x": 0.1, "y": 0.6}
text: 'MIMIC'
disabled: False
font_size: 30
on_release: telemetrystatus.text = 'Fetching Telemetry...'
on_release: app.startTelemetry()
on_release: mimicstopbutton.disabled = False
on_release: mimicstartbutton.disabled = True
Button:
id: mimicstopbutton
size_hint: 0.3,0.1
pos_hint: {"x": 0.1, "y": 0.4}
text: 'Stop'
disabled: True
font_size: 30
on_release: telemetrystatus.text = 'Program Stopped'
on_release: app.stopTelemetry()
on_release: mimicstopbutton.disabled = True
on_release: mimicstartbutton.disabled = False
Button:
size_hint: 0.3,0.1
pos_hint: {"Left": 1, "Bottom": 1}
text: 'Return'
font_size: 30
on_release: root.manager.current = 'main'

''')

if __name__ == '__main__':
MainApp().run()


This is the part of the kv code that has the label I want to update:

<MimicScreen>:
name: 'mimic'
FloatLayout:
id: mimicscreenlayout
Image:
source: 'iss1.png'
allow_stretch: True
keep_ratio: False
Label:
id: psarjvalue
pos_hint: {"center_x": 0.7, "center_y": 0.5}
text: '0.003'
markup: True
color: 1,1,1
font_size: 60


And this is the relevant part of the python side

conn = sqlite3.connect('iss_telemetry.db')
c = conn.cursor()

class MimicScreen(Screen):
def __init__(self, **kwargs):
super(MimicScreen, self).__init__(**kwargs)

def updatePSARJ(self, *args, **kwargs):
c.execute('SELECT two FROM telemetry where one="psarj"')
psarj = c.fetchone()
label = self.psarjvalue
label.text = psarj


label = self.psarjvalue is where the problem occurs

How do I link this to my kv label?
I've tried label = self.ids['psarjvalue'] and same error

...something something object properties?

Thanks for any help

Answer

EDITED: After seeing the full code, the issues seems to be on the scheduling of the method that updates the labels.

Clock.schedule_interval(MimicScreen.updatePSARJ, 1)

I cannot fully test your code due to missing python libs, but perhaps creating a MainApp method that will be passed to Clock.schedule_interval will achieve the same end. (Comment or remove the original Clock.schedule_interval(MimicScreen.updatePSARJ, 1))

MainApp(App):
    def build(self):
        # Create attribute that will be used to reference labels
        self.m_screen = MimicScreen(name = 'mimic')
        root = ScreenManager(transition=WipeTransition())
        root.add_widget(MainScreen(name = 'main'))
        root.add_widget(CalibrateScreen(name = 'calibrate'))
        # Provide the above created attribute instead of creating a new instance
        root.add_widget(self.m_screen)
        root.add_widget(ManualControlScreen(name = 'manualcontrol'))
        root.current= 'main'

        # Put the schedule function here, and reference the new function defined below
        Clock.schedule_interval(self.update_labels, 1)
        return root

    # This method will periodically update the label text
    def update_labels(self, dt):
        c.execute('SELECT two FROM telemetry where one="psarj"')
        psarj = c.fetchone()
        self.m_screen.ids.psarjvalue.text = psarj

    def startTelemetry(*kwargs):
        p.start()

    def stopTelemetry(*kwargs):
        os.kill(p.pid,signal.SIGKILL)