zoukankan      html  css  js  c++  java
  • python查找并删除相同文件-UNIQ File-wxPython-v6

    相比第一版,新增:菜单,对话框,文件过滤器,操作结果保存,配置功能(自己写了一个读写配置文件的功能),提示语优化,模块分化更合理。

    截图:

    源代码:

    UniqFile-wxPython-v6.py:

      1 # -*- coding: gbk -*-
      2 
      3 '''
      4 Author:@DoNotSpyOnMe
      5 Blog: http://www.cnblogs.com/aaronhoo
      6 '''
      7 
      8 import wx,os
      9 from Dialogs import DialogSetFilters,DialogAboutApp,DialogAboutAuthor
     10 from WorkerThread import WorkerThread
     11 from MyConfig import MyConfig
     12 import sys
     13 reload(sys)
     14 sys.setdefaultencoding('utf-8')
     15 
     16 class MyFrame(wx.Frame):
     17     def __init__(self):
     18         wx.Frame.__init__(self,None,title='UNIQ File-wxPython',size=(810,450))
     19         self.LoadControls()
     20         self.InitConfigData()
     21 
     22     def InitConfigData(self):
     23         configFile='metaData/config.txt'
     24         #若不存在该配置文件,创建一个
     25         if not os.path.exists(configFile):
     26             os.makedirs('metaData')
     27             f=open('metaData/config.txt','w')
     28             f.close()
     29         config=MyConfig(configFile)
     30         '''Init configurations'''
     31         config.set('Program','UNIQFile')
     32         config.set('IsExcludeHiddenFiles','False')
     33         config.set('Images','.jpg/.jpeg/.bmp/.png/.gif/.ico')
     34         config.set('Audios','.mp3/.wav/.aiff/.wma/.aac')
     35         config.set('Videos','.mp4/.rmvb/.avi/.wmv/.mov')
     36         config.set('Documents','.pdf/.txt/.doc/.docx/.ppt/.pptx/.xls/.xlsx/.log')
     37         config.set('FileSizeUnit','MB')
     38         config.set('MaxFileSize',128*1024)
     39         config.set('FileSizeStart',0)
     40         config.set('FileSizeEnd', 128*1024)
     41         config.set('FileTypeOption','Default')
     42         config.set('SelectedFileTypes','None')   
     43         config.set('OtherFilesSelected','None')
     44         config.set('IsImagesSelected','False')
     45         config.set('IsAudiosSelected','False')
     46         config.set('IsVideosSelected', 'False')
     47         config.set('IsDocumentsSelected','False')
     48         config.set('IsFileTypeFilterWorking','False')
     49         config.set('IsFileSizeFilterWorking','False')
     50         config.saveConfig() 
     51          
     52     def LoadControls(self):
     53         '''必须先添加菜单,再创建panel容器,否则菜单会被包括在panel当中,导致panel布局错乱'''
     54         self.createMenu()
     55         
     56         '''再创建panel容器'''
     57         pan=wx.Panel(self)   
     58         self.lblDir=wx.StaticText(pan,-1,'Dir:',style=wx.ALIGN_LEFT)
     59         self.txtFile=wx.TextCtrl(pan,size=(380,30))
     60 
     61         self.btnOpen=wx.Button(pan,label='Pick Directory')
     62         self.btnOpen.Bind(wx.EVT_BUTTON, self.OnOpen)
     63         self.btnList=wx.Button(pan,label='Find duplicated')
     64         self.btnList.Bind(wx.EVT_BUTTON, self.OnFind)
     65         self.btnRemove=wx.Button(pan,label='Remove duplicated')
     66         self.btnRemove.Bind(wx.EVT_BUTTON, self.OnRemove)
     67         
     68         hbox=wx.BoxSizer()
     69         hbox.Add(self.lblDir,proportion=0,flag=wx.LEFT,border=5)
     70         hbox.Add(self.txtFile,proportion=0,flag=wx.LEFT,border=5)
     71         hbox.Add(self.btnOpen,proportion=0,flag=wx.LEFT,border=5)
     72         hbox.Add(self.btnList,proportion=0,flag=wx.LEFT,border=5)
     73         hbox.Add(self.btnRemove,proportion=0,flag=wx.LEFT,border=5)
     74         
     75         self.txtContent=wx.TextCtrl(pan,style=wx.TE_MULTILINE|wx.HSCROLL)
     76         vbox=wx.BoxSizer(wx.VERTICAL)
     77 #         vbox.Add(menuBar,proportion=0,flag=wx.EXPAND|wx.ALL,border=5)
     78         vbox.Add(hbox,proportion=0,flag=wx.EXPAND|wx.ALL,border=5)
     79         vbox.Add(self.txtContent,proportion=1,flag=wx.EXPAND,border=5)
     80         pan.SetSizer(vbox) 
     81            
     82     def LoadMetaData(self):
     83         dict={}
     84         dict['Image']='.jpg/.jpeg/.bmp/.png/.gif/.ico'
     85         dict['Audio']='.mp3/.wav/.aiff/.wma/.aac'
     86         dict['Video']='.mp4/.rmvb/.avi/.wmv/.mov'
     87         dict['Document']='.pdf/.txt/.doc/.docx/.ppt/.pptx/.xls/.xlsx/.log'
     88         
     89         return dict    
     90 
     91     def createMenu(self):
     92         menuBar=wx.MenuBar()
     93         
     94         menuFile=wx.Menu()
     95         mOpen=menuFile.Append(-1,'Open Directory')#添加一级子菜单
     96         mSave=menuFile.Append(-1,'Save Result')#添加一级子菜单
     97         menuFile.AppendSeparator()
     98         mExit=menuFile.Append(-1,'Exit')#添加一级子菜单
     99         menuBar.Append(menuFile,'&File')
    100         
    101         menuOptions=wx.Menu()
    102         mSetFilter=menuOptions.Append(-1,'Set Filters')
    103 #         sbmFileTypefilter=mfilter.Append(-1,'File type filter')#添加二级子菜单
    104 #         sbmFileHidden=mfilter.Append(-1,'Ignore option')#添加二级子菜单 忽略隐藏文件 hidden files
    105 #         mIsRecursive=mfilter.Append(-1,'About the author')#添加二级子菜单
    106         menuBar.Append(menuOptions,'&Options')
    107         
    108         menuHelp=wx.Menu()
    109         mAbout=wx.Menu()
    110         sbmAboutApp=mAbout.Append(-1,'About this app')#添加二级子菜单
    111         sbmAboutAuthor=mAbout.Append(-1,'About the author')#添加二级子菜单
    112         menuHelp.AppendMenu(-1,'About',mAbout)#添加一级子菜单
    113         menuBar.Append(menuHelp,'&Help')
    114         '''绑定菜单事件'''
    115         self.Bind(wx.EVT_MENU, self.OnOpen, mOpen)
    116         self.Bind(wx.EVT_MENU, self.OnSave, mSave)
    117         self.Bind(wx.EVT_MENU, self.OnSetFilters, mSetFilter)
    118         self.Bind(wx.EVT_MENU, self.OnAboutApp, sbmAboutApp)
    119         self.Bind(wx.EVT_MENU, self.OnAboutAuthor, sbmAboutAuthor)
    120         self.Bind(wx.EVT_MENU, self.OnExit, mExit)
    121         '''最后组装'''
    122         self.SetMenuBar(menuBar)
    123         
    124     def OnSetFilters(self,event):
    125         dlg=DialogSetFilters(self)
    126         result=dlg.ShowModal()
    127         if result==wx.OK:
    128             pass
    129         elif result==wx.CANCEL:
    130             pass
    131         dlg.Destroy()
    132     
    133     def OnExit(self,event):
    134         self.Close()
    135         
    136     def OnAboutApp(self,event):
    137         dlg=DialogAboutApp(self)
    138         dlg.ShowModal()
    139         dlg.Destroy()
    140         
    141     def OnAboutAuthor(self,event):
    142         dlg=DialogAboutAuthor(self)
    143         dlg.ShowModal()
    144         dlg.Destroy()
    145         
    146     def OnOpen(self,event): 
    147         dlg = wx.DirDialog(None,u"choose a folder",style=wx.DD_DEFAULT_STYLE)  
    148         if dlg.ShowModal() == wx.ID_OK:
    149             dlg.Destroy()
    150             if dlg.GetPath():  
    151                 self.dirSelected=dlg.GetPath() #文件夹路径      
    152                 self.txtFile.SetValue(self.dirSelected)      
    153                 
    154                 self.SetButtons('selected')
    155                 self.txtContent.SetValue('Selected dirctory: %s
    '%self.dirSelected)
    156      
    157     def OnSave(self,event):
    158         if self.txtContent.GetValue()=='':
    159             wx.MessageBox('No results to save.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    160             return
    161         dlg=wx.FileDialog(None, message="Choose a file",defaultDir="", defaultFile="", wildcard="*.txt", style=wx.SAVE) 
    162         if dlg.ShowModal() == wx.ID_OK:
    163             dlg.Destroy()
    164             if dlg.GetPath():
    165                 newfile=dlg.GetPath();
    166 #                 if os.path.exists(newfile):
    167 #                     confirmDlg=wx.MessageDialog(None,'The file selected already exists,do you want to overwrite?','Tip Message',wx.YES_NO|wx.ICON_QUESTION)
    168 #                     if confirmDlg.ShowModal() == wx.YES:
    169                 self.SaveResult(newfile)
    170                 wx.MessageBox('Result has been saved at %s.'%newfile,'Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    171                 return  
    172                
    173         
    174     def SaveResult(self,path):
    175         f=open(path,'wb')
    176         content=self.txtContent.Value
    177         content=content.encode('utf-8')
    178         lines=[]
    179         while content.find('
    ')!=-1:
    180             line=content[:content.index('
    ')+1]
    181             lines.append(line)
    182             content=content[content.index('
    ')+1:]
    183         f.writelines(lines)
    184         f.close() 
    185         
    186     def OnFind(self,event):
    187         if not self.txtFile.GetValue() or not os.path.isdir(self.txtFile.GetValue()):
    188             wx.MessageBox('please select a valid directory first.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    189             return
    190         self.dirSelected=self.txtFile.GetValue()
    191         self.txtContent.SetValue('')
    192         msg='Finding duplicated files in %s
    '%self.dirSelected
    193         self.txtContent.SetValue(msg)
    194         WorkerThread(self,self.dirSelected,'find',msg)
    195 
    196     def OnRemove(self,event): 
    197         if not self.txtFile.GetValue() or not os.path.isdir(self.txtFile.GetValue()):
    198             wx.MessageBox('please select a valid directory first.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    199             return
    200         self.dirSelected=self.txtFile.GetValue()
    201         self.txtContent.SetValue('')
    202         msg='Removing duplicated files in %s
    '%self.dirSelected
    203         self.txtContent.SetValue(msg)
    204         WorkerThread(self,self.dirSelected,'remove',msg)
    205 
    206     def BtnStopHandler(self,event): 
    207         pass
    208 
    209     def SetButtons(self,status):
    210         if status=='init':
    211             self.btnOpen.Enable()
    212             self.btnList.Disable()
    213             self.btnRemove.Disable()
    214 #             self.btnStop.Disable()
    215         elif status=='operating':
    216             self.btnOpen.Disable()
    217             self.btnList.Disable()
    218             self.btnRemove.Disable()
    219 #             self.btnStop.Enable()
    220         elif status=='completed':
    221             self.btnOpen.Enable()
    222             self.btnList.Enable()
    223             self.btnRemove.Enable()
    224 #             self.btnStop.Disable()  
    225         elif status=='selected':
    226             self.btnOpen.Enable()
    227             self.btnList.Enable()
    228             self.btnRemove.Enable()
    229 #             self.btnStop.Disable() 
    230         
    231                         
    232 if __name__=="__main__":
    233     app=wx.App()
    234     MyFrame().Show()
    235     app.MainLoop()

    WorkerThread.py:

      1 # -*- coding: gbk -*-
      2 
      3 '''
      4 Author:@DoNotSpyOnMe
      5 Blog: http://www.cnblogs.com/aaronhoo
      6 '''
      7 from MyConfig import MyConfig
      8 import threading
      9 import math
     10 import platform,os
     11 import hashlib
     12 from Utils import Utils
     13 
     14 class WorkerThread(threading.Thread):
     15     def __init__(self,frame,dir,operation,msg):
     16         """初始化工作线程: 把主窗口传进来"""
     17         threading.Thread.__init__(self)
     18         self.frame = frame#传入主窗口,以便在线程中操作UI界面
     19         self.dir=dir
     20         self.operation=operation
     21         self.LoadConfigFile()
     22         self.msg=msg
     23         self.setDaemon(True)#设置子线程随UI主线程结束而结束
     24 
     25         self.MEGATOBYTENUMBER=math.pow(2,20)#将MB数转为字节数
     26 #         self.SetDirSplitor()
     27         self.Utils=Utils()
     28         self.start() 
     29     #----------------------------------------------------------------------
     30         '''加载配置文件'''
     31     def LoadConfigFile(self):
     32         configFile='metaData/config.txt'
     33         self.config=MyConfig(configFile)
     34 
     35     def run(self):
     36         """执行工作线程"""
     37         self.frame.SetButtons('operating')
     38         try:
     39             if self.operation=='find':
     40                 self.listSameFile(self.dir)
     41                 self.frame.btnList.Enable()
     42             elif self.operation=='remove':
     43                 self.removeSameFile(self.dir)
     44                 self.frame.btnRemove.Enable()            
     45         except Exception,e:
     46             print e
     47         finally:
     48             self.frame.SetButtons('completed')
     49 #         
     50 #     def stop(self):
     51 #         self.keepRunning=False
     52      
     53     def appendMsg(self,msg):
     54         if self.frame:
     55             #以下方式可以实现终端式的刷新:自动滚动到最新行
     56             self.frame.txtContent.AppendText(msg+'
    ')        
     57     
     58     '''Waring:disabled feature,not implemented'''
     59     def filterHiddenFiles(self):
     60         IsExcludeHiddenFiles=eval(self.config.get('IsExcludeHiddenFiles'))
     61         
     62     def filterFileTypes(self,files,FileTypeOption,selectedFileTypes):
     63         fileCopy=[f for f in files]#fileCopy is a copy of files
     64         if FileTypeOption=='Default':
     65             pass
     66         elif FileTypeOption=='Exclude':
     67             for f in files:
     68                 ftype=self.Utils.getFileType(f)
     69                 if self.Utils.isInList(selectedFileTypes, ftype):
     70                     fileCopy.remove(f)
     71         elif FileTypeOption=='Include':
     72             for f in files:
     73                 ftype=self.Utils.getFileType(f)
     74                 if not self.Utils.isInList(selectedFileTypes, ftype):
     75                     fileCopy.remove(f)
     76         del files
     77         return fileCopy
     78     
     79     def getFileRange(self):
     80         fileSizeStart=float(self.config.get('FileSizeStart'))*self.MEGATOBYTENUMBER
     81         inputFileSizeEnd=self.config.get('FileSizeEnd')
     82         if inputFileSizeEnd=='None':
     83             fileSizeEnd=eval(self.config.get('MaxFileSize'))*self.MEGATOBYTENUMBER
     84         else:
     85             fileSizeEnd=float(inputFileSizeEnd)*self.MEGATOBYTENUMBER
     86         return (fileSizeStart,fileSizeEnd)
     87     
     88     def isFileSizeInRange(self,fileSize,fileSizeStart,fileSizeEnd):        
     89         return fileSizeStart<=fileSize<=fileSizeEnd
     90             
     91     def findSameSizeFiles(self,files):
     92         dicSize={}
     93         fileTypeOption=self.config.get('FileTypeOption')
     94         selectedFileTypes=self.config.get('SelectedFileTypes').split(';')
     95         '''过滤文件类型'''
     96         files=self.filterFileTypes(files,fileTypeOption,selectedFileTypes)
     97         
     98         tpFileRange=self.getFileRange()
     99         
    100         for f in files:
    101             size=self.Utils.getFileSize(f)
    102             '''过滤文件大小'''
    103             if not self.isFileSizeInRange(size,tpFileRange[0],tpFileRange[1]):
    104                 continue        
    105             if not dicSize.has_key(size):
    106                 dicSize[size]=f
    107             else:
    108                 dicSize[size]=dicSize[size]+';'+f
    109         dicCopy=dicSize.copy()
    110         for k in dicSize.iterkeys():
    111             if dicSize[k].find(';')==-1:
    112                dicCopy.pop(k) 
    113         del dicSize
    114         return dicCopy
    115   
    116            
    117     def findSameMD5Files(self,files):
    118         dicMD5={}
    119             
    120         for f in files:
    121             self.appendMsg('calculating md5 value of file %s'%f)
    122             md5=self.Utils.getFileMD5(f)        
    123             if not dicMD5.has_key(md5):
    124                 dicMD5[md5]=f
    125             else:
    126                 dicMD5[md5]=dicMD5[md5]+';'+f
    127         dicCopy=dicMD5.copy()
    128         for k in dicMD5.iterkeys():
    129             if dicMD5[k].find(';')==-1:
    130                dicCopy.pop(k) 
    131         del dicMD5
    132         return dicCopy
    133     
    134     def isFileTypeFilterWorking(self):
    135         return eval(self.config.get('IsFileTypeFilterWorking'))  
    136     
    137     def isFileSizeFilterWorking(self):
    138         return eval(self.config.get('IsFileTypeFilterWorking'))
    139     
    140     def LoadFiltersInfo(self): 
    141         flag1=self.isFileTypeFilterWorking()
    142         flag2=self.isFileSizeFilterWorking()
    143         
    144         if flag1 or flag2:
    145             self.appendMsg('Tips:file filters is working:')
    146         if flag1: 
    147             self.appendMsg("File Type Filter:") 
    148             option=self.config.get('FileTypeOption')
    149             fileTypes=self.config.get('SelectedFileTypes')
    150             self.appendMsg("%s:%s
    "%(option if option=='Exclude' else 'Include only',fileTypes))
    151         if flag2:
    152             self.appendMsg("File Size Filter:") 
    153             start=self.config.get('FileSizeStart')
    154             end=self.config.get('FileSizeEnd')
    155             self.appendMsg("%s MB - %s MB
    "%(start,end))
    156         self.appendMsg('Start working...
    ')
    157             
    158     def listSameFile(self,mydir):
    159         msg=''
    160         msgUniq='
    Congratulations,all files are unique.'
    161         try:
    162             existsFlag=False
    163             files=self.Utils.getAllFiles(mydir)
    164             self.appendMsg('%s files found in directory %s
    '%(len(files),mydir))
    165             self.LoadFiltersInfo()
    166             dicFileOfSameSize=self.findSameSizeFiles(files)
    167             groupCount=0
    168             if dicFileOfSameSize=={}:
    169                 self.appendMsg(msgUniq)
    170                 return 
    171             else:
    172                 for k in dicFileOfSameSize.iterkeys():
    173                     filesOfSameSize=dicFileOfSameSize[k].split(';')
    174                     dicSameMD5file=self.findSameMD5Files(filesOfSameSize)
    175                     if dicSameMD5file!={}:
    176                         existsFlag=True
    177                         for k in dicSameMD5file.iterkeys():
    178                             msg=msg+'md5 %s: %s'%(k,dicSameMD5file[k])+'
    '
    179                             groupCount+=1
    180                 if not existsFlag:
    181                     msg=msgUniq
    182                 else:
    183                     msg='
    Duplicated files:
    '+msg+'
    Found %s groups of duplicated files totally.'%groupCount
    184         except Exception,e:
    185             print e
    186             msg='Exception occured.'
    187         finally:
    188             self.appendMsg(msg+'
    '+'
    Operation finished.')
    189     
    190         
    191     def removeSameFile(self,mydir):
    192         msg=''
    193         msgUniq='
    Congratulations,no file is removed since they are all unique.'
    194         try:
    195             existsFlag=False
    196             files=self.Utils.getAllFiles(mydir)
    197             self.appendMsg('%s files found in directory %s
    '%(len(files),mydir))
    198             self.LoadFiltersInfo()
    199             dicFileOfSameSize=self.findSameSizeFiles(files)
    200             if not self.config.get('FileTypeOption')=='Default':
    201                 self.appendMsg('Tips:file filters is working.
    ')
    202             if dicFileOfSameSize=={}:
    203                 self.appendMsg(msgUniq)
    204                 return
    205             else:
    206                 #list the duplicated files first: 
    207                 self.appendMsg('Finding duplicted files:')
    208                 dicFiltered={}
    209                 groupCount=0
    210                 for k in dicFileOfSameSize.iterkeys():
    211                     filesOfSameSize=dicFileOfSameSize[k].split(';')
    212                     dicSameMD5file=self.findSameMD5Files(filesOfSameSize)
    213                     if dicSameMD5file!={}:
    214                         existsFlag=True
    215                         for k in dicSameMD5file.iterkeys():
    216                             msg=msg+'md5 %s: %s'%(k,dicSameMD5file[k])+'
    '
    217                             groupCount+=1
    218                             dicFiltered[k]=dicSameMD5file[k]
    219                 if not existsFlag:
    220                     self.appendMsg(msgUniq)
    221                     return
    222                 else:
    223                     #then remove the duplicated files:
    224                     self.appendMsg('
    Duplicated files:
    '+msg+'
    Found %s groups of duplicated files totally.'%groupCount)
    225                     removeCount=0  
    226                     for k in dicFiltered.iterkeys():
    227                         sameFiles=dicFiltered[k].split(';')
    228                         flagRemove=False
    229                         for f in sameFiles:
    230                             if not flagRemove:
    231                                 flagRemove=True
    232                             else:
    233                                 self.appendMsg('Removing file: %s'%f)
    234                                 os.remove(f)
    235                                 removeCount=removeCount+1
    236                     self.appendMsg('
    %s files are removed.
    '%removeCount)              
    237         except Exception,e:
    238             print e
    239             msg='
    Exception occured.'
    240             self.appendMsg(msg)
    241         finally:
    242             self.appendMsg('
    
    Operation finished.')

    Dialogs.py:

      1 # -*- coding: gbk -*-
      2 
      3 '''
      4 Author:@DoNotSpyOnMe
      5 Blog: http://www.cnblogs.com/aaronhoo
      6 '''
      7 import wx
      8 from wx.html import HtmlWindow
      9 from MyConfig import MyConfig
     10 import re
     11 from Utils import Utils
     12 
     13 class DialogSetFilters(wx.Dialog):
     14     def __init__(self, frame):
     15         wx.Dialog.__init__(self, frame, -1,'Filters',size=(560, 560) )
     16         self.frame=frame
     17         self.LoadConfigFile()
     18         self.LoadControls()
     19         self.InitWithConfigData()
     20         #为控件绑定事件
     21         self.BindControlEvent()
     22         self.Utils=Utils()
     23         
     24         
     25     '''加载配置文件'''
     26     def LoadConfigFile(self):
     27         configFile='metaData/config.txt'
     28         self.config=MyConfig(configFile)
     29            
     30     def LoadControls(self):       
     31         panel = wx.Panel(self)
     32         sizer = wx.GridBagSizer(5,5)#预计需要5行5列,但实际可以超出该设定,GridGagSizer会自动增长
     33         '''加载隐藏文件过滤器的控件'''
     34         sb = wx.StaticBox(panel, label="Hidden File Filter",size=(500,100))
     35         boxsizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
     36         self.cbExclHidden=wx.CheckBox(panel, label="Exclude hidden files(Waring:disabled feature)")
     37         boxsizer.Add((10,10),flag=wx.BOTTOM|wx.TOP,border=10)
     38         boxsizer.Add(self.cbExclHidden,flag=wx.BOTTOM|wx.TOP,border=10)
     39         sizer.Add(boxsizer, pos=(0,0),span=(1,5),flag=wx.LEFT|wx.TOP,border=25)
     40         #pos(0,0)表示位置在第1行,第1列,span=(1,5)表示该boxsizer占据1个行5个列的空间
     41         
     42         '''加载文件类型过滤器的控件'''
     43         sb = wx.StaticBox(panel, label="File Type Filter",size=(500,500))
     44         boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
     45         self.rdDefault=wx.RadioButton(panel,-1,'Default',style=wx.RB_GROUP)
     46         self.rdExclFtyp=wx.RadioButton(panel,-1,'Exclude')
     47         self.rdInclFtyp=wx.RadioButton(panel,-1,'Include Only')
     48         subHbox=wx.BoxSizer(wx.HORIZONTAL)
     49         subHbox.Add(self.rdDefault,flag=wx.LEFT,border=25)
     50         subHbox.Add(self.rdExclFtyp,flag=wx.LEFT,border=5)
     51         subHbox.Add(self.rdInclFtyp,flag=wx.LEFT,border=5)
     52         boxsizer.Add(subHbox)
     53         
     54         '''从配置文件读固定配置'''
     55         self.cbImage=wx.CheckBox(panel, label="Images("+self.config.get('Images')+")")
     56         self.cbAudio=wx.CheckBox(panel, label="Audios("+self.config.get('Audios')+")")
     57         self.cbVideo=wx.CheckBox(panel, label="Videos("+self.config.get('Videos')+")")
     58         self.cbDoc=wx.CheckBox(panel, label="Documents("+self.config.get('Documents')+")")
     59         
     60         self.lblOtherFiles=wx.StaticText(panel,label="Other files ( enter file extensions,split them with ";" )")
     61         self.txtOtherFiles=wx.TextCtrl(panel,size=(400,25))
     62         subVbox=wx.BoxSizer(wx.VERTICAL)
     63         subVbox.Add(self.cbImage,flag=wx.LEFT|wx.TOP,border=10)
     64         subVbox.Add(self.cbAudio,flag=wx.LEFT|wx.TOP,border=10)
     65         subVbox.Add(self.cbVideo,flag=wx.LEFT|wx.TOP,border=10)
     66         subVbox.Add(self.cbDoc,flag=wx.LEFT|wx.TOP,border=10)
     67         subVbox.Add(self.lblOtherFiles,flag=wx.LEFT|wx.TOP,border=10)
     68         subVbox.Add(self.txtOtherFiles,flag=wx.LEFT|wx.TOP|wx.BOTTOM|wx.RIGHT,border=10)
     69         boxsizer.Add(subVbox)
     70         sizer.Add(boxsizer, pos=(1,0), span=(6,5),flag=wx.TOP|wx.LEFT,border=25)
     71         #pos(1,0)表示位置在第2行,第1列,span=(1,5)表示该boxsizer占据6个行5个列的空间
     72         
     73         self.cbExclHidden.Disable()
     74                
     75         '''加载文件大小过滤器的控件'''
     76         sb = wx.StaticBox(panel, label="File Size Filter",size=(500,200))
     77         boxsizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
     78         self.txtFileSize1=wx.TextCtrl(panel,size=(100,25))
     79         lblFileSize1 = wx.StaticText(panel,label="MB")
     80         lblTo = wx.StaticText(panel,label="--")
     81         self.txtFileSize2=wx.TextCtrl(panel,size=(100,25))
     82         lblFileSize2 = wx.StaticText(panel,label="MB")
     83         boxsizer.Add(self.txtFileSize1,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
     84         boxsizer.Add(lblFileSize1,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
     85         boxsizer.Add((20,10))
     86         boxsizer.Add(lblTo,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
     87         boxsizer.Add((20,10))
     88         boxsizer.Add(self.txtFileSize2,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
     89         boxsizer.Add(lblFileSize2,flag=wx.LEFT|wx.BOTTOM|wx.TOP|wx.EXPAND,border=10)
     90         sizer.Add(boxsizer, pos=(7,0),span=(1, 5),flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT , border=25)#pos=(7,0)
     91         #pos等于(7,0)表示位置在第8行,第1列,之所以是第8行,因为前面两个控件已经占据了1+6=7行,所以至少从第8行开始,否则布局错乱
     92         
     93         '''加载按钮'''
     94         self.btnOK = wx.Button(panel, label="OK")
     95         self.btnCancel = wx.Button(panel, label="Cancel")
     96         sizer.Add(self.btnOK, pos=(9,3))
     97         sizer.Add(self.btnCancel, pos=(9,4),flag=wx.BOTTOM|wx.RIGHT, border=5)    
     98         
     99         panel.SetSizer(sizer)
    100         panel.Layout()
    101     
    102     def InitWithConfigData(self):
    103         self.cbExclHidden.SetValue(str(self.config.get('IsExcludeHiddenFiles'))=='True')
    104         
    105         self.rdDefault.SetValue(self.config.get('FileTypeOption')=='Default')
    106         if self.rdDefault.GetValue():
    107             self.DisableFileTypeFilterControls()
    108         
    109         self.rdExclFtyp.SetValue(self.config.get('FileTypeOption')=='Exclude')
    110         self.rdInclFtyp.SetValue(self.config.get('FileTypeOption')=='Include')
    111         
    112         self.cbImage.SetValue(str(self.config.get('IsImagesSelected'))=='True')
    113         self.cbAudio.SetValue(str(self.config.get('IsAudiosSelected'))=='True')
    114         self.cbVideo.SetValue(str(self.config.get('IsVideosSelected'))=='True')
    115         self.cbDoc.SetValue(str(self.config.get('IsDocumentsSelected'))=='True')
    116         self.txtOtherFiles.SetValue('' if self.config.get('OtherFilesSelected')=='None' else self.config.get('OtherFilesSelected'))
    117         
    118         self.txtFileSize1.SetValue(self.config.get('FileSizeStart'))
    119         inputFileSizeEnd=self.config.get('FileSizeEnd')
    120         if inputFileSizeEnd=='None':
    121             self.txtFileSize2.SetValue('')
    122         else:
    123             self.txtFileSize2.SetValue(inputFileSizeEnd)     
    124                 
    125     def BindControlEvent(self):
    126         for eachRadio in [self.rdDefault, self.rdExclFtyp, self.rdInclFtyp]:#绑定事件
    127             self.Bind(wx.EVT_RADIOBUTTON, self.OnRadio, eachRadio)
    128         self.Bind(wx.EVT_BUTTON, self.OnOK,self.btnOK)
    129         self.Bind(wx.EVT_BUTTON, self.OnCancel,self.btnCancel)
    130         
    131     def ValidateInput(self):
    132         
    133         if not self.rdDefault.GetValue():
    134             pattern=re.compile('[^dw.;]+')#数字、英文字母、点号之外的字符定义为非法字符
    135             if re.search(pattern,self.txtOtherFiles.GetValue()):
    136                 wx.MessageBox('Please enter valid file extensions and correct splitor(;).','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    137                 return False
    138         if (self.rdInclFtyp.GetValue() or self.rdExclFtyp.GetValue()) and self.getSelectedFileTypes()=='':
    139             wx.MessageBox('Please enter or select at least one file type.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    140             return False   
    141         if self.txtFileSize1.GetValue()!='' and not self.Utils.isNumReg(self.txtFileSize1.GetValue()):
    142             wx.MessageBox('Please do not enter or enter valid number in the first file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    143             return False
    144         elif self.txtFileSize2.GetValue()!='' and not self.Utils.isNumReg(self.txtFileSize2.GetValue()):
    145             wx.MessageBox('Please do not enter or enter valid number in the second file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    146             return False
    147         elif self.Utils.isNumReg(self.txtFileSize1.GetValue()) and self.Utils.isNumReg(self.txtFileSize2.GetValue()) and eval(self.txtFileSize1.GetValue())>eval(self.txtFileSize2.GetValue()):
    148             wx.MessageBox('The first file size should not be greater than the second file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    149             return False
    150         elif self.Utils.isNumReg(self.txtFileSize2.GetValue()) and eval(self.txtFileSize2.GetValue())>(self.config.get('MaxFileSize')):
    151             wx.MessageBox('The second file size should not be greater than '+self.config.get('MaxFileSize'),'Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
    152             return False
    153         return True
    154     
    155     def getSelectedFileTypes(self):
    156         selectedFileTypes=''
    157         if not self.rdDefault.GetValue():
    158             fileType=''
    159             for chk in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc]:
    160                 if chk.IsChecked():
    161                     label=chk.GetLabel()
    162                     fileType=fileType+label[label.find('(')+1:label.find(')')]+';'
    163             selectedFileTypes=fileType
    164                 
    165         selectedFileTypes+=self.Utils.FormatOtherFileTypes(self.txtOtherFiles.GetValue())
    166         selectedFileTypes= selectedFileTypes.replace('/',';').lower()
    167         return  selectedFileTypes       
    168     
    169     def OnOK(self,event):
    170         if self.ValidateInput():
    171             IsExclHiddenFiles=self.cbExclHidden.GetValue()
    172             if self.rdDefault.GetValue():
    173                 FileTypeOption='Default'
    174             elif self.rdExclFtyp.GetValue():
    175                 FileTypeOption='Exclude'
    176             elif self.rdInclFtyp.GetValue():
    177                 FileTypeOption='Include'
    178    
    179             selectedFileTypes=self.getSelectedFileTypes()
    180             if selectedFileTypes=='':
    181                 self.config.set('SelectedFileTypes','None')
    182             else:
    183                 self.config.set('SelectedFileTypes',selectedFileTypes)
    184             
    185             '''save filters into the config file'''
    186             self.config.set('IsExcludeHiddenFiles',IsExclHiddenFiles) 
    187             self.config.set('FileTypeOption',FileTypeOption)
    188             self.config.set('IsImagesSelected',self.cbImage.IsChecked())
    189             self.config.set('IsAudiosSelected',self.cbAudio.IsChecked())
    190             self.config.set('IsVideosSelected',self.cbVideo.IsChecked())
    191             self.config.set('IsDocumentsSelected',self.cbDoc.IsChecked())
    192             
    193             otherFileTyps=self.txtOtherFiles.GetValue()
    194             if otherFileTyps=='':
    195                 self.config.set('OtherFilesSelected','None')
    196             else:
    197                 self.config.set('OtherFilesSelected',self.Utils.FormatOtherFileTypes(self.txtOtherFiles.GetValue()))
    198             
    199             fileSizeStart=self.txtFileSize1.GetValue()
    200             fileSizeEnd=self.txtFileSize2.GetValue()
    201             if fileSizeStart!='':
    202                 self.config.set('FileSizeStart',fileSizeStart)
    203                 
    204             if fileSizeEnd=='':
    205                 self.config.set('FileSizeEnd','None')
    206             else:
    207                 self.config.set('FileSizeEnd',fileSizeEnd)
    208                 
    209             if FileTypeOption=='Default':
    210                 self.config.set('IsFileTypeFilterWorking','False')
    211             else:
    212                 self.config.set('IsFileTypeFilterWorking','True')
    213                 
    214             if fileSizeStart=='0' or fileSizeEnd!=self.config.get('MaxFileSize'):
    215                 self.config.set('IsFileSizeFilterWorking','True')
    216             else:
    217                 self.config.set('IsFileSizeFilterWorking','False')    
    218             self.config.saveConfig()
    219             self.Close()
    220                
    221     def OnCancel(self,event):
    222         self.Close()
    223     
    224     def OnRadio(self,event):
    225         selectedRadio=event.GetEventObject()
    226         if selectedRadio==self.rdDefault:
    227             self.DisableFileTypeFilterControls()
    228         else:
    229             self.EnableFileTypeFilterControls()
    230         
    231     def DisableFileTypeFilterControls(self):
    232         for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc,self.txtOtherFiles,self.lblOtherFiles]:
    233             control.Disable()
    234         for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc]:
    235             control.SetValue(False)
    236         self.txtOtherFiles.SetValue('')
    237             
    238     def EnableFileTypeFilterControls(self):
    239         for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc,self.txtOtherFiles,self.lblOtherFiles]:
    240             control.Enable()
    241         
    242 class DialogAboutApp(wx.Dialog):
    243     text = '''
    244             <html>
    245             <head>
    246                 <style type="text/css">
    247                     body {font-family:serif;background-color: #ACAA60;}
    248                 </style>
    249             </head>
    250             <body> <!--bgcolor="#ACAA60"-->
    251             <center>
    252             <table bgcolor="#455481" width="100%" cellspacing="0" cellpadding="0" border="1">
    253             <tr>
    254             <td align="center"><h1><span style="color:white">UNIQ File-wxPython</span></h1></td>
    255             </tr>
    256             </table>
    257             </center>
    258             <p><b>UNIQ File-wxPython</b> is a program designed for clearing duplicated files and saving
    259             memory space.
    260             It's broght to you by Aaron Hu(<b><span style="color:red">@DoNotSpyOnMe</span></b> on Sina Weibo).<br/>
    261            
    262             <!--<a href="http://weibo.com/u/1737184870" target="_blank">Contact</a> the author now.-->
    263             </p>
    264             </body>
    265             </html>
    266             '''
    267     def __init__(self, parent):
    268         wx.Dialog.__init__(self, parent, -1,'About this app',
    269         size=(440, 400) )
    270         window = HtmlWindow(self)
    271         window.SetPage(self.text)
    272         button = wx.Button(self, wx.ID_OK, 'OK')
    273         sizer = wx.BoxSizer(wx.VERTICAL)
    274         sizer.Add(window, 1, wx.EXPAND|wx.ALL, 5)
    275         sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
    276         self.SetSizer(sizer)
    277         self.Layout()
    278         
    279 class DialogAboutAuthor(wx.Dialog):
    280     text = '''
    281             <html>
    282             <head>
    283                 <style type="text/css">
    284                     body {font-family:serif;background-color: #ACAA60;}
    285                 </style>
    286             </head>
    287             <body> <!--bgcolor="#ACAA60"-->
    288             <center>
    289             <table bgcolor="#455481" width="100%" cellspacing="0" cellpadding="0" border="1">
    290             <tr>
    291             <td align="center"><h1><span style="color:white">UNIQ File-wxPython</span></h1></td>
    292             </tr>
    293             </table>
    294             </center>
    295             <p>
    296             Contact the author at:<br/><br/>
    297             Sina Weibo:<b><span style="color:red">@DoNotSpyOnMe</span></b><br/>
    298             cnblogs.com:<b>http://www.cnblogs.cn/aaronhoo</b><br/>
    299             </p>
    300             </body>
    301             </html>
    302             '''
    303     def __init__(self, parent):
    304         wx.Dialog.__init__(self, parent, -1,'About the author',
    305         size=(440, 400) )
    306         window = HtmlWindow(self)
    307         window.SetPage(self.text)
    308         button = wx.Button(self, wx.ID_OK, 'OK')
    309         sizer = wx.BoxSizer(wx.VERTICAL)
    310         sizer.Add(window, 1, wx.EXPAND|wx.ALL, 5)
    311         sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
    312         self.SetSizer(sizer)
    313         self.Layout()

    MyConfig.py:

     1 # -*- coding: gbk -*-
     2 '''
     3 Created on 2016年4月20日
     4 
     5 Author: @DoNotSpyOnMe
     6 '''
     7 import re,os
     8 
     9 class MyConfig:
    10     def __init__(self,filepath):
    11         self.configFile=''
    12         self.lines=[]
    13         self.read(filepath)
    14         
    15     def read(self,filepath):
    16         try:
    17             if not os.path.isfile(filepath):
    18                 print 'Error:file %s dose not exist.'%filepath
    19                 return
    20             pattern=re.compile('^.+.txt$')
    21             if not re.search(pattern, filepath):
    22                 print 'Error:only .txt file is supported as config file.' 
    23                 return
    24             f=open(filepath,'r')
    25             self.lines=f.readlines()
    26             f.close()
    27             self.configFile=filepath
    28         except Exception,e:
    29             print e
    30              
    31     def get(self,variable):
    32         try:
    33             if self.validate():
    34                 pattern=re.compile('^'+variable+' = ')
    35                 for line in self.lines:
    36                         if re.search(pattern,line):
    37                             val= line[line.find(' = ')+3:].strip()
    38                             return val
    39                 print "Error:variable '%s' is not found in config file."%variable
    40         except Exception,e:
    41             print e
    42 
    43     def set(self,variable,newValue):
    44         try:
    45             if self.validate():
    46                 count=0
    47                 pattern=re.compile('^'+variable+' = ')
    48                 for line in self.lines:
    49                     if re.search(pattern,line):
    50                         newline=line.replace(line[line.find(' = ')+3:line.rfind('
    ')],str(newValue))
    51                         self.lines[count]=newline
    52                         return
    53                     count=count+1
    54                 #if the variable is no found,create it.
    55                 newline=variable+' = '+str(newValue)
    56                 self.lines.append(newline+'
    ')
    57         except Exception,e:
    58             print e
    59     
    60     def setAndSave(self,variable,newValue):
    61         try:
    62             if self.validate():    
    63                 count=0
    64                 found=False
    65                 pattern=re.compile('^'+variable+' = ')
    66                 for line in self.lines:
    67                     if re.search(pattern,line):
    68                         newline=line.replace(line[line.find(' = ')+3:line.rfind('
    ')],str(newValue))
    69                         self.lines[count]=newline
    70                         found=True
    71                         break
    72                     count=count+1
    73                 if not found:
    74                     newline=variable+' = '+str(newValue)
    75                     self.lines.append(newline+'
    ')
    76                 #save changes.
    77                 f=open(self.configFile,'w')
    78                 f.writelines(self.lines)
    79                 f.close()   
    80         except Exception,e:
    81             print e
    82             
    83     def saveConfig(self):
    84         try:
    85             if self.validate():
    86                 f=open(self.configFile,'w')
    87                 f.writelines(self.lines)
    88                 f.close()   
    89         except Exception,e:
    90             print e
    91             
    92     def validate(self):
    93         if not self.configFile:
    94             print 'Error:config file not found.'
    95             return False
    96         return True

    Utils.py:

      1 # -*- coding: gbk -*-
      2 import hashlib
      3 import os
      4 import platform
      5 import re
      6 
      7 class Utils:
      8     def __init__(self):
      9         self.getDirSplitor()
     10         
     11     def isNumReg(self,str):
     12         regInt='^0$|^[1-9]d*$'#不接受09这样的为整数
     13         regFloat='^0.d+$|^[1-9]d*.d+$'  
     14         regIntOrFloat=regInt+'|'+regFloat#整数或小数
     15         patternIntOrFloat=re.compile(regIntOrFloat)#创建pattern对象,以便后续可以复用
     16         if re.search(regIntOrFloat,str):
     17             return True
     18         else:
     19             return False
     20             
     21     '''使得用户在其他文件类型框中可以输入'.xml;.html'或者'xml;html;'或者'xml;.html'这三种格式'''    
     22     def FormatOtherFileTypes(self,fileTypeInput):
     23         ls=fileTypeInput.split(';')
     24         lsnew=[]
     25         for e in ls:
     26             if e!='':
     27                 if e.find('.')==-1:
     28                     lsnew.append('.'+e)
     29                 else:
     30                     lsnew.append(e)
     31         s=''
     32         for e in lsnew:
     33             s=s+e+';'
     34         return s
     35     
     36       
     37     def divideList(self,ls,each):
     38         dividedLs=[]
     39         eachExact=float(each)
     40         groupCount=len(ls)/each
     41         groupCountExact=len(ls)/eachExact
     42         start=0
     43         for i in xrange(groupCount):
     44             dividedLs.append(ls[start:start+each])
     45             start=start+each
     46         if groupCount<groupCountExact:#假如有余数,将剩余的所有元素加入到最后一个分组
     47             dividedLs.append(ls[groupCount*each:])
     48         return dividedLs
     49         
     50                 
     51     def getFileSize(self,filePath):
     52         return os.path.getsize(filePath)        
     53     
     54     ''' 一般文件的md5计算方法,一次读取文件的全部内容'''           
     55     def CalcMD5(filepath):
     56         with open(filepath,'rb') as f:
     57             md5obj = hashlib.md5()
     58             md5obj.update(f.read())
     59             hash = md5obj.hexdigest()
     60             return hash    
     61     '''大文件计算md5的方法,分批读取文件内容,防止内存爆掉'''    
     62     def getFileMD5(self,filename):
     63         if not os.path.isfile(filename):
     64             return
     65         myhash = hashlib.md5()
     66         f = open(filename,'rb')
     67         while True:
     68             b = f.read(8*1024)
     69             if not b :
     70                 break
     71             myhash.update(b)
     72         f.close()
     73         return myhash.hexdigest()
     74     
     75     def getAllFiles(self,directory):
     76         files=[]
     77 #         dirSplitor=getDirSplitor()
     78         for dirpath, dirnames,filenames in os.walk(directory):
     79             if filenames!=[]:
     80                 for file in filenames:
     81                     files.append(dirpath+self.dirSplitor+file)
     82         files.sort(key=len)#按照文件名的长度排序
     83         return files
     84     
     85     def isInList(self,myList,someValue):
     86         try:
     87             myList.index(someValue)
     88             return True
     89         except:
     90             return False
     91         
     92     def getFileType(self,file):
     93 #         dirSplitor=getDirSplitor()
     94         fileName=file[file.rfind(self.dirSplitor)+1:]
     95         fileType=file[file.rfind('.'):]
     96         return fileType.lower()
     97     
     98     def getDirSplitor(self):
     99         osType=platform.system()
    100         if osType=='Windows':
    101             self.dirSplitor= '\'
    102         elif osType=='Linux':
    103             self.dirSplitor= '/'
    104         else:
    105             self.dirSplitor= '/'
  • 相关阅读:
    A Look Inside Presentation Controllers
    iOS用户数据安全:Keychain、Touch ID以及1Password
    iOS开发UI篇—UITableView全面解析
    iOS 上的相机捕捉
    iOS UIScrollView
    ViewController类中得方法和属性的用途
    ios 中__bridge,__bridge_transfer和__bridge_retained详解
    __weak如何实现对象值自动设置为nil
    iOS开发UI篇—UIWindow简单介绍
    iOS:App启动过程详解
  • 原文地址:https://www.cnblogs.com/aaronhoo/p/5446704.html
Copyright © 2011-2022 走看看