Reputation: 171
I am trying to implement this example of the kivy Hero component, but without using the KV language.
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView
from kivymd.app import MDApp
from kivymd.uix.hero import MDHeroFrom, MDHeroTo
from kivymd.uix.imagelist.imagelist import MDSmartTile, MDSmartTileImage, MDSmartTileOverlayContainer
from kivy.metrics import dp
from kivymd.uix.button import MDButton, MDButtonIcon, MDButtonText
from kivymd.uix.screen import MDScreen
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.uix.label.label import MDLabel
from kivymd.uix.scrollview import StretchOverScrollStencil, MDScrollView
from kivymd.uix.gridlayout import MDGridLayout
from kivy.core.window import Window
class HeroItem(MDHeroFrom):
text = StringProperty()
tag = StringProperty()
manager = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.size_hint_y = None
self.height = "200dp"
self.radius = "24dp"
window_width, window_height = Window.size
def create_tile(self, img_path):
# Create SmartTile
print('type', type(Window.size), type(self.manager), self.manager)
print('parent_size', (self.width, self.height), Window.size, self.manager.size)
print('self.manager', self.manager, self.size_hint)
self.tile = MDSmartTile(id='tile', size_hint=(None, None))#pos_hint={"x": 0, "y": 0.1}, - not worked
self.tile.size = self.manager.size
#size_hint=(None, None) should be None, then only the smooth opening and closing is happening on each tile
#size=(380, 200) is fine but rearrening the window size it is not dynamic
self.tile.bind(on_release=self.on_release)
# Create SmartTileImage
self.image = MDSmartTileImage(id='image', source=img_path, radius="24dp",)
#self.image.size = self.tile.size
self.image.ripple_duration_in_fast = 0.05
self.tile.add_widget(self.image)
# Create Overlay Container
self.overlay = MDSmartTileOverlayContainer(id='overlay',
md_bg_color=(0, 0, 0, .5),
adaptive_height=True,
radius=[0, 0, dp(24), dp(24)],
padding="8dp",
spacing="8dp",
)
#self.overlay.size = self.tile.size
# Create Label
#self.label = Label(text=self.tag, color=(1, 1, 1, 1), size_hint_y=None, height=dp(40))
self.label = MDLabel(text=self.tag+str(Window.size[0]), theme_text_color='Custom', text_color="white", adaptive_height=True)
self.overlay.add_widget(self.label)
self.tile.add_widget(self.overlay)
self.add_widget(self.tile)
def on_transform_in(self, instance_hero_widget, duration):
print('in', duration)
for instance in [
instance_hero_widget,
instance_hero_widget._overlay_container,
instance_hero_widget._image,
]:
Animation(radius=[0, 0, 0, 0], duration=duration).start(instance)
def on_transform_out(self, instance_hero_widget, duration):
print('on', duration)
for instance, radius in {
instance_hero_widget: [dp(24), dp(24), dp(24), dp(24)],
instance_hero_widget._overlay_container: [0, 0, dp(24), dp(24)],
instance_hero_widget._image: [dp(24), dp(24), dp(24), dp(24)],
}.items():
print('radius', radius)
Animation(
radius=radius,
duration=duration,
).start(instance)
def on_release(self, *args):
print('a')
def switch_screen(*args):
#print('self.manager.current_heroes', self.manager.current_heroes)
self.manager.current_heroes = [self.tag]
#print('self.manager.current_heroes', self.manager.current_heroes)
print('?', dir(self.manager.ids))
print('tag', self.tag)
screen_b = self.manager.get_screen("screen B")
screen_b.hero_to.tag = self.tag
self.manager.current = "screen B"
Clock.schedule_once(switch_screen, 0.2)
#class ScreenA(MDScreen):
# pass
class ScreenB(MDScreen):
hero_to = ObjectProperty() # Use ObjectProperty
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.hero_to = MDHeroTo(size_hint=(1, None), height="220dp", pos_hint={"top": 1})
self.add_widget(self.hero_to)
self.button = MDButton(MDButtonText(text="Move Hero To Screen A"),
pos_hint={"center_x": .5}, y = "36dp")#size_hint_y=None, height=dp(50)
self.button.bind(on_release=self.switch_to_screen_a)
self.add_widget(self.button)
def switch_to_screen_a(self, *args):
print('b', self.hero_to.tag)
self.manager.current = "screen A"
self.manager.current_heroes = [self.hero_to.tag]
class Example(MDApp):
def build(self):
self.theme_cls.theme_style_switch_animation = True
self.theme_cls.primary_palette = "Orange"
self.theme_cls.theme_style = "Dark" # "Light"
print('self.theme_cls.backgroundColor', self.theme_cls.backgroundColor)
self.manager = MDScreenManager()
self.manager.primary_palette = "Orange"
self.manager.theme_style = "Dark" # "Light"
self.manager.md_bg_color = (1, 1, 1, 1) #self.theme_cls.backgroundColor
screen_a = MDScreen(name="screen A")
screen_b = ScreenB(name="screen B")
# ScrollView
scroll_view = MDScrollView()
grid = MDGridLayout(cols=2, spacing="12dp", padding="12dp", adaptive_height = True,)
#print('grid', dir(grid))
#print('??', grid.width, grid.height, grid.x, grid.y, grid.adaptive_height, grid.adaptive_width, grid.adaptive_size,
# grid.line_width, grid.minimum_height, grid.minimum_width, grid.minimum_size,
# grid.size_hint)
#grid.width - 100
#0 0 0 True False False 1 0 0 [0, 0]
#[1, None]
#grid.bind(minimum_height=grid.setter('height'))
scroll_view.add_widget(grid)
screen_a.add_widget(scroll_view)
self.manager.add_widget(screen_a)
self.manager.add_widget(screen_b)
# Add HeroItems to Grid
for i in range(12):
hero_item = HeroItem(text=f"Item {i + 1}", tag=f"Tag {i}", manager=self.manager)
hero_item.create_tile('3.png')
if not i % 2:
hero_item.md_bg_color = "lightgrey"
grid.add_widget(hero_item)
return self.manager
Example().run()
However, the images are not showing in proper order. Also, when I resize the window, the image is not resizing automatically with respect to the window size: see output video 1.
I modified the size_hint
parameter of the MDSmartTile
from (None, None)
to (0.5, 1)
, then the images are showing properly and also when resizing the window size, the image also resizing automatically with respect to the window size, but the issue in modifying this size_hint - there is a distortion in the animation when opening the images: see output video 2.
Could this be a parameter issue?
Upvotes: 0
Views: 57
Reputation: 11
I found a partial solution. Before that, some observations.
When you resize the window, the layout gets resized which is also responsible for resizing its children depending on their size_hint. The MDGridLayout should have children with size_hint_x of 1, or just size_hint of (1, None).
Therefore, you should give every Hero widget a size hint of (1, None) for it to extend horizontally
The image order looks alright. I see that the hero widgets with the largest tag id show at the bottom.
The size hint on the MDSmartTile should be 1,1 because you want it to take as much space as its hero parent allows. You should rely on kivy language; it's there to make your life easier.
In the demo example you based your code on, the size of the Tile is bound to that of the Hero widget. Whenever the Hero widget size changes, it automatically updates the Tile widget's size. I tried doing this manually with your code and it didn't work. I'm unsure why, but using kivy language is surely better in this case.
The following animation-related steps also help:
These steps combined with the addition of an MDSharedAxisTransition to the demo code make the animation less unpleasant. So, it's not perfect in my opinion.
Also keep in mind:
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import StringProperty, ObjectProperty
from kivymd.app import MDApp
from kivymd.uix.hero import MDHeroFrom
from kivymd.uix.transition import MDSharedAxisTransition
KV = '''
<HeroItem>
size_hint_x: 1
size_hint_y: None
height: "200dp"
radius: "24dp"
MDSmartTile:
id: tile
size_hint: None, None
size: root.size
on_release: root.on_release()
MDSmartTileImage:
id: image
source: 'https://github.com/kivymd/internal/raw/main/logo/kivymd_logo_blue.png'
radius: dp(24)
MDSmartTileOverlayContainer:
id: overlay
md_bg_color: 0, 0, 0, .5
adaptive_height: True
padding: "8dp"
spacing: "8dp"
radius: [0, 0, dp(24), dp(24)]
MDLabel:
text: root.tag
theme_text_color: "Custom"
text_color: "white"
adaptive_height: True
MDScreenManager:
md_bg_color: self.theme_cls.backgroundColor
transition: app.transition
MDScreen:
name: "screen A"
ScrollView:
MDGridLayout:
id: box
cols: 2
spacing: "12dp"
padding: "12dp"
adaptive_height: True
MDScreen:
name: "screen B"
heroes_to: [hero_to]
MDBoxLayout:
orientaion: 'horizontal'
MDHeroTo:
id: hero_to
size_hint: 1, None
height: "220dp"
pos_hint: {"top": 1}
MDButton:
pos_hint: {"center_x": .5}
y: "36dp"
on_release:
root.current_heroes = [hero_to.tag]
root.current = "screen A"
MDButtonText:
text: "Move Hero To Screen A"
'''
def set_size_hint_one(widget):
widget.ids.tile.size_hint = (1, 1)
def set_size_hint_none(widget):
widget.ids.tile.size_hint = (None, None)
class HeroItem(MDHeroFrom):
text = StringProperty()
manager = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.ids.image.ripple_duration_in_fast = 0.05
def on_transform_in(self, instance_hero_widget, duration):
set_size_hint_none(self)
for instance in [
instance_hero_widget,
instance_hero_widget._overlay_container,
instance_hero_widget._image,
]:
Animation(radius=[0, 0, 0, 0], duration=duration).start(instance)
Clock.schedule_once(lambda x: set_size_hint_one(self), duration+0.1)
def on_transform_out(self, instance_hero_widget, duration):
set_size_hint_none(self)
for instance, radius in {
instance_hero_widget: [dp(24), dp(24), dp(24), dp(24)],
instance_hero_widget._overlay_container: [0, 0, dp(24), dp(24)],
instance_hero_widget._image: [dp(24), dp(24), dp(24), dp(24)],
}.items():
Animation(
radius=radius,
duration=duration,
).start(instance)
Clock.schedule_once(lambda x: set_size_hint_one(self), duration+0.1)
def on_release(self):
def switch_screen(*args):
self.manager.current_heroes = [self.tag]
self.manager.ids.hero_to.tag = self.tag
self.manager.current = "screen B"
Clock.schedule_once(switch_screen, 0.2)
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.transition = MDSharedAxisTransition()
self.transition.transition_axis = "z"
self.transition.duration = 0.2
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(12):
hero_item = HeroItem(
text=f"Item {i + 1}", tag=f"Tag {i}", manager=self.root
)
if not i % 2:
hero_item.md_bg_color = "lightgrey"
self.root.ids.box.add_widget(hero_item)
Example().run()
Upvotes: 1