使用C++实现QML的TreeView Model (一)
QML中的数据访问组件如ListView、TableView、GridView通常使用ListModel做为数据提供者,这种应用有相当大局限性,如无法访问本地文件系统、无法连接到传统的SQL数据库,所以通常在使用中都是通过C++实现数据访问,通过QML进行数据展示和编辑,常用的数据模型组件有QAbstractItemModel、QAbstractTableModel、QSQLTableModel等。所有的高级Model组件都继承自QAbstractItemModel,只要了解QAbstractItemModel的接口函数和运作机理,就可以了解QT的Model/View机制实现方式。QAbstractItemModel是一个抽象类,要实例化QAbstractItemModel必须继承并至少实现以下5个方法:
int rowCount(const QModelIndex &parent=QModelIndex()) const;
int columnCount(const QModelIndex &parent=QModelIndex()) const;
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const;
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
QModelIndex parent(const QModelIndex &child) const;
与 QWidget组件不同的是,在QML的数据模型中,并不通过列(Column)进行数据访问,而是通过Role进行数据访问,例如:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>TableView{<br /> </li><li>id:tableView1<br /></li><li>anchors.fill: parent<br /></li><li>TableViewColumn{<br /></li><li>width:50<br /></li><li>title:""<br /></li><li>role:"tagging" </li><li>}<br /></li><li><br /></li><li>TableViewColumn{<br /></li><li>width:80<br /></li><li>title:"操作"<br /></li><li>role:"name" </li><li>} </li><li>} </li></ol>TableViewColumn是TableView的列定义,TableViewColumn通过role属性定义来向model获取数据,TableView会通过调用model的roleNames()方法来获取model可用的role。所以,除了必须要实现的5个虚函数外,还必须重新实现roleNames()来告诉View有哪些role是可用的,roleNames()的原型如下:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>QHash<int,QByteArray> roleNames() const; </li></ol>
以下是一个完整的Model类定义:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>classSqlMenuEntry:public QAbstractItemModel,public QQmlParserStatus<br /> </li><li>{<br /></li><li>Q_OBJECT<br /></li><li>public:<br /></li><li>explicit SqlMenuEntry(QObject *parent=0);<br /></li><li>~SqlMenuEntry();<br /></li><li>enum MenuEntryRoles{idRole=Qt::UserRole+1,nameRole,defaultEntryRole,customEntryRole,iconRole,iconHoverRole};<br /></li><li>int rowCount(const QModelIndex &parent=QModelIndex()) const;<br /></li><li>int columnCount(const QModelIndex &parent=QModelIndex()) const;<br /></li><li>QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const;<br /></li><li>QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;<br /></li><li>QModelIndex parent(const QModelIndex &child) const;<br /></li><li>QHash<int,QByteArray>roleNames() const;<br /></li><li>private:<br /></li><li>QHash<int,QByteArray> mRoleNames;<br /></li><li> QList<QHash<int,QVariant>> mRecords; //真正的数据保存在这里,QList只能保存二维数据没办法保存树状节点,这里仅仅是例子<br /></li><li>};<br /></li><li><br /></li><li></li></ol>roleNames()的实现相当简单:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>QHash<int, QByteArray> SqlMenuEntry::roleNames() const<br /> </li><li>{<br /></li><li>return mRoleNames;<br /></li><li>} </li></ol>mRoleNames可以在类构造函数中进行初始化:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>SqlMenuEntry::SqlMenuEntry(QObject *parent)<br /> </li><li>:QAbstractItemModel(parent)<br /></li><li>{<br /></li><li>mRoleNames[nameRole] = "name";<br /></li><li>mRoleNames[idRole] = "menuid";<br /></li><li>mRoleNames[iconRole] = "icon";<br /></li><li>mRoleNames[defaultEntryRole] = "default";<br /></li><li>mRoleNames[iconHoverRole] = "iconHover";<br /></li><li>} </li></ol>在QML中就可以通过"name"、"menuid"、"icon"对数据进行访问:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>ListView{<br /> </li><li>model:MenuEntryModel{ }<br /></li><li>delegate:Item{<br /></li><li>Column{<br /></li><li>Text{text:name}<br /></li><li> Text{text:icon}<br /></li><li>}<br /></li><li>}<br /></li><li>} </li></ol>如果仅为二维表提供数据,那么根据以上几个接口函数的名称就可以简单的实现数据供给View视图,其中:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>int SqlMenuEntry::rowCount(const QModelIndex &parent) const<br /> </li><li>{<br /></li><li> return mRecords.size();</li><li><br /></li><li>} </li><li>int SqlMenuEntry::columnCount(const QModelIndex &parent) const </li><li>{ </li><li> return 1; //QML不使用列获取数据,默认返回一列,不返回1例的话,View控件会认为表是空表,不获取数据 </li><li>} </li><li>QModelIndex SqlMenuEntry::index(int row, int column, const QModelIndex &parent) const </li><li>{ </li><li> if((row >= 0)&&(row < mRecords.size())) </li><li> { </li><li> return createIndex(row,column); </li><li> } </li><li> return QModelIndex(); //返回一个无效的空索引 </li><li>} </li><li>QModelIndex SqlMenuEntry::parent(const QModelIndex &child) const </li><li>{ </li><li> return QModelIndex(); //二维表中的行没有parent节点 </li><li>} </li><li>QVariant SqlMenuEntry::data(const QModelIndex &index, int role) const </li><li>{ </li><li> if(index.isValid) </li><li> { </li><li> return mRecords[index.row()][role]; </li><li> } </li><li>} </li></ol>
mRecords中的数据可以按需求生成,如通过QSqlQuery组件从数据库服务器获取,获取添加数据:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li> QHash<int,QVariant> row;<br /> </li><li>row[nameRole] = "name1";<br /></li><li>row[iconRole] = "icon1";<br /></li><li>mRecords.append(row); </li></ol>实现后的model类可以通过
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li>qmlRegisterType<SqlMenuEntry>("com.limutech.tv",1,0,"MenuEntryModel");</li></ol>进行注册,注册后的类可以在QML生成实例:
<ol style="margin:0 1px 0 0px;padding-left:40px;" start="1" class="dp-css"><li> import com.limutech.tv 1.0<br /> </li><li>MenuEntryModel{<br /></li><li>id:menuEntryModel<br /></li><li>}<br /></li><li>ListView{<br /></li><li>model:menuEntryModel<br /></li><li>...<br /></li><li>} </li></ol>View组件获取数据的流程大概如下:
1、View调用rowCount(constQModelIndex &parent)并传递一个空的parent获取根节点的行数;
2、View调用columnCount(const QModelIndex &parent)并传递一个空的parent获取根节点的列数;
3、View对各行列枚举调用index(int row, int column, const QModelIndex &parent)交传行号、列号和空的parent获取根节点QModelIndex;
4、继续以返回的modelIndex为parent获取每行的rowCount和columnCount,如果大于0则该节点还有子节点;
5、调用roleNames返回可用的role列表
6、以返回的modelIndex和role为参数,调用data获取数据并使用相应的delegate进行显示
所以要显示一个树状列表,需要对二维表模型进行完善,处理parent不为空的情况,同时,模型应该可以存储树状数据,在下一节我将继续分享层次模型的实现方式和涉及数据修改的一些实现。

PHP在現代編程中仍然是一個強大且廣泛使用的工具,尤其在web開發領域。 1)PHP易用且與數據庫集成無縫,是許多開發者的首選。 2)它支持動態內容生成和麵向對象編程,適合快速創建和維護網站。 3)PHP的性能可以通過緩存和優化數據庫查詢來提升,其廣泛的社區和豐富生態系統使其在當今技術棧中仍具重要地位。

在PHP中,弱引用是通過WeakReference類實現的,不會阻止垃圾回收器回收對象。弱引用適用於緩存系統和事件監聽器等場景,需注意其不能保證對象存活,且垃圾回收可能延遲。

\_\_invoke方法允許對象像函數一樣被調用。 1.定義\_\_invoke方法使對象可被調用。 2.使用$obj(...)語法時,PHP會執行\_\_invoke方法。 3.適用於日誌記錄和計算器等場景,提高代碼靈活性和可讀性。

Fibers在PHP8.1中引入,提升了並發處理能力。 1)Fibers是一種輕量級的並發模型,類似於協程。 2)它們允許開發者手動控制任務的執行流,適合處理I/O密集型任務。 3)使用Fibers可以編寫更高效、響應性更強的代碼。

PHP社區提供了豐富的資源和支持,幫助開發者成長。 1)資源包括官方文檔、教程、博客和開源項目如Laravel和Symfony。 2)支持可以通過StackOverflow、Reddit和Slack頻道獲得。 3)開發動態可以通過關注RFC了解。 4)融入社區可以通過積極參與、貢獻代碼和學習分享來實現。

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP不是在消亡,而是在不斷適應和進化。 1)PHP從1994年起經歷多次版本迭代,適應新技術趨勢。 2)目前廣泛應用於電子商務、內容管理系統等領域。 3)PHP8引入JIT編譯器等功能,提升性能和現代化。 4)使用OPcache和遵循PSR-12標準可優化性能和代碼質量。

PHP的未來將通過適應新技術趨勢和引入創新特性來實現:1)適應云計算、容器化和微服務架構,支持Docker和Kubernetes;2)引入JIT編譯器和枚舉類型,提升性能和數據處理效率;3)持續優化性能和推廣最佳實踐。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

禪工作室 13.0.1
強大的PHP整合開發環境

WebStorm Mac版
好用的JavaScript開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 Linux新版
SublimeText3 Linux最新版

記事本++7.3.1
好用且免費的程式碼編輯器