天天看點

Qt編寫自定義控件69-代碼行數統計

一、前言

代碼行數統計主要用來統計項目中的所有檔案的代碼行數,其中包括空行、注釋行、代碼行,可以指定過濾拓展名,比如隻想統計.cpp的檔案,也可以指定檔案或者指定目錄進行統計。寫完這個工具第一件事情就是統計了一下自己寫過的最大的項目大概多少行代碼,看下是不是傳說中的一行代碼一塊錢,這個最大的項目從2010年開始的,到現在差不多快10年了,是自己在現在公司寫過的最大的項目,一直在更新更新完善,途中重構過兩次,大的結構改動,統計了下好像有15W行左右的代碼,純代碼大概在10W,其餘是空行和注釋行,着實把自己吓了一跳,還算是中型項目了,然後又統計了下自定義控件的所有代碼,我勒個去,總代碼23W行,純代碼17W行呢,哎呀我去!

開源位址:

https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo

二、實作的功能

  • 1:可分别統計代碼行/空行/注釋行
  • 2:支援指定過濾拓展名
  • 3:支援指定檔案或者指定目錄進行統計
  • 4:分步顯示統計結果,不卡主界面
  • 5:分别展示每個統計過的檔案的大小/總行數/代碼行數等

三、效果圖

Qt編寫自定義控件69-代碼行數統計

四、頭檔案代碼

#ifndef FRMCOUNTCODE_H
#define FRMCOUNTCODE_H

#include <QWidget>

namespace Ui {
class frmCountCode;
}

class frmCountCode : public QWidget
{
    Q_OBJECT

public:
    explicit frmCountCode(QWidget *parent = 0);
    ~frmCountCode();

private:
    Ui::frmCountCode *ui;
    QStringList listFile;

private:
    void initForm();
    bool checkFile(const QString &fileName);
    void countCode(const QString &filePath);
    void countCode(const QStringList &files);
    void countCode(const QString &fileName, int &lineCode, int &lineBlank, int &lineNotes);

private slots:
    void on_btnOpenFile_clicked();
    void on_btnOpenPath_clicked();
    void on_btnClear_clicked();
};

#endif // FRMCOUNTCODE_H

           

五、核心代碼

#pragma execution_character_set("utf-8")

#include "frmcountcode.h"
#include "ui_frmcountcode.h"
#include "qfile.h"
#include "qtextstream.h"
#include "qfiledialog.h"
#include "qfileinfo.h"
#include "qdebug.h"

frmCountCode::frmCountCode(QWidget *parent) : QWidget(parent), ui(new Ui::frmCountCode)
{
    ui->setupUi(this);
    this->initForm();
    on_btnClear_clicked();
}

frmCountCode::~frmCountCode()
{
    delete ui;
}

void frmCountCode::initForm()
{
    QStringList headText;
    headText << "檔案名" << "類型" << "大小" << "總行數" << "代碼行數" << "注釋行數" << "空白行數" << "路徑";
    QList<int> columnWidth;
    columnWidth << 130 << 50 << 70 << 80 << 70 << 70 << 70 << 150;

    int columnCount = headText.count();
    ui->tableWidget->setColumnCount(columnCount);
    ui->tableWidget->setHorizontalHeaderLabels(headText);
    ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableWidget->verticalHeader()->setVisible(false);
    ui->tableWidget->horizontalHeader()->setStretchLastSection(true);
    ui->tableWidget->horizontalHeader()->setHighlightSections(false);
    ui->tableWidget->verticalHeader()->setDefaultSectionSize(20);
    ui->tableWidget->verticalHeader()->setHighlightSections(false);

    for (int i = 0; i < columnCount; i++) {
        ui->tableWidget->setColumnWidth(i, columnWidth.at(i));
    }

    //設定前景色
    ui->txtCount->setStyleSheet("color:#17A086;");
    ui->txtSize->setStyleSheet("color:#CA5AA6;");
    ui->txtRow->setStyleSheet("color:#CD1B19;");
    ui->txtCode->setStyleSheet("color:#22A3A9;");
    ui->txtNote->setStyleSheet("color:#D64D54;");
    ui->txtBlank->setStyleSheet("color:#A279C5;");

    //設定字型加粗
    QFont font;
    font.setBold(true);
    if (font.pointSize() > 0) {
        font.setPointSize(font.pointSize() + 1);
    } else {
        font.setPixelSize(font.pixelSize() + 2);
    }

    ui->txtCount->setFont(font);
    ui->txtSize->setFont(font);
    ui->txtRow->setFont(font);
    ui->txtCode->setFont(font);
    ui->txtNote->setFont(font);
    ui->txtBlank->setFont(font);

#if (QT_VERSION > QT_VERSION_CHECK(4,7,0))
    ui->txtFilter->setPlaceholderText("中間空格隔開,例如 *.h *.cpp *.c");
#endif
}

bool frmCountCode::checkFile(const QString &fileName)
{
    if (fileName.startsWith("moc_") || fileName.startsWith("ui_") || fileName.startsWith("qrc_")) {
        return false;
    }

    QFileInfo file(fileName);
    QString suffix = "*." + file.suffix();
    QString filter = ui->txtFilter->text().trimmed();
    QStringList filters = filter.split(" ");
    return filters.contains(suffix);
}

void frmCountCode::countCode(const QString &filePath)
{
    QDir dir(filePath);
    foreach (QFileInfo fileInfo , dir.entryInfoList()) {
        if (fileInfo.isFile()) {
            QString strFileName = fileInfo.fileName();
            if (checkFile(strFileName)) {
                listFile << fileInfo.filePath();
            }
        } else {
            if(fileInfo.fileName() == "." || fileInfo.fileName() == "..") {
                continue;
            }

            //遞歸找出檔案
            countCode(fileInfo.absoluteFilePath());
        }
    }
}

void frmCountCode::countCode(const QStringList &files)
{
    int lineCode;
    int lineBlank;
    int lineNotes;
    int count = files.count();
    on_btnClear_clicked();
    ui->tableWidget->setRowCount(count);

    quint32 totalLines = 0;
    quint32 totalBytes = 0;
    quint32 totalCodes = 0;
    quint32 totalNotes = 0;
    quint32 totalBlanks = 0;

    for (int i = 0; i < count; i++) {
        QFileInfo fileInfo(files.at(i));
        countCode(fileInfo.filePath(), lineCode, lineBlank, lineNotes);
        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);

        ui->tableWidget->setItem(i, 0, itemName);
        ui->tableWidget->setItem(i, 1, itemSuffix);
        ui->tableWidget->setItem(i, 2, itemSize);
        ui->tableWidget->setItem(i, 3, itemLine);
        ui->tableWidget->setItem(i, 4, itemCode);
        ui->tableWidget->setItem(i, 5, itemNote);
        ui->tableWidget->setItem(i, 6, itemBlank);
        ui->tableWidget->setItem(i, 7, itemPath);

        totalBytes  += fileInfo.size();
        totalLines  += lineAll;
        totalCodes  += lineCode;
        totalNotes  += lineNotes;
        totalBlanks += lineBlank;

        if (i % 100 == 0) {
            qApp->processEvents();
        }
    }

    //顯示統計結果
    listFile.clear();
    ui->txtCount->setText(QString::number(count));
    ui->txtSize->setText(QString::number(totalBytes));
    ui->txtRow->setText(QString::number(totalLines));
    ui->txtCode->setText(QString::number(totalCodes));
    ui->txtNote->setText(QString::number(totalNotes));
    ui->txtBlank->setText(QString::number(totalBlanks));

    //計算百分比
    double percent = 0.0;
    //代碼行所占百分比
    percent = ((double)totalCodes / totalLines) * 100;
    ui->labPercentCode->setText(QString("%1%").arg(percent, 5, 'f', 2, QChar(' ')));
    //注釋行所占百分比
    percent = ((double)totalNotes / totalLines) * 100;
    ui->labPercentNote->setText(QString("%1%").arg(percent, 5, 'f', 2, QChar(' ')));
    //空行所占百分比
    percent = ((double)totalBlanks / totalLines) * 100;
    ui->labPercentBlank->setText(QString("%1%").arg(percent, 5, 'f', 2, QChar(' ')));
}

void frmCountCode::countCode(const QString &fileName, int &lineCode, int &lineBlank, int &lineNotes)
{
    lineCode = lineBlank = lineNotes = 0;
    QFile file(fileName);
    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("//")) {    //注釋行
                    lineNotes++;
                } else if (line.isEmpty()) {    //空白行
                    lineBlank++;
                } else {                        //代碼行
                    lineCode++;
                }
            }

            //注釋結束
            if (line.endsWith("*/")) {
                isNote = false;
            }
        }
    }
}

void frmCountCode::on_btnOpenFile_clicked()
{
    QString filter = QString("代碼檔案(%1)").arg(ui->txtFilter->text().trimmed());
    QStringList files = QFileDialog::getOpenFileNames(this, "選擇檔案", "./", filter);
    if (files.size() > 0) {
        ui->txtFile->setText(files.join("|"));
        countCode(files);
    }
}

void frmCountCode::on_btnOpenPath_clicked()
{
    QString path = QFileDialog::getExistingDirectory(this, "選擇目錄", "./",  QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    if (!path.isEmpty()) {
        ui->txtPath->setText(path);
        listFile.clear();
        countCode(path);
        countCode(listFile);
    }
}

void frmCountCode::on_btnClear_clicked()
{
    ui->txtCount->setText("0");
    ui->txtSize->setText("0");
    ui->txtRow->setText("0");

    ui->txtCode->setText("0");
    ui->txtNote->setText("0");
    ui->txtBlank->setText("0");

    ui->labPercentCode->setText("0%");
    ui->labPercentNote->setText("0%");
    ui->labPercentBlank->setText("0%");
    ui->tableWidget->setRowCount(0);
}

           

六、控件介紹

  1. 超過160個精美控件,涵蓋了各種儀表盤、進度條、進度球、指南針、曲線圖、标尺、溫度計、導覽列、導航欄,flatui、高亮按鈕、滑動選擇器、農曆等。遠超qwt內建的控件數量。
  2. 每個類都可以獨立成一個單獨的控件,零耦合,每個控件一個頭檔案和一個實作檔案,不依賴其他檔案,友善單個控件以源碼形式內建到項目中,較少代碼量。qwt的控件類環環相扣,高度耦合,想要使用其中一個控件,必須包含所有的代碼。
  3. 全部純Qt編寫,QWidget+QPainter繪制,支援Qt4.6到Qt5.13的任何Qt版本,支援mingw、msvc、gcc等編譯器,支援任意作業系統比如windows+linux+mac+嵌入式linux等,不亂碼,可直接內建到Qt Creator中,和自帶的控件一樣使用,大部分效果隻要設定幾個屬性即可,極為友善。
  4. 每個控件都有一個對應的單獨的包含該控件源碼的DEMO,友善參考使用。同時還提供一個所有控件使用的內建的DEMO。
  5. 每個控件的源代碼都有詳細中文注釋,都按照統一設計規範編寫,友善學習自定義控件的編寫。
  6. 每個控件預設配色和demo對應的配色都非常精美。
  7. 超過130個可見控件,6個不可見控件。
  8. 部分控件提供多種樣式風格選擇,多種訓示器樣式選擇。
  9. 所有控件自适應窗體拉伸變化。
  10. 內建自定義控件屬性設計器,支援拖曳設計,所見即所得,支援導入導出xml格式。
  11. 自帶activex控件demo,所有控件可以直接運作在ie浏覽器中。
  12. 內建fontawesome圖形字型+阿裡巴巴iconfont收藏的幾百個圖形字型,享受圖形字型帶來的樂趣。
  13. 所有控件最後生成一個動态庫檔案(dll或者so等),可以直接內建到qtcreator中拖曳設計使用。
  14. 目前已經有qml版本,後期會考慮出pyqt版本,如果使用者需求量很大的話。
  15. 自定義控件插件開放動态庫使用(永久免費),無任何後門和限制,請放心使用。
  16. 目前已提供32個版本的dll,其中qt_5_7_0_mingw530_32這個版本會一直保證最新的完整的。
  17. 不定期增加控件和完善控件,不定期更新SDK,歡迎各位提出建議,謝謝!
  18. Qt入門書籍推薦霍亞飛的《Qt Creator快速入門》《Qt5程式設計入門》,Qt進階書籍推薦官方的《C++ GUI Qt4程式設計》。
  19. 強烈推薦程式員自我修養和規劃系列書《大話程式員》《程式員的成長課》《解憂程式員》,受益匪淺,受益終生!
  20. SDK位址: https://gitee.com/feiyangqingyun/QUCSDK https://github.com/feiyangqingyun/qucsdk