• <tr id="yyy80"></tr>
  • <sup id="yyy80"></sup>
  • <tfoot id="yyy80"><noscript id="yyy80"></noscript></tfoot>
  • 99热精品在线国产_美女午夜性视频免费_国产精品国产高清国产av_av欧美777_自拍偷自拍亚洲精品老妇_亚洲熟女精品中文字幕_www日本黄色视频网_国产精品野战在线观看 ?

    基于Qt的文件內(nèi)容搜索工具設(shè)計與實現(xiàn)

    2019-09-10 19:55:23章俊
    大眾科學(xué)·中旬 2019年8期
    關(guān)鍵詞:多線程

    章俊

    摘 要:該文利用Qt框架實現(xiàn)了一個文件搜索工具,并支持對文件內(nèi)容的搜索。該文件搜索工具是一個集多線程、圖形界面、事件處理的面向?qū)ο缶幊痰膶嵗绦颉?/p>

    關(guān)鍵詞:Qt;C++;多線程;文件搜索;事件處理

    1背景

    Windwos系統(tǒng)原生自帶的“文件搜索工具”的功能簡單,查找文件緩慢,而且不支持基于文本內(nèi)容的文件搜索。本文利用Qt框架,實現(xiàn)了一個支持并發(fā)的文本內(nèi)容的搜索工具。該文件搜索工具在進行搜索時,會把搜索任務(wù)合理分配給輔助線程,并由輔助線程完成任務(wù)后由事件系統(tǒng)通知給主線程。該文件搜索工具是一個集多線程、圖形界面、事件處理的面向?qū)ο缶幊讨R的綜合應(yīng)用實例程序。

    2Qt框架介紹

    Qt是跨平臺的應(yīng)用程序和圖形用戶接口(GUI)開發(fā)框架,由集成開發(fā)工具、跨平臺類庫和集成開發(fā)環(huán)境(IDE)組成,它的開發(fā)語言是C++。

    Qt中的一個核心機制是信號與槽機制,它是Qt的基礎(chǔ)。信號與槽的設(shè)計使用了觀察者模式,當(dāng)某個事件發(fā)生之后,它就會發(fā)出一個信號(signal),信號的發(fā)出是沒有目的的,類似廣播,如果有對象對這個信號感興趣,它就會使用連接(connect)函數(shù)將自己的槽(slot)函數(shù)綁定到這個信號。信號與槽機制取代了回調(diào)函數(shù)的實現(xiàn)方式,當(dāng)信號發(fā)出時,被連接的槽函數(shù)會自動被回調(diào),它可以讓應(yīng)用程序編寫人員把這些互不了解的對象綁定在一起。

    事件是GUI程序的重要部分,GUI應(yīng)用程序是由事件循環(huán)驅(qū)動的。事件是各種由應(yīng)用程序內(nèi)部或者外部產(chǎn)生的事情或者動作的統(tǒng)稱,Qt中使用一個對象來表示一個事件,繼承自QEvent類。事件和信號與槽并不相同,響應(yīng)事件的函數(shù)并不能立即響應(yīng),而是會進入事件序列,等待執(zhí)行。而信號與槽不同,信號發(fā)出后,關(guān)連的槽函數(shù)會立即得到執(zhí)行。

    3軟件介紹

    文件搜索工具會從指定的目錄中搜索含有指定內(nèi)容的文件,并列出匹配到的文件列表。一個文件的搜索過程涉及到把文件的內(nèi)容讀取到內(nèi)存,然后在讀取到的內(nèi)容中匹配指定的搜索關(guān)鍵字,并把匹配到的文件路徑顯示出來,這是一個混合了磁盤讀取和數(shù)據(jù)處理的過程。

    在這個程序中,使用了多個輔助線程完成這個任務(wù),每個線程都有需要用來搜索的一個文件列表。每個線程在進行搜索任務(wù)的同時,通過使用一個自定義事件來與主線程進行通信,把搜索到的結(jié)果通知給主線程(GUI)進行匯總與顯示。

    4詳細設(shè)計與實現(xiàn)

    4.1界面設(shè)計

    使用Qt Creator新建一個Qt Widgets Application程序,并新建一個繼承于QMainWindow類的MainWindow窗口類,在類MainWindow下創(chuàng)建一個搜索目錄設(shè)置框,一個搜索內(nèi)容設(shè)置框,一個搜索結(jié)果顯示列表,一個搜索/取消按鈕。界面效果如圖1所示。

    4.2搜索任務(wù)與界面顯示設(shè)計

    主窗口類MainWindow還有2個私有數(shù)據(jù)類型的項:int done和volatile bool stopped,done整型變量是已經(jīng)匹配成功的文件數(shù)目,stopped布爾值用來通知輔助線程用戶是否已經(jīng)取消操作,此變量使用volatile來修飾,在線程間使用volatile bool是安全的。

    一旦選定搜索目錄、搜索內(nèi)容,用戶按下Search按鈕就開始搜索工作了。按鈕一旦被按下,該按鈕就會變成Cancel按鈕,所以用戶可以在任何時候停止搜索工作。這個按鈕與searchOrCancel()槽相連。

    searchOrCancel()函數(shù)主要內(nèi)容:

    void MainWindow::searchOrCancel()

    {

    m_stopped = true;

    if (QThreadPool::globalInstance()->activeThreadCount())

    QThreadPool::globalInstance()->waitForDone();

    if (m_searchOrCancelButton->text() == tr("&Cancel")) {

    updateUi();

    return;

    }

    QStringList sourceFiles = getFiles(m_directoryLineEdit->text());

    if (!sourceFiles.isEmpty()) {

    m_resultListWidget->clear();

    m_statusBar->clearMessage();

    searchFiles(sourceFiles);

    }

    }

    在這個槽函數(shù)的一開始設(shè)定stopped變量值為true,通知那些運行中的輔助線程都必須停止。然后,檢查是否還有輔助線程在Qt的全局線程序列中運行,如果有的話,那么就阻塞,直到所有的線程都停止。

    點擊Cancel按鈕會調(diào)用updateUi()方法把Cancel按鈕的文本改成Search并返回。

    點擊Search按鈕,就會使用getFiles()函數(shù)獲得搜索目錄所有文件的列表。如果列表為空,則通知用戶錯誤,并返回。如果列表不為空,清除搜索結(jié)果窗口中之前的搜索結(jié)果和狀態(tài)欄的搜索統(tǒng)計結(jié)果。

    4.3任務(wù)分配與線程調(diào)度設(shè)計

    文件搜索工具的具體搜索工作是在多個線程內(nèi)完成的,所以主線程在會把搜索任務(wù)合理的分配給多個線程。在此使用QtConcurrent::run()函數(shù)調(diào)用任務(wù)線程,它會在Qt全局線程池中的一個輔助線程中執(zhí)行該函數(shù)。

    在本應(yīng)用中使用searchFiles()函數(shù)把任務(wù)分配給多個線程,并使線程運行。該函數(shù)接受的形參是需要搜索的文件列表,函數(shù)的主要內(nèi)容:

    void MainWindow::searchFiles(const QStringList &sourceFiles)

    {

    m_stopped = false;

    updateUi();

    m_total = sourceFiles.count();

    m_done = 0;

    const QVector<int> sizes = chunkSizes(sourceFiles.count(),QThread::idealThreadCount());

    const QString &searchContent = m_contentLineEdit->text();

    int offset = 0;

    foreach (const int chunkSize, sizes) {

    QtConcurrent::run(searchFilesTask, this, &m_stopped,

    sourceFiles.mid(offset, chunkSize), searchContent);

    offset += chunkSize;

    }

    checkIfDone();

    }

    在函數(shù)的一開始把stopped變量為false,然后調(diào)用updateUi()函數(shù)更新界面的顯示。把done變量設(shè)置成0,因為還沒有匹配到任何文件。

    如果為每一個文件創(chuàng)建一個線程來執(zhí)行搜索任務(wù),即為每個必須要檢索的文件通過函數(shù)和文件名調(diào)用QtConcurrent::run()一次,這樣會建立和列表中文件數(shù)目一樣多的輔助線程。對于一些非常大的文件,這樣的方法或許奏效,但對于非常多卻不知大小的文件來說,建立如此多的線程所付出的代價會與任務(wù)分散給輔助線程處理所得到的潛在收益不成比例。

    所以在此使用QThread::idealThreadCount()函數(shù)獲取計算程序運行所在平臺上支持的輔助線程的最佳數(shù)目,并把任務(wù)進行劃分,使得每個輔助線程都能得到一個合理的任務(wù)數(shù)量。

    chunkSize()函數(shù)會完成此工作,它會對容器進行劃分,該函數(shù)會根據(jù)容器中給定的文件數(shù)量和期望的文件塊數(shù)(這里是輔助線程的數(shù)目),返回一個劃分的塊大小矢量。chunkSize()函數(shù)的內(nèi)容:

    QVector<int> MainWindow::chunkSizes(int size, int chunkCount)

    {

    if (chunkCount == 1) return QVector<int>() << size;

    QVector<int> result(chunkCount, size / chunkCount);

    if (int remainder = size % chunkCount) {

    int index = 0;

    for (int i = 0; i < remainder; ++i) {

    ++result[index];

    ++index;

    index %= chunkCount;

    }

    }

    return result;

    }

    接下來使用一個循環(huán)來遍歷得到的任務(wù)劃分矢量,并使用QtConcurrent::run()函數(shù)啟動輔助線程。一旦所有的輔助線程得到啟動,就會調(diào)用checkIfDone()槽函數(shù),這個槽函數(shù)會以輪詢的方式確定搜索是否完成。

    4.4搜索任務(wù)線程設(shè)計

    搜索匹配函數(shù)searchFilesTask()會被任務(wù)分配函數(shù)調(diào)用一次或多次,它們在一個或多個輔助線程中運行,每個任務(wù)線程都有一個唯一的需要處理文件的列表,并對列表中的文件進行搜索處理。在搜索任務(wù)線程中,每次搜索匹配文件內(nèi)容之前,都會檢測stopped布爾值來獲取用戶是否已經(jīng)取消了操作,如果已經(jīng)取消,函數(shù)就返回,它正在執(zhí)行的線程也會轉(zhuǎn)變成非激活態(tài),如果沒有取消,則繼續(xù)運行。searchFilesTask()函數(shù)的主要內(nèi)容:

    void searchFilesTask(QObject *receiver, volatile bool *stopped,

    const QStringList &sourceFiles, const QString &searchContent)

    {

    foreach (const QString &source, sourceFiles) {

    if (*stopped) return;

    QFile file(source);

    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) continue;

    if(file.readAll().contains(searchContent.toLocal8Bit().data())) {

    QApplication::postEvent(receiver, new ProgressEvent(hit, source));

    }

    }

    }

    文件搜索過程非常簡單:對于列表中的每個文件,讀取文件中的內(nèi)容,搜索是否含有搜索的關(guān)鍵字,如果沒有,進行列表中下一個文件的搜索;如果有,則使用一個自定義事件,把文件路徑傳遞給主窗口對象。QApplication::postEvent()函數(shù)進行這個自定義事件的傳遞。

    實際上,有兩種方法可用于事件的發(fā)送:QApplication::sendEvent()和QApplication::postEvent()。sendEvent()函數(shù)會立即發(fā)送事件。同時,sendEvent()不會刪除事件,因此,實際應(yīng)用中會在棧(stack)上創(chuàng)建sendEvent()事件。postEvent()函數(shù)會向接收者的事件序列中添加事件,該事件應(yīng)當(dāng)使用new創(chuàng)建在堆(heap)上,以便可以讓它作為接收者事件序列循環(huán)處理過程中的一部分而得到處理,postEvent()函數(shù)會獲取這個事件的所有權(quán),它會析構(gòu)并刪除此事件,它與Qt的事件處理配合得非常好。

    Qt完美地處理了事件從一個線程切換到另外一個線程的情況。使用自定義事件,如下是自定義事件類ProcessEvent的內(nèi)容:

    struct ProgressEvent : public QEvent

    {

    explicit ProgressEvent(bool hit_, const QString &fileName_)

    : QEvent(static_cast<Type>(QEvent::User)), hit(hit_), fileName(fileName_) {}

    const bool hit;

    const QString fileName;

    };

    在此給予每一個自定義事件一個唯一的ID(QEvent::User),ID的類型為QEvent::Type,這樣做就可以避免事件之間的相互混淆。我們把事件定義成struct,并從基類QEvent派生,并將布爾型的hit標志和fileName文本設(shè)置成可公開訪問,便于在界面類MainWindow中訪問。

    在界面類MainWindow中重新實現(xiàn)了QWidget::event(),使其能夠探測并處理自定義事件。界面類MainWindow中重新實現(xiàn)的QWidget::event()的內(nèi)容:

    bool MainWindow::event(QEvent *event)

    {

    if (!m_stopped && event->type() == static_cast<QEvent::Type>(QEvent::User)) {

    ProgressEvent *progressEvent = static_cast<ProgressEvent*>(event);

    if (progressEvent->hit) {

    m_resultListWidget->addItem(progressEvent->fileName);

    ++m_done;

    }

    return true;

    }

    return QMainWindow::event(event);

    }

    此外,如果數(shù)據(jù)處理正在進行,獲取自定義的ProgressEvent,會把事件的信息文本(匹配到的文件路徑)添加到搜索結(jié)果窗口中,并增加匹配到的文件數(shù)量。程序還會返回一個true來表明事件已經(jīng)得到了處理, Qt將會刪除該事件,而不是繼續(xù)尋找另一個能夠處理該事件的處理器。但是,如果搜索過程已經(jīng)停止,對于任何的其他事件,都把任務(wù)傳遞給基類的事件處理器。

    5結(jié)束語

    本文通過使用Qt應(yīng)用框架實現(xiàn)了文件搜索工具,支持對文件內(nèi)容的搜索,并支持多線程搜索。可為類似的圖形界面、多線程復(fù)雜計算任務(wù)軟件的開發(fā)工作提供參考。

    本文還使用Qt框架的自定義事件系統(tǒng)代替?zhèn)鹘y(tǒng)的信號與槽實現(xiàn)輔助線程與主線程間通信應(yīng)用進行了嘗試研究工作。并對兩種事件的發(fā)送方式(QApplication::sendEvent()和QApplication::postEvent())進行了研究。

    參考文獻

    [1] Mark Summerfiled. Advanced Qt Programming[M]. Prentice Hall, 2010-7.

    [2] Jasmin Blanchette, Mark Summerfield. C++ GUI Programming with Qt4[M]. Prentice Hall, 2008-2.

    猜你喜歡
    多線程
    Java多線程同步機制在網(wǎng)絡(luò)售票系統(tǒng)中的應(yīng)用
    科技資訊(2016年29期)2017-02-28 09:30:34
    Java并發(fā)工具包對并發(fā)編程的優(yōu)化
    基于多線程文件傳輸關(guān)鍵技術(shù)研究與實現(xiàn)
    網(wǎng)頁爬蟲技術(shù)的關(guān)鍵技術(shù)研究探索
    一種基于多線程的高速磁盤鏡像算法
    iOS并發(fā)程序設(shè)計中幾種方法的特點及使用技巧研究
    HTM L5 Web WOrker技術(shù)及應(yīng)用研究
    電站鍋爐煤粉參數(shù)遠程監(jiān)控系統(tǒng)的軟件設(shè)計與實現(xiàn)
    一種高并發(fā)認證服務(wù)器的實現(xiàn)
    一種低開銷的并行重復(fù)數(shù)據(jù)刪除算法
    肇东市| 通辽市| 萍乡市| 沅陵县| 盐源县| 宜兰市| 紫金县| 偏关县| 武隆县| 上饶市| 黎川县| 江都市| 宣威市| 封丘县| 鹤庆县| 河源市| 上饶市| 阳新县| 瑞丽市| 溆浦县| 泽普县| 荆州市| 英德市| 侯马市| 梁河县| 靖州| 中山市| 海南省| 饶河县| 隆回县| 荥阳市| 宜宾市| 义马市| 奉新县| 韶关市| 亳州市| 水富县| 丹东市| 上林县| 周宁县| 渭南市|