Qt--自定义Model

众所周知,Qt提供了一套Model/View框架供开发者使用,Model用来提供数据, View则用来提供视觉层的显示。实际上这是一套遵循MVC设计模式的GUI框架,因为Qt还提供了默认的Delegate作为Controller来作为控制器。

MVC-image

MVC的好处这里就不多说了,为了开发者使用方便,Qt还提供了基于项(Item)的Model/View实现----QXxxWidget(QTableWidget、QListWidget等),对于一些简单的应用场景,这已经足够了并且使用起来非常方便。这里我们简单介绍下如何使用自定义的数据模型,来满足各种花式的要求。

1. 选择合适的Model继承

1.1 标准数据模型

Qt实现了4类标准数据模型供我们在不同的场景下使用:

  1. QStringListModel:存储字符串列表
  2. QStandardItemModel:存储树状结构的任意数据
  3. QFileSystemModel:存储本地文件系统上的文件和目录信息
  4. QSqlQueryModel、QSqlRelationalTableModel、QSqlTableModel:存储关系型数据库中的数据

如果使用情况和上述情况之一比较相似,则可以考虑继承对应的模型类,并重新实现少数虚函数。

1.2 抽象数据模型

抽象数据模型有3类:

  1. QAbstractItemModel:项模型,这是所有数据模型的基类。
  2. QAbstractListModel:列表模型,结合QListView使用最合适。
  3. QAbstractTableModel:表模型,结合QTableView使用最合适。

2. 继承抽象模型

Qt官方提供了完善的文档来帮助开发者来自定义模型类。根据官网,子类化模型需要开发者实现的功能(即需要重新实现的虚函数)按功能来分可以分为三类:

  • 项数据处理:这又可以分为三类----只读访问可编辑调整大小
  • 导航和下标创建。
  • 拖拽和MIME类型处理。

我们只需要按照自己的功能需求来实现其中的一些虚函数。

3. 实现一个自定义模型

这里我们来实现一个自定义模型,并在QTableView中使用它,因此我们选择继承QAbstractTableModel,这样我们需要做的改动最少。但使用QTableModel并不意味着我们的数据结构就是Table状的,例如下面的例子中我们根本不需要内部数据结构。

下面我们要实现这样一个数据模型:

  • 内部不存储数据结构
  • 表中的每一个单元获得的数据是整型,并且值为列下标的平方
  • 模型中的数据为只读

3.1 实现CustomeModel

该模型继承自QAbstractTableModel,作为只读模型,我们只需要实现以下几个虚函数:

virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent) const;
virtual int columnCount(const QModelIndex &parent) const;

data()函数与项数据有关,这里数据有好几种角色(role),最基本的就是Qt::DisplayRole,这里为了实现居中效果,我们还处理了Qt::TextAlignmentRole角色:

QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole) {
        return index.column() * index.column();
    }
    if (role == Qt::TextAlignmentRole) {
        return Qt::AlignCenter;
    }
    return QVariant();
}

headerData()函数提供表头数据,包括两个方向(垂直、水平)的表头。同样,这里的数据也有好几种角色,我们只处理Qt::DisplayRole

QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Vertical) {
        if (role == Qt::DisplayRole)    return QVariant("row:" + QString::number(section));
        else                            return QVariant();
    }
    if (orientation == Qt::Horizontal) {
        if (role == Qt::DisplayRole)    return QVariant("column:" + QString::number(section));
        else                            return QVariant();
    }
}

rowCount()columnCount()返回数据模父下标(QModelIndex)的行和列数量,这里我们要判别下标是否有效:因为整个表模型的父下标为无效下标,我们返回表模型的行列数量;当下标有效时,我们返回的是父下标指向处的子表的行列

//  if `parent` is invalid, return the whole table row count!
//  else return the children row count of the `parent`
int MyTableModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    else
        return 10;
}

3.2 运行结果

result-img

完整代码见此处

转载于:https://www.cnblogs.com/lgxZJ/p/7523790.html

<think>我们正在处理一个关于Qt的MVVM框架和可拖拽列表的问题。根据用户提供的信息,Qt的模型-视图架构实际上称为MVD(Model-View-Delegate),其中Delegate负责处理用户界面的交互,如编辑和渲染。用户提到想要使用qt-mvvm库(可能是指基于Qt的MVVM框架)实现一个支持自定义属性且可拖拽的列表。根据引用[1]:Qt中的模型/视图架构(MVD)使用Delegate来处理视图中的交互。因此,实现可拖拽列表的关键在于自定义模型(Model)和委托(Delegate),以及可能需要的视图(View)设置。另外,引用[3]提到了Qt中的信号与槽机制、事件处理等,这些都是实现拖拽功能的基础。用户需要的是:1.支持自定义属性:自定义模型(例如继承自QAbstractListModel)来定义自定义属性(角色)。2.可拖拽:需要设置视图支持拖拽,并且可能需要重写委托的鼠标事件处理,或者使用模型提供的拖拽接口。在Qt中,实现拖拽通常包括以下步骤:-在模型中重写`flags()`方法,为可拖拽的项返回包含`Qt::ItemIsDragEnabled`等标志。-重写模型的`mimeData()`方法,将需要拖拽的数据序列化到QMimeData中。-重写模型的`dropMimeData()`方法,处理拖拽放下的数据。-在视图中设置拖拽模式(如`setDragEnabled(true)`,`setAcceptDrops(true)`,`setDragDropMode()`)。如果使用MVVM模式,我们通常会将模型(Model)作为数据层,而视图模型(ViewModel)作为逻辑层。但是,Qt本身并没有内置的MVVM框架,qt-mvvm库可能是第三方的。由于用户提到了“qt-mvvm库”,我们假设存在这样一个库,但如果没有具体的库文档,我们将基于Qt的标准模型/视图框架来实现类似功能。鉴于用户没有提供具体的qt-mvvm库文档,我们将基于标准的Qt模型/视图架构(MVD)来实现一个可拖拽且支持自定义属性的列表。步骤:1.创建自定义模型,继承自QAbstractListModel(或QAbstractItemModel),在模型中定义自定义属性(通过角色)。2.在模型中支持拖拽操作(重写拖拽相关方法)。3.创建一个视图(如QListView),设置其支持拖拽。4.如果需要自定义显示,可以自定义委托(Delegate)。下面提供一个简单的示例:自定义模型(支持自定义属性,比如每个项有一个名称和一个颜色)并支持拖拽:注意:这个例子将展示一个可以拖拽的列表,每个项有一个显示文本和一个背景色(自定义属性)。首先,自定义模型:```cpp#include<QAbstractListModel>#include<QColor>#include<QMimeData>#include<QByteArray>#include<QDataStream>#include<QIODevice>classCustomItem{public:CustomItem(constQString&name,constQColor&color):m_name(name),m_color(color){}QStringname()const{returnm_name;}QColorcolor()const{returnm_color;}private:QStringm_name;QColorm_color;};classCustomListModel:publicQAbstractListModel{Q_OBJECTpublic:enumCustomRoles{NameRole=Qt::UserRole+1,ColorRole};explicitCustomListModel(QObject*parent=nullptr):QAbstractListModel(parent){//添加示例数据m_items<< CustomItem("Item1",Qt::red)<< CustomItem("Item2",Qt::green)<< CustomItem("Item3",Qt::blue);}introwCount(constQModelIndex&parent=QModelIndex())constoverride{if(parent.isValid())return0;returnm_items.count();}QVariantdata(constQModelIndex&index,introle=Qt::DisplayRole)constoverride{if(!index.isValid()||index.row()<0||index.row()>=m_items.count())returnQVariant();constCustomItem&item=m_items[index.row()];if(role==Qt::DisplayRole||role==NameRole){returnitem.name();}elseif(role==ColorRole){returnitem.color();}returnQVariant();}Qt::ItemFlagsflags(constQModelIndex&index)constoverride{Qt::ItemFlagsdefaultFlags=QAbstractListModel::flags(index);if(index.isValid()){//设置项可拖拽returndefaultFlags|Qt::ItemIsDragEnabled;}//根节点(无效节点)可以作为放置目标returndefaultFlags|Qt::ItemIsDropEnabled;}QMap<int,QVariant>itemData(constQModelIndex&index)constoverride{QMap<int,QVariant>roles;if(index.isValid()){roles[NameRole]=data(index,NameRole);roles[ColorRole]=data(index,ColorRole);}returnroles;}//支持拖拽:序列化自定义数据QMimeData*mimeData(constQModelIndexList&indexes)constoverride{if(indexes.isEmpty())returnnullptr;QMimeData*mimeData=newQMimeData;QByteArraydata;QDataStreamstream(&data,QIODevice::WriteOnly);//我们只处理一个选择QModelIndexindex=indexes.first();if(index.isValid()){QMap<int,QVariant>item=itemData(index);stream<< item[NameRole].toString()<< item[ColorRole].value<QColor>();}mimeData->setData("application/custom-item",data);returnmimeData;}boolcanDropMimeData(constQMimeData*data,Qt::DropActionaction,introw,intcolumn,constQModelIndex&parent)constoverride{Q_UNUSED(action);Q_UNUSED(row);Q_UNUSED(parent);if(column>0)returnfalse;returndata->hasFormat("application/custom-item");}booldropMimeData(constQMimeData*data,Qt::DropActionaction,introw,intcolumn,constQModelIndex&parent)override{if(!canDropMimeData(data,action,row,column,parent))returnfalse;if(action==Qt::IgnoreAction)returntrue;QByteArraybytes=data->data("application/custom-item");QDataStreamstream(&bytes,QIODevice::ReadOnly);QStringname;QColorcolor;stream>>name>>color;//如果拖放时指定了行数,则在指定行插入,否则在父节点的子节点最后插入(或如果父节点无效,则在根节点)intbeginRow;if(row!=-1)beginRow=row;elseif(parent.isValid())beginRow=parent.row();elsebeginRow=rowCount(QModelIndex());beginInsertRows(QModelIndex(),beginRow,beginRow);m_items.insert(beginRow,CustomItem(name,color));endInsertRows();returntrue;}private:QVector<CustomItem>m_items;};```然后,在视图(例如QListView)中设置拖拽模式:```cpp#include<QApplication>#include<QListView>intmain(intargc,char*argv[]){QApplicationapp(argc,argv);CustomListModelmodel;QListViewview;view.setModel(&model);view.setDragEnabled(true);view.setAcceptDrops(true);view.setDropIndicatorShown(true);view.setDragDropMode(QAbstractItemView::DragDrop);//允许拖拽和放置view.show();returnapp.exec();}```上述代码创建了一个自定义模型,每个项有两个自定义属性(NameRole和ColorRole),并支持拖拽和放置。注意:这个例子只支持单个项的拖拽,并且放置时插入新的项(实际上是复制)。如果需要移动,我们可以修改`dropMimeData`,在放置后移除源项(但是需要判断源模型和目的模型是否相同,以及是否移动同一模型内的项)。另外,也可以从源模型中移除被拖拽的项(这需要记录源索引)。关于MVVM:如果用户确实想使用MVVM模式,那么ViewModel应该包含对Model的封装,以及处理业务逻辑。在Qt中,可以使用QAbstractProxyModel作为ViewModel,或者使用第三方的MVVM库。但是,由于用户没有指定qt-mvvm库的具体信息,我们按标准模型/视图实现。另外,如果需要支持自定义委托(Delegate)来显示颜色,我们可以创建自定义委托来绘制背景色。自定义委托示例(只简单绘制背景色和文本):```cpp#include<QStyledItemDelegate>#include<QPainter>classColorItemDelegate:publicQStyledItemDelegate{Q_OBJECTpublic:usingQStyledItemDelegate::QStyledItemDelegate;voidpaint(QPainter*painter,constQStyleOptionViewItem&option,constQModelIndex&index)constoverride{//获取颜色和文本QColorcolor=index.data(CustomListModel::ColorRole).value<QColor>();QStringtext=index.data(CustomListModel::NameRole).toString();//填充背景色painter->fillRect(option.rect,color);//绘制文本painter->drawText(option.rect,Qt::AlignCenter,text);}QSizesizeHint(constQStyleOptionViewItem&option,constQModelIndex&index)constoverride{//可以自定义大小returnQSize(100,30);}};```然后在视图中设置委托:```cppColorItemDelegate*delegate=newColorItemDelegate(&view);view.setItemDelegate(delegate);```这样,列表项将使用自定义委托绘制。总结:以上代码展示了如何使用Qt的模型/视图框架实现一个支持自定义属性(通过自定义角色)且可拖拽的列表。由于用户的问题中提到了“qt-mvvm”库,而上述方法并没有使用该库,因此如果用户需要使用特定的qt-mvvm库,那么可能需要查阅该库的文档。但是,由于qt-mvvm库不是Qt官方的一部分,我们无法提供具体示例。因此,上述代码基于Qt标准模型/视图。相关问题:1.如何在Qt中实现自定义模型(继承QAbstractListModel)?2.如何在Qt模型/视图中实现拖拽功能?3.如何在Qt自定义委托(Delegate)来绘制列表项?4.Qt的模型/视图架构中,如何处理数据的拖拽和放置(DragandDrop)?注意:以上代码仅为示例,可能需要根据实际需求进行调整。引用说明:[^1]解释了Qt的模型/视图架构(MVD)。[^3]提供了Qt中事件处理、布局和控件的基础知识,这些是开发交互式界面所需。因此,我们基于引用[1]和[3]实现了上述功能。</think> ### Qt MVVM实现自定义属性可拖拽列表示例 根据Qt的MVD(Model-View-Delegate)架构和引用内容,下面是一个使用Qt实现支持自定义属性和拖拽功能的列表示例: #### 核心实现思路 1. **自定义模型**:继承`QAbstractListModel`定义带有自定义属性的数据模型 2. **实现拖拽支持**:重写模型的拖拽相关方法 3. **自定义委托**:继承`QStyledItemDelegate`实现自定义渲染和编辑器 4. **视图设置**:使用`QListView`并启用拖拽模式 #### 代码实现 ```cpp #include <QApplication> #include <QListView> #include <QAbstractListModel> #include <QStyledItemDelegate> #include <QMimeData> #include <QDataStream> // 自定义数据对象 class CustomItem { public: CustomItem(QString text, int customProp) : m_text(text), m_customProperty(customProp) {} QString text() const { return m_text; } int customProperty() const { return m_customProperty; } void setCustomProperty(int value) { m_customProperty = value; } private: QString m_text; int m_customProperty; }; // 自定义模型 class CustomListModel : public QAbstractListModel { public: enum CustomRoles { TextRole = Qt::UserRole + 1, CustomPropertyRole }; explicit CustomListModel(QObject *parent = nullptr) : QAbstractListModel(parent) { // 添加示例数据 m_items << CustomItem("Item 1", 10) << CustomItem("Item 2", 20) << CustomItem("Item 3", 30); } int rowCount(const QModelIndex &parent) const override { return m_items.count(); } QVariant data(const QModelIndex &index, int role) const override { if (!index.isValid() || index.row() >= m_items.count()) return QVariant(); const CustomItem &item = m_items[index.row()]; if (role == TextRole || role == Qt::DisplayRole) return item.text(); if (role == CustomPropertyRole) return item.customProperty(); return QVariant(); } // 关键:实现拖拽支持 Qt::ItemFlags flags(const QModelIndex &index) const override { Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; return Qt::ItemIsDropEnabled | defaultFlags; } // 定义自定义角色 QHash<int, QByteArray> roleNames() const override { QHash<int, QByteArray> roles; roles[TextRole] = "text"; roles[CustomPropertyRole] = "customProperty"; return roles; } // 拖拽数据序列化 QMimeData* mimeData(const QModelIndexList &indexes) const override { if (indexes.isEmpty()) return nullptr; QMimeData *mimeData = new QMimeData; QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); // 序列化自定义数据 for (const QModelIndex &index : indexes) { if (index.isValid()) { stream << m_items[index.row()].text() << m_items[index.row()].customProperty(); } } mimeData->setData("application/custom-item", encodedData); return mimeData; } // 接收拖拽数据 bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override { if (!data->hasFormat("application/custom-item") || action != Qt::MoveAction) return false; QByteArray encodedData = data->data("application/custom-item"); QDataStream stream(&encodedData, QIODevice::ReadOnly); QString text; int customProp; stream >> text >> customProp; // 插入新项 beginInsertRows(QModelIndex(), row, row); m_items.insert(row, CustomItem(text, customProp)); endInsertRows(); return true; } // 支持自定义属性编辑 bool setData(const QModelIndex &index, const QVariant &value, int role) override { if (!index.isValid() || role != CustomPropertyRole) return false; m_items[index.row()].setCustomProperty(value.toInt()); emit dataChanged(index, index, {CustomPropertyRole}); return true; } private: QVector<CustomItem> m_items; }; // 自定义委托(用于渲染自定义属性) class CustomItemDelegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { // 调用基类渲染 QStyledItemDelegate::paint(painter, option, index); // 添加自定义属性显示 int customProp = index.data(CustomListModel::CustomPropertyRole).toInt(); QString propText = QString("Prop: %1").arg(customProp); QRect rect = option.rect.adjusted(100, 0, -10, 0); painter->drawText(rect, Qt::AlignRight | Qt::AlignVCenter, propText); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建模型和视图 CustomListModel model; QListView view; // 设置委托 CustomItemDelegate *delegate = new CustomItemDelegate(&view); view.setItemDelegate(delegate); // 启用拖拽 view.setDragEnabled(true); view.setAcceptDrops(true); view.setDragDropMode(QAbstractItemView::InternalMove); view.setDropIndicatorShown(true); // 显示 view.setModel(&model); view.setWindowTitle("Custom Property Drag&Drop List"); view.resize(400, 300); view.show(); return app.exec(); } ``` #### 关键特性说明 1. **自定义属性**: - 通过`CustomPropertyRole`扩展标准模型角色 - 委托中自定义渲染逻辑显示额外属性 - 支持通过`setData()`编辑自定义属性 2. **拖拽功能**: - 启用`Qt::ItemIsDragEnabled`和`Qt::ItemIsDropEnabled`标志 - 使用QMimeData序列化自定义数据类型 - 实现拖拽位置计算和数据处理 3. **MVVM/MVD架构**: - **模型**:`CustomListModel`管理数据 - **视图**:`QListView`负责显示 - **委托**:`CustomItemDelegate`处理渲染和编辑 #### 进阶实现建议 1. **持久化拖拽位置**: ```cpp // 在模型中保存拖拽起始位置 Qt::DropAction supportedDropActions() const override { return Qt::MoveAction | Qt::CopyAction; } ``` 2. **自定义拖拽预览**: ```cpp // 在委托中重写 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; ``` 3. **MVVM双向绑定**: ```qml // QML实现更简洁 ListView { model: CustomModel {} delegate: CustomDelegate { Drag.active: mouseArea.drag.active Drag.dragType: Drag.Internal } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值