I am writing a python application using the kyvimd framework and encountered an incomprehensible error
Below is a model of the situation in which my problem occurs. This code works flawlessly until I clear and repopulate the GridLayout on screen A (these actions can be called using the appropriate buttons). After that, all the animation associated with the movement of the Heroes breaks. In addition, if we add a new Hero and click on it, screen B will not display the picture, and when going back, a KeyError exception will occur.
If you look at the children of screen A while the application is running, you can find MDHeroFrom with the corresponding tag. I couldn't fix this error
My Python file:
from kivymd.uix.hero import MDHeroFrom
from kivymd.uix.screen import MDScreen
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.app import MDApp
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import ObjectProperty, DictProperty
hero_data = [
{'name': 'obj1', 'address': 'aa'},
{'name': 'obj2', 'address': 'bb'},
{'name': 'obj3', 'address': 'cc'},
]
i = 3
class Card(MDHeroFrom):
# argumnts
info = DictProperty()
scr_manager = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.tag = self.info['name']
self.tile.ids.image.ripple_duration_in_fast = 0.05
def on_transform_in(self, instance_hero_widget, duration):
Animation(
radius=[0, 0, 0, 0],
box_radius=[0, 0, 0, 0],
duration=duration,
).start(instance_hero_widget)
def on_transform_out(self, instance_hero_widget, duration):
Animation(
radius=[24, 24, 24, 24],
box_radius=[0, 0, 24, 24],
duration=duration,
).start(instance_hero_widget)
def on_release(self):
"""Switch screen and post data"""
scr_b = self.scr_manager.scr_b
scr_b.set_atr_obj(self.tag, self)
def switch_screen(*args):
self.scr_manager.current_heroes = [self.tag]
scr_b.hero_to.tag = self.tag
self.scr_manager.current = "scr_b"
Clock.schedule_once(switch_screen, 0.2)
class ScreenB(MDScreen):
name = "scr_b"
def set_atr_obj(self, id_obj, hero):
"""Set attribute with id opening obj and remeber hero.
:param:`id_obj` shared object's id
:param:`hero` object of instance `MDHero`"""
self.id_obj = id_obj
self.hero_from = hero
def go_back(self):
"""Returns to the page with the list of houses"""
self.manager.current_heroes = [self.hero_to.tag]
self.manager.current = "scr_a"
class ScreenA(MDScreen):
box = ObjectProperty()
name = 'scr_a'
class Container(MDScreenManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.scr_a = ScreenA()
self.scr_b = ScreenB()
self.add_widget(self.scr_a)
self.add_widget(self.scr_b)
class HeroApp(MDApp):
def __init__(self, **kwargs):
self.data = {
"Add new": [
'plus',
'on_release', self.add
],
"Clear": [
'delete-outline',
'on_release', self.clear
],
"Show": [
'eye-outline',
'on_release', self.show
]
}
super().__init__(**kwargs)
def build(self):
self.theme_cls.theme_style = "Dark"
self.container = Container()
return self.container
def on_start(self):
self.show()
def add(self, btn=None):
hero_data.append({'name': 'New obj', 'address': 'Someone'})
self.clear()
self.show()
def clear(self, btn=None):
self.container.scr_a.box.clear_widgets()
def show(self, btn=None):
box = self.container.scr_a.box
for hero in hero_data:
box.add_widget(Card(info= hero, scr_manager= self.container))
if __name__ == "__main__":
HeroApp().run()
My KV file:
<Card>
padding: 4
size_hint: 0.5, None
size_y: dp(200)
radius: 24
label_item: label_item
tile: tile
MDSmartTile:
id: tile
radius: 24
box_radius: 0, 0, 24, 24
box_color: 0, 0, 0, .5
source: "testdata/house.jpg"
size_hint: None, None
size: root.size
mipmap: True
lines: 2
on_release: root.on_release()
TwoLineListItem:
id: label_item
text: f"[b]{root.info['name']}[/b]"
secondary_text: root.info['address']
pos_hint: {"center_y": .5}
_no_ripple_effect: True
<ScreenB>
heroes_to: [hero_to]
hero_to: hero_to
MDBoxLayout:
orientation: "vertical"
MDHeroTo:
id: hero_to
size_hint: 1, None
height: dp(220)
pos_hint: {"top": 1}
MDLabel:
text: 'Someone widgets'
MDIconButton:
icon: "arrow-left"
pos_hint: {"top": 1, "right": .12}
on_release: root.go_back()
<ScreenA>
box: box
MDGridLayout:
id: box
cols: 2
MDFloatingActionButtonSpeedDial:
data: app.data
I found what the problem is and fixed this error. The thing is, when you add MDHero to your application, KivyMD automatically adds some data about the Hero to memory when the application starts. And after when you clear the container and add Heroes there again, this special data will not be updated. That is why it is necessary to do this operation.
class Container(MDScreenManager):
...
def update_hero_data(self, widget):
self._heroes_data = []
self._create_heroes_data(widget)
class HeroApp(MDApp):
...
def clear(self):
self.container.scr_a.box.clear_widgets()
self.container.update_hero_data(self.container.scr_a)