1. 介紹
複選框有三種形態:全選對勾、全不選空白、半選黑點app
在item中經過:setCheckable(true);可開啓複選框功能,但默認只支持全選對勾、全不選空白,並且自身的狀態變更不會使父/子節點相應,好比子節點所有選中時父節點不會自動勾選函數
下面你提供一個完整的UsingCheckboxItem類,此類繼承自QStandardItem,實現了複選框三種狀態的使用。類內容很純粹並未增長其餘設置,僅爲複選框實現。post
注意,此方式讓一個item調用了其父節點及子節點的data和setdata兩個方法,若不符合設計要求,可仿照此方式在model中重現spa
下面直接上源碼:設計
2. 源碼
2.1. using_checkbox_item.h
- /**
- * @file using_checkbox_item.h
- * @brief 本文件包含支持複選框item類聲明。
- * @version 1.0.0.0
- * @date 2017.12.18
- * @author Techie亮
- */
- #ifndef _H_USINGCHECKBOXITEM_
- #define _H_USINGCHECKBOXITEM_
- #include <QStandardItem>
- #include <QString>
- /**
- * @brief 支持複選框item類
- * 支持複選框三態轉變-全選對勾、全不選空白、半選黑點
- * 子類會自動通知父子節點item,若不符合設計須要可仿照此方式在model中的setDate重現
- */
- class UsingCheckboxItem : public QStandardItem {
- public:
- /**
- * @brief 構造函數
- * @param item顯示內容
- */
- explicit UsingCheckboxItem(const QString &text);
- /**
- * @brief setData重寫
- * @param value data值
- * @param role data類型
- */
- virtual void setData(const QVariant &value, int role = Qt::UserRole + 1);
- };
- #endif // _H_USINGCHECKBOXITEM_
2.2. using_checkbox_item.cpp
- #include "using_checkbox_item.h"
- //構造函數
- UsingCheckboxItem::UsingCheckboxItem(const QString &text)
- : QStandardItem(text) {
- setCheckable(true);
- }
- //setData重寫
- void UsingCheckboxItem::setData(const QVariant &value, int role) {
- if(role == Qt::CheckStateRole) {//針對複選框變更作操做
- Qt::CheckState check_state = (Qt::CheckState)value.toInt();
- QString mtext=text();
- switch (check_state) {
- case Qt::Unchecked: {//取消
- for(int i = 0, num = rowCount(); i < num; i++) {
- child(i)->setData(Qt::Unchecked, Qt::CheckStateRole);
- }
- //修改內容-必須先修改本身再通知父節點
- QStandardItem::setData(value,role);
- //通知父節點,我取消了選擇,直接告訴父節點半選便可
- if(parent())parent()->setData(Qt::PartiallyChecked, role);
- }
- return;//此事件已完成直接return
- case Qt::PartiallyChecked: {//半選
- Qt::CheckState current_state = checkState();//當前狀態
- int checked_num = 0;//被選擇的數量
- int unchecked_num = 0;//未選擇的數量
- bool is_partially = false;
- Qt::CheckState child_state;
- int m_rowCount = rowCount();
- //遍歷全部子節點
- for(int i = 0; i < m_rowCount; i++) {
- child_state = child(i)->checkState();
- //子節點半選,則直接半選
- switch (child_state) {
- case Qt::PartiallyChecked:is_partially = true;break;
- case Qt::Unchecked:unchecked_num++;break;
- case Qt::Checked:checked_num++;break;
- default:checked_num++;break;
- }
- }
- //根據子節點狀態肯定當前節點應該設置的狀態
- Qt::CheckState now_state;
- if(is_partially)
- now_state = Qt::PartiallyChecked;
- else if(checked_num == m_rowCount)
- now_state = Qt::Checked;
- else if(unchecked_num == m_rowCount)
- now_state = Qt::Unchecked;
- else
- now_state = Qt::PartiallyChecked;
- //修改狀態並通知父節點
- if(current_state != now_state) {
- //修改內容-必須先修改本身再通知父節點
- QStandardItem::setData(now_state,role);
- //通知父節點,個人狀態更改,也就是父節點進入半選
- if(parent())parent()->setData(Qt::PartiallyChecked, role);
- }
- }
- return;//此事件已完成直接return
- case Qt::Checked: {//全選
- for(int i = 0, num = rowCount(); i < num; i++) {
- child(i)->setData(Qt::Checked, Qt::CheckStateRole);
- }
- //修改內容-必須先修改本身再通知父節點
- QStandardItem::setData(value,role);
- //通知父節點,我被選了,也就是父節點進入半選
- if(parent()) {
- parent()->setData(Qt::PartiallyChecked, role);
- }
- }
- return;//此事件已完成直接return
- default://若是出現此狀況就是錯了,能夠加錯誤處理
- break;
- }
- }
- QStandardItem::setData(value,role);
- }
3. 說明
- 重寫了setData方法,但僅對CheckStateRole類型data作了操做,其他類型經過最後的QStandardItem::setData(value,role)直接使用默認方式
- setData中每一個case後均直接return,由於會根據value和父子類狀況對實際要使用value作修改,最終賦值給QStandardItem::setData不必定是參數value,因此若不返回,必然會調用最後同樣致使出錯
- 系統默認只有兩個狀態切換選中和補選中,因此能夠借用這個特性,當一個節點狀態修改時都通知其父類爲PartiallyChecked部分選中狀態,由父節點自行判斷子節點狀況並設置自身狀態
- 注意必定要先修改自身狀態之後在通知父節點,不然在父節點函數運行過程當中自身仍爲未修改狀態,會致使判斷錯誤
- 在PartiallyChecked的case中判斷了一下新舊狀態是否改變,若改變會向上一父節點繼續傳遞消息,不改變則馬上中止減小運算量
- 若子節點存在PartiallyChecked狀態的,則當前節點必定爲PartiallyChecked
- 注意最頂級item是沒有parent的因此想父節點傳遞消息前必定要判斷parent是否爲nullptr
- 選擇一個節點那麼此節點必定會在全選-不選兩個狀態切換,而部分選擇僅存在於此節點的子節點發生變更,因此全選-不選兩個case直接對全部子節點賦值