I'm trying to design a user interface in Python / Kivy MD. The layout should include a MDTab with some icons on top, a button at the bottom and some widgets in the middle. By clicking on the tab icons, the widget in the middle should change accordingly. For example modifying the text of a label (see code). For this purpose, I'm trying to use a ScreenManager defined in the Designer.kv file. Anyway, I'm not sure is the most suitable object.
MDBoxLayout:
screen_manager: screen_manager
orientation: "vertical"
padding: 10, 0, 10, 10
MDTabs:
id: tabs
on_tab_switch: app.on_tab_switch(*args)
MDScreenManager:
id: screen_manager
Screen:
name: 'screen1'
MDBoxLayout:
MDCheckbox:
MDLabel:
text: 'TAB 1'
Screen:
name: 'screen2'
MDBoxLayout:
MDCheckbox:
MDLabel:
text: 'TAB 2'
MDRaisedButton:
text: 'CONFIGURE'
size_hint_x: 1
pos_hint: {"center_y":0.5}
I recall the screen_manager object in the .py file using the ObjectProperty.
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.tab import MDTabsBase
from kivymd.uix.floatlayout import MDFloatLayout
from kivy.properties import ObjectProperty
class Tab(MDFloatLayout, MDTabsBase):
'''Class implementing content for a tab.'''
class MainApp(MDApp):
screen_manager = ObjectProperty()
icons = ["clock", "video-3d"]
def build(self):
return Builder.load_file('Designer.kv')
def on_start(self):
for tab_name in self.icons:
self.root.ids.tabs.add_widget(Tab(icon=tab_name))
def on_tab_switch(
self, instance_tabs, instance_tab, instance_tab_label, tab_text
):
'''
Called when switching tabs.
:type instance_tabs: <kivymd.uix.tab.MDTabs object>;
:param instance_tab: <__main__.Tab object>;
:param instance_tab_label: <kivymd.uix.tab.MDTabsLabel object>;
:param tab_text: text or name icon of tab;
'''
count_icon = instance_tab.icon
if count_icon == self.icons[0]:
self.screen_manager.current = 'screen1'
elif count_icon == self.icons[1]:
self.screen_manager.current = 'screen2'
if __name__ == '__main__':
MainApp().run()
When I run the code I get this error: AttributeError: 'NoneType' object has no attribute 'current'.
How can I fix it? Any alternative ideas to switch between screen/layouts by clicking on Tab elements?
There were several things to patch up - the code below should work. screen_manager is not an object property, this is the MDScreenManager and it is given a type-hint to help your IDE indicate this. The application has a top-level widget MyProject which inherits a box layout. This Box Layout contains your tabs and the Screen Manager which, I think is the intention you have.
I added a function to your button and show two different levels where you could deploy it - within the top level widget or within the app. I also moved creation of the tabs to be done sooner, when the top level widget is initialized. The other code is commented away and it will also work; in case you choose to do it this way.
#!/usr/bin/env python3
from kivymd.uix.screenmanager import MDScreenManager
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.tab import MDTabsBase
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.floatlayout import MDFloatLayout
kv = Builder.load_string('''
<MyProject>:
screen_manager: screen_manager
_tabs: _tabs
orientation: "vertical"
padding: 10, 0, 10, 10
MDTabs:
id: _tabs
on_tab_switch: app.on_tab_switch(*args)
MDScreenManager:
id: screen_manager
Screen:
name: 'screen1'
MDBoxLayout:
MDCheckbox:
MDLabel:
text: 'TAB 1'
Screen:
name: 'screen2'
MDBoxLayout:
MDCheckbox:
MDLabel:
text: 'TAB 2'
MDRaisedButton:
text: 'CONFIGURE'
size_hint_x: 1
pos_hint: {"center_y":0.5}
on_release: app.test_1(self)
on_press: root.test_2(self)
''')
class Tab(MDFloatLayout, MDTabsBase):
# Class implementing content for a tab.
pass
class MyProject(MDBoxLayout):
# type-hints
screen_manager: MDScreenManager
_tabs: Tab
# optional to build the icons in this widget
icons = ["clock", "video-3d"]
def test_2(self, my_button):
print(f"Top Level(root) widget{my_button.text}\t{self}")
def __init__(self, **kwargs):
super().__init__(**kwargs)
print(f"{self.screen_manager}\t{self._tabs}")
for tab_name in self.icons:
self._tabs.add_widget(Tab(icon=tab_name))
class MainApp(MDApp):
icons = ["clock", "video-3d"]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._main = MyProject()
def test_1(self, my_button):
print(f"MainAPP {my_button.text}\t{self}")
def build(self):
return self._main
def on_start(self):
pass
# this is one way, but another way is shown above
# for tab_name in self.icons:
# self.root.ids._tabs.add_widget(Tab(icon=tab_name))
def on_tab_switch(
self, instance_tabs, instance_tab, instance_tab_label, tab_text
):
"""
Called when switching tabs
:type instance_tabs: <kivymd.uix.tab.MDTabs object>;
:param instance_tab: <__main__.Tab object>;
:param instance_tab_label: <kivymd.uix.tab.MDTabsLabel object>;
:param tab_text: text or name icon of tab;
"""
print(f"{instance_tabs} {instance_tab_label} {tab_text}")
count_icon = instance_tab.icon
if count_icon == self.icons[0]:
self.root.screen_manager.current = 'screen1'
elif count_icon == self.icons[1]:
self.root.screen_manager.current = 'screen2'
if __name__ == '__main__':
MainApp().run()