QRowTable表格控制項(五)-重寫表頭排序、支援第三次單擊恢復默認排序
- 2019 年 10 月 3 日
- 筆記
原文鏈接:QRowTable表格控制項(五)-重寫表頭排序、支援第三次單擊恢復默認排序
一、原生表格
開發客戶端程式的方式越來越多了,現在很流行的libcef、electron等等都可以作為快速開發客戶端軟體的方案,但是如果需要一個好的用戶體驗,還是離不開原生化的開發,雖然慢,但是性能好啊。
說到原生化開發,那對應的UI庫相對較多,流行的就有Qt、soui、duilib、還有老掉牙的MFC和其他一些第三方公司開源維護的directUI庫等等。網上找到一篇整理的文章,有興趣的同學可以參考C++介面庫。
目前C++客戶端使用最廣泛的就是UI框架就是Qt,它不僅包含了GUI控制項,更多的其實是一種解決方案,使用過Qt的同學都比較清楚,Qt的安裝動態庫有幾十個之多,可是如果你只想使用Qt的GUi模組的話,就只需要包含3個動態庫即可,他們分別是核心模組QtCore、Gui模組QtGui和QtWidget。
當我們將Qt作為我們的開發SDK時,大多數時候原生的控制項+qss美化就可以完成我們的需求,然而總有一些特殊情況,比如產品經理腦殘的時候、或者說是業務場景傻逼了,總是需要完成一些奇奇怪怪交互,那麼我們就需要重寫原生的控制項實現方式。
今天就來說一個案例–表格控制項列排序方式。
對於某一些特殊的場景下,我們的表格展示的數據可能需要排序,這樣的表格控制項Qt已經給我們提供好了,只需要我們從寫一些類和介面即可。前邊已經寫了4篇關於表格控制項的功能,分別是QRowTable表格控制項-支援hover整行、checked整行、指定列排序等、QRowTable表格控制項(二)-紅漲綠跌、QRowTable表格控制項(三)-效率優化之-合理使用QStandardItem和QRowTable表格控制項(四)-效率優化之-優化數據源,感興趣的同學可以去看看。
不幸的是Qt自帶的表格排序功能,即是我們沖寫完所有介面依然不能滿足負責的業務需求–表格表頭連續3點三次是一個循環,什麼意思呢?
傳統表格排序
Qt自帶的表格排序行為是這樣的,默認情況是不排序的,我們可以通過介面開啟排序,或者通過介面設置不排序,當我們啟用排序規則後,假設說我們的表格點擊點擊一下是降序,點擊第二下就是升序,再次點擊是又會恢復到降序規則,這樣是不是還挺完美呢!
這個時候產品有話說了,點擊第三次時,需要設置程式為不排序。
程式設計師:卧槽。。。。你說什麼。。。。聽不到。。。。
新的排序規則
在傳統表格排序的基礎上做一下調整
- 可以支援某些列不允許排序
- 3次連續單擊為一個循環,也就是降序-升序-無序這樣3個狀態循環
不得不說產品的腦洞還是很大的。既然產品都說想要這個功能了,那麼有時間還是得考慮下。跟產品溝通良久後,有了如下安排,雖然這個功能對用戶來說不是一個特別需要的功能,但是當我們的軟體功能穩定後,迭代沒有那麼著急的時候,是不是可以考慮研究下這個而功能呢。
然後也就有了本篇文章
雖然重寫了Qt本身的邏輯,沒想到還是可以實現的!!!
二、效果展示
按照慣例先上圖,看看是不是同學們想要的功能。

三、實現方式
新的表格排序有2個點需要我們去思考,分別是某些列排序、排序交互修改,下面我們分開來去實現
1、排序列訂製
Qt默認提供了可排序介面,但是開啟後我們所有的列都支援排序了,這個時候我們就需要研究Qt的源碼,看看Qt的排序是怎麼觸發的,然後在合理的時機去加上我們不支援排序的邏輯
部落客這裡找到的做法是重寫QHaderView這個類,並重寫實現了滑鼠按下並抬起的介面,在這個介面中判斷我們業務層是不是允許排序。
重寫後的邏輯是這樣的
-
如果不允許排序,我們先調用Qt原有的介面禁用所有列排序功能
-
調用父類的滑鼠抬起函數
-
如果不允許排序,需要調用Qt原有的介面啟用所有列排序功能
以上邏輯的步驟2是原有的邏輯,步驟1和步驟3分別是在進行業務邏輯判斷後進行的邏輯調整,達到我們禁用某些列排序的功能
void QRowHeader::mouseReleaseEvent(QMouseEvent * event) { int column = logicalIndexAt(event->pos().x()); if (m_Indicator.contains(column) && m_Indicator[column] == false && qAbs(event->x() - m_pPressPos.x()) < QApplication::startDragDistance()/*非拖拽*/) { setSectionsClickable(false); QHeaderView::mouseReleaseEvent(event); setSectionsClickable(true); } QHeaderView::mouseReleaseEvent(event); ...
如上程式碼,m_Indicator結構中存儲了我們想要訂製的是否允許排序的列,只需要對外暴露一個修改介面即可,程式碼是不是還很簡單呢。
void QRowHeader::SetSortEnable(int logicalIndex, bool enable) { m_Indicator[logicalIndex] = enable; }
這樣禁用某些列排序的功能就是實現了。
禁用某些列排序這個功能相對來說好實現一些,畢竟重寫的邏輯不是特別複雜,下面我們來看下怎麼修改排序交互邏輯。
2、排序交互修改
有了前面的問題處理思路,這個功能依然如此,我們先跟Qt源碼,看看已有的排序交互邏輯實現怎麼實現的,然後在合理的實際去重置某些變數的值,達到重寫交互邏輯。
部落客這裡跟蹤完Qt默認的排序實現方式後,發現重寫這個功能還是有一定難度的,首先數據排序是一塊,另一塊是一旦啟用排序後,排序箭頭的繪製這塊也需要去重寫。
什麼意思呢?
Qt的排序規則一旦啟用,排序的箭頭就會被繪製,並且繪製箭頭的邏輯還簡單粗暴,不是升序就是降序。
且看如下Qt表頭繪製源碼
如果顯示排序箭頭並且是排序列,則需要繪製排序箭頭,不是降序就是升序
我:我槽。。。什麼鬼,就不能提供一個空的枚舉嗎?
void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { ... if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder) ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; ... }
既然這條路子是走不通了,那麼我們只能取巧,換其他方式。
終於、皇天不負有心人被部落客想到一個好辦法,程式碼量依然還是很少。
回到我們最開始的需求,我們其實就是想讓第三次點擊時,只是一個特殊的操作,然後把第四點擊當做第一次單擊即可。
有了這個想法後,那麼就來干吧,既然還是重寫滑鼠抬起函數,想盡一切辦法監控連續的第三次單擊,把他處理成非排序狀態,然後在下一次單擊時,走正常的排序邏輯。
void QRowHeader::mouseReleaseEvent(QMouseEvent * event) { ... QHeaderView::mouseReleaseEvent(event); column = logicalIndexAt(event->pos().x()); static bool nextNoSort = false; static int prevColumn = -1; if (nextNoSort && prevColumn == column && qAbs(event->x() - m_pPressPos.x()) < QApplication::startDragDistance()) { emit RestoreSort();//恢復默認排序 } if (nextNoSort != true && prevColumn == column && sectionsClickable() && sortIndicatorOrder() == Qt::DescendingOrder) { if (qAbs(event->x() - m_pPressPos.x()) < QApplication::startDragDistance()) { nextNoSort = true; prevColumn = column; } else { nextNoSort = false; } } else { if (nextNoSort && qAbs(event->x() - m_pPressPos.x()) >= QApplication::startDragDistance()) { nextNoSort = true; } else { nextNoSort = false; prevColumn = column; } } ... }
好了,程式碼以上。不過這裡重點還是要說下為什麼這麼干!
首先需要調用父類的mouseReleaseEvent函數,否則拖拽會有問題,而且必須調用這個函數才可以讓記憶體數據正常。
然後我們記錄了一系列記憶體狀態,判斷是不是需要恢復排序狀態,當條件滿足時發出RestoreSort訊號,外部程式只需要接收這個訊號,然後恢復默認排序即可。
恢復默認排序
void QRowTable::RestoreSort() { m_pFilter->SetCompareType(QFilterModel::CT_NULL); m_pFilter->invalidate(); m_pHHeader->setSortIndicator(-1, Qt::DescendingOrder); }
四、相關文章
值得一看的優秀文章:
![]() |
![]() |
很重要–轉載聲明
-
本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords
-
如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。