近来学习python和wxpython,就顺手做了一个小工具:基于wxPython的python代码统计工具,主要功能是可以通过wxpython的gui界面选择一个目录,然后工具将递归搜索该目录找出所有以.py结尾的文件,并对这些文件进行统计,输出包括总的代码行数,注释行数,空行数等。
开发工具:python2.7 + wxPython2.8.11 + wxFormBuilder。
程序界面如下:
代码如下:
pycodecounter.py:
1 import string 2 import sys 3 4 class PyCodeCnt(): 5 def __init__ (self): 6 self.inFileList = [] 7 self.blankLines = 0 8 self.commentLines = 0 9 self.codeLines = 0 10 self.totalLines = 0 11 12 def handlefile(self, fileName): 13 try : 14 f = open(fileName, " r " ) 15 for line in f.readlines(): 16 for char in line: 17 if char == " \n " : 18 self.blankLines += 1 19 break 20 if char == " # " : 21 self.commentLines += 1 22 break 23 elif (char not in string.whitespace) and (char != " # " ): 24 self.codeLines += 1 25 break 26 f.close() 27 self.totalLines += self.blankLines 28 self.totalLines += self.commentLines 29 self.totalLines += self.codeLines 30 return (self.codeLines, self.commentLines, self.blankLines, self.totalLines) 31 32 except IOError: 33 print ( " Error opening \ "" + fileName + " \ " , skipping...\n " ) 34
maindlg_gui.py:
1 # -*- coding: utf-8 -*- 2 3 # ########################################################################## 4 # # Python code generated with wxFormBuilder (version Sep 8 2010) 5 # # http://www.wxformbuilder.org/ 6 # # 7 # # PLEASE DO "NOT" EDIT THIS FILE! 8 # ########################################################################## 9 10 import wx 11 12 # ########################################################################## 13 # # Class MainDialogGUI 14 # ########################################################################## 15 16 class MainDialogGUI ( wx.Dialog ): 17 18 def __init__ ( self, parent ): 19 wx.Dialog. __init__ ( self, parent, id = wx.ID_ANY, title = u " PyCodeCounter " , pos = wx.DefaultPosition, size = wx.Size( 671 , 384 ), style = wx.DEFAULT_DIALOG_STYLE | wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX | wx.RESIZE_BORDER ) 20 21 self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize ) 22 self.SetForegroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNTEXT ) ) 23 self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_HIGHLIGHT ) ) 24 25 bSizerDlg = wx.BoxSizer( wx.VERTICAL ) 26 27 bSizerFileList = wx.BoxSizer( wx.VERTICAL ) 28 29 self.m_tcFileList = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL | wx.TE_MULTILINE | wx.TE_READONLY ) 30 bSizerFileList.Add( self.m_tcFileList, 1 , wx.ALL | wx.EXPAND, 5 ) 31 32 bSizerDlg.Add( bSizerFileList, 1 , wx.EXPAND, 5 ) 33 34 bSizerRes = wx.BoxSizer( wx.VERTICAL ) 35 36 self.m_tcRes = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.HSCROLL | wx.TE_MULTILINE | wx.TE_READONLY ) 37 bSizerRes.Add( self.m_tcRes, 1 , wx.ALL | wx.EXPAND, 5 ) 38 39 bSizerDlg.Add( bSizerRes, 1 , wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) 40 41 bSizerBtn = wx.BoxSizer( wx.HORIZONTAL ) 42 43 self.m_dirPicker = wx.DirPickerCtrl( self, wx.ID_ANY, wx.EmptyString, u " Select a folder " , wx.DefaultPosition, wx.DefaultSize, wx.DIRP_DEFAULT_STYLE ) 44 bSizerBtn.Add( self.m_dirPicker, 1 , wx.ALL | wx.EXPAND, 5 ) 45 46 self.m_btnOK = wx.Button( self, wx.ID_ANY, u " Count " , wx.DefaultPosition, wx.DefaultSize, 0 ) 47 bSizerBtn.Add( self.m_btnOK, 0, wx.ALL, 5 ) 48 49 self.m_btnExit = wx.Button( self, wx.ID_ANY, u " Exit " , wx.DefaultPosition, wx.DefaultSize, 0 ) 50 bSizerBtn.Add( self.m_btnExit, 0, wx.ALIGN_CENTER | wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5 ) 51 52 bSizerDlg.Add( bSizerBtn, 0, wx.ALIGN_BOTTOM | wx.ALIGN_CENTER | wx.EXPAND, 5 ) 53 54 self.SetSizer( bSizerDlg ) 55 self.Layout() 56 57 self.Centre( wx.BOTH ) 58 59 # Connect Events 60 self.m_dirPicker.Bind( wx.EVT_DIRPICKER_CHANGED, self.OnDirChanged ) 61 self.m_btnOK.Bind( wx.EVT_BUTTON, self.OnOK ) 62 self.m_btnExit.Bind( wx.EVT_BUTTON, self.OnClose ) 63 64 def __del__ ( self ): 65 pass 66 67 68 # Virtual event handlers, overide them in your derived class 69 def OnDirChanged( self, event ): 70 event.Skip() 71 72 def OnOK( self, event ): 73 event.Skip() 74 75 def OnClose( self, event ): 76 event.Skip() 77 78 79
maindlg.py:
1 ''' 2 Created on 2010-10-17 3 4 @author: qilei 5 ''' 6 import maindlg_gui 7 import pycodecounter 8 import os 9 10 class MainDialog( maindlg_gui.MainDialogGUI ): 11 def __init__ ( self, parent ): 12 maindlg_gui.MainDialogGUI. __init__ ( self, parent ) 13 self.wildcard = " Python source files(*.py)|*.py|All files(*.*)|*.* " 14 self.dir = '' 15 self.fileList = [] 16 self.totalLines = 0 17 self.codeLines = 0 18 self.CommentLines = 0 19 self.BlackLines = 0 20 21 def OnDirChanged( self, event ): 22 self.dir = self.m_dirPicker.GetPath() 23 event.Skip() 24 25 def OnOK( self, event): 26 ''' Try to read and handle every py-files in the list and 27 update results to dialog. 28 ''' 29 self.totalLines = 0 30 self.codeLines = 0 31 self.CommentLines = 0 32 self.BlackLines = 0 33 fileList = self.walkdir(self.dir) 34 self.fileList = [] # reset to empty 35 for fileName in fileList: 36 if fileName.endswith( ' .py ' ): 37 self.fileList.append(fileName) 38 for eachFile in self.fileList: 39 codeCntRes = self.readfile(eachFile) 40 if codeCntRes: 41 self.totalLines += codeCntRes[ 3 ] 42 self.codeLines += codeCntRes[0] 43 self.CommentLines += codeCntRes[ 1 ] 44 self.BlackLines += codeCntRes[ 2 ] 45 strRes = ( ' Total Lines ' , ' Code Lines ' , ' Comment Lines ' , ' Blank Lines ' ); 46 strRes = str(strRes) 47 strRes += str((self.totalLines, self.codeLines, self.CommentLines, self.BlackLines)) 48 self.m_tcFileList.Clear() 49 # append files to text control 50 self.m_tcFileList.AppendText( ' Total valid files: ' + str(len(self.fileList)) + ' \r ' ) 51 for eachFile in self.fileList: 52 self.m_tcFileList.AppendText(eachFile + ' \r ' ) 53 # update result showing 54 self.m_tcRes.Clear() 55 self.m_tcRes.AppendText( ' Total Lines: ' + str(self.totalLines) + ' \r ' ) 56 self.m_tcRes.AppendText( ' Code Lines: ' + str(self.codeLines) + ' \r ' ) 57 self.m_tcRes.AppendText( ' Comment Lines: ' + str(self.CommentLines) + ' \r ' ) 58 self.m_tcRes.AppendText( ' Blank Lines: ' + str(self.BlackLines) + ' \r ' ) 59 event.Skip() 60 61 def OnClose( self, event ): 62 self.Destroy() 63 event.Skip() 64 65 def walkdir(self, dir): 66 ''' walk dir and its sub-dir to get py-files list ''' 67 resFiles = [] 68 tuples = os.walk(dir) 69 for i in tuples: 70 dirpath = i[0] 71 filenames = i[ 2 ] 72 for file in filenames: 73 resFiles.append( os.path.join(dirpath, file) ) 74 return resFiles 75 76 def readfile( self, fileName ): 77 ''' Read file 'fileName' and get it's code info. ''' 78 if fileName: 79 plc = pycodecounter.PyCodeCnt() 80 return plc.handlefile(fileName) 81 else : 82 return None 83 84 85
main.py:
1 import wx 2 import maindlg 3 4 def main(): 5 app = wx.PySimpleApp() 6 dlg = maindlg.MainDialog(None) 7 dlg.Show() 8 app.MainLoop() 9 10 if __name__ == ' __main__ ' : 11 main() 12