KivyMD icon indicating copy to clipboard operation
KivyMD copied to clipboard

Enhancement request: MDTabs 2.0.1 - added 2 examples

Open RobertFlatt opened this issue 11 months ago • 5 comments

Description of the Feature

I wish to do lazy loading of MDTabsPrimary tabs and tab content with KivyMD 2.X , because the data displayed comes from some server. Of course I assume I must statically define at least one tab and its contents.

I would like

  1. a method to append a new tab (and it's contents) at any time step.
  2. a method to replace the content of any existing tab at any time step.

I was able implement lazy loading of tabs with KivyMD 1.X

Thanks for your time, I think version 2 is great.

RobertFlatt avatar Mar 25 '24 02:03 RobertFlatt

Thank you. Do you have any problems implementing this or can we close this issue?

HeaTTheatR avatar Mar 25 '24 08:03 HeaTTheatR

I can add tabs (but not their content), and I have a problem adding or changing Tab content. I think the issue is a different time step to that in which the MDTabsPrimary was instantiated.

I expect I can workaround by rebuilding the MDTabsPrimary as each tab contents arrives, but there will a (triangular) performance hit due to re-building tab contents on each new tab content. 8 tabs would be a total of 36 tab contents builds.

So I'd like an enhancement, the importance of dynamic loading of hidden tab contents in the context of your project as a whole is yours to prioritize.

Thanks for your time.

RobertFlatt avatar Mar 25 '24 17:03 RobertFlatt

@HeaTTheatR

I tried my assumed workaround, dynamically rebuilding (in Python) both MDTabsPrimary, and also just its children. I ran into problems with tab event scheduling:

   File "C:\Users\Bobf\AppData\Local\Programs\Python\Python311\Lib\site-packages\kivymd\uix\tab\tab.py", line 1241, in <lambda>
     lambda x: self._tabs_carousel.current_slide.tab_item.dispatch(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 AttributeError: 'MDLabel' object has no attribute 'tab_item' 

This MDLabel instance is the replaced tab content.

For the moment I'm out of ideas, so I would like to leave the issue open.

RobertFlatt avatar Mar 26 '24 01:03 RobertFlatt

Okay, thanks for the analysis. I'll check it out...

HeaTTheatR avatar Mar 26 '24 04:03 HeaTTheatR

@HeaTTheatR

Two Examples of changing Content at a later time step. The first example is appending the second example is replacing

  1. Adding more tabs and content at a later time step. The tabs are added but the new content is the content of the previous last tab.
from kivy.lang import Builder
from kivy.clock import Clock

from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.tab import (
    MDTabsItemIcon,
    MDTabsItemText,
    MDTabsItem,
)

KV = '''
MDScreen:
    md_bg_color: self.theme_cls.backgroundColor

    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: dp(320)
'''


class Example(MDApp):
    def on_start(self):
        super().on_start()
        self.add_tabs(["Flights","Trips","Explore"])
        Clock.schedule_once(self.more_tabs, 2)

    def more_tabs(self,dt):
        #### These tabs are added, but not the contents
        self.add_tabs(["Dogs","Cats","Snakes"])

    def add_tabs(self, names):
        for tab_name in names:
            self.root.ids.tabs.add_widget(
                MDTabsItem(
                    MDTabsItemText(
                        text=tab_name,
                    ),
                )
            )
            self.root.ids.related_content_container.add_widget(
                MDLabel(
                    text=tab_name,
                    halign="center",
                )
            )
            self.root.ids.tabs.switch_tab(icon="airplane")

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


Example().run()

===================================================================== 2) Replacing the contents of existing tabs at a later time step. Works, but crashes on subsequent tab change initiated by clicking tab:

   File "C:\Users\Bobf\AppData\Local\Programs\Python\Python311\Lib\site-packages\kivy\uix\carousel.py", line 295, in load_slide
     start, stop = slides.index(self.current_slide), slides.index(slide)

Or with swipe:

   File "C:\Users\Bobf\AppData\Local\Programs\Python\Python311\Lib\site-packages\kivymd\uix\tab\tab.py", line 1034, in android_animation
     a = instance.current_slide.tab_item
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 AttributeError: 'MDLabel' object has no attribute 'tab_item'
from kivy.lang import Builder
from kivy.clock import Clock

from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.tab import (
    MDTabsItemIcon,
    MDTabsItemText,
    MDTabsItem,
)

KV = '''
MDScreen:
    md_bg_color: self.theme_cls.backgroundColor

    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: dp(320)
'''


class Example(MDApp):
    def on_start(self):
        super().on_start()
        self.add_tabs(["Flights","Trips","Explore"])
        # Works, but crashes on tab change.
        Clock.schedule_once(self.replace_tab_content, 2)

    def replace_tab_content(self,dt):
        new_content = ["peace","love","understanding"]
        container = self.root.ids.related_content_container
        container.clear_widgets()
        for content in new_content:
            container.add_widget(
                MDLabel(
                    text=content,
                    halign="center",
                )
            )
        

    def add_tabs(self, names):
        for tab_name in names:
            self.root.ids.tabs.add_widget(
                MDTabsItem(
                    MDTabsItemText(
                        text=tab_name,
                    ),
                )
            )
            self.root.ids.related_content_container.add_widget(
                MDLabel(
                    text=tab_name,
                    halign="center",
                )
            )
            self.root.ids.tabs.switch_tab(icon="airplane")

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


Example().run()


RobertFlatt avatar Apr 03 '24 02:04 RobertFlatt