如何使用python类在kivy中使用下拉小部件

所以,我认为应该至少有两种方法可以让我在这个页面上有一个下拉菜单,但我不能同时工作.我是kivy和编程的新手,但我已阅读文档,似乎我根本不理解.

我创建了以下示例:

import kivy
kivy.require('1.7.2') # replace with your current kivy version !

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown

class CustomDropDown(DropDown):
    pass

class HomeScreen(Screen):
    translateInput = ObjectProperty(None)
    translateButton = ObjectProperty(None)
    translateLabel = ObjectProperty(None)
    top_layout = ObjectProperty(None)
    dd_btn = ObjectProperty(None)
    drop_down = CustomDropDown()
    #notes_dropdown = ObjectProperty(None)


    dropdown = DropDown()
    notes = ['Features', 'Suggestions', 'Abreviations', 'Miscellaneous']
    for note in notes:
        # when adding widgets, we need to specify the height manually (disabling
        # the size_hint_y) so the dropdown can calculate the area it needs.
        btn = Button(text='%r' % note, size_hint_y=None, height=30)

        # for each button, attach a callback that will call the select() method
        # on the dropdown. We'll pass the text of the button as the data of the
        # selection.
        btn.bind(on_release=lambda btn: dropdown.select(btn.text))

        # then add the button inside the dropdown
        dropdown.add_widget(btn)

    # create a big main button
    mainbutton = Button(text='Usage Notes 2', size_hint=(1, 1))

    # show the dropdown menu when the main button is released
    # note: all the bind() calls pass the instance of the caller (here, the
    # mainbutton instance) as the first argument of the callback (here,
    # dropdown.open.).
    mainbutton.bind(on_release=dropdown.open)
    #dd_btn.bind(on_release=dropdown.open)

    # one last thing, listen for the selection in the dropdown list and
    # assign the data to the button text.
    dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
    #dropdown.bind(on_select=lambda instance, x: setattr(dd_btn, 'text', x))

    #top_layout.add_widget(mainbutton)


class dropdApp(App):

    def build(self):

        return HomeScreen()



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

这是相应的.kv文件:

#:kivy 1.7.2

<CustomDropDown>:
    Button:
        text: 'My first Item'
        size_hint_y: None
        height: 44
        on_release: root.select('item1')
    Label:
        text: 'Unselectable item'
        size_hint_y: None
        height: 44
    Button:
        text: 'My second Item'
        size_hint_y: None
        height: 44
        on_release: root.select('item2')

<HomeScreen>:
    id: home_screen
    translateInput: translateInputID
    translateButton: translateButtonID
    translateLabel: labelID
    top_layout: topLayoutID
    #notes_dropdown: notesDropDownID
    dd_btn: btn_ddID

    orientation: 'vertical'
    FloatLayout:
        size_hint: 1, .95
        TextInput:
            id: translateInputID
            text: 'cove'
            #text: 'ﻰﺸَﻣ'
            font_name: "data/fonts/DejaVuSans.ttf"
            background_color: 1, 1, 1, 1
            size_hint: .75, .1
            multiline: False
            pos_hint: {'x': .125, 'y': .45}
            text_size: self.size
            valign: 'middle'
            halign: 'center'
            padding: 5

        Button:
            id: translateButtonID
            text: 'Translate'
            pos_hint: {'x': .35, 'y': .35}
            size_hint: .3, .08
            valign: 'middle'
            halign: 'center'
            text_size: self.size
            on_release: root.translateButtonPressed()
            #on_press: root.manager.current = 'resultsscreen'

        Label:
            id: labelID
            text: 'Translator'
            text_size: self.size
            valign: 'middle'
            halign: 'center'
            pos_hint: {'x': .3, 'y': .75}
            size_hint: .4, .2
            #font_name: "simpo.ttf"
            #font_name: "5thgradecursive.ttf"
            #font_name: "AGA-Rasheeq-Regular.ttf"
            font_name: "data/fonts/DejaVuSans.ttf"
            font_size: 50

    BoxLayout:
        id: topLayoutID
        #cols: 2
        size_hint: 1, .05
        pos_hint: {'x': 0, 'y': .95}
        Button:
            #id: notesDropDownID 
            id: btn_ddID
            text: 'Usage Notes'
            on_release: root.drop_down.open
        Button:
            text: 'About'

>第一个下拉菜单应附加到k​​v文件底部已创建的按钮“使用说明”.它附加到python中的类,“CustomDropDown”,以及kv文件中的相应内容.你可能会注意到这个例子直接来自kivy文档.我想通过创建这条线:

drop_down = CustomDropDown()

它给了我一个python和kivy方面的句柄来操纵它,但正如你可能会注意到当你运行它并单击“使用说明”时,没有任何反应.
>第二个下拉菜单是在python中创建的,也是在kivy文档之后.我认为它会在应用程序顶部创建第三个按钮,标题为“使用说明2”.我尝试添加它时遇到错误.现在这行(我文件中的53):

top_layout.add_widget(mainbutton)

被注释掉或应用程序甚至不会运行,引发错误:

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

我意识到我确实设定了

top_layout = ObjectProperty(None)

但这就是kivy建议为许多小部件做的事情,而且我已经和其他许多小部件做过了没有这个错误.

我确信这些问题中的一个或两个对于那里的人来说很简单.我错过了什么?

最佳答案

The first drop-down menu should be attached to the already created button, “Usage Notes”, at the bottom of the kv file. It is attached to the class in python, “CustomDropDown”, and the corresponding in the kv file.

对于这一部分,你有正确的基本想法,但犯了两个主要错误.

首先是你的类定义错误,你通常几乎不想在类级别放置代码,而应该在类方法中使用.如果要在实例化类时运行它,则应将其放在__init__方法中.我在下面粘贴的代码显示了执行此操作所需的简单更改.您还需要将drop_down更改为self.drop_down以设置类属性,而不仅仅是创建局部变量.

您有时希望在类变量中创建变量,这些变量可用于类的所有实例. Kivy属性就是这样的一个例子,它具有特殊的行为来控制它们在实际类实例中的行为方式.尽管如此,这是常规的一个例外,而不是您想要对大多数代码执行的操作.

实际上,我并不完全确定您的代码如何失败的细节(不确定执行顺序/时间),但最终代码无法正常运行且小部件未正确初始化.如果您遵循正常的__init__程序,这一切都很好.

第二个错误是你的kv文件有on_release:root.drop_down.open.问题是kv冒号右边的所有东西都是纯python,在这种情况下你不调用函数,你只需说明它的名字,所以没有任何反应.您需要使用root.drop_down.open(self)替换它以获得正确的行为,因为open方法需要一个小部件作为参数.

The second drop-down menu is created in python also following the kivy documentation. I would think it would create a third button at the top of the app, titled, “Usage Notes 2”. I just get an error when I try to add it. Right now the line (53 on my file):

这是您的代码在类级别而不是在__init__方法中的错误方式的另一个症状.问题是top_layout是一个ObjectProperty,并且只在kv类中看起来像普通对象,因为Property在类实例中管理其接口的特殊方式.当你的代码在类级别时,它看不到那个特殊的接口,因为它并没有真正从类实例运行(同样的事情导致所有其他问题),所以它抛出一个错误,因为它看到了ObjectProperty而不是它的内容.

随着代码更改如下,添加小部件工作正常,因为__init__从类实例运行,并可以像普通属性一样与属性进行交互.

我做了以下更改,下拉列表似乎都正常工作:

import kivy
kivy.require('1.7.2') # replace with your current kivy version !

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown

class CustomDropDown(DropDown):
    for i in range(5):
        print i 


class HomeScreen(Screen):
    translateInput = ObjectProperty(None)
    translateButton = ObjectProperty(None)
    translateLabel = ObjectProperty(None)
    top_layout = ObjectProperty(None)
    dd_btn = ObjectProperty(None)

    def __init__(self, *args, **kwargs):
        super(HomeScreen, self).__init__(*args, **kwargs)
        self.drop_down = CustomDropDown()
        #notes_dropdown = ObjectProperty(None)


        dropdown = DropDown()
        notes = ['Features', 'Suggestions', 'Abreviations', 'Miscellaneous']
        for note in notes:
            # when adding widgets, we need to specify the height manually (disabling
            # the size_hint_y) so the dropdown can calculate the area it needs.
            btn = Button(text='%r' % note, size_hint_y=None, height=30)

            # for each button, attach a callback that will call the select() method
            # on the dropdown. We'll pass the text of the button as the data of the
            # selection.
            btn.bind(on_release=lambda btn: dropdown.select(btn.text))

            # then add the button inside the dropdown
            dropdown.add_widget(btn)

        # create a big main button
        mainbutton = Button(text='Usage Notes 2', size_hint=(1, 1))
        print 'yay' 

        # show the dropdown menu when the main button is released
        # note: all the bind() calls pass the instance of the caller (here, the
        # mainbutton instance) as the first argument of the callback (here,
        # dropdown.open.).
        mainbutton.bind(on_release=dropdown.open)
        #dd_btn.bind(on_release=dropdown.open)

        # one last thing, listen for the selection in the dropdown list and
        # assign the data to the button text.
        dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
        #dropdown.bind(on_select=lambda instance, x: setattr(dd_btn, 'text', x))

        self.top_layout.add_widget(mainbutton)

class dropdApp(App):

    def build(self):

        return HomeScreen()



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

KV:

<CustomDropDown>:
    Button:
        text: 'My first Item'
        size_hint_y: None
        height: 44
        on_release: root.select('item1')
    Label:
        text: 'Unselectable item'
        size_hint_y: None
        height: 44
    Button:
        text: 'My second Item'
        size_hint_y: None
        height: 44
        on_release: root.select('item2')

<HomeScreen>:
    id: home_screen
    translateInput: translateInputID
    translateButton: translateButtonID
    translateLabel: labelID
    top_layout: topLayoutID
    #notes_dropdown: notesDropDownID
    dd_btn: btn_ddID 

    orientation: 'vertical'
    FloatLayout: 
        size_hint: 1, .95
        TextInput:
            id: translateInputID
            text: 'cove'
            #text: 'ﻰﺸَﻣ' 
            font_name: "data/fonts/DejaVuSans.ttf"
            background_color: 1, 1, 1, 1
            size_hint: .75, .1
            multiline: False
            pos_hint: {'x': .125, 'y': .45} 
            text_size: self.size
            valign: 'middle'
            halign: 'center'
            padding: 5

        Button:
            id: translateButtonID
            text: 'Translate'
            pos_hint: {'x': .35, 'y': .35}
            size_hint: .3, .08
            valign: 'middle'
            halign: 'center'
            text_size: self.size
            on_release: root.translateButtonPressed()
            #on_press: root.manager.current = 'resultsscreen'

        Label:
            id: labelID
            text: 'Translator'
            text_size: self.size
            valign: 'middle'
            halign: 'center'
            pos_hint: {'x': .3, 'y': .75}
            size_hint: .4, .2
            #font_name: "simpo.ttf"
            #font_name: "5thgradecursive.ttf"
            #font_name: "AGA-Rasheeq-Regular.ttf"
            font_name: "data/fonts/DejaVuSans.ttf"
            font_size: 50

    BoxLayout:
        id: topLayoutID
        #cols: 2
        size_hint: 1, .05
        pos_hint: {'x': 0, 'y': .95}
        Button:
            #id: notesDropDownID
            id: btn_ddID
            text: 'Usage Notes'
            on_release: root.drop_down.open(self)
        Button:
            text: 'About'
点赞