Аргумент, передаваемый параметру on_release в списке kivy для всех виджетов, одинаков при выполнении кода. почему? и как это исправить?

#python-3.x #kivy #kivymd

Вопрос:

в настоящее время я пытаюсь создать интерфейс youtube, который фильтрует алгоритм youtube, чтобы вы не тратили свое время на развлечения, и я столкнулся с проблемой, которую я не знаю, как решить из-за недостатка знаний

Проблема: аргумент в функции для on_release в элементе списка одинаков для всех элементов списка (т. е. аргумент функции on_release последнего элемента списка каким-то образом используется для всех элементов списка)

Я выбираю канал 1 (лучшие идеи)
изображение 1

Я получаю «Канал изучения японского языка» вместо «лучшие идеи» изображение канала
2

важно получить правильное название канала, так как тогда я смогу удалить видеоинформацию этого канала

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

     def on_start(self):
        channel_logos_dir = "static/channel_images/"
        channel_logos_list = os.listdir(channel_logos_dir)

        for channel_logo_name in channel_logos_list:
            channel_name = channel_logo_name[:-4]
            print(channel_name)
            image = ImageLeftWidget(source=channel_logos_dir   channel_logo_name)
            item = TwoLineAvatarListItem(text=channel_name, on_release=lambda x: self.chan_page(channel_name))
            item.add_widget(image)
            self.root.ids.subs_scr_id.ids.subs_id.add_widget(item)
 

иерархия папок для изображений:

 static
    |--  channel_images
            |--    Better Ideas.jpg
            |--    Krish Naik.jpg
            |--    Learn Japanese with JapanesePod101.com.jpg
 

Здесь название канала для всех элементов в списке-это название канала последнего элемента

 on_release=lambda x: self.chan_page(channel_name)
 

Весь мой код:

 import os
from kivymd.app import MDApp
from kivymd.uix.screen import Screen
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager
from kivymd.uix.list import TwoLineAvatarListItem, ImageLeftWidget
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.tab import MDTabsBase

Window.size = (300, 500)

channels = {"Krish Naik":"https://www.youtube.com/user/krishnaik06",
            "Learn Japanese with JapanesePod101.com":"https://www.youtube.com/c/japanesepod101",
            "Better Ideas":"https://www.youtube.com/c/BetterIdeas/"}

screen_helper = """
ScreenManager:
    HomeScreen:
        id: home_src_id
    SubscriptionScreen:
        id: subs_scr_id
    ChannelScreen:
        id: channel_scr_id

<HomeScreen>:
    name: 'home'
    BoxLayout:
        orientation: 'vertical'
        MDToolbar:
            title: 'YouTube'
            left_action_items: [["youtube", lambda x: app.navigation_draw()]]
            right_action_items: [["magnify", lambda x: app.search_popup()]]
            MDIconButton:
                icon: "youtube-subscription"
                md_bg_color: app.theme_cls.primary_color
                pos_hint: {'center_x':0.5, 'center_y':0.5}   
                on_press: root.manager.current = "subscriptions"

        MDLabel:
            text: 'Go to subscriptions using top menu'
            halign: 'center'

    MDRectangleFlatButton:
        text:'profile'
        pos_hint: {'center_x':0.5, 'center_y':0.5}   
        on_press: root.manager.current = "subscriptions"

<SubscriptionScreen>:
    name: 'subscriptions'
    BoxLayout:
        orientation: 'vertical'
        MDToolbar:
            title: 'YouTube'
            left_action_items: [["youtube", lambda x: app.navigation_draw()]]
            right_action_items: [["magnify", lambda x: app.search_popup()]]
            MDIconButton:
                icon: "youtube-subscription"
                md_bg_color: app.theme_cls.primary_color
                pos_hint: {'center_x':0.5, 'center_y':0.5}   
                on_press: root.manager.current = "profile"
        ScrollView:
            MDList:
                id: subs_id

<ChannelScreen>
    name: 'channelPage'
    MDBoxLayout:
        orientation: "vertical"

        MDToolbar:
            id: channel_name
            left_action_items: [["keyboard-backspace", lambda x: app.navigation_draw()]]
            right_action_items: [["dots-vertical", lambda x: app.search_popup()]]
    
        MDTabs:
            id: tabs
            on_tab_switch: app.on_tab_switch(*args)
    
<Tab>
    ScrollView:
        MDList:
            id: vids_id
                """


class HomeScreen(Screen):
    pass

class SubscriptionScreen(Screen):
    pass

class ChannelScreen(Screen):
    pass

class Tab(MDFloatLayout, MDTabsBase):
    '''Class implementing content for a tab.'''

sm = ScreenManager()
sm.add_widget(HomeScreen(name='home'))
subscription_screen = SubscriptionScreen(name='subscriptions')
sm.add_widget(subscription_screen)
sm.add_widget(ChannelScreen(name='channelPage'))
sm.current = "home"


class DemoApp(MDApp):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.current_channel = 'Krish Naik'

    def build(self):
        self.theme_cls.primary_palette = 'Red'
        screen = Builder.load_string(screen_helper)
        return screen

    def on_start(self):
        channel_logos_dir = "static/channel_images/"
        channel_logos_list = os.listdir(channel_logos_dir)

        for channel_logo_name in channel_logos_list:
            channel_name = channel_logo_name[:-4]
            image = ImageLeftWidget(source=channel_logos_dir   channel_logo_name)
            item = TwoLineAvatarListItem(text=channel_name, on_release=lambda x: self.chan_page(channel_name))
            item.add_widget(image)
            self.root.ids.subs_scr_id.ids.subs_id.add_widget(item)


        self.root.ids.channel_scr_id.ids.tabs.add_widget(Tab(title=f"Videos"))
        self.root.ids.channel_scr_id.ids.tabs.add_widget(Tab(title=f"Playlists"))
        self.root.ids.channel_scr_id.ids.tabs.add_widget(Tab(title=f"Community"))

    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;
        '''
        for i in range(20):
            image = ImageLeftWidget(source="static/channel_images/Krish Naik.jpg")
            item = TwoLineAvatarListItem(text="krish naik", on_release=lambda x: self.chan_page(channel_name))
            item.add_widget(image)
            instance_tab.ids.vids_id.add_widget(item)

    # @staticmethod
    def chan_page(self,channel_name):
        self.root.current = "channelPage"
        self.root.ids.channel_scr_id.ids.channel_name.title = channel_name
        self.current_channel = channel_name




DemoApp().run()
 

Мой вывод:

 C:UsersrahimPycharmProjectsyt_filtervenvScriptspython.exe C:/Users/rahim/PycharmProjects/yt_filter/mian.py
[INFO   ] [Logger      ] Record log in C:Usersrahim.kivylogskivy_21-07-01_91.txt
[INFO   ] [deps        ] Successfully imported "kivy_deps.gstreamer" 0.3.2
[INFO   ] [deps        ] Successfully imported "kivy_deps.angle" 0.3.0
[INFO   ] [deps        ] Successfully imported "kivy_deps.glew" 0.3.0
[INFO   ] [deps        ] Successfully imported "kivy_deps.sdl2" 0.3.1
[INFO   ] [Kivy        ] v2.0.0
[INFO   ] [Kivy        ] Installed at "C:UsersrahimPycharmProjectsyt_filtervenvlibsite-packageskivy__init__.py"
[INFO   ] [Python      ] v3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)]
[INFO   ] [Python      ] Interpreter at "C:UsersrahimPycharmProjectsyt_filtervenvScriptspython.exe"
[INFO   ] [KivyMD      ] 0.104.2, git-bc7d1f5, 2021-06-06 (installed at "C:UsersrahimPycharmProjectsyt_filtervenvlibsite-packageskivymd__init__.py")
[INFO   ] [Factory     ] 186 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[INFO   ] [Text        ] Provider: sdl2
[INFO   ] [Window      ] Provider: sdl2
[INFO   ] [GL          ] Using the "OpenGL" graphics system
[INFO   ] [GL          ] GLEW initialization succeeded
[INFO   ] [GL          ] Backend used <glew>
[INFO   ] [GL          ] OpenGL version <b'4.6.13596 Compatibility Profile Context 20.10.35.02 27.20.1034.6'>
[INFO   ] [GL          ] OpenGL vendor <b'ATI Technologies Inc.'>
[INFO   ] [GL          ] OpenGL renderer <b'AMD Radeon(TM) Vega 8 Graphics'>
[INFO   ] [GL          ] OpenGL parsed version: 4, 6
[INFO   ] [GL          ] Shading version <b'4.60'>
[INFO   ] [GL          ] Texture max size <16384>
[INFO   ] [GL          ] Texture max units <32>
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Start application main loop
[INFO   ] [Loader      ] using a thread pool of 2 workers
[WARNING] Deprecated property "<StringProperty name=text>" of object "<__main__.Tab object at 0x000002D50A02F120>" has been set, it will be removed in a future version
[INFO   ] [Base        ] Leaving application in progress...

Process finished with exit code 0
 

Спасибо, что уделили мне время

Ответ №1:

Это распространенная ошибка при использовании lambda внутри цикла. lambda Функция ссылается на переменную, область действия которой находится в цикле, и когда lambda она выполняется, эта переменная имеет последнее значение, которое ей было присвоено в цикле. Исправление заключается в создании нового аргумента lambda функции, например:

         item = TwoLineAvatarListItem(text=channel_name, on_release=lambda x, y=channel_name: self.chan_page(y))