按下按钮时生成的 Kivy 三个按钮。按下其中一个新按钮时应移除所有 3 个新按钮

Kivy Three Buttons generated on pressing a Button. One of the New Buttons when pressed should remove All 3 New Buttons

在这段代码中,按下按钮“CREATE”,创建了三个按钮“Remove”,“Btn1”,“Btn2”...按下“Remove”按钮,所有三个按钮(“Remove”,“Btn1”,“Btn2” “Btn1”,“Btn2”)应该被删除。 我尝试了很多方法,但我无法找到一种方法来使新按钮可供同一 class.

下的其他功能访问

主要代码:

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen,ScreenManager

class Main(Screen):
    def remove(self):
        self.ids.Fl.remove_widget(Btnremove)
        self.ids.Fl.remove_widget(Btn1)
        self.ids.Fl.remove_widget(Btn2)

    def addme(self):
        Btnremove=Button(text="Remove",font_size=18,size_hint=(.2,.1),pos_hint={"center_x": .5, "center_y": .7})
        Btn1=Button(text= "Btn1",font_size=18,size_hint=(.2,.1),pos_hint={"center_x":.4,"center_y":.2})
        Btn2=Button(text="Btn2",font_size=18,size_hint=(.2, .1),pos_hint={"center_x": .6, "center_y": .2})
        self.ids.Fl.add_widget(Btnremove)
        self.ids.Fl.add_widget(Btn1)
        self.ids.Fl.add_widget(Btn2)
        Btnremove.bind(on_press=self.remove())

class Manager(ScreenManager):
    pass
kv=Builder.load_file("test2.kv")
screen=Manager()
screen.add_widget(Main(name="main"))
class Test(App):
    def build(self):
        return screen
Test().run()

Kv代码:

<Main>:
    name: "main"
    FloatLayout:
        id: Fl
        Button:
            id: create
            text: "CREATE"
            size_hint: (.55,.175)
            pos_hint: {"center_x":.5,"center_y":.5}
            on_press:
                root.addme()

您可以在 remove 函数中迭代 float layout 的所有子项。

def remove(self):
    children = self.ids.Fl.children

    for btn in children:
        # if the button is not the create button
        if btn.text != "CREATE":
            self.ids.Fl.remove_widget(btn)
I have not tried that.

遍历小部件内的所有子项(按钮)并删除所有没有文本 CREATE 的子项(按钮)。所以它在不知道之前创建的按钮的情况下隐式工作。

注意事项

即使这有望奏效,但它并不能真正解释您的问题。 它只能工作,因为它不知道按钮。它唯一假设的是,有一个 按钮 CREATE 可以保留 (而不是删除)。 不幸的是 CREATE 按钮甚至在配置中的代码之外定义。如果您更改那里的文本(例如更改为 CREATE BUTTONS),您的函数将不再按预期工作。

发现问题

改用实例变量

您声称您的 remove 方法没有按预期工作。 此外,您不知道如何使其他功能可以访问按钮

在你的实例方法remove(self)中你正在处理按钮对象(例如Btnremove),比如它们是全局变量。

def remove(self):
    self.ids.Fl.remove_widget(Btnremove)
    self.ids.Fl.remove_widget(Btn1)
    self.ids.Fl.remove_widget(Btn2)

但事实并非如此。它们是局部变量,仅可见内部另一个实例方法addme(self):

def addme(self):
        Btnremove=Button(text="Remove",font_size=18,size_hint=(.2,.1),pos_hint={"center_x": .5, "center_y": .7})
        Btn1=Button(text= "Btn1",font_size=18,size_hint=(.2,.1),pos_hint={"center_x":.4,"center_y":.2})
        Btn2=Button(text="Btn2",font_size=18,size_hint=(.2, .1),pos_hint={"center_x": .6, "center_y": .2})
  chance

仅按名称绑定函数

您对 on_press 事件的绑定操作定义为调用函数,即 self.remove()。而是通过简单地传递像 self.remove(没有括号!)这样的名称来将其定义为对 function/method 的引用。

请参阅 Kivy 关于 Button 的文档:

To attach a callback when the button is pressed (clicked/touched), use bind:

def callback(instance):
    print('The button <%s> is being pressed' % instance.text)

btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)

这个callback只是方法名(没有括号)。在你的情况下:

# before: will result in `None` bound and raise an error when pressed
Btnremove.bind(on_press=self.remove())

# after: will call the function by name
Btnremove.bind(on_press=self.remove)

恭喜,您自己发现了错误并修复了它。

之前发生了什么,为什么会报错?

因为当将回调绑定和定义为 on_press=self.remove() 时,该方法会立即被调用并且 returns None(该方法不 return 一个对象)。然后绑定 None 而不是函数引用。

所以不利的是发生了两件你没有预料到的事情:

  1. 您在绑定期间删除了按钮(在按下按钮之前)
  2. 按下按钮时,无法调用定义为None的回调。因此错误:

AssertionError: None is not callable

在实例方法之间访问状态或变量

有不同的方法可以解决这个问题(与Kivy无关):

  1. 向方法添加一个参数(按住按钮),这样您就可以在调用时将局部变量作为参数传递
  2. 添加实例变量(按住按钮)这样您的class中的每个实例方法都可以访问它们

向实例方法添加参数

# adding a parameter `widgets`
def remove(self, widgets):
    for w in widgets:
        self.ids.Fl.remove_widget(w)

# calling the method with argument
def addme(self):
    # omitted code
    buttons_to_remove = [Btnremove, Btn1, Btn2]  # creating a list with your buttons
    Btnremove.bind(on_press=self.remove(buttons_to_remove))  # pass the list as argument to the method

⚠️ 警告: 此绑定 on_press=self.remove(buttons_to_remove) 将不起作用,因为回调必须仅按名称作为方法引用传递。不能像 buttons_to_remove 那样传递自定义参数。 Kivy 隐式传递给该方法的唯一参数是 instance 作为对按钮实例本身的引用。

添加实例变量

在这些实例变量上(实例范围用前缀self.标记)我们可以在对象内部共享状态。所以我们可以在所有 实例方法 之间共享状态,因为它们都可以访问实例对象 self。所以他们也可以访问 self 中的所有内容,例如 self.buttons_to_delete.

# accessing instance variable `buttons_to_remove`
def remove(self):
    # accessing the shared instance variable using instance-prefix `self.`
    for btn in self.buttons_to_remove:
        self.ids.Fl.remove_widget(btn)

# adding buttons to the instance-variable `buttons_to_remove`
def addme(self):
    # omitted code
    self.buttons_to_remove = [Btnremove, Btn1, Btn2]  # creating a list with your buttons
    Btnremove.bind(on_press=self.remove())  # the method has access to this list

⚠️警告:如果你之前没有声明实例变量self.buttons_to_remove(例如在构造函数中),而你现在在[之前调用remove =43=],然后在 remove 内部会引发错误,因为您的实例变量尚不存在且未知。

删除小部件

已经提出并回答了类似的问题:

Kivy的remove_widget方法的调用是正确的,期望一个child(类型Widget或此处:Button)传递给它。

就像文档中关于 remove_widget 的这个例子:

>>> from kivy.uix.button import Button
>>> root = Widget()
>>> button = Button()
>>> root.add_widget(button)
>>> root.remove_widget(button) 

注意这里,local 变量 button 对方法 remove_widget 可见且可访问,因为它作为参数传递。