Search code examples
kivy

Python Kivy: Connect TabbePanel to Screen using Screenmanager


I’ve been working on a Python app consisting of a main.py and a gui.kv. All screens (“Main”, “First”, “Second”) are managed by the ScreenManager as shown in the example main.py. Now I would like to link a tab to each screen. By pressing the corresponding tab the screen shall switch. What I managed so far is the addition of a TabbedPanel with 3 TabbedPanelItems (“Main”, “First”, “Second”) to the gui.kv.

The problem: The TappedPanelItem event (on_release: root.manager.current= 'first') doesn’t work as intended. When I press the tab “First” once, the program switches to a blank screen and the “Main” tab marking is still illuminated. The second time I press the tab “First” the “First” screen finally shows the “First”-Screen and the “First“ tab marking illuminates.

main.py:

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition 
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.floatlayout import FloatLayout


class TabbedTest(TabbedPanel):
    pass
class MainScreen(Screen):
    pass
class Firstscreen(Screen):
    pass
class Secondscreen(Screen):
    pass

class TabTest_with_Screenmanager_V2(App):

    def build(self):
        root = FloatLayout()
        #self.tabbedtest = TabbedTest(size_hint_y=0.5, pos_hint={'top': 1.0})
        #root.add_widget(self.tabbedtest)

        # Create the screen manager
        sm = ScreenManager(size_hint_y=0.99, pos_hint={'y': 0},transition=NoTransition())
        sm.add_widget(MainScreen(name='main'))
        sm.add_widget(Firstscreen(name='first'))
        sm.add_widget(Secondscreen(name='second'))
        root.add_widget(sm)

        return root


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

TabTest_with_Screenmanager_V2.kv:

<MainScreen>:
    id: main

    TabbedPanel:
        do_default_tab: False

        TabbedPanelItem:
            text:"Main"
            BoxLayout:
                Label:
                    text:'Main'
              
        TabbedPanelItem:
            text:"First"  
            on_release: root.manager.current= 'first'
            
       
        TabbedPanelItem:
            text:"Second" 
            on_release:root.manager.current= 'second'
           

<Firstscreen>:
    id: first
    TabbedPanel:
        do_default_tab: False

        TabbedPanelItem:
            text:"Main"
            on_state: if self.state == "down":root.manager.current= 'main'

        TabbedPanelItem:
            text:"First"
            BoxLayout:
                Label:
                    text:"first"

        TabbedPanelItem:
            text:"Second"
            on_state: if self.state == "down":root.manager.current= 'second'


<Secondscreen>:
    id: second
    TabbedPanel:
        do_default_tab: False

        TabbedPanelItem:
            text:"Main"
            on_state: if self.state == "down":root.manager.current= 'main'

        TabbedPanelItem:
            text:"First"
            on_state: if self.state == "down":root.manager.current= 'first'

        TabbedPanelItem:
            text:"Second"
            BoxLayout:
                Label:
                    text:'second'
          

Objective: When I press a tab the program shall switch to the corresponding screen and illuminate the corresponding tab, immediately. Do you have any idea what kind of event might work or is there better solution?


Solution

  • You can use some ids and the on_enter() method of each Screen to do this.

    First, assign an id to the TabbedPanel and another to the default tab that you want to be opened when you enter that Screen. For example:

    <MainScreen>:
        id: main
    
        TabbedPanel:
            id: tab
            do_default_tab: False
            
            TabbedPanelItem:
                id: home_tab
                text:"Main"
                BoxLayout:
                    Label:
                        text:'Main'
                  
            TabbedPanelItem:
                text:"First"  
                on_release: root.manager.current='first'
           
            TabbedPanelItem:
                text:"Second" 
                on_release:root.manager.current= 'second'
    

    Then add an on_enter() method to that Screen:

    class MainScreen(Screen):
        def on_enter(self, *args):
            tab = self.ids.tab
            home_tab = self.ids.home_tab
            Clock.schedule_once(lambda *arges: tab.switch_to(home_tab))
    

    The same scheme of ids can be added to each Screen, and the identical on_enter() method can be added to each Screen.