AttributeError:'NoneType'对象没有属性'get_width'

时间:2013-05-25 04:35:02

标签: python-3.x pygame ascii-art

这是来自图像的Ascii art generator的简单脚本,我收到此错误: 我在cmd行中运行它,我使用的是Windows 7操作系统

Traceback (most recent call last):
File "C:\Python33\mbwiga.py", line 251, in <module>
converter.convertImage(sys.argv[-1])
File "C:\Python33\mbwiga.py", line 228, in convertImage
self.getBlobs()
File "C:\Python33\mbwiga.py", line 190, in getBlobs
width, height = self.cat.get_width(), self.cat.get_height()
AttributeError: 'NoneType' object has no attribute 'get_width'
我在这里搞砸了什么.. ??有人可以帮忙..?

以下是有人提出的完整源代码:

import sys
import pygame

NAME = sys.argv[0]
VERSION = "0.1.0" # The current version number.

HELP = """ {0} : An ASCII art generator. Version {1}
Usage:
{0} [-b BLOB_SIZE] [-p FONT_WIDTH:HEIGHT] [-c] image_filename

Commands:
-b | --blob            Change the blob size used for grouping pixels. This is the width of the blob; the height is calculated by multiplying the blob size by the aspect ratio.
-p | --pixel-aspect    Change the font character aspect ratio. By default this is 11:5, which seems to look nice. Change it based on the size of your font. Argument is specified in the format "WIDTH:HEIGHT". The colon is important.
-c | --colour          Use colour codes in the output. {0} uses VT100 codes by default, limiting it to 8 colours, but this might be changed later.

-h | --help            Shows this help.""""


.format(NAME, VERSION)

NO_IMAGE = \
""" Usage: %s [-b BLOB_SIZE] [-p FONT_WIDTH:HEIGHT] image_filename """ % (NAME)

import math

CAN_HAS_PYGAME = False
try:
import pygame
except ImportError:
sys.stderr.write("Can't use Pygame's image handling! Unable to proceed, sorry D:\n")
exit(-1)

VT100_COLOURS = {"000": "[0;30;40m",
             "001": "[0;30;41m",
             "010": "[0;30;42m",
             "011": "[0;30;43m",
             "100": "[0;30;44m",
             "101": "[0;30;45m",
             "110": "[0;30;46m",
             "111": "[0;30;47m",
             "blank": "[0m"}

VT100_COLOURS_I = {"000": "[0;40;30m",
               "001": "[0;40;31m",
               "010": "[0;40;32m",
               "011": "[0;40;33m",
               "100": "[0;40;34m",
               "101": "[0;40;35m",
               "110": "[0;40;36m",
               "111": "[0;40;37m",
               "blank": "[0m"}

# Convenient debug function.
DO_DEBUG = True
def debug(*args):
if not DO_DEBUG: return # Abort early, (but not often).
strrep = ""
for ii in args:
    strrep += str(ii)
sys.stderr.write(strrep + "\n") # Write it to stderr. Niiicce.

# System init.
def init():
""" Start the necessary subsystems. """
pygame.init() # This is the only one at the moment...

# Get a section of the surface.
def getSubsurface(surf, x, y, w, h):
try:
    return surf.subsurface(pygame.Rect(x, y, w, h))
except ValueError as er:
    return getSubsurface(surf, x, y, w - 2, h - 2)

# The main class.
class AAGen:
""" A class to turn pictures into ASCII "art". """
def  __init__(self):
    """ Set things up for a default conversion. """

    # Various blob settings.
    self.aspectRatio = 11.0 / 5.0 # The default on my terminal.
    self.blobW = 12 # The width. Also, the baseline for aspect ratio.
    self.blobH = self.aspectRatio * self.blobW # The height.

    self.blobList = []
    self.cat = None # The currently open file.
    self.chars = """#@%H(ks+i,. """ # The characters to use.

    self.colour = False # Do we use colour?

def processArgs(self):
    """ Process the command line arguments, and remove any pertinent ones. """
    cc = 0
    for ii in sys.argv[1:]:
        cc += 1

        if ii == "-b" or ii == "--blob":
            self.setBlob(int(sys.argv[cc + 1]))

        elif ii == "-p" or ii == "--pixel-aspect":
            jj = sys.argv[cc + 1]
            self.setAspect(float(jj.split(":")[1]) / float(jj.split(":")[0]))
        elif ii == "-c" or ii == "--colour":
            self.colour = True

        elif ii == "-h" or ii == "--help":
            print(HELP)
            exit(0)

    if len(sys.argv) == 1:
        print(NO_IMAGE)
        exit(0)

def setBlob(self, blobW):
    """ Set the blob size. """
    self.blobW = blobW
    self.blobH = int(math.ceil(self.aspectRatio * self.blobW))

def setAspect(self, aspect):
    """ Set the aspect ratio. Also adjust the blob height. """
    self.aspectRatio = aspect
    self.blobH = int(math.ceil(self.blobW * self.aspectRatio))

def loadImg(self, fname):
    """ Loads an image into the store. """
    try:
        tmpSurf = pygame.image.load(fname)
    except:
        print("Either this is an unsupported format, or we had problems loading the file.")
        return None
    self.cat = tmpSurf.convert(32)

    if self.cat == None:
        sys.stderr.write("Problem loading the image %s. Can't convert it!\n"
                         % fname)
        return None

def makeBlob(self, section):
    """ Blob a section into a single ASCII character."""
    pxArr = pygame.surfarray.pixels3d(section)
    colour = [0, 0, 0]
    size = 0 # The number of pixels.

    # Get the density/colours.
    for i in pxArr:
        for j in i:
            size += 1
            # Add to the colour.
            colour[0] += j[0]
            colour[1] += j[1]
            colour[2] += j[2]

    # Get just the greyscale.
    grey = apply(lambda x, y, z: (x + y + z) / 3 / size,
                 colour)

    if self.colour:
        # Get the 3 bit colour.
        threshold = 128
        nearest = ""
        nearest += "1" if (colour[0] / size > threshold) else "0"
        nearest += "1" if (colour[1] / size > threshold) else "0"
        nearest += "1" if (colour[2] / size > threshold) else "0"

        return VT100_COLOURS[nearest], grey

    return grey

# We just use a nasty mean function to find the average value.
#        total = 0
#        for pix in pxArr.flat:
#            total += pix # flat is the array as a single-dimension one.

#        return total / pxArr.size # This is a bad way to do it, it loses huge amounts of precision with large blob size. However, with ASCII art...

  def getBlobs(self):
    """ Get a list of blob locations. """
    self.blobList = [] # Null it out.
    width, height = self.cat.get_width(), self.cat.get_height()

    # If the image is the wrong size for blobs, add extra space.
    if height % self.blobH != 0 or width % self.blobW != 0:
        oldimg = self.cat
        newW = width - (width % self.blobW) + self.blobW
        newH = height - (height % self.blobH) + self.blobH
        self.cat = pygame.Surface((newW, newH))

        self.cat.fill((255, 255, 255))
        self.cat.blit(oldimg, pygame.Rect(0, 0, newW, newH))

    # Loop over subsections.
    for row in range(0, height, int(self.blobH)):
        rowItem = []
        for column in range(0, width, self.blobW):
            # Construct a Rect to use.
            src = pygame.Rect(column, row, self.blobW, self.blobH)
            # Now, append the reference.
            rowItem.append(self.cat.subsurface(src))
        self.blobList.append(rowItem)

    return self.blobList

def getCharacter(self, value, colour = False):
    """ Get the correct character for a pixel value. """
    col = value[0] if colour else ""
    value = value[1] if colour else value
    if not 0 <= value <= 256:
        sys.stderr.write("Incorrect pixel data provided! (given %d)\n"
                         % value)
        return "E"
    char = self.chars[int(math.ceil(value / len(self.chars))) % len(self.chars)]
    return char + col

def convertImage(self, fname):
    """ Convert an image, and print it. """
    self.loadImg(fname)
    self.getBlobs()

    pval = "" # The output value.
    # Loop and add characters.
    for ii in converter.blobList:
        for jj in ii:
            ch = self.makeBlob(jj)
            pval += self.getCharacter(ch, self.colour) # Get the character.
        # Reset the colour at the end of the line.
        if self.colour: pval += VT100_COLOURS["blank"]

        pval += "\n" # Split it up by line.
    pval = pval[:-1] # Cut out the final newline.

    print(pval) # Print it.

# Main program execution.
if __name__ == "__main__":
init()

converter = AAGen()

converter.processArgs()
converter.convertImage(sys.argv[-1])

sys.exit(1)

1 个答案:

答案 0 :(得分:0)

问题隐藏在loadImg的某处。该错误表明self.catNone。 {97}在第97行初始化时self.cat可能会获得None,或者tmpSurf.convert(32)的结果已分配None,并且该调用的结果为Either this is an unsupported format...。在第一种情况下,您应该看到消息Problem loading the image...,在稍后的情况下,当您针对self.cat测试None时,您应该看到消息def loadImg(self, fname): """ Loads an image into the store. """ try: tmpSurf = pygame.image.load(fname) except: print("Either this is an unsupported format, or we had problems loading the file.") return None self.cat = tmpSurf.convert(32) if self.cat == None: sys.stderr.write("Problem loading the image %s. Can't convert it!\n" % fname) return None

return None

顺便说一下,return与没有参数的return None完全相同。此外,可以完全删除最后一个None,因为任何函数在到达正文末尾时都会隐式返回None

为了测试is,建议使用if self.cat is None:运算符,即# Main program execution. if __name__ == "__main__": init() # pygame is initialized here converter = AAGen() # you need the converter object converter.processArgs() # the command-line arguments are # converted to the object attributes converter.convertImage(sys.argv[-1]) # here the conversion happens sys.exit(1) # this is unneccessary for the conversion

根据5月31日的评论

更新

如果你想更进一步,你应该学习一点Python。看一下原始脚本的结尾(缩进修复):

mbwiga.py

如果原始脚本保存在if __name__ == "__main__":中,则可以将其作为脚本调用,也可以将其用作模块。在后一种情况下,test.py下方的主体不会被执行,您必须自己在调用者脚本中执行此操作。假设你有mbwiga试图这样做。假设它位于同一目录中。它必须导入mbwiga.。然后import mbwiga mbwiga.init() # pygame is initialized here converter = mbwiga.AAGen() # you need the converter object # Now the converter is your own object name. It does not take the mbwiga. prefix. # The original converter.processArgs() took the argumens from the command-line # when mbwiga.py was called as a script. If you want to use some of the arguments # you can set the converter object's attributes the way that is shown # in the .processArgs() method definition. Or you can call it the same way to # extract the information from the command line passed when you called the test.py # converter.processArgs() # Now the conversion converter.convertImage('myImageFilename.xxx') # here the conversion happens 成为模块内部功能的前缀。您的代码可能如下所示:

{{1}}