Kivy, StackLayout, object order

老子叫甜甜 提交于 2021-01-29 05:01:18

问题


I have a screen with a StackLayout. The first row of the stack includes a textinput and a "+" button which is meant to add another identical row below the actual one in a loop (i.e. another textinput with another "add" button). Then there is a "Save" button, which is supposed to be always at the end of the stack. The dictionary is supposed to later grab the input from the text field, when pressing the save button, but this should not be relevant to my problem.

There are two problems with my code:

  • First, when I press the "+" button in a row, the button that gets highlighted is not this button itself, but the one in the row below (e.g. if there are three rows and I press the button in the second row, the button in the third row is highlighted)
  • Second and most importantly, starting from the second row onwards, each press of the "+" button adds a new row above the actual one, rather than below it.

I am aware that kivy assigns reverse order to the widgets (i.e. the one added last will be drawn first) and that is why I am manually assigning indexes to the new rows. However, I cannot achieve the desired behavior.

Here is a minimal working version:

from kivy.app import App
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

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

        self.grid = StackLayout()
        self.grid.pos_hint = {"x":0.05,"top":0.8}
        self.grid.size_hint = (0.9,None)
        self.add_widget(self.grid)

        self.i = 1
        self.n = 1
        self.inputs = {}
        self.ing1 = TextInput(size_hint=(0.9,'0.3sp'))
        self.grid.add_widget(self.ing1)
        self.inputs['0'] = 'ing1'

        self.addIng = Button(text="+", size_hint=(0.1,'0.3sp'))
        self.addIng.bind(on_press=self.addIngredient)
        self.grid.add_widget(self.addIng)

        self.doneButton = Button(text="Save")
        self.grid.add_widget(self.doneButton, index=0)

    def addIngredient (self, instance):
        self.ing = TextInput(size_hint=(0.9,'0.3sp'))
        self.inputs[self.i] = 'ing{}'.format(self.i+1)
        self.grid.add_widget(self.ing, index=self.n+1)

        self.addNext = Button(text="+", size_hint=(0.1,'0.3sp'))
        self.addNext.bind(on_press=self.addIngredient)
        self.grid.add_widget(self.addNext, index=self.n+2)
        self.i += 1
        self.n += 2
        print(self.inputs)        

WMan = ScreenManager() 
WMan.add_widget(AddWindow(name='add'))


class RecipApp(App):
    def build(self):
        return WMan

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

What am I missing? Here is a screenshot for better clarity: Screenshot


回答1:


Here is a brute force method to do what you want by rebuilding the StackLayout each time a + `Button is pressed:

def addIngredient(self, instance):
    tmp_children_list = self.grid.children[:]
    self.grid.clear_widgets()
    for index in range(len(tmp_children_list)-1, -1, -1):
        child = tmp_children_list[index]
        self.grid.add_widget(child)
        if child == instance:
            # this is the pressed Button, so add new row after it
            self.n += 1
            self.ing = TextInput(size_hint=(0.9,'0.3sp'))
            self.ing.text = str(self.n)  # add text just for identification
            self.grid.add_widget(self.ing)
            self.addNext = Button(text="+", size_hint=(0.1,'0.3sp'))
            self.addNext.bind(on_press=self.addIngredient)
            self.grid.add_widget(self.addNext)



回答2:


Here is a quick and dirty approach , where your problems should be fixed due to a remove and add of the save button. Between those to actions you add a new row before you finally add the save button again. Hope it helps you to adjust your code. The key part is the custom on_press method of the AddButton.

from kivy.app import App
from kivy.uix.stacklayout import StackLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.core.window import Window


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

    def on_press(self):
        self.parent.remove_widget(self.parent.save_btn)
        self.parent.add_widget(TextInput(size_hint=(None, None), size=(Window.width * 0.8 - self.parent.padding[1], Window.height * 0.1)))
        self.parent.add_widget(AddButton(text="+", size_hint=(None, None), size=(Window.width * 0.2, Window.height * 0.1)))
        self.parent.add_widget(self.parent.save_btn)


class MyApp(App):
    def build(self):
        Window.size = (400, 600)
        layout = StackLayout(orientation="lr-tb", padding=(20, 40))
        layout.save_btn = Button(text="Save", size_hint=(None, None),
                               size=(Window.width - layout.padding[1], Window.height * 0.1))

        layout.add_widget(TextInput(size_hint=(None, None), size=(Window.width * 0.8 - layout.padding[1], Window.height * 0.1)))
        layout.add_widget(AddButton(text="+", size_hint=(None, None), size=(Window.width * 0.2, Window.height * 0.1)))

        layout.add_widget(layout.save_btn)

        return layout

MyApp().run()


来源:https://stackoverflow.com/questions/61611495/kivy-stacklayout-object-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!