如何使用 on_enter 函数在第一个屏幕中更新 KivyMD 标签?

Posted

技术标签:

【中文标题】如何使用 on_enter 函数在第一个屏幕中更新 KivyMD 标签?【英文标题】:How to Update KivyMD Label in the first screen with on_enter function? 【发布时间】:2021-04-27 13:44:29 【问题描述】:

下面是来自https://github.com/attreyabhatt/KivyMD-Basics/blob/master/13%20-%20Switching%20Screens/main.py的kivyMD屏幕管理程序

我已将其添加到 KivyMD 的官方导航抽屉程序https://kivymd.readthedocs.io/en/latest/components/navigation-drawer/index.html

该程序运行良好,但我想使用 on_enter 函数更改 MenuScreen 中的文本。 我在class MenuScreen 中评论了def on_enter。 如果我们在未注释的情况下运行它会显示此错误:

 Traceback (most recent call last):
   File "kivy\properties.pyx", line 861, in kivy.properties.ObservableDict.__getattr__
 KeyError: 'label_id'
 
 During handling of the above exception, another exception occurred:
 
 Traceback (most recent call last):
   File "C:/Users/Administrator/PycharmProjects/kivy_prj1/kivy_mainmd/nav_screen.py", line 170, in <module>
     sm.add_widget(MenuScreen(name='menu'))
   File "C:\Users\Administrator\pyenv\lt_kivyvirt\lib\site-packages\kivy\uix\screenmanager.py", line 997, in add_widget
     self.current = screen.name
   File "kivy\properties.pyx", line 498, in kivy.properties.Property.__set__
   File "kivy\properties.pyx", line 545, in kivy.properties.Property.set
   File "kivy\properties.pyx", line 600, in kivy.properties.Property.dispatch
   File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1154, in kivy._event.EventObservers._dispatch
   File "C:\Users\Administrator\pyenv\lt_kivyvirt\lib\site-packages\kivy\uix\screenmanager.py", line 1062, in on_current
     screen.dispatch('on_enter')
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:/Users/Administrator/PycharmProjects/kivy_prj1/kivy_mainmd/nav_screen.py", line 156, in on_enter
     self.ids.label_id.text = "Label text Updated"
   File "kivy\properties.pyx", line 864, in kivy.properties.ObservableDict.__getattr__
 AttributeError: 'super' object has no attribute '__getattr__'

'label_id' 是我要更改的标签的 id。

from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty

from kivymd.app import MDApp
from kivymd.theming import ThemableBehavior
from kivymd.uix.list import OneLineIconListItem, MDList
from kivy.uix.screenmanager import ScreenManager, Screen

KV = '''
# Menu item in the DrawerList list.
<ItemDrawer>:
    theme_text_color: "Custom"
    on_release: self.parent.set_color_item(self)

    IconLeftWidget:
        id: icon
        icon: root.icon
        theme_text_color: "Custom"
        text_color: root.text_color


<ContentNavigationDrawer>:
    orientation: "vertical"
    padding: "8dp"
    spacing: "8dp"

    AnchorLayout:
        anchor_x: "left"
        size_hint_y: None
        height: avatar.height

        Image:
            id: avatar
            size_hint: None, None
            size: "56dp", "56dp"
            source: "data/logo/kivy-icon-256.png"

    MDLabel:
        text: "KivyMD library"
        font_style: "Button"
        size_hint_y: None
        height: self.texture_size[1]

    MDLabel:
        text: "kivydevelopment@gmail.com"
        font_style: "Caption"
        size_hint_y: None
        height: self.texture_size[1]

    ScrollView:

        DrawerList:
            id: md_list



Screen:
    ScreenManager:
        MenuScreen:
        ProfileScreen:
        UploadScreen:

    NavigationLayout:

        ScreenManager:

            Screen:

                BoxLayout:
                    orientation: 'vertical'

                    MDToolbar:
                        title: "Navigation Drawer"
                        elevation: 10
                        left_action_items: [['menu', lambda x: nav_drawer.toggle_nav_drawer()]]

                    Widget:


        MDNavigationDrawer:
            id: nav_drawer

            ContentNavigationDrawer:
                id: content_drawer


<MenuScreen>:
    name: 'menu'
    MDRectangleFlatButton:
        text: 'Profile'
        pos_hint: 'center_x':0.5,'center_y':0.6
        on_press: root.manager.current = 'profile'
    MDRectangleFlatButton:
        text: 'Upload'
        pos_hint: 'center_x':0.5,'center_y':0.5
        on_press: root.manager.current = 'upload'
    MDLabel:
        id:label_id
        text: 'update this at on enter'
        pos_hint: 'center_x':0.5,'center_y':0.4
        halign: 'center'
    
<ProfileScreen>:
    name: 'profile'
    MDLabel:
        text: 'Profile'
        halign: 'center'
    MDRectangleFlatButton:
        text: 'Back'
        pos_hint: 'center_x':0.5,'center_y':0.1
        on_press: root.manager.current = 'menu'
        
<UploadScreen>:
    name: 'upload'
    MDLabel:
        text: 'Upload'
        halign: 'center'
    MDRectangleFlatButton:
        text: 'Back'
        pos_hint: 'center_x':0.5,'center_y':0.1
        on_press: root.manager.current = 'menu'
'''


class ContentNavigationDrawer(BoxLayout):
    pass


class ItemDrawer(OneLineIconListItem):
    icon = StringProperty()
    text_color = ListProperty((0, 0, 0, 1))


"""Instead of just giving MDList in kv file give DrawerList for adding the ThemableBehavior with it and created color 
changing fn """


class DrawerList(ThemableBehavior, MDList):
    def set_color_item(self, instance_item):
        """Called when tap on a menu item."""

        # Set the color of the icon and text for the menu item.
        for item in self.children:
            if item.text_color == self.theme_cls.primary_color:
                item.text_color = self.theme_cls.text_color
                break
        instance_item.text_color = self.theme_cls.primary_color
        # print(f"inst:instance_item.text_color", f"thm_txtself.theme_cls.text_color",
        #       f"thm_prmself.theme_cls.primary_color")


class MenuScreen(Screen):
    pass
    # def on_enter(self, *args):
    #     self.ids.label_id.text = "Label text Updated"


class ProfileScreen(Screen):
    pass



class UploadScreen(Screen):
    pass


# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(ProfileScreen(name='profile'))
sm.add_widget(UploadScreen(name='upload'))


class TestNavigationDrawer(MDApp):
    def build(self):
        return Builder.load_string(KV)

    def on_start(self):
        icons_item = 
            "folder": "My files",
            "account-multiple": "Shared with me",
            "star": "Starred",
            "history": "Recent",
            "checkbox-marked": "Shared with me",
            "upload": "Upload",
        
        for icon_name in icons_item.keys():
            self.root.ids.content_drawer.ids.md_list.add_widget(
                ItemDrawer(icon=icon_name, text=icons_item[icon_name])
            )


TestNavigationDrawer().run()

所以有人知道如何在 KivyMD 中更改第一个屏幕标签或 on_enter 中的任何内容吗? 注意:取消注释 # def on_enter(self, *args): # self.ids.label_id.text = "Label text Updated" 这个。 它会显示错误。

【问题讨论】:

【参考方案1】:

我也有同样的问题。

似乎错误从这里开始:sm.add_widget(MenuScreen(name='menu')) 在将class MenuScreen 添加到屏幕管理器时,会触发 on_enter。 这通常可以通过在顶部添加 Builder 来解决,但对于 KivyMD,我认为这是不可能的。

【讨论】:

【参考方案2】:

我还没有完全找到答案,但找到了解决方法(必须以某种方式运行应用程序)。

第 1 步:删除这些行:

sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(ProfileScreen(name='profile'))
sm.add_widget(UploadScreen(name='upload'))

像这样编辑 kv 程序:

Screen:
    ScreenManager:
        MenuScreen:
            name:"menu"
        ProfileScreen:
            name:"profile"
        UploadScreen:
            name:"upload"

您可以在 python 程序的任何屏幕管理器类中使用self.parent.current = "screen_name",而不是sm.current = "screen_name。 如果您需要更改其他布局内部的屏幕,那么您可以使用self.parent.parent...,而不是使用MDApp.get_running_app().root.ids.screen_manger.current = "screen_name"

第2步:为避免第一个屏幕出现on_enter问题,创建一个仅在应用程序启动时显示的徽标屏幕,如下所示:

在python程序中添加这个

class HomeScreen(Screen):
    pass

并像这样编辑 Kv 程序:

Screen:
    ScreenManager:
        HomeScreen:
            name:"home"
        MenuScreen:
            name:"menu"
        ProfileScreen:
            name:"profile"
        UploadScreen:
            name:"upload"

并将其添加到 kv 程序中

<HomeScreen>:
    BoxLayout:
    Button:
        size:self.size
        on_press:
            root.manager.current = "menu"

您可以在按钮上添加图像或动画。

【讨论】:

感谢您的回答。这是有效的。虽然这是一条捷径,但猜想这是必须的,因为没有其他答案。

以上是关于如何使用 on_enter 函数在第一个屏幕中更新 KivyMD 标签?的主要内容,如果未能解决你的问题,请参考以下文章

Python / Kivy - 在调用函数的另一个屏幕中替换标签的值

如何在使用 pop 时重新加载或调用一些函数 initState()

如何将变量值从循环A更新到另一个循环B - 在bash中

Python / Kivy - 在另一个调用函数的屏幕中替换标签的值

如何在python脚本中默认情况下在第二个TFT屏幕上运行套接字服务器控制台,而不是在主TFT屏幕上?

如何使用 kivy StringProperty?