2017-05-31 26 views
0

我正在构建一个带有多个屏幕的Kivy应用程序。其中一个屏幕会建立一个列表,我将传递给其他功能,以便在另一个屏幕上显示结果。一个在我的.py文件的屏幕包含方法get_items():Kivy:AttributeError:'NoneType'对象在访问列表对象时没有属性'bind'

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确实仍然是一个列表,我可以通过其他屏幕将它打印出来并将其长度打印到终端。但是,我不能将它作为Label的文本,即使我将它转换为字符串。例如,

Button: 
    text: str(app.itemlist) 

返回错误'NoneType' object has no attribute '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)) 

上面看到的项目和菜单类在另一个.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) 
+0

尝试在您的应用程序中事先创建一个名为'itemlist'的ListProperty。另外,发布完整的代码,我将更容易以这种方式为您提供帮助。 –

+0

谢谢你愿意帮助我。我发布的代码可能比需要的代码多,但它应该是发现错误所需的一切。总而言之,main.py,我的.kv文件和另一个.py文件定义了main.py文件中使用的'Item'和'Menu'类。该错误发生在.kv文件的最后一行。如您所见,我试图将标签的文本设置为'itemList'的字符串表示形式,该形式在'WhitmansMenu'屏幕中构建,并通过'app.itemList'调用。同样,错误是:'NoneType'对象没有属性'bind'。谢谢! – blakebullwinkel

回答

0

好吧,这似乎工作: https://pastebin.com/A2hEKHLx

当kivy尝试从.kv文件自动获取你的应用程序,它使用App.get_running_app()功能。出于某种原因,它返回无。我在LengthExact课中手动做了一下,看看。我还为itemList添加了默认值[]

+0

对不起,我不明白这是如何解决这个问题的。就像你说的那样,它只是返回一个黑屏。你需要我澄清代码的任何部分? – blakebullwinkel

+0

唉,对不起。我将通过链接到pastebin代码编辑我的答案。它现在似乎工作。我不确定为什么kivy无法从.kv文件正确执行'App.get_running_app()'。 –

+0

谢谢,这解决了这个问题!我接受了答案,但它似乎造成了第二个问题 - 但我认为我们非常接近。出于某种原因,当我点击WhitmansMenu页面上的按钮(这些是切换按钮,btw)时,它们不再停留在“向下”的位置。我在''下的.kv文件中添加了一个print语句,并注意到'app.itemList'实际返回了所需列表的两个副本。 (在'on_enter:'下,我输入'print(app.itemList)')。任何想法出了什么问题?再次感谢。 – blakebullwinkel