QTreeView的拖拽功能

Qt树形结构的拖拽功能(drag/drop)

在这里插入图片描述

树形Model的定义

class TreeNode
{
public:
	TreeNode(const QString& text = "") :mText(text) {}
	~TreeNode(){}

	QString mText;

	TreeNode* mParentNode;				//父结点
	QList<TreeNode*> mChildNodes;			//孩子结点
};



class TreeModel : public QAbstractItemModel
{
public:

TreeModel(QObject* parent = NULL);
~TreeModel();

void SetRootNode(TreeNode *node);
bool AddTreeNode(TreeNode* pNode);																			//添加一个子节点
int TreeNodeHaveEqualInfo(TreeNode* parentNode, TreeNode* pAddChildNode);								//判断待加入节点,在父节点中是否有相同信息
TreeNode* FindParentNode(TreeNode* pNode, TreeNode* childNode);									//根据给定的字节点,再根节点中,寻找其父节点

//结点所在的位置,和其父结点
TreeNode* NodeFromIndex(const QModelIndex &index) const;

//实现 QAbstractItemModel的类的成员函数
QModelIndex index(int row, int column,
	const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;

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


//
Qt::ItemFlags flags(const QModelIndex &index) const
{
	Qt::ItemFlags flag = QAbstractItemModel::flags(index);
	return flag | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;				//允许Model拖动,这个很重要
}

QMimeData*  mimeData(const QModelIndexList &indexes) const
{
	QMimeData* mimeD = QAbstractItemModel::mimeData(indexes);				//先获取原始的mimeData;


	if (indexes.size() > 0)
	{
		QModelIndex index = indexes.at(0);

		TreeModel* node = (TreeModel*)index.internalPointer();			
		QByteArray encoded;
		QDataStream stream(&encoded, QIODevice::WriteOnly);
		stream << (qint64)(node);
		mimeD->setData("Node/NodePtr", encoded);							//将自己需要的数据 存入到MimeData中
	}
	else
		mimeD->setData("Node/NodePtr", "NULL");

	return mimeD;
	//mimeD->setData()
}

public:
TreeNode* mRootNode;							//树的根结点
};

树形Model的实现

TreeModel::TreeModel(QObject* parent) : QAbstractItemModel(parent)
{
this->mRootNode = NULL;
//构造一个根节点,在Qt中根节点的父节点为空,默认不显示,构造一个空节点充当父节点

mRootNode = new TreeNode();
mRootNode->mParentNode = NULL;
}

TreeModel::~TreeModel()
{
	//将设备的根结点才此位置析构
	if (mRootNode)
		delete mRootNode;
	mRootNode = NULL;
}

//设置设备树的根结点
void TreeModel::SetRootNode(TreeNode *node)
{
	//要更新model数据时,先调用beginResetModel,结束时调用endResetModel,所有绑定在Model上的视图都会更新
	this->beginResetModel();
	if (mRootNode)
	{
		delete mRootNode;
		mRootNode = NULL;
	}
	mRootNode = node;
	endResetModel();
}

//往某个父节点,添加一个子节点,如果parentNode为空,添加到父节点下
bool TreeModel::AddTreeNode(TreeNode* pNode)
{
	if (this->TreeNodeHaveEqualInfo(mRootNode, pNode) > 0)			//如果待加入节点,在根节点中有相同Ip,不允许加入
	{
		//TODO MSG
		delete pNode;												//不加入的节点 析构掉。
		return true;
	}
	this->beginResetModel();
	do
	{
		if (!pNode->mParentNode)						//parentNode为空,直接加载在根节点下
		{
			mRootNode->mChildNodes.append(pNode);
			pNode->mParentNode = mRootNode;
			break;
		}
	} while (0);

	//如果parentNode在现有的树体系中,加入现有树体系,如果不在,加入根节点中
	/*TreeNode* pNode = FindParentNode(mRootNode, pNode->mParentNode);
	if (!pNode)
	{
		mRootNode->mChildNodes.append(pNode);
		pNode->mParentNode = mRootNode;
		return true;
	}*/

	this->endResetModel();
	return true;
}

//pNode 为要查找节点, childNode 为被查找节点,返回被查找节点的父节点,pNode和childNode之间有层级关系
TreeNode* TreeModel::FindParentNode(TreeNode* pNode, TreeNode* childNode)
{
	//if (!pNode)
	//	return NULL;

	//if (pNode->mChildNodes.indexOf(childNode))								//如果子节点在所在的父节点,返回父节点
	//	return pNode;
	//for (int i = 0; i < pNode->mChildNodes.count(); i++)
	//{
	//	return FindParentNode(pNode->mChildNodes.at(i), childNode);
	//}
	return NULL;
}

int TreeModel::TreeNodeHaveEqualInfo(TreeNode* parentNode, TreeNode* pAddChildNode)
{
	/*for (int i = 0; i < parentNode->mChildNodes.size(); i++)
	{
		if (parentNode->mChildNodes.at(i)->mDeviceInfo.mDevIp != pAddChildNode->mDeviceInfo.mDevIp)
			continue;
		else
			return 1;
	}
	*/
	return 0;
}

//根据QModelIndex,返回指向其父结点的指针
TreeNode* TreeModel::NodeFromIndex(const QModelIndex &index) const
{
	if (index.isValid())				//如果是有效结点
		return static_cast<TreeNode*>(index.internalPointer());
	else
		return mRootNode;
}

//重载实现的QAbstractItemModel类的函数,基类中带const的函数,重载时也要是const
//index函数,根据行,列,返回一个父结点为parent的元素
QModelIndex TreeModel::index(int row, int column,
	const QModelIndex &parent) const
{
	//如果参数条件不满足,返回为空
	if (!mRootNode || row < 0 || column < 0)
		return QModelIndex();

	TreeNode* parentNode = NodeFromIndex(parent);		//获取指向此结点的指针
	if (!parentNode)					//如果没有父结点
		return QModelIndex();

	TreeNode* rowNode = parentNode->mChildNodes.at(row);
	if (!rowNode)
		return QModelIndex();
	return this->createIndex(row, column, rowNode);			//创建一个QModelIndex,参数3为结点指向数据指针
}

QModelIndex TreeModel::parent(const QModelIndex &child) const
{
	TreeNode *node = NodeFromIndex(child);
	if (!node)
		return QModelIndex();
	TreeNode *parentNode = node->mParentNode;
	if (!parentNode)
		return QModelIndex();
	TreeNode *grandparentNode = parentNode->mParentNode;
	if (!grandparentNode)
		return QModelIndex();

	int row = grandparentNode->mChildNodes.indexOf(parentNode);
	return createIndex(row, 0, parentNode);
}

//返回给定父结点下,行的个数
int TreeModel::rowCount(const QModelIndex &parent) const
{
	if (parent.column() > 0)
		return 0;
	TreeNode* parentNode = NodeFromIndex(parent);
	if (!parentNode)
		return 0;
	return parentNode->mChildNodes.count();				//返回父结点中子结点个数   
}

int TreeModel::columnCount(const QModelIndex &parent) const
{
	return 1;											//返回列数,此处为1列
}

QVariant TreeModel::data(const QModelIndex &index, int role) const
{

	qDebug() << "TreeModel::data";
	switch (role)
	{
	case Qt::DisplayRole:										//显示汉字
	{
		TreeNode *node = NodeFromIndex(index);
		if (!node)
			return QVariant();
		if (index.column() == 0)
			return node->mText;				//显示设备的Ip
	}
	break;
	case Qt::DecorationRole:									//图标相关的role
	{
		//qDebug() << "Qt::DecorationRole";
		QPixmap Icon = QPixmap("D:/QtProject/QtGuiApplication1/release/33.png");		//在树上画图标
		QVariant var;
		var.setValue(Icon);
		return var;
	}
	break;
	/*case Qt::UserRole:
	{
		TreeNode *node = NodeFromIndex(index);

		QVariant var;
		var.setValue<TreeNode*>(node);
		return var;
	}
	break;*/
	default:
		break;
	}


	return QVariant();
}

QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
	{
		if (section == 0)
		{
			return tr("Txt");
		}

	}
	return QVariant();
}

界面设置

在这里插入图片描述

拖放界面的头文件

class TreeDrag : public QMainWindow
{
	Q_OBJECT

public:
	TreeDrag(QWidget *parent = Q_NULLPTR);

protected:
	void dropEvent(QDropEvent *event);								//放下动作
	void dragEnterEvent(QDragEnterEvent *event);					//托到进入窗口动作
	void dragMoveEvent(QDragMoveEvent *event);						//拖着物体在窗口移动
	void dragLeaveEvent(QDragLeaveEvent *event);					//拖走了没有释放
private:
	Ui::TreeDragClass ui;

	TreeModel* mModel;
};

拖放界面的实现

TreeDrag::TreeDrag(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);

	this->setAcceptDrops(true);				//允许接受拖拽事件
	mModel = new TreeModel(this);

	TreeNode* node = new TreeNode();
	node->mText = "Test";
	node->mParentNode = NULL;


	for (int i = 0; i < 10; i++)
	{
		TreeNode* childNode = new TreeNode();
		childNode->mText = QString("192.168.1.%1").arg(QString::number(100 + i + 1));
		childNode->mParentNode = node;
		node->mChildNodes.append(childNode);
	}

	mModel->AddTreeNode(node);

	ui.treeView->setModel(mModel);
}


void TreeDrag::dragEnterEvent(QDragEnterEvent *event)
{
	QStringList formats = event->mimeData()->formats();
	qDebug() << "dragEnterEvent formats = " << formats;
	if (event->mimeData()->hasFormat("Node/NodePtr"))
		event->accept();
	else
		event->ignore();
}

void TreeDrag::dragLeaveEvent(QDragLeaveEvent *event)
{
	qDebug() << "dragLeaveEvent";
}


void TreeDrag::dragMoveEvent(QDragMoveEvent *event)
{
	QStringList formats = event->mimeData()->formats();
	qDebug() << "dragMoveEvent formats = " << formats;
	if (event->mimeData()->hasFormat("Node/NodePtr")) {
		event->setDropAction(Qt::MoveAction);
		event->accept();
	}
	else {
		event->ignore();
	}
}

void TreeDrag::dropEvent(QDropEvent *event)
{
	QStringList formats = event->mimeData()->formats();
	qDebug() << "dropEvent formats = " << formats;
	if (event->mimeData()->hasFormat("Node/NodePtr"))
	{
		QVariant varData = event->mimeData()->data("Node/NodePtr");
		QByteArray byteData = varData.toByteArray();
		QDataStream stream(&byteData, QIODevice::ReadWrite);
		qint64 node;
		stream >> (node);
		TreeNode* devNode = (TreeNode*)(node);

		if (devNode)
			ui.lineEdit->setText(devNode->mText);

		event->setDropAction(Qt::MoveAction);
		event->accept();
	}
	else
	{
		event->ignore();
	}
}

源代码连接

源代码连接:https://download.csdn.net/download/u013125105/11653837

  • 3
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: QTreeViewQt的一个控件,用于显示树形结构的数据。它提供了拖拽节点的功能,可以将节点拖拽到其他位置。 要实现节点的拖拽,我们需要先将拖拽支持打开。在QTreeView中调用setDragEnabled(true)函数即可开启拖拽支持。 接下来,在需要拖拽的节点上调用setDragEnabled(true)函数,表示该节点可以被拖拽。我们可以在构建树形结构数据时,为需要支持拖拽的节点设置这个属性。 然后,我们需要实现三个事件处理函数:dragEnterEvent、dragMoveEvent和dropEvent。dragEnterEvent函数在拖拽进入TreeView控件时被调用,dragMoveEvent函数在拖拽过程中被调用,dropEvent函数在拖拽结束时被调用。 在dragEnterEvent和dragMoveEvent函数中,我们需要设置拖拽的操作类型和反馈。可以调用event->setDropAction(Qt::MoveAction)函数设置操作类型为移动操作。调用event->accept()函数表示接受拖拽操作。 在dropEvent函数中,我们需要实现节点的真实移动。可以通过调用model的removeRow和insertRow函数来移动节点的位置。 最后,我们可以在我们自定义的Model中重新实现mimeData函数和dropMimeData函数,来自定义节点的数据格式和处理。 通过以上步骤,我们就可以实现在QTreeView控件中拖拽节点的功能了。 ### 回答2: QTreeViewQt框架中的一个控件,用于显示树状结构的数据。它提供了一个可进行拖拽的节点功能,允许用户在树中拖动节点到其他位置。 要实现QTreeView的节点拖拽功能,需要进行以下步骤: 1. 设置QTreeView为可拖拽模式: QTreeView通过setDragEnabled(true)方法来启用节点拖拽功能。 2. 实现节点拖拽的数据源: 通过实现数据源类,在该类中重写mimeData()方法来指定拖拽时传输的数据。一般可以使用QMimeData类来创建自定义的数据类型,并将需要传输的数据保存在其中。 3. 设置数据源属性: 调用QTreeView的setDragDropMode()方法,并设置为QAbstractItemView::DragOnly模式,以指定控件为拖拽操作的数据源。 4. 处理拖拽操作的事件: 在QTreeView中,可以通过重写dragEnterEvent()、dragMoveEvent()和dropEvent()方法,来处理拖拽操作的事件。其中,dragEnterEvent()方法用于指定拖拽源和接收者的行为,dragMoveEvent()方法用于更新拖拽源和接收者之间的显示,dropEvent()方法则用于处理拖拽操作完成后的处理逻辑。 5. 处理拖拽节点位置的变化: 拖拽节点时,节点在树中的位置会发生变化。因此,在dropEvent()方法中,需要实现逻辑来更新节点在树中的位置。 通过以上步骤的实现,可以实现QTreeView拖拽节点功能。用户可以通过拖动节点,将其移动到其他位置,并且在操作完成后,可以根据需求进行后续的处理逻辑,例如更新数据模型或界面显示等。 ### 回答3: 在使用QTreeView实现节点拖拽的过程中,主要涉及到以下几个步骤。 首先,我们需要设置QTreeView拖拽模式,通过设置setDragEnabled(True)来启用节点拖拽功能。同时,还可以设置setDragDropMode(QAbstractItemView.DragDrop)来指定拖拽和放置的模式。 接下来,我们需要重写QTreeView的相关方法,以实现节点的拖放操作。我们可以重写dragEnterEvent()方法,用于设置拖拽进入时所显示的状态以及接受拖拽的操作。我们也可以重写dragMoveEvent()方法,用于设置拖拽过程中的状态。 然后,我们还需要重写dropEvent()方法,用于处理节点的放置操作。在这个方法中,我们可以获取拖拽的节点以及目标放置的位置,然后进行相应的处理。比如,我们可以通过QStandardItemModel的相关方法进行节点的移动操作,比如insertRow()方法可以在目标位置插入节点,removeRow()方法可以移除拖拽的节点。 最后,我们需要设置节点的可拖拽和可放置的操作,通过设置基于节点角色和数据的标志位来实现。我们可以通过setDragDropOverwriteMode(False)来设置放置操作时是否重写源数据,还可以通过setDropIndicatorShown(True)来设置放置时是否显示指示器。 以上就是使用QTreeView实现节点拖拽的基本步骤。在实际应用中,我们可以根据具体的需求进行相应的扩展和优化,以实现更复杂的功能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值