Динамическое управление TabbedPanel в Кивах

Я пытаюсь сделать TabbedPannel невидимым в начале,

Затем, когда определенное задание выполнено, я хочу показать вкладки после этого.

from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.lang import Builder

Builder.load_string("""

<Test>:
    size_hint: .5, .5
    pos_hint: {'center_x': .5, 'center_y': .5}
    do_default_tab: False

    TabbedPanelItem:
        text: 'first tab'
        Label:
            text: 'First tab content area'
    TabbedPanelItem:
        text: 'tab2'
        BoxLayout:
            Label:
                text: 'Second tab content area'
            Button:
                text: 'Button that does nothing'
    TabbedPanelItem:
        text: 'tab3'
        RstDocument:
            text:
                '\\n'.join(("Hello world", "-----------",
                "You are in the third tab."))

""")


class Test(TabbedPanel):
    pass


class TabbedPanelApp(App):
    def build(self):
        return Test()


if __name__ == '__main__':
    TabbedPanelApp().run()

Код выше взят из Kivy Document.

То, что я пытаюсь сделать, это

  1. Скрыть вкладки (также предотвращает перемещение вкладок)
  2. Когда определенное действие выполнено, вкладки сдвигаются вниз за пределы экрана.

Любая идея?

1 ответ

Решение

Звучало как забавное маленькое испытание, поэтому вот что я придумал:

'''
TabbedPanel
============

Test of the widget TabbedPanel.
'''
from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelStrip
from kivy.lang import Builder


class Test(TabbedPanel):

    def __init__(self, **kwargs):
        super(Test, self).__init__(**kwargs)
        self.initialTabHeight = None
        self.myTabsList = None
        self.start_top = None
        self.tabs_showing = True

        # this TabbedPanelStrip will be a copy of the real one (self._tab_strip)
        self.tmp_tab_strip = TabbedPanelStrip(
            tabbed_panel=self,
            rows=1, size_hint=(None, None),
            height=self.tab_height, width=self.tab_width)

        # this is the movable Widget that contains the tabs
        self.movable_tab_strip = ScrollView(size_hint=(None, None), height=self.tab_height)

        # These value are needed to set the width of self.movable_tab_strip, but
        # they aren't always available when self.first is called below
        self._tab_strip.bind(width=self.tab_strip_width_changed)
        self.bind(width=self.panel_width_changed)

        Clock.schedule_once(self.first)

    def tab_strip_width_changed(self, instance, new_width):
        self.movable_tab_strip.width = min(self.tmp_tab_strip.width, self.width)

    def panel_width_changed(self, instance, new_width):
        self.movable_tab_strip.width = min(self.tmp_tab_strip.width, self.width)

    def first(self, *args):
        # show tab2, so that the Button will be available
        self.switch_to(self.parent.ids.tab2)

        # save some info
        self.initialTabHeight = self.tab_height
        self.myTabsList = self.tab_list.copy()

        tsw = 0
        for tab in self.myTabsList:
            if tab.size_hint_x:
                tsw += 100
            else:
                tsw += tab.width
        self.tmp_tab_strip.width = tsw
        self.movable_tab_strip.add_widget(self.tmp_tab_strip)

        # actually remove the tabs
        self.do_clear_widgets()

    def do_clear_widgets(self, *args):
        # eliminate the tabs and populate the moveable_tab_strip
        #self.movable_tab_strip.width = min(self.tmp_tab_strip.width, self.width)
        self.tab_height = 0
        self.clear_tabs()
        for tab in reversed(self.myTabsList):
            self.tmp_tab_strip.add_widget(tab)
        self.tabs_showing = False

    def do_progress(self, animation, widget, progression):
        # grow the tab height when the moveable_tab_strip impinges on the TabbedPanel
        # this has the effect of appearing to shrink the TappedPanel to the size it will have when the tabs are replaced
        if self.start_top > self.movable_tab_strip.y:
            self.tab_height = self.start_top - self.movable_tab_strip.y

    def do_replace_tabs(self, *args):
        # replace the moveable_tab_trip with the actual tabs
        self.tmp_tab_strip.clear_widgets()
        for tab in reversed(self.myTabsList):
            self.add_widget(tab)
        self.tab_height = self.initialTabHeight
        self.parent.remove_widget(self.movable_tab_strip)

    def do_tab_toggle(self, *args):
        if self.tabs_showing:
            self.do_clear_widgets()
        else:
            self.anim = Animation(pos=(self.x+2, self.y + self.height - self.movable_tab_strip.height))
            self.movable_tab_strip.pos = (self.x + 2, App.get_running_app().root_window.height)
            self.start_top = self.top
            self.parent.add_widget(self.movable_tab_strip)
            self.anim.bind(on_progress=self.do_progress)
            self.anim.bind(on_complete=self.do_replace_tabs)
            self.anim.start(self.movable_tab_strip)
            self.tabs_showing = True


class MyLayout(FloatLayout):
    pass


theRoot = Builder.load_string("""

MyLayout:
    Test:
        id: thePanel
        size_hint: .5, .5
        pos_hint: {'center_x': .5, 'center_y': .5}
        do_default_tab: False

        TabbedPanelItem:
            text: 'first tab'
            Label:
                id: theLabel
                text: 'First tab content area'
        TabbedPanelItem:
            id: tab2
            text: 'tab2'
            BoxLayout:
                Label:
                    text: 'Second tab content area'
                Button:
                    text: 'Button that does something'
                    on_press: thePanel.do_tab_toggle()
        TabbedPanelItem:
            id: tab3
            text: 'tab3'
            RstDocument:
                text:
                    '\\n'.join(("Hello world", "-----------",
                    "You are in the third tab."))
        TabbedPanelItem:
            text: 'tab4'
            Label:
                text: 'This is Tab4'
        TabbedPanelItem:
            id:tab5
            text: 'tab5'
            Label:
                text: 'This is Tab5'

""")


class TabbedPanelApp(App):
    def build(self):
        return theRoot

if __name__ == '__main__':
    TabbedPanelApp().run()

Идея состоит в том, чтобы создать "подвижную" полосу вкладок, заполнить ее фактическими вкладками из TabbedPanel, а затем оживить эту полосу вкладок. Я изменил строку языка kv, чтобы поместить все это в FloatLayout (Я думаю, что это делает Animation Полегче). Button, который раньше ничего не делал, теперь переключает вкладки. в do_tab_toggle() Метод, я устанавливаю положение х подвижной вкладки в положение х TabbedPanel плюс 2. 2 - фактор выдумки, и я не смог определить, зачем он нужен, и он может отличаться для других TabbedPanel экземпляров. "Удалить" часть do_tab_toggle() также может быть анимирован, но это оставлено в качестве упражнения для читателя:-)

Другие варианты - просто очистить вкладки, используя clear_tabs() метод или для анимации tab_height собственность TabbedPanel (анимация высоты до нуля, чтобы скрыть вкладки).

РЕДАКТИРОВАТЬ: добавлена ​​пара привязок для захвата данных, которые не являются надежно доступными, когда first метод называется.

Другие вопросы по тегам