Kivy: текст моей метки не обновляется после добавления экранов

#python #arduino #kivy

#python #arduino #kivy

Вопрос:

Итак, я хотел прочитать данные из моего Arduino через serial port и обновить данные, которые я прочитал, до текста метки для отображения. Он работает, когда у меня есть только простой код для чтения и обновления, но когда я добавляю ScreenManager и Screen , он перестает обновлять текст.

В конечном итоге мне понадобится другая анимация в соответствии с полученными данными, это больше для тестирования, работает ли эта функция

Заранее спасибо!

Вот весь мой код

 import os
os.environ['KIVY_GL_BACKEND'] ='gl'

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty, ListProperty)
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.text import LabelBase
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition


LabelBase.register(name='Sans',fn_regular="Sansation-Regular.ttf")

import serial

kivy = Builder.load_string("""
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<MyManager>:
    transition: FadeTransition()
    MainScreen:
    OperationScreen:

<MainScreen>:
    name: 'main'
    Label:
        text: 'Welcome'
        font_size: 40
        on_touch_up : app.root.current = 'operation'

    Label:
        text: 'dafault'
        font_size: 20
        pos: -200,-100
        id: data_label


<OperationScreen>:
    name: 'operation'
    Label:
        text: 'Youre in'
        font_size: 40
""")   

class OperationScreen(Screen):
    pass

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

    def Read(self,dt):
        Clock.unschedule(self.Read)
        data = arduino.readline()
        if data != '':
            self.ids.data_label.text = data
        Clock.schedule_once(self.Read)
    pass

class MyManager(ScreenManager):
    pass

class mainApp(App):
    Main = MainScreen()
    def build(self):
        Clock.schedule_once(self.Main.Read)
        return MyManager()

if __name__ == '__main__':
    try:
        arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    except:
        print("failed to connect")
    mainApp().run()
  

Я ожидаю, что метка с текстом «по умолчанию» будет изменяться соответствующим образом, но она просто замерла с значением «по умолчанию»

Ответ №1:

Проблема

При запуске вашего приложения есть два экземпляра class MainScreen . Один из них был создан в файле kv. Другой экземпляр был создан вручную, Main = MainScreen() в class mainApp .

Планирование метода Read() выполняется в экземпляре, созданном вручную, Main = MainScreen() и с этим не связано модальное представление.

Решение

  • В файле kv добавьте id: main_screen для MainScreen:
  • Удалить Main = MainScreen() в class mainApp
  • Реализовать конструктор для class MyManager()
  • Переместите планирование из class mainApp в конструктор class MyManager()
  • В вашем случае лучше использовать Clock.create_trigger() вместо Clock.schedule_once()
  • Правильный способ отменить событие синхронизации — это либо event.cancel() , либо Clock.unschedule(event)

Пример

main.py

 import os

os.environ['KIVY_GL_BACKEND'] = 'gl'

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty, ListProperty)
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.text import LabelBase
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition

# LabelBase.register(name='Sans', fn_regular="Sansation-Regular.ttf")

import serial

kivy = Builder.load_string("""
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<MyManager>:
    transition: FadeTransition()
    MainScreen:
        id: main_screen
    OperationScreen:

<MainScreen>:
    name: 'main'
    Label:
        text: 'Welcome'
        font_size: 40
        on_touch_up : app.root.current = 'operation'

    Label:
        text: 'dafault'
        font_size: 20
        pos: -200,-100
        id: data_label


<OperationScreen>:
    name: 'operation'
    Label:
        text: 'Youre in'
        font_size: 40
""")


class OperationScreen(Screen):
    pass


class MainScreen(Screen):

    def Read(self, dt):
        data = str(dt)
        # data = arduino.readline()
        if data != '':
            self.ids.data_label.text = data
            self.manager.event_trigger()
        else:
            self.manager.event_trigger.cancel()


class MyManager(ScreenManager):
    event_trigger = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(MyManager, self).__init__(**kwargs)
        self.event_trigger = Clock.create_trigger(self.ids.main_screen.Read)
        self.event_trigger()


class mainApp(App):

    def build(self):
        return MyManager()


if __name__ == '__main__':
    # try:
    #     arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    # except:
    #     print("failed to connect")
    mainApp().run()
  

Вывод

Результат

Комментарии:

1. Спасибо за ответ! Не могли бы вы рассказать мне немного о том, когда мы будем использовать def __init__(self, **kwargs): и super(MyManager, self).__init__(**kwargs)

2. Внедряйте конструктор __init__() всякий раз, когда вы хотите, чтобы что-то было сделано один раз во время создания экземпляра. Например, инициализировать атрибуты класса, получать аргументы, переданные от вызывающего объекта, создавать триггер события и т.д.

3. Пожалуйста, не забудьте принять ответ и / или проголосовать за него. Спасибо.