最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python+PyQt5設(shè)計(jì)實(shí)現(xiàn)一個C++源代碼行數(shù)統(tǒng)計(jì)工具

 更新時(shí)間:2025年10月21日 09:18:31   作者:老歌老聽老掉牙  
在軟件開發(fā)過程中,代碼行數(shù)是一個基礎(chǔ)但重要的軟件度量指標(biāo),本文介紹一個基于PyQt5的圖形化C++源代碼行數(shù)統(tǒng)計(jì)工具,感興趣的小伙伴可以了解下

引言

在軟件開發(fā)過程中,代碼行數(shù)(Lines of Code, LOC)是一個基礎(chǔ)但重要的軟件度量指標(biāo)。雖然它不能完全代表代碼質(zhì)量或開發(fā)效率,但在項(xiàng)目規(guī)模評估、進(jìn)度跟蹤和復(fù)雜度分析中仍然具有參考價(jià)值。對于C++項(xiàng)目而言,由于頭文件(.h)和實(shí)現(xiàn)文件(.cpp)的分離,需要一個專門的工具來準(zhǔn)確統(tǒng)計(jì)源代碼規(guī)模。

本文介紹一個基于PyQt5的圖形化C++源代碼行數(shù)統(tǒng)計(jì)工具,該工具能夠遞歸遍歷指定目錄,統(tǒng)計(jì)所有C++源文件的行數(shù),并提供詳細(xì)的分類統(tǒng)計(jì)結(jié)果。

程序設(shè)計(jì)架構(gòu)

整體架構(gòu)設(shè)計(jì)

該工具采用經(jīng)典的Model-View-Controller(MVC)架構(gòu)模式,但在PyQt5的上下文中有所調(diào)整:

  • View層:由PyQt5的各類窗口部件組成,負(fù)責(zé)用戶交互和結(jié)果展示
  • Controller層:處理用戶事件,協(xié)調(diào)View和Model之間的數(shù)據(jù)流
  • Model層:文件統(tǒng)計(jì)線程,負(fù)責(zé)實(shí)際的文件遍歷和行數(shù)統(tǒng)計(jì)

多線程設(shè)計(jì)

考慮到大型項(xiàng)目可能包含數(shù)千個源文件,統(tǒng)計(jì)過程可能耗時(shí)較長,因此采用多線程設(shè)計(jì)至關(guān)重要。主線程負(fù)責(zé)UI渲染和事件處理,工作線程負(fù)責(zé)文件統(tǒng)計(jì),通過PyQt5的信號-槽機(jī)制實(shí)現(xiàn)線程間通信。

其中Ttotal?是總耗時(shí), Ttraverse?是文件遍歷時(shí)間, Tread?是文件讀取時(shí)間,Tcount?是行數(shù)統(tǒng)計(jì)時(shí)間。

核心模塊實(shí)現(xiàn)

主窗口類設(shè)計(jì)

import sys
import os
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, 
                             QPushButton, QLabel, QTextEdit, QFileDialog, 
                             QWidget, QProgressBar, QMessageBox, QSplitter)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFont

class CodeLineCounter(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.selected_directory = ""
        self.count_thread = None
        
    def init_ui(self):
        self.setWindowTitle('C++源代碼行數(shù)統(tǒng)計(jì)工具')
        self.setGeometry(100, 100, 900, 700)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 創(chuàng)建界面組件
        self.create_control_panel(main_layout)
        self.create_progress_bar(main_layout)
        self.create_result_display(main_layout)
        
    def create_control_panel(self, parent_layout):
        control_layout = QHBoxLayout()
        
        self.select_btn = QPushButton('選擇目錄')
        self.select_btn.clicked.connect(self.select_directory)
        
        self.count_btn = QPushButton('開始統(tǒng)計(jì)')
        self.count_btn.clicked.connect(self.start_counting)
        self.count_btn.setEnabled(False)
        
        self.path_label = QLabel('未選擇目錄')
        self.path_label.setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; }")
        
        control_layout.addWidget(self.select_btn)
        control_layout.addWidget(self.count_btn)
        control_layout.addWidget(self.path_label, 1)
        
        parent_layout.addLayout(control_layout)
    
    def create_progress_bar(self, parent_layout):
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        parent_layout.addWidget(self.progress_bar)
    
    def create_result_display(self, parent_layout):
        self.result_label = QLabel('請選擇包含C++源代碼的目錄')
        self.result_label.setAlignment(Qt.AlignCenter)
        self.result_label.setStyleSheet("QLabel { font-size: 14px; padding: 10px; }")
        parent_layout.addWidget(self.result_label)
        
        splitter = QSplitter(Qt.Vertical)
        
        self.file_list = QTextEdit()
        self.file_list.setReadOnly(True)
        self.file_list.setPlaceholderText('文件統(tǒng)計(jì)結(jié)果將顯示在這里...')
        self.file_list.setFont(QFont("Consolas", 9))
        
        self.detail_text = QTextEdit()
        self.detail_text.setReadOnly(True)
        self.detail_text.setPlaceholderText('詳細(xì)統(tǒng)計(jì)信息將顯示在這里...')
        self.detail_text.setFont(QFont("Consolas", 9))
        
        splitter.addWidget(self.file_list)
        splitter.addWidget(self.detail_text)
        splitter.setSizes([400, 200])
        
        parent_layout.addWidget(splitter, 1)

文件統(tǒng)計(jì)線程類

class FileCounterThread(QThread):
    progress_updated = pyqtSignal(int)
    file_counted = pyqtSignal(str, int)
    finished_counting = pyqtSignal(dict, int)
    
    def __init__(self, directory):
        super().__init__()
        self.directory = directory
        self.file_extensions = ['.h', '.cpp', '.hpp', '.cc', '.cxx', '.c', '.hh']
        
    def run(self):
        file_results = {}
        total_lines = 0
        file_count = 0
        
        cpp_files = self.find_cpp_files()
        total_files = len(cpp_files)
        
        for i, file_path in enumerate(cpp_files):
            line_count = self.count_file_lines(file_path)
            if line_count >= 0:
                file_results[file_path] = line_count
                total_lines += line_count
                self.file_counted.emit(file_path, line_count)
                
            progress = int((i + 1) / total_files * 100)
            self.progress_updated.emit(progress)
        
        self.finished_counting.emit(file_results, total_lines)
    
    def find_cpp_files(self):
        cpp_files = []
        for root, dirs, files in os.walk(self.directory):
            for file in files:
                if any(file.lower().endswith(ext) for ext in self.file_extensions):
                    cpp_files.append(os.path.join(root, file))
        return cpp_files
    
    def count_file_lines(self, file_path):
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
                return len(lines)
        except Exception as e:
            print(f"無法讀取文件 {file_path}: {e}")
            return -1

事件處理邏輯

class CodeLineCounter(QMainWindow):
    # ... 前面的代碼 ...
    
    def select_directory(self):
        directory = QFileDialog.getExistingDirectory(
            self, 
            '選擇包含C++源代碼的目錄',
            '',
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
        )
        
        if directory:
            self.selected_directory = directory
            self.path_label.setText(directory)
            self.count_btn.setEnabled(True)
            self.result_label.setText('目錄已選擇,點(diǎn)擊"開始統(tǒng)計(jì)"按鈕進(jìn)行統(tǒng)計(jì)')
            
    def start_counting(self):
        if not self.selected_directory:
            QMessageBox.warning(self, '警告', '請先選擇目錄!')
            return
            
        self.reset_ui_for_counting()
        self.start_counting_thread()
        
    def reset_ui_for_counting(self):
        self.file_list.clear()
        self.detail_text.clear()
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.count_btn.setEnabled(False)
        self.select_btn.setEnabled(False)
        self.result_label.setText('正在統(tǒng)計(jì)中...')
        
    def start_counting_thread(self):
        self.count_thread = FileCounterThread(self.selected_directory)
        self.count_thread.file_counted.connect(self.on_file_counted)
        self.count_thread.progress_updated.connect(self.progress_bar.setValue)
        self.count_thread.finished_counting.connect(self.on_counting_finished)
        self.count_thread.start()
        
    def on_file_counted(self, file_path, line_count):
        rel_path = os.path.relpath(file_path, self.selected_directory)
        self.file_list.append(f"{rel_path}: {line_count} 行")
        
    def on_counting_finished(self, file_results, total_lines):
        self.progress_bar.setVisible(False)
        self.count_btn.setEnabled(True)
        self.select_btn.setEnabled(True)
        self.display_final_results(file_results, total_lines)
    
    def display_final_results(self, file_results, total_lines):
        file_types = self.analyze_file_types(file_results)
        
        detail_text = self.generate_summary_text(file_results, total_lines, file_types)
        detail_text += self.generate_file_type_statistics(file_types)
        detail_text += self.generate_top_files_list(file_results)
        
        self.detail_text.setText(detail_text)
        self.result_label.setText(f'統(tǒng)計(jì)完成!共 {len(file_results)} 個文件,總行數(shù): {total_lines:,}')
    
    def analyze_file_types(self, file_results):
        file_types = {}
        for file_path in file_results.keys():
            ext = os.path.splitext(file_path)[1].lower()
            file_types[ext] = file_types.get(ext, 0) + 1
        return file_types
    
    def generate_summary_text(self, file_results, total_lines, file_types):
        text = f"統(tǒng)計(jì)完成!\n"
        text += f"=" * 50 + "\n"
        text += f"目錄: {self.selected_directory}\n"
        text += f"總文件數(shù): {len(file_results)}\n"
        text += f"總行數(shù): {total_lines:,}\n\n"
        return text
    
    def generate_file_type_statistics(self, file_types):
        text = "文件類型統(tǒng)計(jì):\n"
        for ext, count in sorted(file_types.items()):
            text += f"  {ext}: {count} 個文件\n"
        return text
    
    def generate_top_files_list(self, file_results):
        if not file_results:
            return ""
            
        sorted_files = sorted(file_results.items(), key=lambda x: x[1], reverse=True)
        text = f"\n行數(shù)最多的文件 (前10個):\n"
        for i, (file_path, line_count) in enumerate(sorted_files[:10]):
            rel_path = os.path.relpath(file_path, self.selected_directory)
            text += f"  {i+1}. {rel_path}: {line_count:,} 行\(zhòng)n"
        return text

算法分析與優(yōu)化

文件遍歷算法

文件遍歷采用深度優(yōu)先搜索(DFS)策略,使用os.walk()函數(shù)遞歸遍歷目錄樹。該算法的時(shí)間復(fù)雜度為O(n),其中 n是目錄中的文件和子目錄總數(shù)。

其中d是目錄樹的最大深度,bi?是第 i 層的分支因子。

行數(shù)統(tǒng)計(jì)算法

行數(shù)統(tǒng)計(jì)采用簡單的逐行讀取方法,對于每個文件:

  • 打開文件并設(shè)置適當(dāng)?shù)木幋a處理
  • 讀取所有行到內(nèi)存中
  • 統(tǒng)計(jì)行數(shù)

這種方法的時(shí)間復(fù)雜度為O(m),其中 m 是文件中的行數(shù)。

內(nèi)存優(yōu)化策略

對于極大的源文件,我們采用流式讀取而非一次性加載到內(nèi)存:

def count_file_lines_streaming(file_path):
    try:
        line_count = 0
        with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
            for line in f:
                line_count += 1
        return line_count
    except Exception as e:
        print(f"無法讀取文件 {file_path}: {e}")
        return -1

完整可運(yùn)行代碼

import sys
import os
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, 
                             QPushButton, QLabel, QTextEdit, QFileDialog, 
                             QWidget, QProgressBar, QMessageBox, QSplitter)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFont

class FileCounterThread(QThread):
    progress_updated = pyqtSignal(int)
    file_counted = pyqtSignal(str, int)
    finished_counting = pyqtSignal(dict, int)
    
    def __init__(self, directory):
        super().__init__()
        self.directory = directory
        self.file_extensions = ['.h', '.cpp', '.hpp', '.cc', '.cxx', '.c', '.hh']
        
    def run(self):
        file_results = {}
        total_lines = 0
        
        cpp_files = self.find_cpp_files()
        total_files = len(cpp_files)
        
        for i, file_path in enumerate(cpp_files):
            line_count = self.count_file_lines(file_path)
            if line_count >= 0:
                file_results[file_path] = line_count
                total_lines += line_count
                self.file_counted.emit(file_path, line_count)
                
            progress = int((i + 1) / total_files * 100)
            self.progress_updated.emit(progress)
        
        self.finished_counting.emit(file_results, total_lines)
    
    def find_cpp_files(self):
        cpp_files = []
        for root, dirs, files in os.walk(self.directory):
            for file in files:
                if any(file.lower().endswith(ext) for ext in self.file_extensions):
                    cpp_files.append(os.path.join(root, file))
        return cpp_files
    
    def count_file_lines(self, file_path):
        try:
            line_count = 0
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                for line in f:
                    line_count += 1
            return line_count
        except Exception as e:
            print(f"無法讀取文件 {file_path}: {e}")
            return -1

class CodeLineCounter(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.selected_directory = ""
        self.count_thread = None
        
    def init_ui(self):
        self.setWindowTitle('C++源代碼行數(shù)統(tǒng)計(jì)工具')
        self.setGeometry(100, 100, 900, 700)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        main_layout = QVBoxLayout(central_widget)
        
        control_layout = QHBoxLayout()
        
        self.select_btn = QPushButton('選擇目錄')
        self.select_btn.clicked.connect(self.select_directory)
        
        self.count_btn = QPushButton('開始統(tǒng)計(jì)')
        self.count_btn.clicked.connect(self.start_counting)
        self.count_btn.setEnabled(False)
        
        self.path_label = QLabel('未選擇目錄')
        self.path_label.setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; }")
        
        control_layout.addWidget(self.select_btn)
        control_layout.addWidget(self.count_btn)
        control_layout.addWidget(self.path_label, 1)
        
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        
        self.result_label = QLabel('請選擇包含C++源代碼的目錄')
        self.result_label.setAlignment(Qt.AlignCenter)
        self.result_label.setStyleSheet("QLabel { font-size: 14px; padding: 10px; }")
        
        splitter = QSplitter(Qt.Vertical)
        
        self.file_list = QTextEdit()
        self.file_list.setReadOnly(True)
        self.file_list.setPlaceholderText('文件統(tǒng)計(jì)結(jié)果將顯示在這里...')
        self.file_list.setFont(QFont("Consolas", 9))
        
        self.detail_text = QTextEdit()
        self.detail_text.setReadOnly(True)
        self.detail_text.setPlaceholderText('詳細(xì)統(tǒng)計(jì)信息將顯示在這里...')
        self.detail_text.setFont(QFont("Consolas", 9))
        
        splitter.addWidget(self.file_list)
        splitter.addWidget(self.detail_text)
        splitter.setSizes([400, 200])
        
        main_layout.addLayout(control_layout)
        main_layout.addWidget(self.progress_bar)
        main_layout.addWidget(self.result_label)
        main_layout.addWidget(splitter, 1)
        
    def select_directory(self):
        directory = QFileDialog.getExistingDirectory(
            self, 
            '選擇包含C++源代碼的目錄',
            '',
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
        )
        
        if directory:
            self.selected_directory = directory
            self.path_label.setText(directory)
            self.count_btn.setEnabled(True)
            self.result_label.setText('目錄已選擇,點(diǎn)擊"開始統(tǒng)計(jì)"按鈕進(jìn)行統(tǒng)計(jì)')
            
    def start_counting(self):
        if not self.selected_directory:
            QMessageBox.warning(self, '警告', '請先選擇目錄!')
            return
            
        self.file_list.clear()
        self.detail_text.clear()
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.count_btn.setEnabled(False)
        self.select_btn.setEnabled(False)
        self.result_label.setText('正在統(tǒng)計(jì)中...')
        
        self.count_thread = FileCounterThread(self.selected_directory)
        self.count_thread.file_counted.connect(self.on_file_counted)
        self.count_thread.progress_updated.connect(self.progress_bar.setValue)
        self.count_thread.finished_counting.connect(self.on_counting_finished)
        self.count_thread.start()
        
    def on_file_counted(self, file_path, line_count):
        rel_path = os.path.relpath(file_path, self.selected_directory)
        self.file_list.append(f"{rel_path}: {line_count} 行")
        
    def on_counting_finished(self, file_results, total_lines):
        self.progress_bar.setVisible(False)
        self.count_btn.setEnabled(True)
        self.select_btn.setEnabled(True)
        
        file_types = {}
        for file_path, line_count in file_results.items():
            ext = os.path.splitext(file_path)[1].lower()
            file_types[ext] = file_types.get(ext, 0) + 1
        
        detail_text = f"統(tǒng)計(jì)完成!\n"
        detail_text += f"=" * 50 + "\n"
        detail_text += f"目錄: {self.selected_directory}\n"
        detail_text += f"總文件數(shù): {len(file_results)}\n"
        detail_text += f"總行數(shù): {total_lines:,}\n\n"
        
        detail_text += "文件類型統(tǒng)計(jì):\n"
        for ext, count in sorted(file_types.items()):
            detail_text += f"  {ext}: {count} 個文件\n"
        
        if file_results:
            sorted_files = sorted(file_results.items(), key=lambda x: x[1], reverse=True)
            detail_text += f"\n行數(shù)最多的文件 (前10個):\n"
            for i, (file_path, line_count) in enumerate(sorted_files[:10]):
                rel_path = os.path.relpath(file_path, self.selected_directory)
                detail_text += f"  {i+1}. {rel_path}: {line_count:,} 行\(zhòng)n"
        
        self.detail_text.setText(detail_text)
        self.result_label.setText(f'統(tǒng)計(jì)完成!共 {len(file_results)} 個文件,總行數(shù): {total_lines:,}')

def main():
    app = QApplication(sys.argv)
    app.setApplicationName('C++代碼行數(shù)統(tǒng)計(jì)工具')
    
    window = CodeLineCounter()
    window.show()
    
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

使用說明與功能特點(diǎn)

安裝依賴

pip install PyQt5

運(yùn)行程序

python cpp_line_counter.py

主要功能特點(diǎn)

  • 多格式支持:支持.h, .cpp, .hpp, .cc, .cxx, .c, .hh等多種C++文件格式
  • 遞歸統(tǒng)計(jì):自動遞歸遍歷所有子目錄
  • 實(shí)時(shí)顯示:統(tǒng)計(jì)過程中實(shí)時(shí)顯示每個文件的結(jié)果
  • 進(jìn)度反饋:進(jìn)度條顯示整體統(tǒng)計(jì)進(jìn)度
  • 詳細(xì)報(bào)告:提供文件類型統(tǒng)計(jì)和最大文件排名
  • 錯誤容錯:自動處理無法讀取的文件,繼續(xù)統(tǒng)計(jì)其他文件
  • 編碼兼容:支持UTF-8編碼,兼容各種編碼格式的源文件

性能分析與優(yōu)化建議

性能瓶頸分析

在實(shí)際使用中,主要性能瓶頸可能出現(xiàn)在:

  • I/O操作:大量小文件的讀取操作
  • 內(nèi)存使用:極大文件的處理
  • UI更新:頻繁的界面刷新

優(yōu)化策略

  • 批量處理:將小文件分組批量讀取
  • 異步I/O:使用異步文件讀取提高并發(fā)性
  • 采樣統(tǒng)計(jì):對于極大文件,可以采用采樣統(tǒng)計(jì)方法
  • 緩存機(jī)制:緩存已統(tǒng)計(jì)文件的結(jié)果

擴(kuò)展功能建議

  • 代碼復(fù)雜度分析:集成圈復(fù)雜度、函數(shù)長度等指標(biāo)
  • 注釋率統(tǒng)計(jì):區(qū)分代碼行和注釋行
  • 歷史對比:支持不同版本間的代碼變化統(tǒng)計(jì)
  • 導(dǎo)出功能:支持將統(tǒng)計(jì)結(jié)果導(dǎo)出為CSV或Excel格式

結(jié)論

本文介紹的C++源代碼行數(shù)統(tǒng)計(jì)工具基于PyQt5框架,采用了多線程架構(gòu)和模塊化設(shè)計(jì),具有良好的用戶體驗(yàn)和可擴(kuò)展性。通過數(shù)學(xué)建模和算法分析,我們深入探討了工具的性能特征和優(yōu)化方向。

該工具不僅滿足了基本的代碼行數(shù)統(tǒng)計(jì)需求,還為后續(xù)的功能擴(kuò)展奠定了堅(jiān)實(shí)基礎(chǔ)。在實(shí)際軟件開發(fā)過程中,這樣的工具能夠幫助開發(fā)團(tuán)隊(duì)更好地理解項(xiàng)目規(guī)模,進(jìn)行有效的項(xiàng)目管理和技術(shù)決策。

代碼行數(shù)雖然只是一個基礎(chǔ)度量指標(biāo),但在恰當(dāng)?shù)恼Z境下,結(jié)合其他質(zhì)量指標(biāo),仍然能夠?yàn)檐浖こ虒?shí)踐提供有價(jià)值的參考信息。

到此這篇關(guān)于Python+PyQt5設(shè)計(jì)實(shí)現(xiàn)一個C++源代碼行數(shù)統(tǒng)計(jì)工具的文章就介紹到這了,更多相關(guān)Python統(tǒng)計(jì)C++代碼行數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

永兴县| 红河县| 遵义县| 顺义区| 丹棱县| 扶绥县| 绵竹市| 团风县| 阿城市| 抚州市| 巴彦淖尔市| 成都市| 九龙坡区| 藁城市| 治多县| 宜春市| 敖汉旗| 澄江县| 商丘市| 临安市| 甘洛县| 曲沃县| 靖边县| 广水市| 白沙| 永顺县| 香河县| 双牌县| 密云县| 安阳市| 静海县| 武义县| 惠州市| 邯郸市| 虹口区| 巫溪县| 嵊州市| 宜城市| 海南省| 镇远县| 察隅县|