Qt製作簡單標籤雲(上)

前言:這兩天看Qt看到了網絡通信這塊,就想起了以前想寫的一個小項目:標籤雲(當時不知道這個名,後來查了才知道的)。花了一些心思,做了個簡易的標籤雲版本。記錄在此。

本項目分以下幾個部分進行記錄:
1、項目概況
2、Http向服務器請求關鍵詞分詞服務
3、標籤雲顯示
4、關鍵詞及其權重顯示
5、小結

一、項目概況

首先說下什麼是標籤雲?
標籤雲是一套相關的標籤以及與此相應的權重。簡單來說就是通過圖形化的方式顯示一些關鍵詞及其權重信息。
這個小項目整體的思路是用戶輸入一段文字,然後程序通過向服務器發送此段文字,並請求關鍵詞提取服務,從服務器獲得相關的數據後顯示出來(通過標籤雲和關鍵詞及其權重直接顯示)。最後的結果如下圖所示:
用戶交互頁面
標籤雲:
標籤雲
關鍵詞及其權重:
關鍵詞及其權重

二、Http向服務器請求關鍵詞分詞服務

當用戶輸入待提取關鍵詞的一段文本後,需要進行對文本提取關鍵詞信息,以便後面的標籤顯示和權重信息顯示。本人目前還沒有獨立寫這個算法的能力,所以這裏借用的是網上的API提取關鍵詞及其權重信息。
網上的這種服務並不是很多,我這裏使用的是玻森數據提供的分詞服務,通過Http協議提交一段文本信息給服務器,然後對應的回覆就包括了關鍵詞及其權重。當然這裏需要根據其API的要求將數據解析一下。具體的API使用方法可以參見:玻森數據

具體的代碼如下:

 //設置網絡相關信息
    manager = new QNetworkAccessManager(this);
    request = new QNetworkRequest(QUrl(url));
    request->setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/json"));
    request->setRawHeader("Accept","application/json");
    request->setRawHeader("X-Token",myToken.toLatin1());
    //關聯信號和槽
    connect(manager, &QNetworkAccessManager::finished,
            this, &HttpPost::replyFinished);

說明:這裏主要是根據API的要求進行一些Header的設置。

void HttpPost::on_OKpushButton_clicked()
{
    QString send_data = ui->textEdit->toPlainText();        //獲取要提取的文本信息
    //上傳格式化JSON數據至提取關鍵字服務器
    if(!send_data.isEmpty())
    {
        QJsonObject json_obj;
        json_obj.insert(send_data,1);
        manager->post(*request,QJsonDocument(json_obj).toJson());
    }

    content.clear();        //清空上次目錄內容
}

說明:這裏需要將文本信息轉化爲JSON數據格式然後上傳至遠端服務器上。具體的JSON用法並不是很難,可以自己百度。

//收到服務器的返回信息
void HttpPost::replyFinished(QNetworkReply *reply)
{
    QJsonParseError error;
    QJsonDocument doucment = QJsonDocument::fromJson(reply->readAll(),&error);  //讀取JSon數據

    if (!doucment.isNull() && (error.error == QJsonParseError::NoError))
    {
        if(doucment.isArray())
           {
                QJsonArray array0 = doucment.array();       //一層數組

                QJsonArray array1 = array0.at(0).toArray();     //二層數組

                int key_num = array1.count();           //關鍵字個數
                for(int i = 0;i<key_num;++i)
                {
                      QJsonArray array2 = array1.at(i).toArray();       //三層數組

                      QJsonValue weigh = array2.at(0);      //返回的關鍵詞權重
                      QJsonValue tag = array2.at(1);         //返回的標籤
                     content.insert(weigh.toDouble(),tag.toString());       //取得標籤雲的權重和文本信息
                }
            }
    }
    ShowTagCloud();     //顯示詞雲標籤
    ShowWeiInfo();     //顯示關鍵詞權重頁面
    reply->deleteLater();
}

說明:從服務器中返回的信息也是JSON類型,需要進行解析。返回的JSON類型數據格式如下:

[
    [
        [
            0.5310796475788876,
            "文本"
        ],
        [
            0.5134422418759108,
            "概括"
        ],
        [
            0.4609161239551629,
            "關鍵詞"
        ],
        [
            0.4586252129798319,
            "常用"
        ],
        [
            0.1738515297575574,
            "作爲"
        ],
        [
            0.032630636042815325,
            "對"
        ],
        [
            0.01617369671100372,
            "是"
        ]
    ]
]

三、標籤雲顯示

這裏根據從服務器傳來的數據進行解析之後,進行標籤雲的顯示。其過程就是根據關鍵詞構造不同的QGraphicsSimpleTextItem,根據關鍵詞的權重使其顯示不同的大小。顯示的顏色是隨機生成的,這就生成了TagItem(即標籤)然後把這一個個標籤添加至場景中以便顯示。
這裏要注意一點的是,在標籤添加至場景的過程中要進行碰撞檢測,以防兩個或多個標籤重疊在一起。(這裏需要繪製標籤的大小,用的是QFontMetrics返回文字的大小,但是好像不是很穩定,有些情況下還是會個把個的衝突)。

主要的代碼如下:

//根據關鍵詞信息設置標籤Item
void TagCloudShow::SetItems()
{
    int max_poi_size = 80;          //字體最大值
    int min_poi_size = 20;          //字體最小值

    int dif_size = max_poi_size-min_poi_size;
    QList<double> keys = content.uniqueKeys();      //獲得鍵值
    foreach (double key, keys)
    {
      foreach(QString val_str,content.values(key))
      {
           TagItem *item = new TagItem(val_str);
           //根據權重,設置標籤字體大小:權重即這裏的key值
           int cur_size =min_poi_size+dif_size*key;
           QFont font;
           font.setPointSize(cur_size);
           item->setFont(font);
           //加入字體列表
           show_items.push_back(item);
      }
    }
}
//將標籤Item加入至場景中,這裏要進行衝突檢測以免標籤位置衝突。
void TagCloudShow::AddToScene()
{
    int length = show_items.size();

    int num = (length<=50)?length:50;       //最多50個標籤
    const int rep_num = 20;             //碰撞檢測嘗試重複次數
    for(int i = length-1;i>=(length-num);--i)
    {
        TagItem *item = show_items[i];
        int j = 0;

        //隨機生成標籤項的位置:不能與其他標籤項衝突,也不能超出顯示框
        while(true)
        {
            //產生隨機位置
            QPoint pos(qrand()%1000,qrand()%600);
            item->setPos(pos);
            scene->addItem(item);

            //bool is_col = !(scene->collidingItems(item,Qt::IntersectsItemBoundingRect).isEmpty());
            //衝突檢測,判斷當前Item是否和其他標籤Item衝突
            bool is_col = !(item->collidingItems(Qt::IntersectsItemBoundingRect).isEmpty());

            //防止標籤超出頁面
            int width =item->pos().x()+item->boundingRect().width();
            int height =item->pos().y()+ item->boundingRect().height();
            if(width >= 1000 || width <= 0)
                is_col = true;
            if(height>=600 || height<=0)
                is_col = true;
            if(is_col)
            {
                if(j>rep_num)       //衝突超限,丟棄此標籤
                {
                    scene->removeItem(item);
                    qDebug()<<item->text()<<"構造標籤失敗!";
                    break;
                }
                else
                {
                   ++j;
                    scene->removeItem(item);        //移走此item,重新生成隨機位置
                }
            }
            else
                break;
        }
      
    }
}

文章有點長,這部分先寫到這兒。後面的見:Qt製作簡單標籤雲(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章