Kivy: AttributeError: 'NoneType' object has no attribute 'bind' when accessing list object
Kivy: AttributeError: 'NoneType' object has no attribute 'bind' when accessing list object
我正在构建一个具有多个屏幕的 Kivy 应用程序。其中一个屏幕构建了一个列表,我将把它传递给其他函数,这些函数将在另一个屏幕上显示结果。我的 .py 文件中的屏幕之一包含方法 get_items():
class Menu(Screen):
def get_items(self):
return self._items
self._items 是一个列表,它也会根据用户在此 class 中的操作进行初始化和修改。所有这些都有效,我可以在终端中打印 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)
returns 错误 'NoneType' object has no attribute 'bind'
。如果我将 itemlist 传递给一个 returns 字符串的函数(这最终是我想要做的),我会得到同样的错误。我已经坚持了很长一段时间 - 任何帮助都将不胜感激。
编辑
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 classes 是在另一个 .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
class里面手动做了,你看看。我还为 itemList
添加了默认值 []
。
我正在构建一个具有多个屏幕的 Kivy 应用程序。其中一个屏幕构建了一个列表,我将把它传递给其他函数,这些函数将在另一个屏幕上显示结果。我的 .py 文件中的屏幕之一包含方法 get_items():
class Menu(Screen):
def get_items(self):
return self._items
self._items 是一个列表,它也会根据用户在此 class 中的操作进行初始化和修改。所有这些都有效,我可以在终端中打印 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)
returns 错误 'NoneType' object has no attribute 'bind'
。如果我将 itemlist 传递给一个 returns 字符串的函数(这最终是我想要做的),我会得到同样的错误。我已经坚持了很长一段时间 - 任何帮助都将不胜感激。
编辑
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 classes 是在另一个 .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
class里面手动做了,你看看。我还为 itemList
添加了默认值 []
。