Kivy。如何在kv中添加小部件,以及如何从py脚本中引用它们?

问题描述 投票:0回答:1

在一个 前一个问题我曾问过如何在按下一个按钮时能有一行文字输入,而这一切都来自一个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个回答
0
投票

这里是你的代码的修改版本,它可以实现我认为你想要的东西。

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 那是被按下的。而该类中的其他代码只是为了添加一些识别信息。如果这些东西对你来说并不重要,你可以去掉这个类中的 OneRow 的类定义,并将其替换为 <OneRow>:kv<OneRow@BoxLayout>: 而其中 OneRow 你可以直接设置 index=1.

© www.soinside.com 2019 - 2024. All rights reserved.