在urwid中使用ListBox时出现'AttributeError'

时间:2013-07-31 18:50:18

标签: python user-interface python-2.7 console-application urwid

代码,使用urwid库,下面应该在控制台屏幕上显示yml文件的内容(如给定的here)(根据Application Form布局:

代码是:

import sys
sys.path.append('./lib')
import os
from pprint import pprint
import random
import urwid
ui = urwid.raw_display.Screen()
pile = urwid.Pile([])
from collections import OrderedDict
import yaml_ordered_dict
import collections
import yaml

import itertools

import copy

class FormDisplay(object):

    def __init__(self):
        global ui
        #self.ui = urwid.raw_display.Screen()
        self.ui = ui
        self.palette = self.ui.register_palette([
            ('Field', 'dark green, bold', 'black'), # information fields, Search: etc.
            ('Info', 'dark green', 'black'), # information in fields
            ('Bg', 'black', 'black'), # screen background
            ('InfoFooterText', 'white', 'dark blue'), # footer text
            ('InfoFooterHotkey', 'dark cyan, bold', 'dark blue'), # hotkeys in footer text
            ('InfoFooter', 'black', 'dark blue'),  # footer background
            ('InfoHeaderText', 'white, bold', 'dark blue'), # header text
            ('InfoHeader', 'black', 'dark blue'), # header background
            ('BigText', RandomColor(), 'black'), # main menu banner text
            ('GeneralInfo', 'brown', 'black'), # main menu text
            ('LastModifiedField', 'dark cyan, bold', 'black'), # Last modified:
            ('LastModifiedDate', 'dark cyan', 'black'), # info in Last modified:
            ('PopupMessageText', 'black', 'dark cyan'), # popup message text
            ('PopupMessageBg', 'black', 'dark cyan'), # popup message background
            ('SearchBoxHeaderText', 'light gray, bold', 'dark cyan'), # field names in the search box
            ('SearchBoxHeaderBg', 'black', 'dark cyan'), # field name background in the search box
            ('OnFocusBg', 'white', 'dark magenta') # background when a widget is focused
           ])
    urwid.set_encoding('utf8')

    def main(self):
        global ui
        #self.view = ui.run_wrapper(formLayout)
        #urwid initialisation first or yml readin first. i chose yml reading
        yOrDict = yaml_ordered_dict.load('ApplyForm.yml')
        yOrDictLevel1 = yOrDict.get('ApplyForm')
        self.ui.start()
        self.view = formLayout(yOrDictLevel1)

        self.loop = urwid.MainLoop(self.view, self.palette)
        self.loop.widget = self.view
        self.loop.run()

def find_value(needle, container):
    # Already found the object. Return.
    if isinstance(container, basestring) and needle in container:
        return True

    values = None
    if isinstance(container, dict):
        values = container.values()
    elif hasattr(container, '__iter__'):
        values = container.__iter__()

    if values is None:
        return False

    # Check deeper in the container, if needed.
    for val in values:
        if find_value(needle, val):
            return True

    # No match found.
    return False

def getChildMap(tmpMap):
  tMap = tmpMap
  tmpLen = len(tMap)
  listOfKeys =  tMap.keys()
  topKey = listOfKeys[0]
  #print('topkey', topKey)
  mapObtained = tMap.get(topKey)

  #print('getChildMap',mapObtained)
  return mapObtained

def getMapForKeyIndex(tmpMap,index):
  listOfKeysForMap =  tmpMap.keys()
  i = index 
  mapObtained = collections.OrderedDict(itertools.islice(tmpMap.items(), i, i+1))
  #print(type(mapObtained))
  #print("getMapForKeyIndex: key and map",i,mapObtained)
  return mapObtained

def checkValOfKey(key,tmpMap):
    tMap = tmpMap

    for k, v in tMap.items():
        if k == key :
            tVal = tMap[key]

    #print('checkValOfKey:tVal',tVal)
    return tVal

def removeTopItem(tmpMap):
    tMap = tmpMap
    listOfKeys = tMap.keys()
    topKey = listOfKeys[0]
    del tMap[topKey]
    return tMap

def createBlock(mapToWorkWith) :
    global pile
    #pile = urwid.Pile([])


    tmapToWorkWith = copy.deepcopy(mapToWorkWith)
    tmapToWorkWith = getChildMap(tmapToWorkWith)
    #print('createBlock: tmapToWorkWith', tmapToWorkWith)
    reducedMapToCarry =  copy.deepcopy(tmapToWorkWith)

    listOfKeys =  tmapToWorkWith.keys()

    #if blockPresentAsValueInMap(tmapToWorkWith) == True :
    if checkValOfKey('$type',tmapToWorkWith) == 'Block' :
        #create a block nd pile
        print('XXXXXXXXcreate block and pile listXXXXXXXX')
        #pile = urwid.Pile([])
        keytoBeginPile = 2
        #if displayPresentAsKeyInMap(tmapToWorkWith) == True :
        #if checkContainmentOfKeyInMap('$display',(tmapToWorkWith)) :
        reducedMapToCarry = removeTopItem(reducedMapToCarry)
        reducedMapToCarry = removeTopItem(reducedMapToCarry)

        if (tmapToWorkWith.has_key('$display')) == True :
            print('display value ',tmapToWorkWith['$display'])
            displayText = tmapToWorkWith['$display']
            text = urwid.Text(('GeneralInfo', displayText),
                        align='center')
            pile.contents.append(text)


            keytoBeginPile +=1
            #addDisplayToPile
            reducedMapToCarry = removeTopItem(reducedMapToCarry)


        for k in range(keytoBeginPile,len(tmapToWorkWith)) :
            #//get the value of key at indexkeytoBeginPile in tmapToWorkWith
            tmpMap = tmapToWorkWith.get(listOfKeys[k])
            if  checkValOfKey('$type',tmpMap) != 'Block' :
                print('##add to Pile##')
                print('display value ',tmpMap['$display'])
                displayText = tmpMap['$display']
                text = urwid.Text(('GeneralInfo', displayText),
                        align='center')
                #pile.contents.append(text,options='pack')
                pile.append(text)
                keytoBeginPile +=1
                reducedMapToCarry = removeTopItem(reducedMapToCarry)
                #print('for Loop traversal: map', reducedMapToCarry)
                #mapToPass(pop)
                continue
            else :
                createBlock(reducedMapToCarry)

        #return  urwid.LineBox(urwid.AttrWrap(urwid.Overlay(urwid.LineBox(urwid.Padding(
         #       pile, align='center', left=3, right=3)), Banner, 'center', 150, 'middle', None),
          #      'GeneralInfo'))

def formLayout(topMap):
        global ui
        global pile

        f3 = open('OrderedMapInFinalApp.yaml',"w")
        newMap = topMap #check if needed at all
        yaml.dump(newMap, f3)


        content = urwid.SimpleListWalker([])
        listbox = urwid.ListBox(content)

        #orderedMap has come
        #read the key value and start for loop
        print('lenght of map',len(newMap))
        for i in range(len(newMap)):
            mapToWorkWith = getMapForKeyIndex(newMap,i)
            #print('for loop i nd mapToWorkWith Is',i,mapToWorkWith)

            if i == 0 :
                if (checkValOfKey('$type',mapToWorkWith) == 'Block') :
                    print('1st key read')
                    continue

            else :
                if (mapToWorkWith.has_key('TITLE') == True):
                    print('2nd key read')
                    continue

                else :
                    pile = ([])
                    createBlock(mapToWorkWith) 
                    #content.append(addBlockToOverallLayout())
                    content.append(pile)
                    continue
        fill = urwid.Filler(listbox)

        frame = urwid.Frame(fill,header=urwid.Pile([textT,textSH]),footer=textF)

        dim = ui.get_cols_rows()
        #ui is treated as global handle for all functions, either belonging
        #to any class or standalone functions such as formLayout
        #need to check if screen has been started
        if not ui._started:
            print("Screen has not been started, so no use of rendering.Thus return :-( ")
            return
        #ui.draw_screen(dim, listbox.render(dim, True))
        return frame
        #return listbox

def RandomColor():
    '''Pick a random color for the main menu text'''
    listOfColors = ['dark red', 'dark green', 'brown', 'dark blue',
                    'dark magenta', 'dark cyan', 'light gray',
                    'dark gray', 'light red', 'light green', 'yellow',
                    'light blue', 'light magenta', 'light cyan', 'default']
    color = listOfColors[random.randint(0, 14)]
    return color

def main():
    #global ui
    form = FormDisplay()
    form.main()

########################################
##### MAIN ENTRY POINT
########################################
if __name__ == '__main__':
    main()

#

代码使用ui来表示控制台屏幕。 我使用了一个全局列表框api,它将包含来自yml文件的值,其值为$ display。代码未能给出以下错误:

Traceback (most recent call last):  
  File "./yamlUrwidUIPhase8FrameFinalAppC.py", line 308, in <module>  
    main()  
  File "./yamlUrwidUIPhase8FrameFinalAppC.py", line 302, in main  
    form.main()  
  File "./yamlUrwidUIPhase8FrameFinalAppC.py", line 61, in main  
    self.loop.run()  
  File "/home/gehna/urwidWorkInProgress/urwid/main_loop.py", line 272, in run  
    self.screen.run_wrapper(self._run)  
  File "/home/gehna/urwidWorkInProgress/urwid/raw_display.py", line 242, in        run_wrapper  
    return fn()  
  File "/home/gehna/urwidWorkInProgress/urwid/main_loop.py", line 312, in _run  
    self.draw_screen()  
  File "/home/gehna/urwidWorkInProgress/urwid/main_loop.py", line 563, in draw_screen  
    canvas = self._topmost_widget.render(self.screen_size, focus=True)  
  File "/home/gehna/urwidWorkInProgress/urwid/widget.py", line 141, in cached_render  
    canv = fn(self, size, focus=focus)  
  File "/home/gehna/urwidWorkInProgress/urwid/container.py", line 1058, in render  
    focus and self.focus_part == 'body')  
  File "/home/gehna/urwidWorkInProgress/urwid/widget.py", line 141, in cached_render  
    canv = fn(self, size, focus=focus)  
  File "/home/gehna/urwidWorkInProgress/urwid/decoration.py", line 811, in render  
    top, bottom = self.filler_values(size, focus)  
  File "/home/gehna/urwidWorkInProgress/urwid/decoration.py", line 796, in  filler_values  
    height = self._original_widget.rows((maxcol,),focus=focus)  
AttributeError: 'ListBox' object has no attribute 'rows'  

我用ddd和pydb调试器检查了代码的解析部分,解析yml部分似乎没问题,print语句正确地给出了$ display的值。我使用ui的方式有问题吗?我应该使用draw_screen吗?

2 个答案:

答案 0 :(得分:1)

奇怪,您的代码应该按原样运行。我在创建Frames并给出错误的参数顺序时遇到了同样的错误,所以我最好的猜测是改变这一行

   frame = urwid.Frame(fill,header=urwid.Pile([textT,textSH]),footer=textF)

   frame = urwid.Frame(fill,urwid.Pile([textT,textSH]))

   frame = urwid.Frame(header=urwid.Pile([textT,textSH]),fill)

暂时丢弃页脚,直到你在Frame内找到正确的参数顺序然后将其放回去。

答案 1 :(得分:1)

我在尝试将QuestionBox tutorial example放入ListBox时遇到了同样的错误。这answer为我解决了这个问题。

import urwid

def exit_on_q(key):
    if key in ('q', 'Q'):
        raise urwid.ExitMainLoop()

class QuestionBox(urwid.Filler):
    def keypress(self, size, key):
        if key != 'enter':
            return super(QuestionBox, self).keypress(size, key)
        self.original_widget = urwid.Text(
            u"Nice to meet you,\n%s.\n\nPress Q to exit." %
            edit.edit_text)

edit = urwid.Edit(u"What is your name?\n")
fill = urwid.BoxAdapter(QuestionBox(edit), height=2)
lb = urwid.ListBox([fill])
loop = urwid.MainLoop(lb, unhandled_input=exit_on_q)
loop.run()