python - Kivy:访问列表对象时出现AttributeError: 'NoneType'对象没有属性 'bind'

标签 python python-3.x kivy kivy-language

我正在构建一个具有多个屏幕的 Kivy 应用程序。其中一个屏幕构建了一个列表,我将其传递给其他函数,这些函数将在另一个屏幕上显示结果。我的 .py 文件中的屏幕之一包含方法 get_items():

class Menu(Screen):
    def get_items(self):
        return self._items

self._items 是一个列表,它也根据此类中的用户操作进行初始化和修改。所有这些都有效,我可以在终端中打印 self._items 。但是,我需要做的是将这个列表传递到另一个屏幕,我通过将 self._items 转换为可在应用程序中的任何位置访问的列表来实现此目的(我在菜单屏幕上有一个按钮,按下时可以执行此操作):

Button:
    on_press:
        app.itemlist = root.get_items()

所以我认为发生的事情是我通过函数 get_items() 将列表 self._items 分配给对象 app.itemlist 。我已经验证 app.itemlist 确实仍然是一个列表,我可以通过其他屏幕将它及其长度打印到终端。但是,例如,即使我将其转换为字符串,我也无法将其设为标签的文本。例如,

Button:
    text: str(app.itemlist)

返回错误“NoneType”对象没有属性“bind”。如果我将 itemlist 传递给返回字符串的函数(这最终是我想要做的),我会得到相同的错误。我已经在这个问题上坚持了一段时间了 - 任何帮助都将不胜感激。

编辑

main.py:

import kivy
kivy.require('1.10.0')

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen
from snartoolsmod import *

Builder.load_file("snartools.kv")

class DiningButton(Button):
    pass

class DirectionButton(Button):
    pass

class ItemButton(Button):
    pass

class SubmenuButton(Button):
    pass

class HomeScreen(Screen):
    pass

class WhitmansMenu(Screen):

    def initialize_list(self):
        itemList = Menu('whitmans')
        global allItems
        allItems = itemList.getCopy()
        return itemList

    def modify_list(self, itemList, value, dish):
        if value == 'down':
            for item in itemList:
                if dish == item.getDish():
                    itemList.remove(item)
                    break
        else:
            for item in allItems:
                if dish == item.getDish():
                    itemList.append(item)
                    break

    def modify_state(self, value, submenu, length):
        if value == 'down':
            for i in range(1, length+1):
                button = self.ids[str(submenu)+'_item'+str(i)]
                button.state = 'down'
        else:
            for i in range(1, length+1):
                button = self.ids[str(submenu)+'_item'+str(i)]
                button.state = 'normal'

class ToolScreen(Screen):
    pass

class ToolButton(Button):
    pass

class InstructionsLabel(Label):
    pass

class SubLabel(Label):
    pass

class LengthExact(Screen):

    def get_list(self, itemList):
        return itemList

class LengthRange(Screen):
    pass

class PriceExact(Screen):
    pass

class PriceRange(Screen):
    pass

class MoreExact(Screen):
    pass

class MoreRange(Screen):
    pass

screen_manager = ScreenManager()
screen_manager.add_widget(HomeScreen(name="home_screen"))
screen_manager.add_widget(WhitmansMenu(name="whitmans_menu"))
screen_manager.add_widget(ToolScreen(name="tool_screen"))
screen_manager.add_widget(LengthExact(name="length_exact"))
screen_manager.add_widget(LengthRange(name="length_range"))
screen_manager.add_widget(PriceExact(name="price_exact"))
screen_manager.add_widget(PriceRange(name="price_range"))
screen_manager.add_widget(MoreExact(name="more_exact"))
screen_manager.add_widget(MoreRange(name="more_range"))

class SnartoolsApp(App):

    def build(self):
        return screen_manager

app = SnartoolsApp()
app.run()

.kv 文件(只是基本部分 - 它很长):

<WhitmansMenu>:
    on_enter: app.itemList = root.initialize_list()
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size
    FloatLayout:
        DirectionButton:
            text: "Back"
            pos_hint: {'left': 1, 'top': 1}
            on_press:
                root.manager.transition.duration = 0
                root.manager.current = "home_screen"
        DirectionButton:
            text: "Done"
            pos_hint: {'right': 1, 'top': 1}
            on_press:
                root.manager.transition.duration = 0
                root.manager.current = "tool_screen"
    BoxLayout:
        orientation: "vertical"
        pos_hint: {'top': 0.86}
        InstructionsLabel:
            text: "Select all menus and items to exclude from your order"
        ScrollView:
            GridLayout:
                cols: 1
                padding: 20
                spacing: 5
                size_hint_y: None
                height: self.minimum_height
                SubmenuButton:
                    id: fryer
                    text: 'Fryer'
                    on_state: root.modify_state(fryer.state, 'fryer', 7)
                ItemButton:
                    id: fryer_item1
                    text: 'Fried Green Beans'
                    on_state: root.modify_list(app.itemList, fryer_item1.state, 'Fried Green Beans')
                # A bunch more buttons below...
<LengthExact>:
    canvas.before:
        Color:
            rgba: 1, 1, 1, 1
        Rectangle:
            pos: self.pos
            size: self.size
    FloatLayout:
        DirectionButton:
            text: "Back"
            pos_hint: {'left': 1, 'top': 1}
            on_press:
                root.manager.transition.duration = 0
                root.manager.current = "tool_screen"
    GridLayout:
        cols: 1
        pos_hint: {'top': 0.86}
        BoxLayout:
            orientation: "vertical"
            InstructionsLabel:
                text: "Enter the number of items you want to order"
            SubLabel:
                text: root.get_list(str(app.itemList))

上面看到的 Item 和 Menu 类是在另一个 .py 文件中定义的:

class Item(object):
    __slots__ = ["_canteen", "_submenu", "_dish", "_price"]

    def __init__(self, canteen, submenu, dish, price):
        self._canteen = canteen
        self._submenu = submenu
        self._dish = dish
        self._price = float(price)

    def getCanteen(self):
        return self._canteen

    def getSubmenu(self):
        return self._submenu

    def getDish(self):
        return self._dish

    def getPrice(self):
        return self._price

    def __len__(self):
        return 1

    def __eq__(self, other):
        return (self._dish == other._dish) and \
               (self._price == other._price)

    def __repr__(self):
        return "Item({0},{1},{2},{3})".format(self._canteen, self._submenu, self._dish, self._price)

    def __str__(self):
        return "<{0},{1},{2},{3}>".format(self._canteen, self._submenu, self._dish, self._price)


class Menu(object):
    __slots__ = ["_canteen", "_result"]

    def __init__(self, canteen):
        self._canteen = canteen
        self._result = []
        with open(self._canteen + '.csv', 'r') as f:
            csvr = csv.reader(f)
            for row in csvr:
                item = Item(row[0], row[1], row[2], row[3])
                self._result.append(item)

    def getCanteen(self):
        return self._canteen

    def getSubmenus(self):
        submenus = []
        for item in self._result:
            if item._submenu not in submenus:
                submenus.append(item._submenu)
        return submenus

    def getDishes(self):
        dishes = []
        for item in self._result:
            dishes.append(item._dish)
        return dishes

    def getPrices(self):
        prices = []
        for item in self._result:
            if item._price not in prices:
                prices.append(item._price)
        return prices

    def specifySubmenus(self, submenus):
        items = []
        for item in self._result:
            if item._submenu in submenus:
                items.append(item)
        self._result = items

    def sortMenu(self):
        return sorted(self._result, key=lambda x: x._price)

    def longestOrder(self, price):
        sortedMenu = self.sortMenu()
        minPrice = sortedMenu[0]._price
        return int(price / minPrice)

    def shortestOrder(self, price):
        sortedMenu = self.sortMenu()
        maxPrice = sortedMenu[-1]._price
        return int(price / maxPrice)

    def longOrder(self, price, sortedMenu=None):
        if sortedMenu == None:
            sortedMenu = self.sortMenu()
        sum = 0
        order = []
        for item in sortedMenu:
            if sum + item._price <= price:
                order.append(item._dish)
                sum += item._price
        return order

    def lengthExact(self, length):
        orders = []
        maxLen = len(self.longOrder(7))
        assert length == int(length) and 0 < length <= maxLen
        sortedMenu = self.sortMenu()
        maxPrice = 7 - (length - 1) * sortedMenu[0]._price
        for item in sortedMenu:
            if item._price > maxPrice:
                sortedMenu.remove(item)
        allCombos = list(itertools.combinations_with_replacement(sortedMenu, length))
        for combo in allCombos:
            sum = 0
            for item in combo:
                sum += item._price
            if sum <= 7:
                orders.append(combo)
        return orders

    def lengthRange(self, lower, upper):
        allCombos = []
        orders = []
        maxLen = self.longestOrder(7)
        assert lower == int(lower) and upper == int(upper) and 0 < lower < upper <= maxLen
        sortedMenu = self.sortMenu()
        for i in range(lower, upper + 1):
            maxPrice = 7 - (i - 1) * sortedMenu[0]._price
            for item in sortedMenu:
                if item._price > maxPrice:
                    sortedMenu.remove(item)
            allCombos += list(itertools.combinations_with_replacement(sortedMenu, i))
        for combo in allCombos:
            sum = 0
            for item in combo:
                sum += item._price
            if sum <= 7:
                orders.append(combo)
        return orders

    def priceExact(self, price):
        allCombos = []
        orders = []
        assert 0 < price <= 7
        sortedMenu = self.sortMenu()
        cheapestPrice = sortedMenu[0]._price
        maxLen = self.longestOrder(price)
        minLen = self.shortestOrder(price)
        for i in range(minLen, maxLen + 1):
            if i == 1:
                for item in self._result:
                    if item._price == price:
                        sortedMenu.remove(item)
            else:
                maxPrice = price - (i - 1) * cheapestPrice
                for item in sortedMenu:
                    if item._price > maxPrice:
                        sortedMenu.remove(item)
                allCombos += list(itertools.combinations_with_replacement(sortedMenu, i))
        for combo in allCombos:
            sum = 0
            for item in combo:
                sum += item._price
            if sum == price:
                orders.append(combo)
        return orders

    def priceRange(self, lower, upper):
        allCombos = []
        orders = []
        assert 0 < lower <= upper <= 7
        sortedMenu = self.sortMenu()
        cheapestPrice = sortedMenu[0]._price
        maxLen = self.longestOrder(upper)
        minLen = self.shortestOrder(upper)
        for i in range(minLen, maxLen + 1):
            if i == 1:
                for item in self._result:
                    if item._price >= lower and item._price <= upper:
                        orders.append(item)
            else:
                maxPrice = upper - (i - 1) * cheapestPrice
                for item in sortedMenu:
                    if item._price > maxPrice:
                        sortedMenu.remove(item)
                allCombos += list(itertools.combinations_with_replacement(sortedMenu, i))
        for combo in allCombos:
            sum = 0
            for item in combo:
                sum += item._price
            if sum >= lower and sum <= upper:
                orders.append(combo)
        return orders

    def moreExact(self, order, price):
        orderPrice = 0
        for item in self._result:
            if item._dish in order:
                orderPrice += item._price
        assert orderPrice <= price <= 7
        remaining = price - orderPrice
        return self.priceExact(remaining)

    def moreRange(self, order, lower, upper):
        orderPrice = 0
        for item in self._result:
            if item._dish in order:
                orderPrice += item._price
        assert lower < upper <= 7 and orderPrice < upper
        low = lower - orderPrice
        high = upper - orderPrice
        return self.priceRange(low, high)

    def append(self, value):
        return self._result.append(value)

    def remove(self, value):
        return self._result.remove(value)

    def getCopy(self):
        return self._result.copy()

    def __iter__(self):
        return iter(self._result)

    def __len__(self):
        return len(self._result)

    def __repr__(self):
        return "Menu({})".format(self._result)

    def __str__(self):
        return "{}".format(self._result)

最佳答案

好的,这似乎有效: https://pastebin.com/A2hEKHLx

当 kivy 尝试自动从 .kv 文件获取您的应用程序时,它会使用 App.get_running_app() 函数。由于某种原因,它返回 None。我是在LengthExact类中手动完成的,你看一下。我还为 itemList 添加了默认值 []

关于python - Kivy:访问列表对象时出现AttributeError: 'NoneType'对象没有属性 'bind',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44275919/

相关文章:

python - 在使用 Scrapy 进行身份验证的同时抓取 LinkedIn

Python 3 & Kivy : React only to double tap "not" single tap

python - 使用 oAuth 的网站的 redirect_uri

python - tensorflow-gpu 库是否自动在 GPU 上运行 tensorflow 代码(非 GPU)?

python - 凯撒密码 仅加密一个字母而不是整个字符串

python - 如果我取消注释 for 循环中的 print() 函数, 'groups' 列表将填充空列表而不是结果

python - 带有圆角和阴影 Kivy 的图像

python - Tensorflow:如何在多类分类中使用 tf.keras.metrics?

javascript - 使用 django channel 将 webRTC 视频流发送到服务器

python - 使用 lambda 在回调中设置属性