【问题标题】:Kivy: How to add widgets dinamically in kv, and how to refer to them from py script?Kivy:如何在 kv 中动态添加小部件,以及如何从 py 脚本中引用它们?
【发布时间】:2020-05-12 23:56:58
【问题描述】:

previous question 中,我询问了如何在按下按钮时动态添加一行文本输入,全部来自 py 脚本。

我现在正尝试将我的所有布局代码移动到一个 kv 文件。虽然这对于默认显示在屏幕上的小部件来说非常简单,但我不确定如何从 kv 文件中定义动态添加的文本输入。

目前,我的解决方案是在 kv 文件中创建“默认”小部件,并通过 addIngredient 方法从 py 脚本中添加其他小部件。低于最低工作版本。

kv 文件:

WMan:
    AddWindow:

<AddWindow>:
    name: 'add'
    ingsGrid: ingsGrid
    ing1: ing1
    quant1: quant1
    addIng: addIng
    saveButton: saveButton

    StackLayout:
        id: ingsGrid
        size_hint: .9, None
        height: self.minimum_height
        orientation: 'lr-tb'
        spacing: '5sp', '5sp'


        TextInput:
            id: ing1
            multiline: False
            size_hint: .65, None
            height: self.minimum_height
        TextInput:
            id: quant1
            multiline: False
            size_hint: .25, None
            height: self.minimum_height
        Button:
            id: addIng
            text: "+"
            size_hint: .1, None
            height: ing1.height
            on_release: root.addIngredient(self)
        Button:
            id: saveButton
            text: "Save"
            size_hint: .3, None
            on_release:
                root.saveRec(self)

py 脚本如下:

from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.stacklayout import StackLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder


class AddWindow(Screen):
    def __init__(self, **kwargs):
        super(AddWindow, self).__init__(**kwargs)

        recipeName = ObjectProperty(None)
        ingsGrid = ObjectProperty(None)
        ing1 = ObjectProperty(None)
        quant1 = ObjectProperty(None)

        self.i = 1
        self.ingsList = {}
        self.ingsList[0] = ing1
        self.quants = {}
        self.quants[0] = quant1

    def addIngredient(self, instance):
        tmp_children_list = self.ingsGrid.children[:]

        self.ingsGrid.clear_widgets()

        # range(start,stop[, step])
        for index in range(len(tmp_children_list)-1, -1, -1):
            # the last item, then last-1, etc
            child = tmp_children_list[index]
            # add the last first (new ones will be added on top)
            self.ingsGrid.add_widget(child)
            # if child is the pressed button
            if child == instance:
                self.ing = TextInput(
                    size_hint=(.65, None),
                    height='30sp')
                self.ingsGrid.add_widget(self.ing)
                self.ingsList[self.i] = self.ing

                self.quant = TextInput(
                    size_hint=(0.25, None),
                    height='30sp')
                self.ingsGrid.add_widget(self.quant)
                self.quants[self.i] = self.quant
                self.i += 1

                self.addNext = Button(
                    text="+",
                    size_hint=(0.1, None),
                    height='30sp')
                self.addNext.bind(on_press=self.addIngredient)
                self.ingsGrid.add_widget(self.addNext)

    def saveRec(self, instance): # grab all inputs and send to SQLite db
        print(self.ingsList)
        print(self.ingsList[0].text)
        print(self.ingsList[1].text)


class WMan(ScreenManager):
    pass

kv = Builder.load_file("test.kv")

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

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

我的问题是双重的:首先,虽然这种动态添加行的方式可以正常工作,但在 kv 文件中定义一半布局,而在 py 中定义另一半布局对我来说有点混乱脚本。所以我的第一个问题是:

1. Is there a way to move the entire layout to the kv file?

第二个问题是:

2. How do I access the content of textInput 'ing1' (the one created in the kv file)?

当我运行print(self.ingsList) 时,我得到:

{0: <ObjectProperty name=>, 1: <kivy.uix.textinput.TextInput object at 0x000002077FB89C88>}

所以虽然我可以很容易地做到print(self.ingsList[1].text),但运行print(self.ingsList[0].text) 会报错:

AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'

【问题讨论】:

    标签: python kivy kivy-language


    【解决方案1】:

    这是您的代码的修改版本,可以满足您的需求:

    from kivy.app import App
    from kivy.properties import ObjectProperty, NumericProperty
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.lang import Builder
    
    
    class AddWindow(Screen):
        def __init__(self, **kwargs):
            super(AddWindow, self).__init__(**kwargs)
    
            recipeName = ObjectProperty(None)
            ingsGrid = ObjectProperty(None)
            ing1 = ObjectProperty(None)
            quant1 = ObjectProperty(None)
    
            self.i = 1
            self.ingsList = {}
            self.ingsList[0] = ing1
            self.quants = {}
            self.quants[0] = quant1
    
        def saveRec(self, instance): # grab all inputs and send to SQLite db
            for child in self.ids.ingsGrid.children:
                if isinstance(child, OneRow):
                    print('ingedient name:', child.ids.ing1.text)
                    print('quantity:', child.ids.quant1.text)
                    print('')
    
    
    class WMan(ScreenManager):
        pass
    
    
    class OneRow(BoxLayout):
        inst_count = NumericProperty(0)
        count = 0
        def __init__(self, **kwargs):
            OneRow.count += 1
            self.inst_count = OneRow.count
            super(OneRow, self).__init__(**kwargs)
    
        def get_index(self):
            par = self.parent
            if par is None:
                return None
            index = 0
            for index in range(len(par.children) - 1, -1, -1):
                child = par.children[index]
                if child == self:
                    return index
    
    kv_str = '''
    #:import Factory kivy.factory.Factory
    WMan:
        AddWindow:
            id: add
    
    #:set row_height 30
    <OneRow>:
        orientation: 'horizontal'
        size_hint_y: None
        height: row_height
        TextInput:
            id: ing1
            multiline: False
            size_hint: .65, None
            height: row_height
        TextInput:
            id: quant1
            multiline: False
            text: str(root.inst_count)
            size_hint: .25, None
            height: row_height
        Button:
            id: addIng
            text: "+"
            size_hint: .1, None
            height: row_height
            on_release: app.root.ids.add.ids.ingsGrid.add_widget(Factory.OneRow(), index=root.get_index())
    
    <AddWindow>:
        name: 'add'
        ingsGrid: ingsGrid
        saveButton: saveButton
    
        StackLayout:
            id: ingsGrid
            size_hint: .9, None
            height: self.minimum_height
            orientation: 'lr-tb'
            spacing: '5sp', '5sp'
    
            OneRow:
            Button:
                id: saveButton
                text: "Save"
                size_hint: .3, None
                on_release:
                    root.saveRec(self)
    '''
    
    # kv = Builder.load_file("test.kv")
    kv = Builder.load_string(kv_str)
    
    class TestApp(App):
        def build(self):
            return kv
    
    if __name__ == "__main__":
        TestApp().run()
    

    我使用Builder.load_string() 而不是load_file() 只是为了我自己的方便。

    我创建了一个名为 OneRow 的类,它包含成分列表中的一行,而 + Button 现在只添加了该类的一个实例。该类的get_index() 方法仅用于将新成分定位在按下Button 的行下方。该类中的其他代码只是添加一些识别信息。如果这些东西对您来说不重要,您可以从 python 中删除 OneRow 类定义,只需将 kv 中的 &lt;OneRow&gt;: 替换为 &lt;OneRow@BoxLayout&gt;: 并在添加 OneRow 的地方设置 @ 987654334@.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-20
      • 2018-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多