python对象AttributeError:类型对象'Track'没有属性'title'

时间:2011-02-22 14:39:07

标签: python wxpython objectlistview

如果这是一个菜鸟问题,我道歉,但我似乎无法想出这个问题。

我定义了一个定义音乐曲目的对象 (注意:最初只有ATTRIBUTE和self.ATTRIBUTE。我编辑了这些值以帮助消除混淆。它们对问题没有影响)

class Track(object):
  def __init__(self, title, artist, album, source, dest):
    """
    Model of the Track Object

    Contains the followign attributes:
    'Title', 'Artist', 'Album', 'Source', 'Dest'
    """
    self.atrTitle = title
    self.atrArtist = artist
    self.atrAlbum = album
    self.atrSource = source
    self.atrDest = dest

我使用ObjectListView创建特定目录中的曲目列表

....other code....
self.aTrack = [Track(sTitle,sArtist,sAlbum,sSource, sDestDir)]
self.TrackOlv.AddObjects(self.aTrack)
....other code....

现在我想迭代列表并打印出每个项目的单个值

list = self.TrackOlv.GetObjects()

for item in list:
    print item.atrTitle

此操作失败并显示错误

AttributeError: type object 'Track' has no attribute 'atrTitle'

让我感到困惑的是,如果我在对象列表视图显示中突出显示单个项目并使用以下代码,它将正确打印出突出显示项目的单个值

list = self.TrackOlv.GetSelectedObject()
print list.atrTitle

编辑:每个请求的完整来源。要查看错误,请浏览到源目录w / .mp3文件,然后单击打印按钮。

#Boa:Frame:Frame1

import wx
import os
import glob
import shutil
import datetime
from mutagen.mp3 import MP3
from mutagen.easyid3 import EasyID3
import mutagen.id3
import unicodedata

from ObjectListView import ObjectListView, ColumnDefn



########################################################################
class Track(object):
    def __init__(self, title, artist, album, source, dest):
        """
        Model of the Track Object

        Contains the followign attributes:
        'Title', 'Artist', 'Album', 'Source', 'Dest'
        """
        self.atrTitle = title
        self.atrArtist = artist
        self.atrAlbum = album
        self.atrSource = source
        self.atrDest = dest       

class Action(object):
    def __init__(self, timestamp, action, result):
        self.timestamp = timestamp
        self.action = action
        self.result = result

########################################################################
# Non GUI
########################################################################

def selectFolder(sMessage):
    print "Select Folder"
    dlg = wx.DirDialog(None, message = sMessage)

    if dlg.ShowModal() == wx.ID_OK:
        # User has selected something, get the path, set the window's title to the path
        filename = dlg.GetPath()   
    else:
        filename = "None Selected"

    dlg.Destroy()
    return filename 

def getList(SourceDir):
    print "getList"
    listOfFiles = None
    print "-list set to none"

    listOfFiles = glob.glob(SourceDir + '/*.mp3')

    return listOfFiles

def getListRecursive(SourceDir):
    print "getListRecursive"
    listOfFiles = None
    listOfFiles = []
    print "-list set to none"

    for root, dirs, files in os.walk(SourceDir):
        for file in files:
            if file.endswith(".mp3"):
                listOfFiles.append(os.path.join(root,file))

    #print listOfFiles

    return listOfFiles

def strip_accents(s):
    print "strip_accents"
    return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))   

def replace_all(text):
    print "replace_all " + text
    dictionary = {'\\':"", '?':"", '/':"", '...':"", ':':"", '&':"and"}

    print text
    print text.decode('utf-8')

    text = strip_accents(text.decode('utf-8'))

    for i, j in dictionary.iteritems():
        text = text.replace(i,j)

    return text

def getTitle(fileName):
    print "getTitle"
    audio = MP3(fileName)

    try:
        sTitle = str(audio["TIT2"])
    except KeyError:
        sTitle = os.path.basename(fileName)
        frame.lvActions.Append([datetime.datetime.now(),fileName,"Title tag does not exist, set to filename"])

    # TODO: Offer to set title to filename
    ## If fileName != filename then
    ##  prompt user for action
    ##  Offer Y/n/a

    sTitle = replace_all(sTitle)

    return sTitle

def getArtist(fileName):
    print "get artist"

    audio = MP3(fileName)

    try:
        sArtist = str(audio["TPE1"])
    except KeyError:
        sArtist = "unkown"
        frame.lvActions.Append([datetime.datetime.now(),fileName,"Artist tag does not exist, set to unkown"])

    #Replace all special chars that cause dir path errors
    sArtist = replace_all(sArtist)

    #if name = 'The Beatles' change to 'Beatles, The'
    if sArtist.lower().find('the') == 0:
        sArtist = sArtist.replace('the ',"")
        sArtist = sArtist.replace('The ',"")
        sArtist = sArtist + ", The"

    return sArtist

def getAblum(fileName):
    print "get album"
    audio = MP3(fileName)

    try:
        sAlbum = str(audio["TALB"])
    except KeyError:
        sAlbum = "unkown"
        frame.lvActions.Append([datetime.datetime.now(),fileName,"Album tag does not exist, set to unkown"])

    #Replace all special chars that cause dir path error    
    sAlbum = replace_all(sAlbum)
    return sAlbum


########################################################################
# GUI
########################################################################
class MainPanel(wx.Panel):
    #----------------------------------------------------------------------
    def __init__(self, parent):
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)

        self.TrackOlv = ObjectListView(self, wx.ID_ANY,
                                       style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.setTracks() 

        # Allow the cell values to be edited when double-clicked
        self.TrackOlv.cellEditMode = ObjectListView.CELLEDIT_SINGLECLICK

        self.ActionsOlv = ObjectListView(self, wx.ID_ANY,
                                         style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.setActions()         

        # create browse to source button
        sourceBtn = wx.Button(self, wx.ID_ANY, "Browse Source")
        sourceBtn.Bind(wx.EVT_BUTTON, self.onBrowseSource)        

        # create source txt box
        self.txSource = wx.TextCtrl(self, wx.ID_ANY, name=u'txSource', value=u'')

        # create browse dest button
        destBtn = wx.Button(self, wx.ID_ANY, "Browse Destination")
        destBtn.Bind(wx.EVT_BUTTON, self.onBrowseDest)

        # create dest txt box 
        self.txDest = wx.TextCtrl(self, wx.ID_ANY, name=u'txDest', value=u'')         

        # create Move Files button
        moveBtn = wx.Button(self, wx.ID_ANY, "Move Files")
        moveBtn.Bind(wx.EVT_BUTTON, self.onMoveFiles)

        # print list button - debug only
        printBtn = wx.Button(self, wx.ID_ANY, "Print List")
        printBtn.Bind(wx.EVT_BUTTON, self.onPrintBtn)

        # create check box to include all sub files
        self.cbSubfolders = wx.CheckBox(self, wx.ID_ANY,
              label=u'Include Subfolders', name=u'cbSubfolders', style=0)
        self.cbSubfolders.SetValue(True)
        self.cbSubfolders.Bind(wx.EVT_CHECKBOX, self.OnCbSubfoldersCheckbox)

        # create check box to repace file names
        self.cbReplaceFilename = wx.CheckBox(self, wx.ID_ANY,
              label=u'Replace Filename with Title Tag',
              name=u'cbReplaceFilename', style=0)
        self.cbReplaceFilename.SetValue(False)
        self.cbReplaceFilename.Bind(wx.EVT_CHECKBOX, self.OnCbReplaceFilenameCheckbox)

        # Create some sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        feedbackSizer = wx.BoxSizer(wx.VERTICAL)
        sourceSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL) 

        feedbackSizer.Add(self.TrackOlv, 1, wx.ALL|wx.EXPAND, 2)
        feedbackSizer.Add(self.ActionsOlv, 1, wx.ALL|wx.EXPAND, 2)

        sourceSizer.Add(sourceBtn, 0, wx.ALL, 2)
        sourceSizer.Add(self.txSource, 1, wx.ALL|wx.EXPAND, 2)

        sourceSizer.Add(destBtn, 0, wx.ALL, 2)
        sourceSizer.Add(self.txDest, 1, wx.ALL|wx.EXPAND, 2)

        btnSizer.Add(printBtn)
        btnSizer.Add(moveBtn, 0, wx.ALL, 2)
        btnSizer.Add(self.cbSubfolders, 0, wx.ALL, 2)
        btnSizer.Add(self.cbReplaceFilename, 0, wx.ALL, 2)

        mainSizer.Add(feedbackSizer, 1 , wx.ALL|wx.EXPAND, 2)
        mainSizer.Add(sourceSizer, 0, wx.ALL|wx.EXPAND, 2)
        #mainSizer.Add(destSizer, 0, wx.ALL|wx.EXPAND, 2)
        #mainSizer.Add(destSizer, 0, wx.All|wx.Expand, 2)
        mainSizer.Add(btnSizer, 0, wx.ALL, 2)
        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

    #----------------------------------------------------------------------
    # Set the GUI column headers and width
    #----------------------------------------------------------------------
    def setTracks(self, data=None):
        self.TrackOlv.SetColumns([
            ColumnDefn("Title", "left", 100, "title"),
            ColumnDefn("Artist", "left", 100, "artist"),
            ColumnDefn("Album", "left", 100, "album"),
            ColumnDefn("Source", "left", 300, "source"),
            ColumnDefn("Destination", "left", 300, "dest"),
        ]) 

    def setActions(self, data=None):
        self.ActionsOlv.SetColumns([
            ColumnDefn("Time", "left", 100, "timestamp"),
            ColumnDefn("Action", "left", 450, "action"),
            ColumnDefn("Result", "left", 450, "result")
        ])

    #----------------------------------------------------------------------
    # GUI EVENTS
    #-----------------------------------------------------------------------
    EventList = [Action]

    #Select Source of files
    def onBrowseSource(self, event):
        print "OnBrowseSource"    
        source = selectFolder("Select the Source Directory")

        print source

        self.txSource.SetValue(source)
        self.anEvent = [Action(datetime.datetime.now(),source,"Set as Source dir")]
        self.ActionsOlv.AddObjects(self.anEvent)

        self.populateList()

    #Select Source of files
    def onBrowseDest(self, event):
        print "OnBrowseDest"    
        dest = selectFolder("Select the Destination Directory")

        print dest

        self.txDest.SetValue(dest)
        self.anEvent = [Action(datetime.datetime.now(),dest,"Set as Destination dir")]
        self.ActionsOlv.AddObjects(self.anEvent)

        self.populateList()

    def OnCbSubfoldersCheckbox(self, event):
        print "cbSubfolder"
        self.populateList()       

    def OnCbReplaceFilenameCheckbox(self, event):
        print "cbReplaceFilename"
        self.populateList()

    def onMoveFiles(self, event):
        print "onMoveFiles"
        self.moveFiles()

    def onPrintBtn(self, event):
        print "onPrintBtn"

        #Why does this work
        #rowObj = self.dataOlv.GetSelectedObject()
        #print rowObj.author
        #print rowObj.title

        #debug - how many item in the list... why does it only print 1?
        test = self.TrackOlv.GetItemCount()
        print test

        print "aphex"
        print self.TrackOlv.GetObjects()

        for item in xrange(self.TrackOlv.GetItemCount()):
            stitle = self.TrackOlv.GetObjectAt(item)

            print stitle.atrTitle


    #-------------
    #Computations
    #-------------

    def defineDestFilename(self, sFullDestPath):
        print "define dest"

        iCopyX = 0
        bExists = False
        sOrigName = sFullDestPath

        #If the file does not exist return original path/filename
        if os.path.isfile(sFullDestPath) == False:
            print "-" + sFullDestPath + " is valid"
            return sFullDestPath

        #Add .copyX.mp3 to the end of the file and retest until a new filename is found
        while bExists == False:
            sFullDestPath = sOrigName
            iCopyX += 1
            sFullDestPath = sFullDestPath + ".copy" + str(iCopyX) + ".mp3"
            if os.path.isfile(sFullDestPath) == False:
                print "-" + sFullDestPath + " is valid"
                self.lvActions.Append([datetime.datetime.now(),"Desitnation filename changed since file exists",sFullDestPath])
                bExists = True

        #return path/filename.copyX.mp3
        return sFullDestPath


    def populateList(self):
        print "populateList"

        sSource = self.txSource.Value
        sDest = self.txDest.Value

        #Initalize list to reset all values on any option change
        self.initialList = [Track]
        self.TrackOlv.SetObjects(self.initialList)

        #Create list of files
        if self.cbSubfolders.Value == True:
            listOfFiles = getListRecursive(sSource)
        else:
            listOfFiles = getList(sSource)    

        print listOfFiles

        #prompt if no files detected
        if listOfFiles == []:
            self.anEvent = [Action(datetime.datetime.now(),"Parse Source for .MP3 files","No .MP3 files in source directory")]
            self.ActionsOlv.AddObjects(self.anEvent)

        #Populate list after both Source and Dest are chosen
        if len(sDest) > 1 and len(sDest) > 1:     
            print "-iterate listOfFiles"

            for file in listOfFiles:

                (sSource,sFilename) = os.path.split(file)

                print sSource
                print sFilename

                #sFilename = os.path.basename(file)
                sTitle = getTitle(file)
                try:
                    sArtist = getArtist(file)
                except UnicodeDecodeError:
                    print "unicode"
                    sArtist = "unkown"

                sAlbum = getAblum(file)

                # Make path = sDest + Artist + Album
                sDestDir = os.path.join (sDest, sArtist)
                sDestDir = os.path.join (sDestDir, sAlbum) 

                #If file exists change destination to *.copyX.mp3
                if self.cbReplaceFilename.Value == True:
                    sDestDir = self.defineDestFilename(os.path.join(sDestDir,sTitle))
                else:
                    sDestDir = self.defineDestFilename(os.path.join(sDestDir,sFilename))

                # Populate listview with drive contents

                #sSource = self.txSource.Value
                sDest = self.txDest.Value

                # TODO: Make source = exact source of track, not parent source
                # TODO: Seperate dest and filename
                self.aTrack = Track(sTitle,sArtist,sAlbum,sSource, sDestDir)
                self.TrackOlv.AddObjects(self.aTrack)
                self.Update()


                #populate list to later use in move command
                #self.validatedMove.append([file,sDestDir])
                print "-item added to SourceDest list"
        else:
            print "-list not iterated"

    def moveFiles (self):
        print "move files"

        #for track in self.TrackOlv:
        #    print "-iterate SourceDest"
        #    #create dir
        #    (sDest,filename) = os.path.split(self.TrackOlv)
        #    print "-check dest"
        #    
        #    if not os.path.exists(sDest):
        #        print "-Created dest"
        #        os.makedirs(sDest)
        #        self.lvActions.Append([datetime.datetime.now(),sDest,"Created"])
        #        self.Update()
        #        self.lvActions.EnsureVisible(self.lvActions.GetItemCount() -1)
        #
        #    #Move File
        #    print "-move file"
        #    shutil.move(SourceDest[0],SourceDest[1])
        #    self.lvActions.Append([datetime.datetime.now(),filename,"Moved"])
        #    self.Update()
        #    self.lvActions.EnsureVisible(self.lvActions.GetItemCount() -1)
        #
        #self.lvActions.Append([datetime.datetime.now(),"Move Complete","Success"])
        #self.Update()
        #self.lvActions.EnsureVisible(self.lvActions.GetItemCount() -1)    



########################################################################
class MainFrame(wx.Frame):
    #----------------------------------------------------------------------
    def __init__(self):
        wx.Frame.__init__(self, parent=None, id=wx.ID_ANY,
                          title="MP3 Manager", size=(1024,768)) #W by H
        panel = MainPanel(self)

########################################################################
class GenApp(wx.App):

    #----------------------------------------------------------------------
    def __init__(self, redirect=False, filename=None):
        wx.App.__init__(self, redirect, filename)

    #----------------------------------------------------------------------
    def OnInit(self):
        # create frame here
        frame = MainFrame()
        frame.Show()
        return True

#----------------------------------------------------------------------
def main():
    """
    Run the demo
    """
    app = GenApp()
    app.MainLoop()

if __name__ == "__main__":
    main()

5 个答案:

答案 0 :(得分:5)

您没有包含错误:

....other code....
self.aTrack = [Track(sTitle,sArtist,sAlbum,sSource, sDestDir)]
self.TrackOlv.AddObjects(self.aTrack)
....other code....

它位于第一个“....其他代码......”的某个地方,可能就在你第一次创建self.TrackOlv的行上。

查看您的错误消息:

AttributeError: type object 'Track' has no attribute 'atrTitle'

它说实际的类(“类型对象'Track'”)缺少属性。也就是说,您已将实际的类Track添加到列表中,而不是该类的实例。它可能是列表中的第一个项目,或者你的循环在抛出错误之前会打印一些标题。

请注意以下不同的错误消息:

>>> class Foo(object): pass
...
>>> Foo.cat
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: type object 'Foo' has no attribute 'cat'
>>> Foo().cat
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'cat'

答案 1 :(得分:1)

为什么要在单成员列表中实例化Track个对象?更改以下代码,看看是否有帮助。

self.aTrack = [Track(sTitle,sArtist,sAlbum,sSource, sDestDir)]
self.TrackOlv.AddObjects(self.aTrack)

更改为:

self.aTrack = Track(sTitle,sArtist,sAlbum,sSource, sDestDir)
self.TrackOlv.AddObjects(self.aTrack)

顺便说一句,我在我的解释器中运行了你的代码并且工作正常:

IronPython 2.7 Beta 1 (2.7.0.10) on .NET 4.0.30319.1
Type "help", "copyright", "credits" or "license" for more information.
>>> class Track(object):
...   def __init__(self, title, artist, album, source, dest):
...     """
...     Model of the Track Object
...
...     Contains the followign attributes:
...     'Title', 'Artist', 'Album', 'Source', 'Dest'
...     """
...     self.atrTitle = title
...     self.atrArtist = artist
...     self.atrAlbum = album
...     self.atrSource = source
...     self.atrDest = dest
...
>>> list = [Track('good','h','g','1','d:')]
>>> for item in list:
...     print item.atrTitle
...
good
>>>

更新 @ccwhite:根据您的第二条评论:

>>> list = [Track('1','2','3','4','5'), Track('a','b','c','d','e')]
>>> for item in list:
...     print item.atrTitle
...
1
a
>>>

答案 2 :(得分:0)

你真的在一个对象中创建了属性,而是局部变量。您需要按照以下方式执行此操作:

class Track(object):
  def __init__(self, title, artist, album, source, dest):
    """
    Model of the Track Object

    Contains the followign attributes:
    'Title', 'Artist', 'Album', 'Source', 'Dest'
    """
    self.atrTitle = title
    self.atrArtist = artist
    self.atrAlbum = album
    self.atrSource = source
    self.atrDest = dest

答案 3 :(得分:0)

您需要将属性附加到传递给Track的 init 方法的self对象。像这样:

class Track(object):
  def __init__(self, title, artist, album, source, dest):
    """
    Add the properties to the self (aka this) object.
    """
    self.atrTitle = title
    self.atrArtist = artist
    self.atrAlbum = album
    self.atrSource = source
    self.atrDest = dest

答案 4 :(得分:0)

而不是将atrTitle和其他属性声明为

atrTitle = title
atrArtist = artist
atrAlbum = album
atrSource = source
atrDest = dest

您应该在每次分配之前添加 self。,如下所示:

self.atrTitle = title
self.atrArtist = artist
self.atrAlbum = album
self.atrSource = source
self.atrDest = dest