【问题标题】:Kivy Three Buttons generated on pressing a Button. One of the New Buttons when pressed should remove All 3 New Buttons按下按钮时生成的 Kivy 三个按钮。按下新按钮之一应删除所有 3 个新按钮
【发布时间】:2021-08-21 06:52:36
【问题描述】:

在此代码中,在按下按钮“CREATE”时,会创建三个按钮“Remove”、“Btn1”、“Btn2”... 在按下“Remove”按钮时,所有三个按钮(“Remove”、“Btn1 ","Btn2") 应该被删除。 我尝试了很多方法,但找不到使新按钮可以访问同一类下的其他功能的方法。

主代码:

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()

【问题讨论】:

  • 如果该布局仅包含这三个按钮,则使用self.ids.Fl.clear_widgets()
  • 但是主按钮不会也被删除......我需要它是一个循环......当我按下主按钮“CREATE”时,三个新的btns被创建......打开按下新 btns 之一“Btnremove”...只会删除创建的 3 个新按钮,而不是主按钮...
  • 去掉所有按钮后,重新添加主按钮即可。
  • 我的实际代码要复杂得多...但功能是相同的...这就是为什么我需要它以与我相同的方式工作...再次动态添加按钮不会接受我提出的功能。据我所知,它仅适用于 kv 代码
  • 只是好奇:你问的similar question有什么不同?

标签: python kivy kivy-language


【解决方案1】:

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

注意事项

即使预计这会起作用,但这并不能真正解释您的问题。 它只起作用,因为它不了解按钮。唯一的假设是,有一个按钮CREATE 可以保留(而不是删除)。 不幸的是,CREATE 按钮甚至在配置中的代码之外也被定义了。如果您在此处更改文本(例如更改为 CREATE BUTTONS),您的函数将无法按预期工作。

发现的问题

改用实例变量

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

在你的instance方法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 之类的名称(不带括号!)。

Button 上查看 Kivy 的文档:

要在按下按钮(单击/触摸)时附加 回调,请使用绑定:

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()时,该方法被立即调用并返回None(该方法不返回对象)。然后绑定None 而不是函数引用。

发生了两件令人意想不到的事情:

  1. 您在绑定期间移除了按钮(在按下按钮之前)
  2. 当按钮被按下时,定义为None的回调不能被调用。因此出现错误:

AssertionError: None is not callable

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

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

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

为实例方法添加参数

# 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 作为对按钮实例本身的引用。

添加实例变量

通过这些实例变量(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(例如在构造函数中),而您现在在addme 之前调用remove,然后在remove 中调用它将引发错误,因为您的实例变量尚不存在且未知。

删除小部件

已经提出并回答了一个类似的问题: How to address remove_widget what widget to remove inside another layout in Kivy

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) 

请注意,本地变量 buttonremove_widget 方法是可见和可访问的,因为它是作为参数传递的。

【讨论】:

  • 哇...非常感谢您花时间解释这个...
  • 对不起,我再次执行了它...得到一个 AssertionError: None is not callable...知道为什么吗?
  • 我使用了你的第二种方法,做了一些更改... def remove(self,widget): and Btnremove.bind(on_press=self.remove) 注意:我删除了 self.remove 中的 () () 及其工作...
  • @ZaynNaz:我也没有测试我的解决方案,你自己发现并解决了我的错误?️我会更新我的答案。谢谢!
【解决方案2】:

您可以在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)
我没试过。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 2015-04-04
    • 1970-01-01
    相关资源
    最近更新 更多