环境:VS2017,Qt5.12.5
目的:VS本身可以统计代码行数,但个人感觉不太准,于是参考网上资料自己写了一个
注意事项:界面通过代码编写,非Designer设计
效果图:
源码:
CodeStatisticsTool.h
#pragma once
#include <QtWidgets/QWidget>
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTableWidget>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QProgressBar>
QT_BEGIN_NAMESPACE
class QAction;
class QDialogButtonBox;
class QGroupBox;
class QLabel;
class QLineEdit;
class QMenu;
class QMenuBar;
class QPushButton;
class QProgressBar;
class QTextEdit;
QT_END_NAMESPACE
class CodeStatisticsTool : public QWidget
{
Q_OBJECT
public:
CodeStatisticsTool(QWidget* parent = Q_NULLPTR);
~CodeStatisticsTool();
private:
void initParseFileInfoTable();
bool checkFile(const QString& fileName);
void collectSuitFilePathList(const QString& dirPath, const bool& recursion, QStringList& allFilePathList);
void parseAllFileCode(const QStringList& filePathList);
void parseFileCode(const QString& filePath, int& lineCode, int& lineNotes, int& lineBlank);
private slots:
void onSelectDirButtonClicked();
void onClearDirButtonClicked();
//UI
private:
void createMenu();
void createSettingGroupBox();
QMenuBar* m_menuBar;
QMenu* m_fileMenu;
QAction* m_exitAction;
QGroupBox* m_settingGroupBox;
QLabel* m_filterLabel;
QLineEdit* m_filterContents;
QLabel* m_dirLabel;
QLineEdit* m_dirContents;
QPushButton* m_selectDirButton;
QProgressBar* m_progressBar;
QPushButton* m_clearDirButton;
QTextEdit* m_summary;
QTableWidget* m_parseFileInfoTable;
private:
QStringList m_filePathList;
};
CodeStatisticsTool.cpp
#pragma execution_character_set("utf-8")
#include "CodeStatisticsTool.h"
#include <QtWidgets>
CodeStatisticsTool::CodeStatisticsTool(QWidget* parent)
: QWidget(parent)
{
createMenu();
createSettingGroupBox();
m_summary = new QTextEdit();
m_summary->setFixedHeight(200);
m_summary->setText("");
initParseFileInfoTable();
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setMenuBar(m_menuBar);
mainLayout->addWidget(m_settingGroupBox);
mainLayout->addWidget(m_summary);
mainLayout->addWidget(m_parseFileInfoTable);
setLayout(mainLayout);
connect(m_selectDirButton, &QPushButton::clicked, this, &CodeStatisticsTool::onSelectDirButtonClicked);
connect(m_clearDirButton, &QPushButton::clicked, this, &CodeStatisticsTool::onClearDirButtonClicked);
setWindowTitle("解析代码行");
setWindowState(Qt::WindowMaximized);
}
CodeStatisticsTool::~CodeStatisticsTool()
{
//QMessageBox::about(NULL, "提示", "对话框结束");
}
void CodeStatisticsTool::initParseFileInfoTable()
{
const int windowWidth = 1200;
m_parseFileInfoTable = new QTableWidget(this);
QStringList headTextList;
headTextList << "文件名称" << "文件类型" << "文件大小(B)" << "总行数" << "代码行数" << "注释行数" << "空白行数" << "路径";
QList<int> columnWidthList;
columnWidthList << 0.2 * windowWidth << 0.1 * windowWidth << 0.1 * windowWidth
<< 0.1 * windowWidth << 0.1 * windowWidth << 0.1 * windowWidth << 0.1 * windowWidth
<< 0.2 * windowWidth;
int columnCount = headTextList.count();
m_parseFileInfoTable->setColumnCount(columnCount);
m_parseFileInfoTable->setHorizontalHeaderLabels(headTextList);
m_parseFileInfoTable->setSelectionBehavior(QAbstractItemView::SelectRows);
m_parseFileInfoTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_parseFileInfoTable->setSelectionMode(QAbstractItemView::SingleSelection);
m_parseFileInfoTable->verticalHeader()->setVisible(false);
m_parseFileInfoTable->horizontalHeader()->setStretchLastSection(true);
m_parseFileInfoTable->horizontalHeader()->setHighlightSections(false);
m_parseFileInfoTable->verticalHeader()->setDefaultSectionSize(20);
m_parseFileInfoTable->verticalHeader()->setHighlightSections(false);
for (int i = 0; i < columnCount; ++i)
{
m_parseFileInfoTable->setColumnWidth(i, columnWidthList.at(i));
}
}
bool CodeStatisticsTool::checkFile(const QString& fileName)
{
if (fileName.startsWith("moc_") || fileName.startsWith("ui_") || fileName.startsWith("qrc_"))
{
return false;
}
QFileInfo file(fileName);
QString suffix = "*." + file.suffix();
QString filterContents = m_filterContents->text().trimmed();
QStringList filterList = filterContents.split(" ");
return filterList.contains(suffix);
}
void CodeStatisticsTool::collectSuitFilePathList(const QString& dirPath, const bool& recursion, QStringList& allFilePathList)
{
QDir dir(dirPath);
QFileInfoList fileInfos = dir.entryInfoList();
foreach(QFileInfo fileInfo, fileInfos)
{
QString fileName = fileInfo.fileName();
if (fileInfo.isFile() && checkFile(fileName))
{
allFilePathList.push_back(fileInfo.filePath());
}
else
{
if ("." == fileName || ".." == fileName)
{
continue;
}
collectSuitFilePathList(fileInfo.absoluteFilePath(), recursion, allFilePathList);
}
}
}
void CodeStatisticsTool::parseAllFileCode(const QStringList& filePathList)
{
m_parseFileInfoTable->setRowCount(0);
int lineCode;
int lineBlank;
int lineNotes;
int count = filePathList.count();
m_parseFileInfoTable->setRowCount(count);
int totalLines = 0;
int totalBytes = 0;
int totalCodes = 0;
int totalNotes = 0;
int totalBlanks = 0;
for (int i = 0; i < count; ++i)
{
QFileInfo fileInfo(filePathList.at(i));
parseFileCode(fileInfo.filePath(), lineCode, lineNotes, lineBlank);
int lineAll = lineCode + lineBlank + lineNotes;
QTableWidgetItem* itemName = new QTableWidgetItem();
itemName->setText(fileInfo.fileName());
QTableWidgetItem* itemSuffix = new QTableWidgetItem();
itemSuffix->setText(fileInfo.suffix());
QTableWidgetItem* itemSize = new QTableWidgetItem();
itemSize->setText(QString::number(fileInfo.size()));
QTableWidgetItem* itemLine = new QTableWidgetItem();
itemLine->setText(QString::number(lineAll));
QTableWidgetItem* itemCode = new QTableWidgetItem;
itemCode->setText(QString::number(lineCode));
QTableWidgetItem* itemNote = new QTableWidgetItem;
itemNote->setText(QString::number(lineNotes));
QTableWidgetItem* itemBlank = new QTableWidgetItem;
itemBlank->setText(QString::number(lineBlank));
QTableWidgetItem* itemPath = new QTableWidgetItem;
itemPath->setText(fileInfo.filePath());
itemSuffix->setTextAlignment(Qt::AlignCenter);
itemSize->setTextAlignment(Qt::AlignCenter);
itemLine->setTextAlignment(Qt::AlignCenter);
itemCode->setTextAlignment(Qt::AlignCenter);
itemNote->setTextAlignment(Qt::AlignCenter);
itemBlank->setTextAlignment(Qt::AlignCenter);
m_parseFileInfoTable->setItem(i, 0, itemName);
m_parseFileInfoTable->setItem(i, 1, itemSuffix);
m_parseFileInfoTable->setItem(i, 2, itemSize);
m_parseFileInfoTable->setItem(i, 3, itemLine);
m_parseFileInfoTable->setItem(i, 4, itemCode);
m_parseFileInfoTable->setItem(i, 5, itemNote);
m_parseFileInfoTable->setItem(i, 6, itemBlank);
m_parseFileInfoTable->setItem(i, 7, itemPath);
totalBytes += fileInfo.size();
totalLines += lineAll;
totalCodes += lineCode;
totalNotes += lineNotes;
totalBlanks += lineBlank;
if (i % 100 == 0)
{
qApp->processEvents();
}
}
QString summary;
summary += ("文件数量:" + QString::number(count) + "\n");
summary += ("文件大小(B):" + QString::number(totalBytes) + "\n");
summary += ("文件总行数:" + QString::number(totalLines) + "\n");
summary += ("文件总代码行数:" + QString::number(totalCodes) + "\n");
summary += ("文件总注释行数:" + QString::number(totalNotes) + "\n");
summary += ("文件总空白行数:" + QString::number(totalBlanks) + "\n");
m_summary->setText(summary);
}
void CodeStatisticsTool::parseFileCode(const QString& filePath, int& lineCode, int& lineNotes, int& lineBlank)
{
lineCode = 0;
lineNotes = 0;
lineBlank = 0;
QFile file(filePath);
if (file.open(QFile::ReadOnly))
{
QTextStream out(&file);
QString line;
bool isNote = false;
while (!out.atEnd())
{
line = out.readLine();
if (line.startsWith(" "))
{
line.remove(" ");
}
if (line.startsWith("/*"))
{
isNote = true;
}
if (isNote)
{
lineNotes++;
}
else
{
if (line.startsWith("//") || line.startsWith("///"))
{
lineNotes++;
}
else if (line.isEmpty())
{
lineBlank++;
}
else
{
lineCode++;
}
}
if (line.endsWith("*/"))
{
isNote = false;
}
}
}
}
void CodeStatisticsTool::onSelectDirButtonClicked()
{
QString dir = QFileDialog::getExistingDirectory(this, "选择目录", "./", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (!dir.isEmpty())
{
m_dirContents->setText(dir);
m_filePathList.clear();
collectSuitFilePathList(dir, true, m_filePathList);
parseAllFileCode(m_filePathList);
}
}
void CodeStatisticsTool::onClearDirButtonClicked()
{
m_dirContents->setText("");
m_summary->setText("");
m_parseFileInfoTable->setRowCount(0);
}
void CodeStatisticsTool::createMenu()
{
m_menuBar = new QMenuBar();
m_fileMenu = new QMenu("文件", this);
m_exitAction = m_fileMenu->addAction("退出");
m_menuBar->addMenu(m_fileMenu);
connect(m_exitAction, SIGNAL(triggered()), this, SLOT(close()));
}
void CodeStatisticsTool::createSettingGroupBox()
{
m_settingGroupBox = new QGroupBox("设置");
QVBoxLayout* settingLayout = new QVBoxLayout();
QHBoxLayout* selectDirLayout = new QHBoxLayout();
m_dirLabel = new QLabel("目录:", this);
m_dirContents = new QLineEdit(this);
m_selectDirButton = new QPushButton("选择目录", this);
m_selectDirButton->setToolTip("遍历目录中所有的源码文件");
selectDirLayout->addWidget(m_dirLabel);
selectDirLayout->addWidget(m_dirContents);
selectDirLayout->addWidget(m_selectDirButton);
settingLayout->addLayout(selectDirLayout);
m_clearDirButton = new QPushButton("清空目录", this);
settingLayout->addWidget(m_clearDirButton);
QHBoxLayout* filterLayout = new QHBoxLayout();
m_filterLabel = new QLabel("过滤:", this);
m_filterContents = new QLineEdit(this);
m_filterContents->setText("*.h *.hpp *.c *.cpp");
filterLayout->addWidget(m_filterLabel);
filterLayout->addWidget(m_filterContents);
settingLayout->addLayout(filterLayout);
m_settingGroupBox->setLayout(settingLayout);
}