如何从文件中的变量项创建功能按钮 Python Kivy APP

How to create function buttons from variable items in a file Python Kivy APP

我实际上正在尝试创建一个应用程序,但我遇到了一个大问题

具体来说,它应该检查一个装满图片的文件夹中的项目,并且应该为每个项目在 Scollview GridLayout 列表中创建一个 ImageButton(已定义)。目前有效..

但是当其中一张显示的图片被按下时,我想让它改变主屏幕上图片的路径

这是我的Python代码

#This is outside the MDApp class
class ImageButton(ButtonBehavior, Image):

#Inside the MDApp class
def item_choser(self, item) 
 .
 .
 .
  elif item == "car":
    
    #It first clears the grid layout        
      self.root.ids.pictures_grid.clear_widgets()
    

    #Then, the path folder of the items(pictures) is being defined              
      pictures = glob.glob(f"./images/items/cars/*.jpg")
     

    #For loop created to assign            
      for i in pictures:
            
         #This "z" is the number after path in format <path_test_golf7_tez.jpg>      
         #Different for every picture (01, 02, 03 etc)
         z = i[-21:-19]
          
         #Creating button with bind
         z = ImageButton(source=i, allow_stretch=True, keep_ratio=True)
         z.bind(on_release=lambda x:self.chosen(i))  <<--- Here is my actual problem
         print(z)

         #Adding it into the grid
         self.root.ids.pictures_grid.add_widget(z)




def chosen(self, selectedCar):
    
    print(selectedCar)

    self.root.ids.main_image_display.source = selectedCar

这是路径文件夹包含的内容:

...cars_test_golf8_tez.jpg

...cars_test_golf7_tez.jpg

...cars_test_passat_te.jpg

...cars_test_crafter_t.jpg

所有照片都摆放正确。对于绑定后打印的每个“ z ”,它在内存中显示一个不同的对象,所以直到这里一切都很好


但这就是我的问题开始的地方。

假设你想选择帕萨特,所以你按下它的图片,但不知何故,无论你按下什么图片,chosen 函数总是会打印“...cars_test_crafter_t.jpg

如何编写此代码才能将 1 个按钮绑定到它的功能?

这是找到的最接近的答案,但要么我不明白如何在我的代码中使用和集成它,要么它根本无济于事 [lambda x, i=i : i * x for i in range( len(pictures))] 因为它不起作用

这可能不是完整的答案,但我不能在评论中挤出太多代码。

制作 gala 列表,然后将每张图片附加到其中:

gala = []
for i in pictures:
   z = i[-21:-19]
   tmp = ImageButton(source=i, allow_stretch=True, keep_ratio=True)
   tmp.bind(on_release=lambda x, i=i:self.chosen(i))  # closure hack
   self.root.ids.pictures_grid.add_widget(tmp)
   gala.append(tmp)   # can reference later >  gala[0]

关于'hack'

目标函数 (lamda) 不会立即 运行。定义函数时,python使用后期绑定。这样,python 存储了方法地址(lambda)和参数地址(i)。它存储参数值。稍后调用该函数时,将使用参数变量的当前(最新)值。

举个例子。当调用 chosen 时,i 将等于 2,因为这是循环完成但实际调用函数之前 i 的值。

for i in range(3):  0-2
    tmp.bind(on_release=lambda i:self.chosen(i)) # address of i is stored, not value

黑客通过强制提前绑定解决了这个问题。使用 i=i 告诉 python 该参数有一个默认值(如 def myfunc(i=5))并且应该使用早期绑定。使用早期绑定,(默认)参数值在创建方法时存储,解决了循环问题。

for i in range(3):  0-2
    tmp.bind(on_release=lambda x, i=i:self.chosen(i)) # value of i is stored

可能不需要 x 参数。您可以通过实验来确认。

@inclement 在他的评论中提供了有用的 link:https://docs.python-guide.org/writing/gotchas/#late-binding-closures

我认为问题出在 ImageButton Class - 您需要检查那里是否确实按下了那个按钮。

class ImageButton(Button):
    
    # ....          

    def on_touch_down(self, touch):
    """
    Check which of the buttons was pressed
    :param touch:
    :return:
    """
    if self.collide_point(*touch.pos):
        print(self.source)
        # You could then trigger an event from here and pass the Source/ID whatever as parameter
    else:
        # This Button wasn't pressed
        pass

您可以使用 on_touch_down 函数的 touch 参数进行检查。检查 touch.pos 与哪个按钮碰撞器发生碰撞。 on_touch_down 事件会为所有 ImageButton 触发,即使只按下一个!

我总是使用按钮 ID 来识别按钮 - link 具有更多数据。