I apologize if this is a noob question, but I can't seem to figure this one out.
I have defined an object that defines a music track (NOTE: originally had the just ATTRIBUTE vs self.ATTRIBUTE. I edited those values in to help remove confusion. They had no affect on the problem)
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
I use ObjectListView to create a list of tracks in a specific directory
....other code....
self.aTrack = [Track(sTitle,sArtist,sAlbum,sSource, sDestDir)]
self.TrackOlv.AddObjects(self.aTrack)
....other code....
Now I want to iterate the list and print out a single value of each item
list = self.TrackOlv.GetObjects()
for item in list:
print item.atrTitle
This fails with the error
AttributeError: type object 'Track' has no attribute 'atrTitle'
What really confuses me is if I highlight a single item in the Object List View display and use the following code, it will correctly print out the single value for the highlighted item
list = self.TrackOlv.GetSelectedObject()
print list.atrTitle
EDIT: Full source per request. To see error, browse to source dir w/ .mp3 files then click the print button.
#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()
You didn't include the bug:
....other code....
self.aTrack = [Track(sTitle,sArtist,sAlbum,sSource, sDestDir)]
self.TrackOlv.AddObjects(self.aTrack)
....other code....
It's somewhere in the first "....other code....", probably on the line where you first create the self.TrackOlv.
Look at your error message:
AttributeError: type object 'Track' has no attribute 'atrTitle'
It's saying the actual class ("type object 'Track'") is lacking the attribute. That is, you've added the actual class Track to your list instead of an instance of that class. It's probably the first item in the list, or your loop would have printed some titles before throwing the error.
Note the different error messages below:
>>> 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'