Qt實現表格樹控制項-自繪樹節點虛線

来源:https://www.cnblogs.com/swarmbees/archive/2019/08/07/11312691.html
-Advertisement-
Play Games

[TOC] 原文鏈接: "Qt實現表格樹控制項 自繪樹節點虛線" 一、開心一刻 一程式員第一次上女朋友家她媽板著臉問 :你想娶我女兒,有多少存款? 程式員低了下頭:五百! 她媽更鄙視了:才五百塊,買個廁所都不夠! 程式員忙說:不是人民幣! 她媽:就算是美元,還是不夠買廁所! 程式員:其實是比特幣! 她 ...


目錄

原文鏈接:Qt實現表格樹控制項-自繪樹節點虛線

一、開心一刻

一程式員第一次上女朋友家她媽板著臉問 :你想娶我女兒,有多少存款?

程式員低了下頭:五百!

她媽更鄙視了:才五百塊,買個廁所都不夠!

程式員忙說:不是人民幣!

她媽:就算是美元,還是不夠買廁所!

程式員:其實是比特幣!

她媽:哇,賢婿,我給你買只大龍蝦去

二、自繪樹節點?

自繪樹節點?聽起來都挺複雜的,可是為什麼還要自繪樹節點呢?這充分說明產品的腦子是什麼東西都能想出來的。

有一天產品說我們的軟體里缺少一個美麗的樹控制項,然後就要求開發去實現這個功能。

對於有一定開發經驗的同學可能直接會去百度,或者上Qt幫助文檔上查找資料,然後發現直接設置qss就能達到我們需要的效果,於是一頓操作後,發現效果還是不錯滴。

setStyleSheet(""
    "QTreeView {outline:none;show-decoration-selected: 1;}"
    "QTreeView {outline:none;border:0px;}"
    "QTreeView::branch{     background-color: transparent;  }"
    "QTreeView::item:hover, QTreeView::branch:hover { background-color: transparent;border-color: rgb(255, 0, 0);}"
    "QTreeView::item:selected, QTreeView::branch:selected { background-color: #C5E0F7;}"
    "QTreeView::branch:open:has-children{image: url(:/branch-expand.png);}"
    "QTreeView::branch:closed:has-children{image: url(:/branch-collapse.png);}"
    "QTreeView::branch:has-siblings:!adjoins-item{border-image:url(:/branch-line.png) 0;}"
    "QTreeView::branch:has-siblings:adjoins-item{border-image:url(:/branch-more.png) 0;}"
    "QTreeView::branch:!has-children:!has-siblings:adjoins-item{border-image:url(:/branch-end.png) 0;   }"
    "QTreeView::branch:has-children:!has-siblings:closed,QTreeView::branch:closed:has-children:has-siblings{border-image:none;image: url(:/branch-collapse.png);    }"
    "QTreeView::branch:open:has-children:!has-siblings,QTreeView::branch:open:has-children:has-siblings{border-image:none;image: url(:/branch-expand.png);  }"
    );

遂找來產品驗證,當產品看到這個效果後,臉直接都綠了。

產品:我不是說要一個樹形控制項嗎?行高需要能動態調整那種!

開發:。。。

開發:行高調整了,那branch上貼的圖拉伸後不是模糊了麽。。。

產品:。。。

產品:我不管,這個行高可拖拽功能很重要,怎麼實現我不管,但是功能必須要有。

開發:卧槽,看來只有出終極大法了,直接自繪吧

三、效果展示

如下圖所示,是一個簡單的樹branch自繪效果。

此處主要是展示一個demo效果,如果需要美化需要專業設計師出圖來做。

四、實現思路

既然要自己繪製樹形節點,那必然要去研究Qt的源碼。

1、可擴展介面

首先我們打開QTreeView類的幫助文檔,查找這個類都有哪些可供重寫的介面,然後就發現了這麼幾個函數

看名字大概都知道是什麼意思,不過這裡還是做簡要說明

函數名 | 含義 --- | --- drawBranches| 繪製branch drawRow | 繪製行 drawTree | 繪製樹 indexRowSizeHint | 預設行高 rowHeight| 獲取行高

前邊提到我們要自己繪製branch線條,但是其餘的東西還是要走Qt預設的繪製風格,因此在重寫繪製函數時,千萬不要忘記了調用原有的繪製方法。

表格中前3個函數就是繪製樹控制項的具體方法,這3個函數搭配起來完成了樹控制項內容格子的繪製。下麵我們來重寫這3個函數,分別完成我們的需求

2、函數重寫

a、繪製行drawRow

drawRow顧名思義就是繪製一行的意思,這裡也確實如此。為什麼要重寫這個函數呢?答案也很簡單。

樹控制項本身是不具有垂直分割線的,既然我們要模擬表格的樣式,那麼垂直分割線必然是需要的。

實現代碼可能像下麵這樣,是不是很簡單。

void FrozenTreeView::drawRow(QPainter * painter, const QStyleOptionViewItem & options, const QModelIndex & index) const
{
    QTreeView::drawRow(painter, options, index);

    //繪製網格線
    QPen pen;
    pen.setWidth(m_iWidth);
    pen.setColor(m_gridLineColor);

    painter->save();
    painter->setPen(pen);
    painter->drawRect(options.rect);
    painter->restore();
}

b、繪製branch

繪製行函數主要是添加了單元格邊框繪製,接下來就是第一列的branch繪製。

繪製branch時一定不要忘記調用原有的繪製函數,否則界面顯示會異常。

{
    painter->save();
    QTreeView::drawBranches(painter, rect, index);
    painter->restore();
}

繪製branch時主要是根據當前節點是否展開、是否有孩子節點、是否有兄弟節點等狀態來聯合判斷併進行繪製

如下是繪製代碼,可能有些長,但是應該比較好理解。

需要註意的點

  1. 除根節點外,每個節點都需要繪製文字前邊的水平線
  2. 有父親的節點需要繪製垂直線。繪製的豎線是否繪製到底,取決於是否有向下的兄弟
  3. 有爺爺的節點可能需要額外繪製向下的豎線。是否繪製取決於自己的父親是否有向下的兄弟
  4. 規則3其實是一個迴圈的處理,也就是說爺爺如果有爸爸,也就是說節點如果有祖爺爺,那麼可能還需要繪製更多的向下豎線。是否繪製取決於節點的爺爺是否有向下的兄弟

代碼這裡就不細說了,有興趣的可以自己研究研究。繪製規則就是上述4點

//繪製branch
{
    DataNode * node = static_cast<DataNode *>(index.internalPointer());
    bool hasChild = node->children().size() != 0;//是否有孩子

    QList<DataNode *> & children = node->parent()->children();
    bool has_next_siblings = children.indexOf(node) != (children.size() - 1);//是否有向後的兄弟
    bool has_pre_siblings = children.indexOf(node) != 0;//是否有向前的兄弟

    int level = node->level();
    int indentaion = indentation();//縮進
    int indentaions = indentaion * (level - 1);//縮進距離

    QRect r = rect;
    r.setLeft(r.left() + indentaions);//圖標繪製位置

    painter->save();
    painter->setPen(m_branchLine);

    bool expaned = isExpanded(index);//節點是否展開

    QLine line(r.center() + QPoint(0, r.top() - r.center().y()), r.center() + QPoint(0, r.bottom() - r.center().y()));
    line.translate(-indentaion, 0);
    //QLine line(r.topLeft(), r.bottomLeft());
    //迴圈繪製(具有兄弟節點的)父節點向下的豎線
    DataNode * parent_node = node->parent();
    DataNode * sub_node = node;
    bool isNeed = node->children().size() == 0;
    for (int i = level - 1; i >= 0; --i)
    {
        QList<DataNode *> & children = parent_node->children();
        bool has_next_siblings = children.indexOf(sub_node) != (children.size() - 1);//父節點是否有(向後的)兄弟

        if (has_next_siblings)
        {
            painter->drawLine(line);
        }
        

        if (level - 1 == i)
        {
            QPoint pos = (line.p1() + line.p2()) / 2;
            QPoint pos2 = pos + QPoint(indentaion / 2, 0);

            painter->drawLine(pos, pos2);

            if (!has_next_siblings)
            {
                painter->drawLine(line.p1(), (line.p1() + line.p2()) / 2);
            }
        }

        sub_node = parent_node;
        parent_node = parent_node->parent();
        line.translate(-indentaion, 0);
    }

    QPixmap pix;
    if (expaned)
    {
        if (hasChild)
        {
            pix = QPixmap(":/branch-expand.png");
        }
    }
    else
    {
        if (hasChild)
        {
            pix = QPixmap(":/branch-collapse.png");
        }
    }
    if (pix.isNull() == false)
    {
        QRect pixRect = QRect(QPoint(0, 0), pix.size());
        pixRect.moveCenter(r.center());

        if (expaned)
        {
            QLine line(r.center(), r.center() + QPoint(0, r.bottom() - r.center().y()));
            painter->drawLine(line);
        }

        painter->drawPixmap(pixRect, pix);
    }

    painter->restore();
}

3、同步左側表頭

上一篇文章Qt實現表格樹控制項-支持多級表頭 中已經說了,我們的表格控制項是使用QTableView+QTreeView來實現的,那麼我們操作樹控制項時必然要對錶格中的表頭進行同步操作了。

樹控制項摺疊時隱藏垂直表頭指定行

void collapsed_p(DataNode * node)
{
    QList<DataNode *> childNodeList = node->children();
    //DataManager::getInstance()->allChildNode(node, childNodeList);

    int size = childNodeList.size();
    for (int i = 0; i < size; ++i)
    {
        int serial = DataManager::getInstance()->serialNoOfNode(childNodeList.at(i));
        VHeaderView::instance->SetRowHide(serial, true);

        QModelIndex subIndex = FrozenTreeView::instance->rowIndex(serial);

        collapsed_p(childNodeList.at(i));
    }
}

void FrozenTreeView::onCollapsed(const QModelIndex & index)
{
    if (!index.isValid())
        return;

    DataNode * node = static_cast<DataNode*>(index.internalPointer());
    if (nullptr == node)
        return;

    collapsed_p(node);
    VHeaderView::instance->UpdateCache();

    //要對水平頭的最後一列進行重設大小,引起水平頭自己的更新操作,從而使整個界面顯示正確
    HHeaderView::instance->resizeLastSection(true);
}

樹控制項展開時顯示垂直表頭指定行

void expanded_p(DataNode * node)
{
    QList<DataNode *> childNodeList = node->children();

    int size = childNodeList.size();
    for (int i = 0; i < size; ++i)
    {
        int serial = DataManager::getInstance()->serialNoOfNode(childNodeList.at(i));
        VHeaderView::instance->SetRowHide(serial, false);

        QModelIndex subIndex = FrozenTreeView::instance->rowIndex(serial);

        if (FrozenTreeView::instance->isExpanded(subIndex))
        {
            expanded_p(childNodeList.at(i));
        }
    }
}

void FrozenTreeView::onExpanded(const QModelIndex & index)
{
    DataNode * node = static_cast<DataNode *>(index.internalPointer());
    if (nullptr == node)
        return;

    VHeaderView::instance->blockSignals(true);
    expanded_p(node);
    VHeaderView::instance->UpdateCache();
    VHeaderView::instance->blockSignals(false);

    //要對水平頭的最後一列進行重設大小,引起水平頭自己的更新操作,從而使整個界面顯示正確
    HHeaderView::instance->resizeLastSection(false);
}

五、相關文章


值得一看的優秀文章:

  1. 財聯社-產品展示
  2. 廣聯達-產品展示
  3. Qt定製控制項列表
  4. 牛逼哄哄的Qt庫

  1. Qt實現表格控制項-支持多級列表頭、多級行表頭、單元格合併、字體設置等

  2. Qt高仿Excel表格組件-支持凍結列、凍結行、內容自適應和合併單元格

  3. 屬性瀏覽器控制項QtTreePropertyBrowser編譯成動態庫(設計師插件)

  4. 超級實用的屬性瀏覽器控制項--QtTreePropertyBrowser

  5. Qt之表格控制項螞蟻線

  6. QRowTable表格控制項-支持hover整行、checked整行、指定列排序等

  7. Qt實現表格樹控制項-支持多級表頭

  8. QTableView表格控制項區域選擇-自繪選擇區域





如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!!














很重要--轉載聲明

  1. 本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords

  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前幾天和隔壁鄰居玩鬥地主被髮現了,牌被沒收了,鬥地主是鬥不了了,但我還想和鄰居玩耍。 想破腦袋終於讓我想到一個游戲,數獨!什麼叫數獨?數獨就是可以讓我趁老王不在的時候和隔壁鄰居一起玩耍的游戲! ...
  • 土壤濕度感測器和dh11會獲取數據,然後樹莓派會處理這些數據,讀出土壤溫濕度和空氣溫濕度,並將這些數據上傳到雲伺服器, 雲伺服器會將這些數據顯示在網頁上,同時樹莓派會根據這些數據來決定是否控制繼電器進而控制水泵來澆水,當然,水泵是否澆水也可以通過事先做的網頁看到。 ...
  • https://www.datamentor.io/r-programming/data-frame/ Check if a variable is a data frame or not We can check if a variable is a data frame or not using ...
  • 通過實現 Aware 介面,可以在 Spring 啟動時,調用介面定義的方法,將 Spring 底層的一些組件註入到自定義的 Bean 中。 下麵列出了幾個 Spring 在 Aware 介面基礎上,進行擴展的介面,分別會在創建 Bean 時直接執行,或者通過 BeanPostProcessor 間 ...
  • 上一篇博客的最後簡單提了下CommitLog的刷盤 【RocketMQ中Broker的消息存儲源碼分析】 (這篇博客和上一篇有很大的聯繫) Broker的CommitLog刷盤會啟動一個線程,不停地將緩衝區的內容寫入磁碟(CommitLog文件)中,主要分為非同步刷盤和同步刷盤 非同步刷盤又可以分為兩種 ...
  • 一、Format類 1.直接實例化 2.可以繼承Format添加特殊字元 3.三個參數 (1)fmt:指定消息格式化字元串,如果不指定該參數則預設使用message的原始值 (2)datemt:指定日期格式字元串,如果不指定該參數,則預設使用“%Y-%m-%d %H:%M:%S" (3)style: ...
  • 項目進行微信開發, 認證了一個微信服務號專門用於內部測試,但是內部可能存在多套不同環境(開發dev、測試sit、預發佈uat)等,由於微信限制一個服務號只能配置一個網頁授權功能變數名稱, 又不可能給每個環境單獨配一個服務號,這樣不僅需要成本而且很浪費資源, 所以重點需要解決下麵這個問題: 1、可以自動區分環 ...
  • 函數概念: 函數是用來完成某種特定任務的可重用代碼塊; 函數可以使程式更具模塊化,擁有良好的結構; 函數定義後在程式中可以重覆調用; 函數分為內置函數和自定義函數 考點: 變數的作用域和靜態變數 延伸1,函數的參數及參數的引用傳遞。 延伸2,函數的返回值及引用返回。 延伸3,外部文件的導入。 延伸4 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...