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

截图:

源代码:

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\n'%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. def SaveResult(self,path):
  174. f=open(path,'wb')
  175. content=self.txtContent.Value
  176. content=content.encode('utf-8')
  177. lines=[]
  178. while content.find('\n')!=-1:
  179. line=content[:content.index('\n')+1]
  180. lines.append(line)
  181. content=content[content.index('\n')+1:]
  182. f.writelines(lines)
  183. f.close()
  184.  
  185. def OnFind(self,event):
  186. if not self.txtFile.GetValue() or not os.path.isdir(self.txtFile.GetValue()):
  187. wx.MessageBox('please select a valid directory first.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  188. return
  189. self.dirSelected=self.txtFile.GetValue()
  190. self.txtContent.SetValue('')
  191. msg='Finding duplicated files in %s\n'%self.dirSelected
  192. self.txtContent.SetValue(msg)
  193. WorkerThread(self,self.dirSelected,'find',msg)
  194.  
  195. def OnRemove(self,event):
  196. if not self.txtFile.GetValue() or not os.path.isdir(self.txtFile.GetValue()):
  197. wx.MessageBox('please select a valid directory first.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  198. return
  199. self.dirSelected=self.txtFile.GetValue()
  200. self.txtContent.SetValue('')
  201. msg='Removing duplicated files in %s\n'%self.dirSelected
  202. self.txtContent.SetValue(msg)
  203. WorkerThread(self,self.dirSelected,'remove',msg)
  204.  
  205. def BtnStopHandler(self,event):
  206. pass
  207.  
  208. def SetButtons(self,status):
  209. if status=='init':
  210. self.btnOpen.Enable()
  211. self.btnList.Disable()
  212. self.btnRemove.Disable()
  213. # self.btnStop.Disable()
  214. elif status=='operating':
  215. self.btnOpen.Disable()
  216. self.btnList.Disable()
  217. self.btnRemove.Disable()
  218. # self.btnStop.Enable()
  219. elif status=='completed':
  220. self.btnOpen.Enable()
  221. self.btnList.Enable()
  222. self.btnRemove.Enable()
  223. # self.btnStop.Disable()
  224. elif status=='selected':
  225. self.btnOpen.Enable()
  226. self.btnList.Enable()
  227. self.btnRemove.Enable()
  228. # self.btnStop.Disable()
  229.  
  230. if __name__=="__main__":
  231. app=wx.App()
  232. MyFrame().Show()
  233. 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+'\n')
  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. def findSameMD5Files(self,files):
  117. dicMD5={}
  118.  
  119. for f in files:
  120. self.appendMsg('calculating md5 value of file %s'%f)
  121. md5=self.Utils.getFileMD5(f)
  122. if not dicMD5.has_key(md5):
  123. dicMD5[md5]=f
  124. else:
  125. dicMD5[md5]=dicMD5[md5]+';'+f
  126. dicCopy=dicMD5.copy()
  127. for k in dicMD5.iterkeys():
  128. if dicMD5[k].find(';')==-1:
  129. dicCopy.pop(k)
  130. del dicMD5
  131. return dicCopy
  132.  
  133. def isFileTypeFilterWorking(self):
  134. return eval(self.config.get('IsFileTypeFilterWorking'))
  135.  
  136. def isFileSizeFilterWorking(self):
  137. return eval(self.config.get('IsFileTypeFilterWorking'))
  138.  
  139. def LoadFiltersInfo(self):
  140. flag1=self.isFileTypeFilterWorking()
  141. flag2=self.isFileSizeFilterWorking()
  142.  
  143. if flag1 or flag2:
  144. self.appendMsg('Tips:file filters is working:')
  145. if flag1:
  146. self.appendMsg("File Type Filter:")
  147. option=self.config.get('FileTypeOption')
  148. fileTypes=self.config.get('SelectedFileTypes')
  149. self.appendMsg("%s:%s\n"%(option if option=='Exclude' else 'Include only',fileTypes))
  150. if flag2:
  151. self.appendMsg("File Size Filter:")
  152. start=self.config.get('FileSizeStart')
  153. end=self.config.get('FileSizeEnd')
  154. self.appendMsg("%s MB - %s MB\n"%(start,end))
  155. self.appendMsg('Start working...\n')
  156.  
  157. def listSameFile(self,mydir):
  158. msg=''
  159. msgUniq='\nCongratulations,all files are unique.'
  160. try:
  161. existsFlag=False
  162. files=self.Utils.getAllFiles(mydir)
  163. self.appendMsg('%s files found in directory %s\n'%(len(files),mydir))
  164. self.LoadFiltersInfo()
  165. dicFileOfSameSize=self.findSameSizeFiles(files)
  166. groupCount=0
  167. if dicFileOfSameSize=={}:
  168. self.appendMsg(msgUniq)
  169. return
  170. else:
  171. for k in dicFileOfSameSize.iterkeys():
  172. filesOfSameSize=dicFileOfSameSize[k].split(';')
  173. dicSameMD5file=self.findSameMD5Files(filesOfSameSize)
  174. if dicSameMD5file!={}:
  175. existsFlag=True
  176. for k in dicSameMD5file.iterkeys():
  177. msg=msg+'md5 %s: %s'%(k,dicSameMD5file[k])+'\n'
  178. groupCount+=1
  179. if not existsFlag:
  180. msg=msgUniq
  181. else:
  182. msg='\nDuplicated files:\n'+msg+'\nFound %s groups of duplicated files totally.'%groupCount
  183. except Exception,e:
  184. print e
  185. msg='Exception occured.'
  186. finally:
  187. self.appendMsg(msg+'\n'+'\nOperation finished.')
  188.  
  189. def removeSameFile(self,mydir):
  190. msg=''
  191. msgUniq='\nCongratulations,no file is removed since they are all unique.'
  192. try:
  193. existsFlag=False
  194. files=self.Utils.getAllFiles(mydir)
  195. self.appendMsg('%s files found in directory %s\n'%(len(files),mydir))
  196. self.LoadFiltersInfo()
  197. dicFileOfSameSize=self.findSameSizeFiles(files)
  198. if not self.config.get('FileTypeOption')=='Default':
  199. self.appendMsg('Tips:file filters is working.\n')
  200. if dicFileOfSameSize=={}:
  201. self.appendMsg(msgUniq)
  202. return
  203. else:
  204. #list the duplicated files first:
  205. self.appendMsg('Finding duplicted files:')
  206. dicFiltered={}
  207. groupCount=0
  208. for k in dicFileOfSameSize.iterkeys():
  209. filesOfSameSize=dicFileOfSameSize[k].split(';')
  210. dicSameMD5file=self.findSameMD5Files(filesOfSameSize)
  211. if dicSameMD5file!={}:
  212. existsFlag=True
  213. for k in dicSameMD5file.iterkeys():
  214. msg=msg+'md5 %s: %s'%(k,dicSameMD5file[k])+'\n'
  215. groupCount+=1
  216. dicFiltered[k]=dicSameMD5file[k]
  217. if not existsFlag:
  218. self.appendMsg(msgUniq)
  219. return
  220. else:
  221. #then remove the duplicated files:
  222. self.appendMsg('\nDuplicated files:\n'+msg+'\nFound %s groups of duplicated files totally.'%groupCount)
  223. removeCount=0
  224. for k in dicFiltered.iterkeys():
  225. sameFiles=dicFiltered[k].split(';')
  226. flagRemove=False
  227. for f in sameFiles:
  228. if not flagRemove:
  229. flagRemove=True
  230. else:
  231. self.appendMsg('Removing file: %s'%f)
  232. os.remove(f)
  233. removeCount=removeCount+1
  234. self.appendMsg('\n%s files are removed.\n'%removeCount)
  235. except Exception,e:
  236. print e
  237. msg='\nException occured.'
  238. self.appendMsg(msg)
  239. finally:
  240. self.appendMsg('\n\nOperation 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. def LoadConfigFile(self):
  26. configFile='metaData/config.txt'
  27. self.config=MyConfig(configFile)
  28.  
  29. def LoadControls(self):
  30. panel = wx.Panel(self)
  31. sizer = wx.GridBagSizer(5,5)#预计需要55列,但实际可以超出该设定,GridGagSizer会自动增长
  32. '''加载隐藏文件过滤器的控件'''
  33. sb = wx.StaticBox(panel, label="Hidden File Filter",size=(500,100))
  34. boxsizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
  35. self.cbExclHidden=wx.CheckBox(panel, label="Exclude hidden files(Waring:disabled feature)")
  36. boxsizer.Add((10,10),flag=wx.BOTTOM|wx.TOP,border=10)
  37. boxsizer.Add(self.cbExclHidden,flag=wx.BOTTOM|wx.TOP,border=10)
  38. sizer.Add(boxsizer, pos=(0,0),span=(1,5),flag=wx.LEFT|wx.TOP,border=25)
  39. #pos(0,0)表示位置在第1行,第1列,span=(1,5)表示该boxsizer占据1个行5个列的空间
  40.  
  41. '''加载文件类型过滤器的控件'''
  42. sb = wx.StaticBox(panel, label="File Type Filter",size=(500,500))
  43. boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
  44. self.rdDefault=wx.RadioButton(panel,-1,'Default',style=wx.RB_GROUP)
  45. self.rdExclFtyp=wx.RadioButton(panel,-1,'Exclude')
  46. self.rdInclFtyp=wx.RadioButton(panel,-1,'Include Only')
  47. subHbox=wx.BoxSizer(wx.HORIZONTAL)
  48. subHbox.Add(self.rdDefault,flag=wx.LEFT,border=25)
  49. subHbox.Add(self.rdExclFtyp,flag=wx.LEFT,border=5)
  50. subHbox.Add(self.rdInclFtyp,flag=wx.LEFT,border=5)
  51. boxsizer.Add(subHbox)
  52.  
  53. '''从配置文件读固定配置'''
  54. self.cbImage=wx.CheckBox(panel, label="Images("+self.config.get('Images')+")")
  55. self.cbAudio=wx.CheckBox(panel, label="Audios("+self.config.get('Audios')+")")
  56. self.cbVideo=wx.CheckBox(panel, label="Videos("+self.config.get('Videos')+")")
  57. self.cbDoc=wx.CheckBox(panel, label="Documents("+self.config.get('Documents')+")")
  58.  
  59. self.lblOtherFiles=wx.StaticText(panel,label="Other files ( enter file extensions,split them with \";\" )")
  60. self.txtOtherFiles=wx.TextCtrl(panel,size=(400,25))
  61. subVbox=wx.BoxSizer(wx.VERTICAL)
  62. subVbox.Add(self.cbImage,flag=wx.LEFT|wx.TOP,border=10)
  63. subVbox.Add(self.cbAudio,flag=wx.LEFT|wx.TOP,border=10)
  64. subVbox.Add(self.cbVideo,flag=wx.LEFT|wx.TOP,border=10)
  65. subVbox.Add(self.cbDoc,flag=wx.LEFT|wx.TOP,border=10)
  66. subVbox.Add(self.lblOtherFiles,flag=wx.LEFT|wx.TOP,border=10)
  67. subVbox.Add(self.txtOtherFiles,flag=wx.LEFT|wx.TOP|wx.BOTTOM|wx.RIGHT,border=10)
  68. boxsizer.Add(subVbox)
  69. sizer.Add(boxsizer, pos=(1,0), span=(6,5),flag=wx.TOP|wx.LEFT,border=25)
  70. #pos(1,0)表示位置在第2行,第1列,span=(1,5)表示该boxsizer占据6个行5个列的空间
  71.  
  72. self.cbExclHidden.Disable()
  73.  
  74. '''加载文件大小过滤器的控件'''
  75. sb = wx.StaticBox(panel, label="File Size Filter",size=(500,200))
  76. boxsizer = wx.StaticBoxSizer(sb, wx.HORIZONTAL)
  77. self.txtFileSize1=wx.TextCtrl(panel,size=(100,25))
  78. lblFileSize1 = wx.StaticText(panel,label="MB")
  79. lblTo = wx.StaticText(panel,label="--")
  80. self.txtFileSize2=wx.TextCtrl(panel,size=(100,25))
  81. lblFileSize2 = wx.StaticText(panel,label="MB")
  82. boxsizer.Add(self.txtFileSize1,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
  83. boxsizer.Add(lblFileSize1,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
  84. boxsizer.Add((20,10))
  85. boxsizer.Add(lblTo,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
  86. boxsizer.Add((20,10))
  87. boxsizer.Add(self.txtFileSize2,flag=wx.LEFT|wx.BOTTOM|wx.TOP,border=10)
  88. boxsizer.Add(lblFileSize2,flag=wx.LEFT|wx.BOTTOM|wx.TOP|wx.EXPAND,border=10)
  89. sizer.Add(boxsizer, pos=(7,0),span=(1, 5),flag=wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT , border=25)#pos=(7,0)
  90. #pos等于(7,0)表示位置在第8行,第1列,之所以是第8行,因为前面两个控件已经占据了1+6=7行,所以至少从第8行开始,否则布局错乱
  91.  
  92. '''加载按钮'''
  93. self.btnOK = wx.Button(panel, label="OK")
  94. self.btnCancel = wx.Button(panel, label="Cancel")
  95. sizer.Add(self.btnOK, pos=(9,3))
  96. sizer.Add(self.btnCancel, pos=(9,4),flag=wx.BOTTOM|wx.RIGHT, border=5)
  97.  
  98. panel.SetSizer(sizer)
  99. panel.Layout()
  100.  
  101. def InitWithConfigData(self):
  102. self.cbExclHidden.SetValue(str(self.config.get('IsExcludeHiddenFiles'))=='True')
  103.  
  104. self.rdDefault.SetValue(self.config.get('FileTypeOption')=='Default')
  105. if self.rdDefault.GetValue():
  106. self.DisableFileTypeFilterControls()
  107.  
  108. self.rdExclFtyp.SetValue(self.config.get('FileTypeOption')=='Exclude')
  109. self.rdInclFtyp.SetValue(self.config.get('FileTypeOption')=='Include')
  110.  
  111. self.cbImage.SetValue(str(self.config.get('IsImagesSelected'))=='True')
  112. self.cbAudio.SetValue(str(self.config.get('IsAudiosSelected'))=='True')
  113. self.cbVideo.SetValue(str(self.config.get('IsVideosSelected'))=='True')
  114. self.cbDoc.SetValue(str(self.config.get('IsDocumentsSelected'))=='True')
  115. self.txtOtherFiles.SetValue('' if self.config.get('OtherFilesSelected')=='None' else self.config.get('OtherFilesSelected'))
  116.  
  117. self.txtFileSize1.SetValue(self.config.get('FileSizeStart'))
  118. inputFileSizeEnd=self.config.get('FileSizeEnd')
  119. if inputFileSizeEnd=='None':
  120. self.txtFileSize2.SetValue('')
  121. else:
  122. self.txtFileSize2.SetValue(inputFileSizeEnd)
  123.  
  124. def BindControlEvent(self):
  125. for eachRadio in [self.rdDefault, self.rdExclFtyp, self.rdInclFtyp]:#绑定事件
  126. self.Bind(wx.EVT_RADIOBUTTON, self.OnRadio, eachRadio)
  127. self.Bind(wx.EVT_BUTTON, self.OnOK,self.btnOK)
  128. self.Bind(wx.EVT_BUTTON, self.OnCancel,self.btnCancel)
  129.  
  130. def ValidateInput(self):
  131.  
  132. if not self.rdDefault.GetValue():
  133. pattern=re.compile('[^\d\w\.\;]+')#数字、英文字母、点号之外的字符定义为非法字符
  134. if re.search(pattern,self.txtOtherFiles.GetValue()):
  135. wx.MessageBox('Please enter valid file extensions and correct splitor(;).','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  136. return False
  137. if (self.rdInclFtyp.GetValue() or self.rdExclFtyp.GetValue()) and self.getSelectedFileTypes()=='':
  138. wx.MessageBox('Please enter or select at least one file type.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  139. return False
  140. if self.txtFileSize1.GetValue()!='' and not self.Utils.isNumReg(self.txtFileSize1.GetValue()):
  141. wx.MessageBox('Please do not enter or enter valid number in the first file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  142. return False
  143. elif self.txtFileSize2.GetValue()!='' and not self.Utils.isNumReg(self.txtFileSize2.GetValue()):
  144. wx.MessageBox('Please do not enter or enter valid number in the second file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  145. return False
  146. elif self.Utils.isNumReg(self.txtFileSize1.GetValue()) and self.Utils.isNumReg(self.txtFileSize2.GetValue()) and eval(self.txtFileSize1.GetValue())>eval(self.txtFileSize2.GetValue()):
  147. wx.MessageBox('The first file size should not be greater than the second file size.','Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  148. return False
  149. elif self.Utils.isNumReg(self.txtFileSize2.GetValue()) and eval(self.txtFileSize2.GetValue())>(self.config.get('MaxFileSize')):
  150. wx.MessageBox('The second file size should not be greater than '+self.config.get('MaxFileSize'),'Tip Message',wx.YES_DEFAULT|wx.ICON_INFORMATION)
  151. return False
  152. return True
  153.  
  154. def getSelectedFileTypes(self):
  155. selectedFileTypes=''
  156. if not self.rdDefault.GetValue():
  157. fileType=''
  158. for chk in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc]:
  159. if chk.IsChecked():
  160. label=chk.GetLabel()
  161. fileType=fileType+label[label.find('(')+1:label.find(')')]+';'
  162. selectedFileTypes=fileType
  163.  
  164. selectedFileTypes+=self.Utils.FormatOtherFileTypes(self.txtOtherFiles.GetValue())
  165. selectedFileTypes= selectedFileTypes.replace('/',';').lower()
  166. return selectedFileTypes
  167.  
  168. def OnOK(self,event):
  169. if self.ValidateInput():
  170. IsExclHiddenFiles=self.cbExclHidden.GetValue()
  171. if self.rdDefault.GetValue():
  172. FileTypeOption='Default'
  173. elif self.rdExclFtyp.GetValue():
  174. FileTypeOption='Exclude'
  175. elif self.rdInclFtyp.GetValue():
  176. FileTypeOption='Include'
  177.  
  178. selectedFileTypes=self.getSelectedFileTypes()
  179. if selectedFileTypes=='':
  180. self.config.set('SelectedFileTypes','None')
  181. else:
  182. self.config.set('SelectedFileTypes',selectedFileTypes)
  183.  
  184. '''save filters into the config file'''
  185. self.config.set('IsExcludeHiddenFiles',IsExclHiddenFiles)
  186. self.config.set('FileTypeOption',FileTypeOption)
  187. self.config.set('IsImagesSelected',self.cbImage.IsChecked())
  188. self.config.set('IsAudiosSelected',self.cbAudio.IsChecked())
  189. self.config.set('IsVideosSelected',self.cbVideo.IsChecked())
  190. self.config.set('IsDocumentsSelected',self.cbDoc.IsChecked())
  191.  
  192. otherFileTyps=self.txtOtherFiles.GetValue()
  193. if otherFileTyps=='':
  194. self.config.set('OtherFilesSelected','None')
  195. else:
  196. self.config.set('OtherFilesSelected',self.Utils.FormatOtherFileTypes(self.txtOtherFiles.GetValue()))
  197.  
  198. fileSizeStart=self.txtFileSize1.GetValue()
  199. fileSizeEnd=self.txtFileSize2.GetValue()
  200. if fileSizeStart!='':
  201. self.config.set('FileSizeStart',fileSizeStart)
  202.  
  203. if fileSizeEnd=='':
  204. self.config.set('FileSizeEnd','None')
  205. else:
  206. self.config.set('FileSizeEnd',fileSizeEnd)
  207.  
  208. if FileTypeOption=='Default':
  209. self.config.set('IsFileTypeFilterWorking','False')
  210. else:
  211. self.config.set('IsFileTypeFilterWorking','True')
  212.  
  213. if fileSizeStart=='' or fileSizeEnd!=self.config.get('MaxFileSize'):
  214. self.config.set('IsFileSizeFilterWorking','True')
  215. else:
  216. self.config.set('IsFileSizeFilterWorking','False')
  217. self.config.saveConfig()
  218. self.Close()
  219.  
  220. def OnCancel(self,event):
  221. self.Close()
  222.  
  223. def OnRadio(self,event):
  224. selectedRadio=event.GetEventObject()
  225. if selectedRadio==self.rdDefault:
  226. self.DisableFileTypeFilterControls()
  227. else:
  228. self.EnableFileTypeFilterControls()
  229.  
  230. def DisableFileTypeFilterControls(self):
  231. for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc,self.txtOtherFiles,self.lblOtherFiles]:
  232. control.Disable()
  233. for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc]:
  234. control.SetValue(False)
  235. self.txtOtherFiles.SetValue('')
  236.  
  237. def EnableFileTypeFilterControls(self):
  238. for control in [self.cbImage,self.cbAudio,self.cbVideo,self.cbDoc,self.txtOtherFiles,self.lblOtherFiles]:
  239. control.Enable()
  240.  
  241. class DialogAboutApp(wx.Dialog):
  242. text = '''
  243. <html>
  244. <head>
  245. <style type="text/css">
  246. body {font-family:serif;background-color: #ACAA60;}
  247. </style>
  248. </head>
  249. <body> <!--bgcolor="#ACAA60"-->
  250. <center>
  251. <table bgcolor="#455481" width="100%" cellspacing="0" cellpadding="0" border="1">
  252. <tr>
  253. <td align="center"><h1><span style="color:white">UNIQ File-wxPython</span></h1></td>
  254. </tr>
  255. </table>
  256. </center>
  257. <p><b>UNIQ File-wxPython</b> is a program designed for clearing duplicated files and saving
  258. memory space.
  259. It's broght to you by Aaron Hu(<b><span style="color:red">@DoNotSpyOnMe</span></b> on Sina Weibo).<br/>
  260.  
  261. <!--<a href="http://weibo.com/u/1737184870" target="_blank">Contact</a> the author now.-->
  262. </p>
  263. </body>
  264. </html>
  265. '''
  266. def __init__(self, parent):
  267. wx.Dialog.__init__(self, parent, -1,'About this app',
  268. size=(440, 400) )
  269. window = HtmlWindow(self)
  270. window.SetPage(self.text)
  271. button = wx.Button(self, wx.ID_OK, 'OK')
  272. sizer = wx.BoxSizer(wx.VERTICAL)
  273. sizer.Add(window, 1, wx.EXPAND|wx.ALL, 5)
  274. sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
  275. self.SetSizer(sizer)
  276. self.Layout()
  277.  
  278. class DialogAboutAuthor(wx.Dialog):
  279. text = '''
  280. <html>
  281. <head>
  282. <style type="text/css">
  283. body {font-family:serif;background-color: #ACAA60;}
  284. </style>
  285. </head>
  286. <body> <!--bgcolor="#ACAA60"-->
  287. <center>
  288. <table bgcolor="#455481" width="100%" cellspacing="0" cellpadding="0" border="1">
  289. <tr>
  290. <td align="center"><h1><span style="color:white">UNIQ File-wxPython</span></h1></td>
  291. </tr>
  292. </table>
  293. </center>
  294. <p>
  295. Contact the author at:<br/><br/>
  296. Sina Weibo:<b><span style="color:red">@DoNotSpyOnMe</span></b><br/>
  297. cnblogs.com:<b>http://www.cnblogs.cn/aaronhoo</b><br/>
  298. </p>
  299. </body>
  300. </html>
  301. '''
  302. def __init__(self, parent):
  303. wx.Dialog.__init__(self, parent, -1,'About the author',
  304. size=(440, 400) )
  305. window = HtmlWindow(self)
  306. window.SetPage(self.text)
  307. button = wx.Button(self, wx.ID_OK, 'OK')
  308. sizer = wx.BoxSizer(wx.VERTICAL)
  309. sizer.Add(window, 1, wx.EXPAND|wx.ALL, 5)
  310. sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
  311. self.SetSizer(sizer)
  312. 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('\n')],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+'\n')
  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('\n')],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+'\n')
  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. def divideList(self,ls,each):
  37. dividedLs=[]
  38. eachExact=float(each)
  39. groupCount=len(ls)/each
  40. groupCountExact=len(ls)/eachExact
  41. start=0
  42. for i in xrange(groupCount):
  43. dividedLs.append(ls[start:start+each])
  44. start=start+each
  45. if groupCount<groupCountExact:#假如有余数,将剩余的所有元素加入到最后一个分组
  46. dividedLs.append(ls[groupCount*each:])
  47. return dividedLs
  48.  
  49. def getFileSize(self,filePath):
  50. return os.path.getsize(filePath)
  51.  
  52. ''' 一般文件的md5计算方法,一次读取文件的全部内容'''
  53. def CalcMD5(filepath):
  54. with open(filepath,'rb') as f:
  55. md5obj = hashlib.md5()
  56. md5obj.update(f.read())
  57. hash = md5obj.hexdigest()
  58. return hash
  59. '''大文件计算md5的方法,分批读取文件内容,防止内存爆掉'''
  60. def getFileMD5(self,filename):
  61. if not os.path.isfile(filename):
  62. return
  63. myhash = hashlib.md5()
  64. f = open(filename,'rb')
  65. while True:
  66. b = f.read(8*1024)
  67. if not b :
  68. break
  69. myhash.update(b)
  70. f.close()
  71. return myhash.hexdigest()
  72.  
  73. def getAllFiles(self,directory):
  74. files=[]
  75. # dirSplitor=getDirSplitor()
  76. for dirpath, dirnames,filenames in os.walk(directory):
  77. if filenames!=[]:
  78. for file in filenames:
  79. files.append(dirpath+self.dirSplitor+file)
  80. files.sort(key=len)#按照文件名的长度排序
  81. return files
  82.  
  83. def isInList(self,myList,someValue):
  84. try:
  85. myList.index(someValue)
  86. return True
  87. except:
  88. return False
  89.  
  90. def getFileType(self,file):
  91. # dirSplitor=getDirSplitor()
  92. fileName=file[file.rfind(self.dirSplitor)+1:]
  93. fileType=file[file.rfind('.'):]
  94. return fileType.lower()
  95.  
  96. def getDirSplitor(self):
  97. osType=platform.system()
  98. if osType=='Windows':
  99. self.dirSplitor= '\\'
  100. elif osType=='Linux':
  101. self.dirSplitor= '/'
  102. else:
  103. self.dirSplitor= '/'

python查找并删除相同文件-UNIQ File-wxPython-v6的更多相关文章

  1. python查找并删除相同文件-UNIQ File-wxPython版本

    今天用wxPython做了一个GUI程序,我称之为UNIQ File,实现查找指定目录内的相同文件,主要原理是计算文件的md5值(计算前先找出文件大小相同的文件,然后计算这些文件的md5值,而不是所有 ...

  2. python查找并删除相同文件-UNIQ File-script版本

    今天用wxPython做了一个GUI程序,实现查找指定目录内的相同文件,主要原理是计算文件的md5值(计算前先找出文件大小相同的文件,然后计算这些文件的md5值,而不是所有文件都计算,大大减少了md5 ...

  3. Linux查找并删除重复文件的命令行fdupes工具,dupeGuru图形工具

    查了几十个网页,找到这个接近满意的解决方案http://unix.stackexchange.com/questions/146197/fdupes-delete-files-aft... 不过正则里 ...

  4. Linux系统中查找、删除重复文件,释放磁盘空间。

    在Linux系操作系统中查找并删除重复文件的方法的确有很多,不过这里介绍的是一款非常简单实用的软件FSlint.FSlint是一个重复文件查找工具,可以使用它来清除不必要的重复文件,笔者经常使用它来释 ...

  5. Python小工具--删除svn文件

    有的时候我们需要删除项目下的svn相关文件,但是SVN会在所有的目录下都创建隐藏文件.svn,手工一个个目录查找然后删除显然比较麻烦.所以这里提供了一个Python小工具用于批量删除svn的相关文件: ...

  6. 利用lsof命令查找已经删除的文件来释放磁盘空间

    测试环境一台服务器/目录空间使用率达到97%,但是通过du -sh *发现实际空间没用到那么多,初步怀疑,之前删除的文件,有运行中的进程一直占用,导致空间没有释放,如图通过du -sh *发现共实际使 ...

  7. 在 Linux 中查找和删除重复文件

    原文链接:https://www.linuxprobe.com/linux-FSlint.html FSlint同时具有GUI和CLI模式.因此,对于新手来说,这是一个用户友好的工具.FSlint不仅 ...

  8. fso查找被删除的文件

    <html> <head> </head> <body> 源目录:<input id="txtOld" value=" ...

  9. python 递归删除空文件夹

    Python如何递归删除空文件夹 1.Python如何递归删除空文件夹,这个问题很常见.但大多数人的解决办法都是自己实现递归函数解决这个问题,其实根本不用那么麻烦.Python中的os.walk提供了 ...

随机推荐

  1. throw er; // Unhandled 'error' event

    events.js:72 throw er; // Unhandled 'error' event ^Error: listen EADDRINUSE at errnoException (net.j ...

  2. Mac按键

    ⌘——Command ⌃ ——Control ⌥——Option (alt) ⇧——Shift ⇪——Caps Lock ⌫——Delete

  3. 新浪微博客户端(50)-解决输入Emotion表情逐渐变小的问题

    UITextView+Extension.h #import <UIKit/UIKit.h> @interface UITextView (Extension) /** 插入属性文本 */ ...

  4. matlab求解相关系数

    最近收到一项新任务,要求两个矩阵的相关系数,说白了就是转换成向量两两计算.本来这个工作我是想自己写个小程序搞定的,但是大家纷纷反映matlab自带了此项功能,本着活到老学到老的心态,我开始查找这个函数 ...

  5. \r,\n,\r\n的区别

    http://www.studyofnet.com/news/285.html \n是换行,英文是New line,表示使光标到行首\r是回车,英文是Carriage return,表示使光标下移一格 ...

  6. jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法

    前言: 前一篇文章中重点总结了一下then方法,它主要用来处理多个异步任务按顺序执行,即前一个任务处理完了,再继续下一个,以此类推: 而这一章节jQuery.when方法也是处理多个异步任务,它把多个 ...

  7. Javascript 中我很想说说的 this

    this是每一个想要深入学习Javascript的人必过的一关,我为this看过很多书查过很多资料,虽然对this有了一定的了解并且也经常使用this,但是如果有人问我  this是什么呀? 我依旧不 ...

  8. edwin报警和监控平台近期的更新(python源码)

    edwin从发布以来, 得到了不少关注, 获得了不少star. 最近又做了一些很有意义的改进, 同时完善了部分文档. 项目地址: https://github.com/harryliu/edwin , ...

  9. iOS:使用MVC模式帮ViewController瘦身

    如何给UIViewController瘦身 随着程序逻辑复杂度的提高,你是否也发现了App中一些ViewController的代码行数急剧增多,达到了2,3千行,甚至更多.这时如果想再添加一点功能或者 ...

  10. C#5.0 特性

    Visual Studio 2012 中 Visual C# 的新增功能 Lambda表达式 表达式树:把代码,转换成数据,然后分析数据发现其组成部分,最后转换成可以传递到其他程序的字符串 LinQ表 ...