访问具有ID的动态添加的小部件

时间:2019-06-13 20:03:45

标签: python python-3.x kivy keyerror

目标
我想创建一个可以动态添加按钮的小脚本,但仍然可以让我通过root对特定按钮执行功能。


我的方法
我做了这个脚本。

它能够在顶部动态添加大按钮。
当按下这些按钮时,每个按钮都会略微改变其自身的颜色。

它的底部有两个小按钮。
第一个按钮会在顶部动态添加新的大按钮。
第二个按钮重置顶部第一个大按钮的颜色。

我的代码

#!/usr/bin/env python3
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout

Builder.load_string('''
<RootWidget>:  
    Button:
        text: 'Add'
        size_hint: (None, None)
        size: (40, 40)
        pos: (40, 40)
        group: 'action'
        on_press: root.createNextTarget()
    Button:
        text: 'res'
        size_hint: (None, None)
        size: (40, 40)
        pos: (100, 40)
        group: 'action'
        on_press: root.resetTarget()
''')

class RootWidget(FloatLayout):
    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(**kwargs)
        #note: self.ids isn't populated yet. I guess we can't use it yet.
        self.createNextTarget()

    def resetTarget(self):
        f_target = self.ids['targetbutton0']
        f_target.background_color = (1.0, 1.0, 1.0, 1.0)
        return True

    def countTargets(self):
        return [str(x.__class__.__name__) for x in self.children if x != None].count('TargetButton')

    def createNextTarget(self):
        f_nextButton = TargetButton(id="targetbutton"+str(self.countTargets()),
                               size_hint=(None, None),
                               pos=(80 + (10 + 60) * self.countTargets(), 100),
                               size=(60, 60),
                               background_normal = '',
                               background_color = (1, 1, 1, 1),
                               group = 'target')
        self.add_widget(f_nextButton)
        f_nextButton.bind(on_press=TargetButton.lowerAllRGB)

class TargetButton(Button):
    def __init__(self, **kwargs):
        super(TargetButton, self).__init__(**kwargs)

    def lowerAllRGB(self):
        f_r, f_g, f_b, f_a = self.background_color
        if f_r >= 0.1: f_r = f_r - 0.1
        if f_g >= 0.1: f_g = f_g - 0.1
        if f_b >= 0.1: f_b = f_b - 0.1
        self.background_color = (f_r, f_g, f_b, f_a)
        return True

class TestApp(App):
    def build(self):
        return RootWidget()

    def on_stop(self):
        print("TestApp.on_stop: finishing", self.root.ids)

if __name__ == '__main__':
    TestApp().run()

问题
如果我尝试按下重置按钮(通过root.ids访问小部件),则会收到错误消息:KeyError: 'targetbutton0'

找到a post about a similar problem后,我以为root.idsRootWidget.__init__期间不起作用。
但是,当我在RootWidget.__init__完成之后使用按钮添加按钮时,TestApp.on_stop()仍会打印:     TestApp.on_stop:完成{}

所以root.ids仍然是空的,尽管我为每个小部件都分配了id属性,但似乎不包含任何动态添加的小部件。

我对您的问题

  1. 考虑到我动态添加小部件的方式,使用root.ids就我的目的而言就一文不值了吗?
  2. 我是否可以通过id访问我的小部件?
    我看到another question here提出了类似的要求。但这没有回答我有关动态添加的小部件的问题。

1 个答案:

答案 0 :(得分:1)

问题1-root.ids / self.ids

  

鉴于我动态添加小部件的方式,只是使用root.ids   对我而言毫无价值?

答案

分配给动态添加的窗口小部件的

id不存储在self.idsroot.ids中。因此,您无法使用self.ids['targetbutton0']self.ids.targetbutton0访问动态添加的窗口小部件。如果这样做,您将得到一个KeyError,因为在字典类型属性self.ids中找不到它。

解析您的kv文件时,Kivy会收集所有标记有ID的小部件,并将它们放在此 self.ids 字典类型属性中。

注意: 这些类型的id(即分配给动态创建的小部件的id)已弃用,并将在以后的Kivy版本中删除。

[WARNING] Deprecated property "<StringProperty name=id>" of object "<kivy.uix.button.Button object at 0x7feeec0968d0>" has been set, it will be removed in a future version

问题2

  

我是否可以通过id访问我的小部件?

解决方案

您可以创建自己的字典类型属性ID列表。

摘要

from kivy.properties import DictProperty

class RootWidget(FloatLayout):
    dynamic_ids = DictProperty({})    # declare class attribute, dynamic_ids

    def __init__(self, **kwargs):
        super(RootWidget, self).__init__(**kwargs)
        self.createNextTarget()

    def resetTarget(self):
        f_target = self.dynamic_ids['targetbutton0']
        f_target.background_color = (0.0, 1.0, 1.0, 1.0)    # cyan colour
        return True

    ...

    def createNextTarget(self):
        id = "targetbutton" + str(self.countTargets())
        f_nextButton = TargetButton(id=id,
                               size_hint=(None, None),
                               pos=(80 + (10 + 60) * self.countTargets(), 100),
                               size=(60, 60),
                               background_normal = '',
                               background_color = (1, 1, 1, 1),    # white colour
                               group = 'target')
        self.add_widget(f_nextButton)
        self.dynamic_ids[id] = f_nextButton
        f_nextButton.bind(on_press=TargetButton.lowerAllRGB)

输出

Result