开发者

python ObjectListView updating display with blank lines, even though the list has valid data

开发者 https://www.devze.com 2023-02-12 12:29 出处:网络
I have a list that updates ObjectListView, it previously worked, but in troubleshooting a different issue, I somehow broke it but now can\'t figure out why its not working.

I have a list that updates ObjectListView, it previously worked, but in troubleshooting a different issue, I somehow broke it but now can't figure out why its not working.

When the program parses a directory it is supposed to updated the list view with info about .mp3 files found. It is updating the display, but only with blank lines. However, if you force an output to the command line it shows that the content of the list is correct.

To even confuse me further, I have a second list that acts as a type of 'log'. It works correctly, and they both use the same technique to update their respective lists. I have checked the code to find where (what I am guessing) my typo is, but for the life of me I can't figure out why this is now not working.

The list that is not updating correctly is TrackOlv. To see the error in action simply browse to a source dir that contains .mp3 files and then select a destination dir. You will see the display update with blank lines instead of line of data.

#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 "-- repl_orig: " + text
    print "-- repl_decode: " + 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:
        sAl开发者_高级运维bum = 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)

        #Create Track Object List
        self.TrackOlv = ObjectListView(self, wx.ID_ANY, style=wx.LC_REPORT|wx.SUNKEN_BORDER)
        self.TrackOlv.cellEditMode = ObjectListView.CELLEDIT_SINGLECLICK
        self.setTracks()  

        #Create Actions Objet List
        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
    #-----------------------------------------------------------------------

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

        print "--source :" + source

        self.txSource.SetValue(source)
        self.anEvent = Action(datetime.datetime.now(),source,"Set as Source dir")
        self.ActionsOlv.AddObject(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.AddObject(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?
        itemcount = self.TrackOlv.GetItemCount()        
        print "-- itemcount: " + str(itemcount)

        getData = self.TrackOlv.GetItemData(1)
        print "-- getData: " + str(getData)       

        trackList = self.TrackOlv.GetObjects()        
        for tracks in trackList:         
            print tracks.atrTitle, tracks.atrArtist, tracks.atrAlbum, tracks.atrSource, tracks.atrDest


    #-------------
    #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
        itemcount = self.TrackOlv.GetItemCount()        
        print "-- itemcount: " + str(itemcount)
        if itemcount > 0:
            self.TrackOlv.DeleteAllItems()
            print "--list cleaned"

        #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.AddObject(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:

                #gest Source and Filename
                (sSource,sFilename) = os.path.split(file)
                print "-- source: " + sSource
                print "-- filename: " + sFilename

                #sFilename = os.path.basename(file)
                sTitle = getTitle(file)
                print "-- title: " + sTitle

                #Get artist
                try:
                    sArtist = getArtist(file)
                except UnicodeDecodeError:
                    print "unicode"
                    sArtist = "unkown"
                print "-- artist: " + sArtist

                #Get Album
                sAlbum = getAblum(file)
                print "-- album: " + sAlbum

                # Make dest 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))
                print "-- dest: " + sDestDir

                # Populate listview with track info                
                self.aTrack = Track(sTitle,sArtist,sAlbum,sSource,sDestDir)        
                self.TrackOlv.AddObject(Track(sTitle,sArtist,sAlbum,sSource,sDestDir))
                self.Update()

                print "** ITEM ADDED **"
        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()


The problem is that the 4th argument in the ColumnDefn in your setTracks method don't match the Track object's attribute names. If they don't match exactly, then it won't map correctly and nothing will be shown in the widget.

So, change "title" to "atrTitle", etc in the setTracks method or do the reverse in the Track object.

This is one of the few got'chas I've had with ObjectListView.

0

精彩评论

暂无评论...
验证码 换一张
取 消