python查找并删除相同文件-UNIQ File-wxPython-v6
相比第一版,新增:菜单,对话框,文件过滤器,操作结果保存,配置功能(自己写了一个读写配置文件的功能),提示语优化,模块分化更合理。
截图:
源代码:
UniqFile-wxPython-v6.py:
- # -*- coding: gbk -*-
- '''
- Author:@DoNotSpyOnMe
- Blog: http://www.cnblogs.com/aaronhoo
- '''
- import wx,os
- from Dialogs import DialogSetFilters,DialogAboutApp,DialogAboutAuthor
- from WorkerThread import WorkerThread
- from MyConfig import MyConfig
- import sys
- reload(sys)
- sys.setdefaultencoding('utf-8')
- class MyFrame(wx.Frame):
- def __init__(self):
- wx.Frame.__init__(self,None,title='UNIQ File-wxPython',size=(810,450))
- self.LoadControls()
- self.InitConfigData()
- def InitConfigData(self):
- configFile='metaData/config.txt'
- #若不存在该配置文件,创建一个
- if not os.path.exists(configFile):
- os.makedirs('metaData')
- f=open('metaData/config.txt','w')
- f.close()
- config=MyConfig(configFile)
- '''Init configurations'''
- config.set('Program','UNIQFile')
- config.set('IsExcludeHiddenFiles','False')
- config.set('Images','.jpg/.jpeg/.bmp/.png/.gif/.ico')
- config.set('Audios','.mp3/.wav/.aiff/.wma/.aac')
- config.set('Videos','.mp4/.rmvb/.avi/.wmv/.mov')
- config.set('Documents','.pdf/.txt/.doc/.docx/.ppt/.pptx/.xls/.xlsx/.log')
- config.set('FileSizeUnit','MB')
- config.set('MaxFileSize',128*1024)
- config.set('FileSizeStart',0)
- config.set('FileSizeEnd', 128*1024)
- config.set('FileTypeOption','Default')
- config.set('SelectedFileTypes','None')
- config.set('OtherFilesSelected','None')
- config.set('IsImagesSelected','False')
- config.set('IsAudiosSelected','False')
- config.set('IsVideosSelected', 'False')
- config.set('IsDocumentsSelected','False')
- config.set('IsFileTypeFilterWorking','False')
- config.set('IsFileSizeFilterWorking','False')
- config.saveConfig()
- def LoadControls(self):
- '''必须先添加菜单,再创建panel容器,否则菜单会被包括在panel当中,导致panel布局错乱'''
- self.createMenu()
- '''再创建panel容器'''
- pan=wx.Panel(self)
- self.lblDir=wx.StaticText(pan,-1,'Dir:',style=wx.ALIGN_LEFT)
- self.txtFile=wx.TextCtrl(pan,size=(380,30))
- self.btnOpen=wx.Button(pan,label='Pick Directory')
- self.btnOpen.Bind(wx.EVT_BUTTON, self.OnOpen)
- self.btnList=wx.Button(pan,label='Find duplicated')
- self.btnList.Bind(wx.EVT_BUTTON, self.OnFind)
- self.btnRemove=wx.Button(pan,label='Remove duplicated')
- self.btnRemove.Bind(wx.EVT_BUTTON, self.OnRemove)
- hbox=wx.BoxSizer()
- hbox.Add(self.lblDir,proportion=0,flag=wx.LEFT,border=5)
- hbox.Add(self.txtFile,proportion=0,flag=wx.LEFT,border=5)
- hbox.Add(self.btnOpen,proportion=0,flag=wx.LEFT,border=5)
- hbox.Add(self.btnList,proportion=0,flag=wx.LEFT,border=5)
- hbox.Add(self.btnRemove,proportion=0,flag=wx.LEFT,border=5)
- self.txtContent=wx.TextCtrl(pan,style=wx.TE_MULTILINE|wx.HSCROLL)
- vbox=wx.BoxSizer(wx.VERTICAL)
- # vbox.Add(menuBar,proportion=0,flag=wx.EXPAND|wx.ALL,border=5)
- vbox.Add(hbox,proportion=0,flag=wx.EXPAND|wx.ALL,border=5)
- vbox.Add(self.txtContent,proportion=1,flag=wx.EXPAND,border=5)
- pan.SetSizer(vbox)
- def LoadMetaData(self):
- dict={}
- dict['Image']='.jpg/.jpeg/.bmp/.png/.gif/.ico'
- dict['Audio']='.mp3/.wav/.aiff/.wma/.aac'
- dict['Video']='.mp4/.rmvb/.avi/.wmv/.mov'
- dict['Document']='.pdf/.txt/.doc/.docx/.ppt/.pptx/.xls/.xlsx/.log'
- return dict
- def createMenu(self):
- menuBar=wx.MenuBar()
- menuFile=wx.Menu()
- mOpen=menuFile.Append(-1,'Open Directory')#添加一级子菜单
- mSave=menuFile.Append(-1,'Save Result')#添加一级子菜单
- menuFile.AppendSeparator()
- mExit=menuFile.Append(-1,'Exit')#添加一级子菜单
- menuBar.Append(menuFile,'&File')
- menuOptions=wx.Menu()
- mSetFilter=menuOptions.Append(-1,'Set Filters')
- # sbmFileTypefilter=mfilter.Append(-1,'File type filter')#添加二级子菜单
- # sbmFileHidden=mfilter.Append(-1,'Ignore option')#添加二级子菜单 忽略隐藏文件 hidden files
- # mIsRecursive=mfilter.Append(-1,'About the author')#添加二级子菜单
- menuBar.Append(menuOptions,'&Options')
- menuHelp=wx.Menu()
- mAbout=wx.Menu()
- sbmAboutApp=mAbout.Append(-1,'About this app')#添加二级子菜单
- sbmAboutAuthor=mAbout.Append(-1,'About the author')#添加二级子菜单
- menuHelp.AppendMenu(-1,'About',mAbout)#添加一级子菜单
- menuBar.Append(menuHelp,'&Help')
- '''绑定菜单事件'''
- self.Bind(wx.EVT_MENU, self.OnOpen, mOpen)
- self.Bind(wx.EVT_MENU, self.OnSave, mSave)
- self.Bind(wx.EVT_MENU, self.OnSetFilters, mSetFilter)
- self.Bind(wx.EVT_MENU, self.OnAboutApp, sbmAboutApp)
- self.Bind(wx.EVT_MENU, self.OnAboutAuthor, sbmAboutAuthor)
- self.Bind(wx.EVT_MENU, self.OnExit, mExit)
- '''最后组装'''
- self.SetMenuBar(menuBar)
- def OnSetFilters(self,event):
- dlg=DialogSetFilters(self)
- result=dlg.ShowModal()
- if result==wx.OK:
- pass
- elif result==wx.CANCEL:
- pass
- dlg.Destroy()
- def OnExit(self,event):
- self.Close()
- def OnAboutApp(self,event):
- dlg=DialogAboutApp(self)
- dlg.ShowModal()
- dlg.Destroy()
- def OnAboutAuthor(self,event):
- dlg=DialogAboutAuthor(self)
- dlg.ShowModal()
- dlg.Destroy()
- def OnOpen(self,event):
- dlg = wx.DirDialog(None,u"choose a folder",style=wx.DD_DEFAULT_STYLE)
- if dlg.ShowModal() == wx.ID_OK:
- dlg.Destroy()
- if dlg.GetPath():
- self.dirSelected=dlg.GetPath() #文件夹路径
- self.txtFile.SetValue(self.dirSelected)
- self.SetButtons('selected')
- self.txtContent.SetValue('Selected dirctory: %s\n'%self.dirSelected)
- def OnSave(self,event):
- if self.txtContent.GetValue()=='':
- wx.MessageBox('No results to save.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return
- dlg=wx.FileDialog(None, message="Choose a file",defaultDir="", defaultFile="", wildcard="*.txt", style=wx.SAVE)
- if dlg.ShowModal() == wx.ID_OK:
- dlg.Destroy()
- if dlg.GetPath():
- newfile=dlg.GetPath();
- # if os.path.exists(newfile):
- # confirmDlg=wx.MessageDialog(None,'The file selected already exists,do you want to overwrite?','Tip Message',wx.YES_NO|wx.ICON_QUESTION)
- # if confirmDlg.ShowModal() == wx.YES:
- self.SaveResult(newfile)
- wx.MessageBox('Result has been saved at %s.'%newfile,'Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return
- def SaveResult(self,path):
- f=open(path,'wb')
- content=self.txtContent.Value
- content=content.encode('utf-8')
- lines=[]
- while content.find('\n')!=-1:
- line=content[:content.index('\n')+1]
- lines.append(line)
- content=content[content.index('\n')+1:]
- f.writelines(lines)
- f.close()
- def OnFind(self,event):
- if not self.txtFile.GetValue() or not os.path.isdir(self.txtFile.GetValue()):
- wx.MessageBox('please select a valid directory first.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return
- self.dirSelected=self.txtFile.GetValue()
- self.txtContent.SetValue('')
- msg='Finding duplicated files in %s\n'%self.dirSelected
- self.txtContent.SetValue(msg)
- WorkerThread(self,self.dirSelected,'find',msg)
- def OnRemove(self,event):
- if not self.txtFile.GetValue() or not os.path.isdir(self.txtFile.GetValue()):
- wx.MessageBox('please select a valid directory first.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return
- self.dirSelected=self.txtFile.GetValue()
- self.txtContent.SetValue('')
- msg='Removing duplicated files in %s\n'%self.dirSelected
- self.txtContent.SetValue(msg)
- WorkerThread(self,self.dirSelected,'remove',msg)
- def BtnStopHandler(self,event):
- pass
- def SetButtons(self,status):
- if status=='init':
- self.btnOpen.Enable()
- self.btnList.Disable()
- self.btnRemove.Disable()
- # self.btnStop.Disable()
- elif status=='operating':
- self.btnOpen.Disable()
- self.btnList.Disable()
- self.btnRemove.Disable()
- # self.btnStop.Enable()
- elif status=='completed':
- self.btnOpen.Enable()
- self.btnList.Enable()
- self.btnRemove.Enable()
- # self.btnStop.Disable()
- elif status=='selected':
- self.btnOpen.Enable()
- self.btnList.Enable()
- self.btnRemove.Enable()
- # self.btnStop.Disable()
- if __name__=="__main__":
- app=wx.App()
- MyFrame().Show()
- app.MainLoop()
WorkerThread.py:
- # -*- coding: gbk -*-
- '''
- Author:@DoNotSpyOnMe
- Blog: http://www.cnblogs.com/aaronhoo
- '''
- from MyConfig import MyConfig
- import threading
- import math
- import platform,os
- import hashlib
- from Utils import Utils
- class WorkerThread(threading.Thread):
- def __init__(self,frame,dir,operation,msg):
- """初始化工作线程: 把主窗口传进来"""
- threading.Thread.__init__(self)
- self.frame = frame#传入主窗口,以便在线程中操作UI界面
- self.dir=dir
- self.operation=operation
- self.LoadConfigFile()
- self.msg=msg
- self.setDaemon(True)#设置子线程随UI主线程结束而结束
- self.MEGATOBYTENUMBER=math.pow(2,20)#将MB数转为字节数
- # self.SetDirSplitor()
- self.Utils=Utils()
- self.start()
- #----------------------------------------------------------------------
- '''加载配置文件'''
- def LoadConfigFile(self):
- configFile='metaData/config.txt'
- self.config=MyConfig(configFile)
- def run(self):
- """执行工作线程"""
- self.frame.SetButtons('operating')
- try:
- if self.operation=='find':
- self.listSameFile(self.dir)
- self.frame.btnList.Enable()
- elif self.operation=='remove':
- self.removeSameFile(self.dir)
- self.frame.btnRemove.Enable()
- except Exception,e:
- print e
- finally:
- self.frame.SetButtons('completed')
- #
- # def stop(self):
- # self.keepRunning=False
- def appendMsg(self,msg):
- if self.frame:
- #以下方式可以实现终端式的刷新:自动滚动到最新行
- self.frame.txtContent.AppendText(msg+'\n')
- '''Waring:disabled feature,not implemented'''
- def filterHiddenFiles(self):
- IsExcludeHiddenFiles=eval(self.config.get('IsExcludeHiddenFiles'))
- def filterFileTypes(self,files,FileTypeOption,selectedFileTypes):
- fileCopy=[f for f in files]#fileCopy is a copy of files
- if FileTypeOption=='Default':
- pass
- elif FileTypeOption=='Exclude':
- for f in files:
- ftype=self.Utils.getFileType(f)
- if self.Utils.isInList(selectedFileTypes, ftype):
- fileCopy.remove(f)
- elif FileTypeOption=='Include':
- for f in files:
- ftype=self.Utils.getFileType(f)
- if not self.Utils.isInList(selectedFileTypes, ftype):
- fileCopy.remove(f)
- del files
- return fileCopy
- def getFileRange(self):
- fileSizeStart=float(self.config.get('FileSizeStart'))*self.MEGATOBYTENUMBER
- inputFileSizeEnd=self.config.get('FileSizeEnd')
- if inputFileSizeEnd=='None':
- fileSizeEnd=eval(self.config.get('MaxFileSize'))*self.MEGATOBYTENUMBER
- else:
- fileSizeEnd=float(inputFileSizeEnd)*self.MEGATOBYTENUMBER
- return (fileSizeStart,fileSizeEnd)
- def isFileSizeInRange(self,fileSize,fileSizeStart,fileSizeEnd):
- return fileSizeStart<=fileSize<=fileSizeEnd
- def findSameSizeFiles(self,files):
- dicSize={}
- fileTypeOption=self.config.get('FileTypeOption')
- selectedFileTypes=self.config.get('SelectedFileTypes').split(';')
- '''过滤文件类型'''
- files=self.filterFileTypes(files,fileTypeOption,selectedFileTypes)
- tpFileRange=self.getFileRange()
- for f in files:
- size=self.Utils.getFileSize(f)
- '''过滤文件大小'''
- if not self.isFileSizeInRange(size,tpFileRange[0],tpFileRange[1]):
- continue
- if not dicSize.has_key(size):
- dicSize[size]=f
- else:
- dicSize[size]=dicSize[size]+';'+f
- dicCopy=dicSize.copy()
- for k in dicSize.iterkeys():
- if dicSize[k].find(';')==-1:
- dicCopy.pop(k)
- del dicSize
- return dicCopy
- def findSameMD5Files(self,files):
- dicMD5={}
- for f in files:
- self.appendMsg('calculating md5 value of file %s'%f)
- md5=self.Utils.getFileMD5(f)
- if not dicMD5.has_key(md5):
- dicMD5[md5]=f
- else:
- dicMD5[md5]=dicMD5[md5]+';'+f
- dicCopy=dicMD5.copy()
- for k in dicMD5.iterkeys():
- if dicMD5[k].find(';')==-1:
- dicCopy.pop(k)
- del dicMD5
- return dicCopy
- def isFileTypeFilterWorking(self):
- return eval(self.config.get('IsFileTypeFilterWorking'))
- def isFileSizeFilterWorking(self):
- return eval(self.config.get('IsFileTypeFilterWorking'))
- def LoadFiltersInfo(self):
- flag1=self.isFileTypeFilterWorking()
- flag2=self.isFileSizeFilterWorking()
- if flag1 or flag2:
- self.appendMsg('Tips:file filters is working:')
- if flag1:
- self.appendMsg("File Type Filter:")
- option=self.config.get('FileTypeOption')
- fileTypes=self.config.get('SelectedFileTypes')
- self.appendMsg("%s:%s\n"%(option if option=='Exclude' else 'Include only',fileTypes))
- if flag2:
- self.appendMsg("File Size Filter:")
- start=self.config.get('FileSizeStart')
- end=self.config.get('FileSizeEnd')
- self.appendMsg("%s MB - %s MB\n"%(start,end))
- self.appendMsg('Start working...\n')
- def listSameFile(self,mydir):
- msg=''
- msgUniq='\nCongratulations,all files are unique.'
- try:
- existsFlag=False
- files=self.Utils.getAllFiles(mydir)
- self.appendMsg('%s files found in directory %s\n'%(len(files),mydir))
- self.LoadFiltersInfo()
- dicFileOfSameSize=self.findSameSizeFiles(files)
- groupCount=0
- if dicFileOfSameSize=={}:
- self.appendMsg(msgUniq)
- return
- else:
- for k in dicFileOfSameSize.iterkeys():
- filesOfSameSize=dicFileOfSameSize[k].split(';')
- dicSameMD5file=self.findSameMD5Files(filesOfSameSize)
- if dicSameMD5file!={}:
- existsFlag=True
- for k in dicSameMD5file.iterkeys():
- msg=msg+'md5 %s: %s'%(k,dicSameMD5file[k])+'\n'
- groupCount+=1
- if not existsFlag:
- msg=msgUniq
- else:
- msg='\nDuplicated files:\n'+msg+'\nFound %s groups of duplicated files totally.'%groupCount
- except Exception,e:
- print e
- msg='Exception occured.'
- finally:
- self.appendMsg(msg+'\n'+'\nOperation finished.')
- def removeSameFile(self,mydir):
- msg=''
- msgUniq='\nCongratulations,no file is removed since they are all unique.'
- try:
- existsFlag=False
- files=self.Utils.getAllFiles(mydir)
- self.appendMsg('%s files found in directory %s\n'%(len(files),mydir))
- self.LoadFiltersInfo()
- dicFileOfSameSize=self.findSameSizeFiles(files)
- if not self.config.get('FileTypeOption')=='Default':
- self.appendMsg('Tips:file filters is working.\n')
- if dicFileOfSameSize=={}:
- self.appendMsg(msgUniq)
- return
- else:
- #list the duplicated files first:
- self.appendMsg('Finding duplicted files:')
- dicFiltered={}
- groupCount=0
- for k in dicFileOfSameSize.iterkeys():
- filesOfSameSize=dicFileOfSameSize[k].split(';')
- dicSameMD5file=self.findSameMD5Files(filesOfSameSize)
- if dicSameMD5file!={}:
- existsFlag=True
- for k in dicSameMD5file.iterkeys():
- msg=msg+'md5 %s: %s'%(k,dicSameMD5file[k])+'\n'
- groupCount+=1
- dicFiltered[k]=dicSameMD5file[k]
- if not existsFlag:
- self.appendMsg(msgUniq)
- return
- else:
- #then remove the duplicated files:
- self.appendMsg('\nDuplicated files:\n'+msg+'\nFound %s groups of duplicated files totally.'%groupCount)
- removeCount=0
- for k in dicFiltered.iterkeys():
- sameFiles=dicFiltered[k].split(';')
- flagRemove=False
- for f in sameFiles:
- if not flagRemove:
- flagRemove=True
- else:
- self.appendMsg('Removing file: %s'%f)
- os.remove(f)
- removeCount=removeCount+1
- self.appendMsg('\n%s files are removed.\n'%removeCount)
- except Exception,e:
- print e
- msg='\nException occured.'
- self.appendMsg(msg)
- finally:
- self.appendMsg('\n\nOperation finished.')
Dialogs.py:
- # -*- coding: gbk -*-
- '''
- Author:@DoNotSpyOnMe
- Blog: http://www.cnblogs.com/aaronhoo
- '''
- import wx
- from wx.html import HtmlWindow
- from MyConfig import MyConfig
- import re
- from Utils import Utils
- class DialogSetFilters(wx.Dialog):
- def __init__(self, frame):
- wx.Dialog.__init__(self, frame, -1,'Filters',size=(560, 560) )
- self.frame=frame
- self.LoadConfigFile()
- self.LoadControls()
- self.InitWithConfigData()
- #为控件绑定事件
- self.BindControlEvent()
- self.Utils=Utils()
- '''加载配置文件'''
- def LoadConfigFile(self):
- configFile='metaData/config.txt'
- self.config=MyConfig(configFile)
- def LoadControls(self):
- panel = wx.Panel(self)
- sizer = wx.GridBagSizer(5,5)#预计需要5行5列,但实际可以超出该设定,GridGagSizer会自动增长
- '''加载隐藏文件过滤器的控件'''
- sb = wx.StaticBox(panel, label="Hidden File Filter",size=(500,100))
- boxsizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
- self.cbExclHidden=wx.CheckBox(panel, label="Exclude hidden files(Waring:disabled feature)")
- boxsizer.Add((10,10),flag=wx.BOTTOM|wx.TOP,border=10)
- boxsizer.Add(self.cbExclHidden,flag=wx.BOTTOM|wx.TOP,border=10)
- sizer.Add(boxsizer, pos=(0,0),span=(1,5),flag=wx.LEFT|wx.TOP,border=25)
- #pos(0,0)表示位置在第1行,第1列,span=(1,5)表示该boxsizer占据1个行5个列的空间
- '''加载文件类型过滤器的控件'''
- sb = wx.StaticBox(panel, label="File Type Filter",size=(500,500))
- boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
- self.rdDefault=wx.RadioButton(panel,-1,'Default',style=wx.RB_GROUP)
- self.rdExclFtyp=wx.RadioButton(panel,-1,'Exclude')
- self.rdInclFtyp=wx.RadioButton(panel,-1,'Include Only')
- subHbox=wx.BoxSizer(wx.HORIZONTAL)
- subHbox.Add(self.rdDefault,flag=wx.LEFT,border=25)
- subHbox.Add(self.rdExclFtyp,flag=wx.LEFT,border=5)
- subHbox.Add(self.rdInclFtyp,flag=wx.LEFT,border=5)
- boxsizer.Add(subHbox)
- '''从配置文件读固定配置'''
- self.cbImage=wx.CheckBox(panel, label="Images("+self.config.get('Images')+")")
- self.cbAudio=wx.CheckBox(panel, label="Audios("+self.config.get('Audios')+")")
- self.cbVideo=wx.CheckBox(panel, label="Videos("+self.config.get('Videos')+")")
- self.cbDoc=wx.CheckBox(panel, label="Documents("+self.config.get('Documents')+")")
- self.lblOtherFiles=wx.StaticText(panel,label="Other files ( enter file extensions,split them with \";\" )")
- self.txtOtherFiles=wx.TextCtrl(panel,size=(400,25))
- subVbox=wx.BoxSizer(wx.VERTICAL)
- subVbox.Add(self.cbImage,flag=wx.LEFT|wx.TOP,border=10)
- subVbox.Add(self.cbAudio,flag=wx.LEFT|wx.TOP,border=10)
- subVbox.Add(self.cbVideo,flag=wx.LEFT|wx.TOP,border=10)
- subVbox.Add(self.cbDoc,flag=wx.LEFT|wx.TOP,border=10)
- subVbox.Add(self.lblOtherFiles,flag=wx.LEFT|wx.TOP,border=10)
- subVbox.Add(self.txtOtherFiles,flag=wx.LEFT|wx.TOP|wx.BOTTOM|wx.RIGHT,border=10)
- boxsizer.Add(subVbox)
- sizer.Add(boxsizer, pos=(1,0), span=(6,5),flag=wx.TOP|wx.LEFT,border=25)
- #pos(1,0)表示位置在第2行,第1列,span=(1,5)表示该boxsizer占据6个行5个列的空间
- self.cbExclHidden.Disable()
- '''加载文件大小过滤器的控件'''
- sb = wx.StaticBox(panel, label="File Size Filter",size=(500,200))
- boxsizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
- self.txtFileSize1=wx.TextCtrl(panel,size=(100,25))
- lblFileSize1 = wx.StaticText(panel,label="MB")
- lblTo = wx.StaticText(panel,label="--")
- self.txtFileSize2=wx.TextCtrl(panel,size=(100,25))
- lblFileSize2 = wx.StaticText(panel,label="MB")
- boxsizer.Add(self.txtFileSize1,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
- boxsizer.Add(lblFileSize1,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
- boxsizer.Add((20,10))
- boxsizer.Add(lblTo,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
- boxsizer.Add((20,10))
- boxsizer.Add(self.txtFileSize2,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
- boxsizer.Add(lblFileSize2,flag=wx.LEFT|wx.BOTTOM|wx.TOP|wx.EXPAND,border=10)
- sizer.Add(boxsizer, pos=(7,0),span=(1, 5),flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT , border=25)#pos=(7,0)
- #pos等于(7,0)表示位置在第8行,第1列,之所以是第8行,因为前面两个控件已经占据了1+6=7行,所以至少从第8行开始,否则布局错乱
- '''加载按钮'''
- self.btnOK = wx.Button(panel, label="OK")
- self.btnCancel = wx.Button(panel, label="Cancel")
- sizer.Add(self.btnOK, pos=(9,3))
- sizer.Add(self.btnCancel, pos=(9,4),flag=wx.BOTTOM|wx.RIGHT, border=5)
- panel.SetSizer(sizer)
- panel.Layout()
- def InitWithConfigData(self):
- self.cbExclHidden.SetValue(str(self.config.get('IsExcludeHiddenFiles'))=='True')
- self.rdDefault.SetValue(self.config.get('FileTypeOption')=='Default')
- if self.rdDefault.GetValue():
- self.DisableFileTypeFilterControls()
- self.rdExclFtyp.SetValue(self.config.get('FileTypeOption')=='Exclude')
- self.rdInclFtyp.SetValue(self.config.get('FileTypeOption')=='Include')
- self.cbImage.SetValue(str(self.config.get('IsImagesSelected'))=='True')
- self.cbAudio.SetValue(str(self.config.get('IsAudiosSelected'))=='True')
- self.cbVideo.SetValue(str(self.config.get('IsVideosSelected'))=='True')
- self.cbDoc.SetValue(str(self.config.get('IsDocumentsSelected'))=='True')
- self.txtOtherFiles.SetValue('' if self.config.get('OtherFilesSelected')=='None' else self.config.get('OtherFilesSelected'))
- self.txtFileSize1.SetValue(self.config.get('FileSizeStart'))
- inputFileSizeEnd=self.config.get('FileSizeEnd')
- if inputFileSizeEnd=='None':
- self.txtFileSize2.SetValue('')
- else:
- self.txtFileSize2.SetValue(inputFileSizeEnd)
- def BindControlEvent(self):
- for eachRadio in [self.rdDefault, self.rdExclFtyp, self.rdInclFtyp]:#绑定事件
- self.Bind(wx.EVT_RADIOBUTTON, self.OnRadio, eachRadio)
- self.Bind(wx.EVT_BUTTON, self.OnOK,self.btnOK)
- self.Bind(wx.EVT_BUTTON, self.OnCancel,self.btnCancel)
- def ValidateInput(self):
- if not self.rdDefault.GetValue():
- pattern=re.compile('[^\d\w\.\;]+')#数字、英文字母、点号之外的字符定义为非法字符
- if re.search(pattern,self.txtOtherFiles.GetValue()):
- wx.MessageBox('Please enter valid file extensions and correct splitor(;).','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return False
- if (self.rdInclFtyp.GetValue() or self.rdExclFtyp.GetValue()) and self.getSelectedFileTypes()=='':
- wx.MessageBox('Please enter or select at least one file type.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return False
- if self.txtFileSize1.GetValue()!='' and not self.Utils.isNumReg(self.txtFileSize1.GetValue()):
- wx.MessageBox('Please do not enter or enter valid number in the first file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return False
- elif self.txtFileSize2.GetValue()!='' and not self.Utils.isNumReg(self.txtFileSize2.GetValue()):
- wx.MessageBox('Please do not enter or enter valid number in the second file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return False
- elif self.Utils.isNumReg(self.txtFileSize1.GetValue()) and self.Utils.isNumReg(self.txtFileSize2.GetValue()) and eval(self.txtFileSize1.GetValue())>eval(self.txtFileSize2.GetValue()):
- wx.MessageBox('The first file size should not be greater than the second file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return False
- elif self.Utils.isNumReg(self.txtFileSize2.GetValue()) and eval(self.txtFileSize2.GetValue())>(self.config.get('MaxFileSize')):
- wx.MessageBox('The second file size should not be greater than '+self.config.get('MaxFileSize'),'Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
- return False
- return True
- def getSelectedFileTypes(self):
- selectedFileTypes=''
- if not self.rdDefault.GetValue():
- fileType=''
- for chk in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc]:
- if chk.IsChecked():
- label=chk.GetLabel()
- fileType=fileType+label[label.find('(')+1:label.find(')')]+';'
- selectedFileTypes=fileType
- selectedFileTypes+=self.Utils.FormatOtherFileTypes(self.txtOtherFiles.GetValue())
- selectedFileTypes= selectedFileTypes.replace('/',';').lower()
- return selectedFileTypes
- def OnOK(self,event):
- if self.ValidateInput():
- IsExclHiddenFiles=self.cbExclHidden.GetValue()
- if self.rdDefault.GetValue():
- FileTypeOption='Default'
- elif self.rdExclFtyp.GetValue():
- FileTypeOption='Exclude'
- elif self.rdInclFtyp.GetValue():
- FileTypeOption='Include'
- selectedFileTypes=self.getSelectedFileTypes()
- if selectedFileTypes=='':
- self.config.set('SelectedFileTypes','None')
- else:
- self.config.set('SelectedFileTypes',selectedFileTypes)
- '''save filters into the config file'''
- self.config.set('IsExcludeHiddenFiles',IsExclHiddenFiles)
- self.config.set('FileTypeOption',FileTypeOption)
- self.config.set('IsImagesSelected',self.cbImage.IsChecked())
- self.config.set('IsAudiosSelected',self.cbAudio.IsChecked())
- self.config.set('IsVideosSelected',self.cbVideo.IsChecked())
- self.config.set('IsDocumentsSelected',self.cbDoc.IsChecked())
- otherFileTyps=self.txtOtherFiles.GetValue()
- if otherFileTyps=='':
- self.config.set('OtherFilesSelected','None')
- else:
- self.config.set('OtherFilesSelected',self.Utils.FormatOtherFileTypes(self.txtOtherFiles.GetValue()))
- fileSizeStart=self.txtFileSize1.GetValue()
- fileSizeEnd=self.txtFileSize2.GetValue()
- if fileSizeStart!='':
- self.config.set('FileSizeStart',fileSizeStart)
- if fileSizeEnd=='':
- self.config.set('FileSizeEnd','None')
- else:
- self.config.set('FileSizeEnd',fileSizeEnd)
- if FileTypeOption=='Default':
- self.config.set('IsFileTypeFilterWorking','False')
- else:
- self.config.set('IsFileTypeFilterWorking','True')
- if fileSizeStart=='' or fileSizeEnd!=self.config.get('MaxFileSize'):
- self.config.set('IsFileSizeFilterWorking','True')
- else:
- self.config.set('IsFileSizeFilterWorking','False')
- self.config.saveConfig()
- self.Close()
- def OnCancel(self,event):
- self.Close()
- def OnRadio(self,event):
- selectedRadio=event.GetEventObject()
- if selectedRadio==self.rdDefault:
- self.DisableFileTypeFilterControls()
- else:
- self.EnableFileTypeFilterControls()
- def DisableFileTypeFilterControls(self):
- for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc,self.txtOtherFiles,self.lblOtherFiles]:
- control.Disable()
- for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc]:
- control.SetValue(False)
- self.txtOtherFiles.SetValue('')
- def EnableFileTypeFilterControls(self):
- for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc,self.txtOtherFiles,self.lblOtherFiles]:
- control.Enable()
- class DialogAboutApp(wx.Dialog):
- text = '''
- <html>
- <head>
- <style type="text/css">
- body {font-family:serif;background-color: #ACAA60;}
- </style>
- </head>
- <body> <!--bgcolor="#ACAA60"-->
- <center>
- <table bgcolor="#455481" width="100%" cellspacing="0" cellpadding="0" border="1">
- <tr>
- <td align="center"><h1><span style="color:white">UNIQ File-wxPython</span></h1></td>
- </tr>
- </table>
- </center>
- <p><b>UNIQ File-wxPython</b> is a program designed for clearing duplicated files and saving
- memory space.
- It's broght to you by Aaron Hu(<b><span style="color:red">@DoNotSpyOnMe</span></b> on Sina Weibo).<br/>
- <!--<a href="http://weibo.com/u/1737184870" target="_blank">Contact</a> the author now.-->
- </p>
- </body>
- </html>
- '''
- def __init__(self, parent):
- wx.Dialog.__init__(self, parent, -1,'About this app',
- size=(440, 400) )
- window = HtmlWindow(self)
- window.SetPage(self.text)
- button = wx.Button(self, wx.ID_OK, 'OK')
- sizer = wx.BoxSizer(wx.VERTICAL)
- sizer.Add(window, 1, wx.EXPAND|wx.ALL, 5)
- sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
- self.SetSizer(sizer)
- self.Layout()
- class DialogAboutAuthor(wx.Dialog):
- text = '''
- <html>
- <head>
- <style type="text/css">
- body {font-family:serif;background-color: #ACAA60;}
- </style>
- </head>
- <body> <!--bgcolor="#ACAA60"-->
- <center>
- <table bgcolor="#455481" width="100%" cellspacing="0" cellpadding="0" border="1">
- <tr>
- <td align="center"><h1><span style="color:white">UNIQ File-wxPython</span></h1></td>
- </tr>
- </table>
- </center>
- <p>
- Contact the author at:<br/><br/>
- Sina Weibo:<b><span style="color:red">@DoNotSpyOnMe</span></b><br/>
- cnblogs.com:<b>http://www.cnblogs.cn/aaronhoo</b><br/>
- </p>
- </body>
- </html>
- '''
- def __init__(self, parent):
- wx.Dialog.__init__(self, parent, -1,'About the author',
- size=(440, 400) )
- window = HtmlWindow(self)
- window.SetPage(self.text)
- button = wx.Button(self, wx.ID_OK, 'OK')
- sizer = wx.BoxSizer(wx.VERTICAL)
- sizer.Add(window, 1, wx.EXPAND|wx.ALL, 5)
- sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
- self.SetSizer(sizer)
- self.Layout()
MyConfig.py:
- # -*- coding: gbk -*-
- '''
- Created on 2016年4月20日
- Author: @DoNotSpyOnMe
- '''
- import re,os
- class MyConfig:
- def __init__(self,filepath):
- self.configFile=''
- self.lines=[]
- self.read(filepath)
- def read(self,filepath):
- try:
- if not os.path.isfile(filepath):
- print 'Error:file %s dose not exist.'%filepath
- return
- pattern=re.compile('^.+\.txt$')
- if not re.search(pattern, filepath):
- print 'Error:only .txt file is supported as config file.'
- return
- f=open(filepath,'r')
- self.lines=f.readlines()
- f.close()
- self.configFile=filepath
- except Exception,e:
- print e
- def get(self,variable):
- try:
- if self.validate():
- pattern=re.compile('^'+variable+' = ')
- for line in self.lines:
- if re.search(pattern,line):
- val= line[line.find(' = ')+3:].strip()
- return val
- print "Error:variable '%s' is not found in config file."%variable
- except Exception,e:
- print e
- def set(self,variable,newValue):
- try:
- if self.validate():
- count=0
- pattern=re.compile('^'+variable+' = ')
- for line in self.lines:
- if re.search(pattern,line):
- newline=line.replace(line[line.find(' = ')+3:line.rfind('\n')],str(newValue))
- self.lines[count]=newline
- return
- count=count+1
- #if the variable is no found,create it.
- newline=variable+' = '+str(newValue)
- self.lines.append(newline+'\n')
- except Exception,e:
- print e
- def setAndSave(self,variable,newValue):
- try:
- if self.validate():
- count=0
- found=False
- pattern=re.compile('^'+variable+' = ')
- for line in self.lines:
- if re.search(pattern,line):
- newline=line.replace(line[line.find(' = ')+3:line.rfind('\n')],str(newValue))
- self.lines[count]=newline
- found=True
- break
- count=count+1
- if not found:
- newline=variable+' = '+str(newValue)
- self.lines.append(newline+'\n')
- #save changes.
- f=open(self.configFile,'w')
- f.writelines(self.lines)
- f.close()
- except Exception,e:
- print e
- def saveConfig(self):
- try:
- if self.validate():
- f=open(self.configFile,'w')
- f.writelines(self.lines)
- f.close()
- except Exception,e:
- print e
- def validate(self):
- if not self.configFile:
- print 'Error:config file not found.'
- return False
- return True
Utils.py:
- # -*- coding: gbk -*-
- import hashlib
- import os
- import platform
- import re
- class Utils:
- def __init__(self):
- self.getDirSplitor()
- def isNumReg(self,str):
- regInt='^0$|^[1-9]\d*$'#不接受09这样的为整数
- regFloat='^0\.\d+$|^[1-9]\d*\.\d+$'
- regIntOrFloat=regInt+'|'+regFloat#整数或小数
- patternIntOrFloat=re.compile(regIntOrFloat)#创建pattern对象,以便后续可以复用
- if re.search(regIntOrFloat,str):
- return True
- else:
- return False
- '''使得用户在其他文件类型框中可以输入'.xml;.html'或者'xml;html;'或者'xml;.html'这三种格式'''
- def FormatOtherFileTypes(self,fileTypeInput):
- ls=fileTypeInput.split(';')
- lsnew=[]
- for e in ls:
- if e!='':
- if e.find('.')==-1:
- lsnew.append('.'+e)
- else:
- lsnew.append(e)
- s=''
- for e in lsnew:
- s=s+e+';'
- return s
- def divideList(self,ls,each):
- dividedLs=[]
- eachExact=float(each)
- groupCount=len(ls)/each
- groupCountExact=len(ls)/eachExact
- start=0
- for i in xrange(groupCount):
- dividedLs.append(ls[start:start+each])
- start=start+each
- if groupCount<groupCountExact:#假如有余数,将剩余的所有元素加入到最后一个分组
- dividedLs.append(ls[groupCount*each:])
- return dividedLs
- def getFileSize(self,filePath):
- return os.path.getsize(filePath)
- ''' 一般文件的md5计算方法,一次读取文件的全部内容'''
- def CalcMD5(filepath):
- with open(filepath,'rb') as f:
- md5obj = hashlib.md5()
- md5obj.update(f.read())
- hash = md5obj.hexdigest()
- return hash
- '''大文件计算md5的方法,分批读取文件内容,防止内存爆掉'''
- def getFileMD5(self,filename):
- if not os.path.isfile(filename):
- return
- myhash = hashlib.md5()
- f = open(filename,'rb')
- while True:
- b = f.read(8*1024)
- if not b :
- break
- myhash.update(b)
- f.close()
- return myhash.hexdigest()
- def getAllFiles(self,directory):
- files=[]
- # dirSplitor=getDirSplitor()
- for dirpath, dirnames,filenames in os.walk(directory):
- if filenames!=[]:
- for file in filenames:
- files.append(dirpath+self.dirSplitor+file)
- files.sort(key=len)#按照文件名的长度排序
- return files
- def isInList(self,myList,someValue):
- try:
- myList.index(someValue)
- return True
- except:
- return False
- def getFileType(self,file):
- # dirSplitor=getDirSplitor()
- fileName=file[file.rfind(self.dirSplitor)+1:]
- fileType=file[file.rfind('.'):]
- return fileType.lower()
- def getDirSplitor(self):
- osType=platform.system()
- if osType=='Windows':
- self.dirSplitor= '\\'
- elif osType=='Linux':
- self.dirSplitor= '/'
- else:
- self.dirSplitor= '/'
python查找并删除相同文件-UNIQ File-wxPython-v6的更多相关文章
- python查找并删除相同文件-UNIQ File-wxPython版本
今天用wxPython做了一个GUI程序,我称之为UNIQ File,实现查找指定目录内的相同文件,主要原理是计算文件的md5值(计算前先找出文件大小相同的文件,然后计算这些文件的md5值,而不是所有 ...
- python查找并删除相同文件-UNIQ File-script版本
今天用wxPython做了一个GUI程序,实现查找指定目录内的相同文件,主要原理是计算文件的md5值(计算前先找出文件大小相同的文件,然后计算这些文件的md5值,而不是所有文件都计算,大大减少了md5 ...
- Linux查找并删除重复文件的命令行fdupes工具,dupeGuru图形工具
查了几十个网页,找到这个接近满意的解决方案http://unix.stackexchange.com/questions/146197/fdupes-delete-files-aft... 不过正则里 ...
- Linux系统中查找、删除重复文件,释放磁盘空间。
在Linux系操作系统中查找并删除重复文件的方法的确有很多,不过这里介绍的是一款非常简单实用的软件FSlint.FSlint是一个重复文件查找工具,可以使用它来清除不必要的重复文件,笔者经常使用它来释 ...
- Python小工具--删除svn文件
有的时候我们需要删除项目下的svn相关文件,但是SVN会在所有的目录下都创建隐藏文件.svn,手工一个个目录查找然后删除显然比较麻烦.所以这里提供了一个Python小工具用于批量删除svn的相关文件: ...
- 利用lsof命令查找已经删除的文件来释放磁盘空间
测试环境一台服务器/目录空间使用率达到97%,但是通过du -sh *发现实际空间没用到那么多,初步怀疑,之前删除的文件,有运行中的进程一直占用,导致空间没有释放,如图通过du -sh *发现共实际使 ...
- 在 Linux 中查找和删除重复文件
原文链接:https://www.linuxprobe.com/linux-FSlint.html FSlint同时具有GUI和CLI模式.因此,对于新手来说,这是一个用户友好的工具.FSlint不仅 ...
- fso查找被删除的文件
<html> <head> </head> <body> 源目录:<input id="txtOld" value=" ...
- python 递归删除空文件夹
Python如何递归删除空文件夹 1.Python如何递归删除空文件夹,这个问题很常见.但大多数人的解决办法都是自己实现递归函数解决这个问题,其实根本不用那么麻烦.Python中的os.walk提供了 ...
随机推荐
- throw er; // Unhandled 'error' event
events.js:72 throw er; // Unhandled 'error' event ^Error: listen EADDRINUSE at errnoException (net.j ...
- Mac按键
⌘——Command ⌃ ——Control ⌥——Option (alt) ⇧——Shift ⇪——Caps Lock ⌫——Delete
- 新浪微博客户端(50)-解决输入Emotion表情逐渐变小的问题
UITextView+Extension.h #import <UIKit/UIKit.h> @interface UITextView (Extension) /** 插入属性文本 */ ...
- matlab求解相关系数
最近收到一项新任务,要求两个矩阵的相关系数,说白了就是转换成向量两两计算.本来这个工作我是想自己写个小程序搞定的,但是大家纷纷反映matlab自带了此项功能,本着活到老学到老的心态,我开始查找这个函数 ...
- \r,\n,\r\n的区别
http://www.studyofnet.com/news/285.html \n是换行,英文是New line,表示使光标到行首\r是回车,英文是Carriage return,表示使光标下移一格 ...
- jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法
前言: 前一篇文章中重点总结了一下then方法,它主要用来处理多个异步任务按顺序执行,即前一个任务处理完了,再继续下一个,以此类推: 而这一章节jQuery.when方法也是处理多个异步任务,它把多个 ...
- Javascript 中我很想说说的 this
this是每一个想要深入学习Javascript的人必过的一关,我为this看过很多书查过很多资料,虽然对this有了一定的了解并且也经常使用this,但是如果有人问我 this是什么呀? 我依旧不 ...
- edwin报警和监控平台近期的更新(python源码)
edwin从发布以来, 得到了不少关注, 获得了不少star. 最近又做了一些很有意义的改进, 同时完善了部分文档. 项目地址: https://github.com/harryliu/edwin , ...
- iOS:使用MVC模式帮ViewController瘦身
如何给UIViewController瘦身 随着程序逻辑复杂度的提高,你是否也发现了App中一些ViewController的代码行数急剧增多,达到了2,3千行,甚至更多.这时如果想再添加一点功能或者 ...
- C#5.0 特性
Visual Studio 2012 中 Visual C# 的新增功能 Lambda表达式 表达式树:把代码,转换成数据,然后分析数据发现其组成部分,最后转换成可以传递到其他程序的字符串 LinQ表 ...