kivymd / KivyMD

KivyMD is a collection of Material Design compliant widgets for use with Kivy, a framework for cross-platform, touch-enabled graphical applications. https://youtube.com/c/KivyMD https://twitter.com/KivyMD https://habr.com/ru/users/kivymd https://stackoverflow.com/tags/kivymd
https://kivymd.readthedocs.io
MIT License
2.14k stars 655 forks source link

hovering Effect is casuing a major log #1619

Open gibrilhamideh opened 4 months ago

gibrilhamideh commented 4 months ago
from kivy.lang import Builder
from kivy.properties import (
    StringProperty
)

from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.tab import (
    MDTabsItemIcon,
    MDTabsItemText,
    MDTabsItem,
)

from kivymd.uix.list import MDListItem

KV = '''

#:kivy 2.3.0
#:import MyList main.MyList

MDScreen:
    md_bg_color: self.theme_cls.backgroundColor
    pos_hint: {"center_x": .5, "top": 1}

    MDTabsPrimary:
        id: tabs
        pos_hint: {"center_x": .5, "center_y": .5}
        size_hint_x: .6

        MDDivider:

        MDTabsCarousel:
            id: related_content_container
            size_hint_y: None
            height: root.height - tabs.ids.tab_scroll.height

<MyList>:
    MDListItemHeadlineText:
        text: root.text

    MDListItemTrailingIcon:
        icon: 'fan'     

<OtherContant>:

    MDScrollView:

        MDBoxLayout:
            adaptive_height: True
            orientation: 'vertical'
            spacing: dp(10)
            padding: dp(25)
            cols: 3
            on_parent:
                if not self.children: \
                [self.add_widget(MyList(text=str(i))) for i in range(1, 200)]

'''

class MyList(MDListItem):

    text = StringProperty()
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

class OtherContant(MDBoxLayout):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

class Example(MDApp):
    def on_start(self):
        super().on_start()
        for tab_icon, tab_name in {
            "airplane": "Flights",
            "treasure-chest": "Trips",
            "compass-outline": "Explore",
            "fan": "fan",
        }.items():
            self.root.ids.tabs.add_widget(
                MDTabsItem(
                    MDTabsItemIcon(
                        icon=tab_icon,
                    ),
                    MDTabsItemText(
                        text=tab_name,
                    ),
                )
            )
            self.root.ids.related_content_container.add_widget(
                OtherContant()
            )
            self.root.ids.tabs.switch_tab(icon="airplane")

    def build(self):
        self.theme_cls.primary_palette = "Olive"
        return Builder.load_string(KV)

Example().run()

* OS: unbuntu 20.04

* Python: python 3.11

* Kivy: 2.3.0

* KivyMD: 2.0.1.dev

problem:

trying to scroll view through MDListItems with the mouse wheel with moving my mouse causing a major lag,

but scrolling with the mouse wheel with out hovering my mouse does not lag.

explination:

i know real life application doesn't create 800 MDListItems i just put that much widgets to magnify the problem

the application i am working on is 42 screens and switching from kivymd 1.2.0.dev to 2.0.1.dev, you can see the effect on my #application dramatically, causing it to lag alot.

gibrilhamideh commented 4 months ago

i think i figured out whats going on

from kivymd.uix.behaviors import HoverBehavior

kivymd 2.0.1.dev is binding to "Window.bind(mouse_pos=self.on_mouse_update)" through the class "StateLayerBehavior" for all the following widgets

MDCard, MDLabel MDIcon MDTextField, MDIconButton, MDButton, MDFabButton, MDExtendedFabButton, MDChip, MDSwitch, MDCheckbox, BaseListItem, MDTabsItemBase

so just by hovering the mouse will fire self.on_mouse_update for thousands of widgets if the application is large and will cause a lag.

recommended solution:

i think by settings a parameter of example hover_behavior = BooleanProperty() for all the previous mentions classes, the user can decide what widget can have the hover effect

and more effeciant widgets, for example MDListItem can have 3 MDLabels 2 MDIcons and they all all bind to "on_mouse_update" through inherting from StateLayerBehavior, so only one widget will fire 6 events per mouse move per pixel, imagine having 1000 widgets, that will cause a lot of delay

HeaTTheatR commented 4 months ago

@gibrilhamideh Thanks for the analysis! This makes it easier to localize and fix this bug.

gibrilhamideh commented 4 months ago

No problem, anytime!