setContextMenuPolicy(Qt::CustomContextMenu);
通过创建一个结构体对象,将该对象绑定到该节点上,并且进行该节点的信息保存.
#include <QApplication>
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QString>
#include <QDebug>
// 定义一个结构体 Name
struct Name {
QString name;
int value;
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建一个标准项模型
QStandardItemModel model;
// 创建根节点
QStandardItem* rootNode = new QStandardItem("Root Node");
// 创建 Name 结构体的实例
Name nameData;
nameData.name = "John";
nameData.value = 42;
// 将 Name 结构体实例保存为 QVariant,并关联到根节点上
QVariant customData = QVariant::fromValue(nameData);
// 创建一个标准项,并设置其文本为 Name 结构体的 name 成员
QStandardItem* item = new QStandardItem(customData.value<Name>().name);
// 将关联的 QVariant 数据保存到标准项的 UserRole 角色上
item->setData(customData, Qt::UserRole);
// 将标准项添加到根节点下
rootNode->appendRow(item);
// 将根节点添加到模型中
model.appendRow(rootNode);
// 创建树形视图并设置模型
QTreeView treeView;
treeView.setModel(&model);
// 显示树形视图
treeView.show();
// 获得标签的名字
QString name = item->text();
QVariant retrievedData = item->data(Qt::UserRole); // 获取关联的 QVariant 数据
// read data from iten
Name retrievedNameData = retrievedData.value<Name>(); // 将 QVariant 转换为 Name 结构体的实例
qDebug() << "Name: " << retrievedNameData.name;
qDebug() << "Value: " << retrievedNameData.value;
qDebug() << "name: " << name;
return app.exec();
void QStandardItem::setData(const QVariant &value, int role = Qt::UserRole + 1)
将给定角色的项数据设置为指定值。
//将自定义的结构体对象绑定对象
void QStandardItem::setData(const QVariant &value, int role = Qt::UserRole + 1)
改变当前的节点的名称
QObject::connect(&treeView, &QTreeView::doubleClicked, [&](const QModelIndex &index) {
if (index.isValid()) {
// 获取当前节点的文本
QString oldName = index.data().toString();
// 使用QLineEdit进行编辑
QLineEdit *edit = new QLineEdit();
edit->setText(oldName);
edit->setAlignment(Qt::AlignCenter);
// 将QLineEdit作为节点的编辑器
treeView.setIndexWidget(index, edit);
// 当编辑器失去焦点时,保存新的节点名称
QObject::connect(edit, &QLineEdit::editingFinished, [=]() {
QString newName = edit->text();
// 保存新的节点名称,这里你可以将其存储到合适的数据结构中
qDebug() << "节点" << oldName << "被重命名为" << newName;
// 移除编辑器,显示新的节点名称
treeView.setIndexWidget(index, nullptr);
// 更新模型中的数据
model.setData(index, newName);
右键点击事件
void MainWindow::slotTreeMenu(const QPoint &pos)
QModelIndex curIndex = ui->treeView->indexAt(pos); //当前点击的元素的index
QModelIndex index = curIndex.sibling(curIndex.row(),0); //该行的第1列元素的index
if (index.isValid())
if(index.parent() != ui->treeView->rootIndex()) //不是一级节点,因为只对二级节点往其他年级移动
QMenu menu;
QAction* actionParent = menu.addAction(QStringLiteral("移动到年级")); //父菜单
QMenu* subMenu = new QMenu(&menu); //子菜单
subMenu->addAction(QStringLiteral("1年级"), this, SLOT(slotTreeMenuMove(bool)));
subMenu->addAction(QStringLiteral("2年级"), this, SLOT(slotTreeMenuMove(bool)));
subMenu->addAction(QStringLiteral("3年级"), this, SLOT(slotTreeMenuMove(bool)));
actionParent->setMenu(subMenu);
menu.exec(QCursor::pos());
void MainWindow::slotTreeMenuMove(bool checked)
//通过action的文本可以判断选择的哪个子菜单,如果文本不够用也可以用data接口
QAction* action = qobject_cast<QAction*>(sender());
QString grade = action->text();
//执行移动
//...
要先设置菜单策略,使用接口:
setContextMenuPolicy(Qt::CustomContextMenu);
建立对应的时间连接
connect(t, &QTreeView::customContextMenuRequested, this, &MainWindow::slotTreeMenu);
带有图标的点击事件
menu.addAction(QIcon(":/image/add.png"),QStringLiteral("添加"), this, SLOT(slotTreeMenuAdd(bool)));
menu.addAction(QIcon(":/image/delete.png"),QStringLiteral("删除"), this, SLOT(slotTreeMenuDelete(bool)));
设计多级的列表
void MainWindow::slotTreeMenu(const QPoint &pos)
QModelIndex curIndex = ui->treeView->indexAt(pos); //当前点击的元素的index
QModelIndex index = curIndex.sibling(curIndex.row(),0); //该行的第1列元素的index
if (index.isValid())
if(index.parent() != ui->treeView->rootIndex()) //不是一级节点,因为只对二级节点往其他年级移动
QMenu menu;
QAction* actionParent = menu.addAction(QStringLiteral("移动到年级")); //父菜单
QMenu* subMenu = new QMenu(&menu); //子菜单
subMenu->addAction(QStringLiteral("1年级"), this, SLOT(slotTreeMenuMove(bool)));
subMenu->addAction(QStringLiteral("2年级"), this, SLOT(slotTreeMenuMove(bool)));
subMenu->addAction(QStringLiteral("3年级"), this, SLOT(slotTreeMenuMove(bool)));
actionParent->setMenu(subMenu);
menu.exec(QCursor::pos());
void MainWindow::slotTreeMenuMove(bool checked)
//通过action的文本可以判断选择的哪个子菜单,如果文本不够用也可以用data接口
QAction* action = qobject_cast<QAction*>(sender());
QString grade = action->text();
//执行移动
//...
右键菜单事件
void contextMenuEvent(QContextMenuEvent* event) override {
QModelIndex index = indexAt(event->pos());
if (index.isValid()) {
QMenu contextMenu(this);
QAction* action1 = contextMenu.addAction("Action 1");
QAction* action2 = contextMenu.addAction("Action 2");
// 连接菜单项的点击事件到槽函数
connect(action1, &QAction::triggered, this, [=]() {
handleAction1(index);
connect(action2, &QAction::triggered, this, [=]() {
handleAction2(index);
contextMenu.exec(event->globalPos());
QVariant
QVariant类的作用类似于最常见的Qt数据类型的联合.因为C++禁止联合体包含具有非默认构造函数或析构函数的类型,所以大多数有趣的Qt类不能在联合体中使用。如果没有QVariant,这将是QObject::property()和数据库工作等的问题。
一个QVariant对象一次只保存一个typeId()的值。(Some类型是多值的,例如字符串列表。)你可以找出变量的类型T,使用convert()将其转换为不同的类型,使用其中一个toT()函数获取其值(例如,toSize()),并检查是否可以使用canConvert()将该类型转换为特定类型。
名为toT()的方法(例如toInt(),toString())都是常量。如果您请求存储的类型,它们将返回存储对象的副本。如果你请求一个可以从存储的类型生成的类型,toT()会复制和转换,并保持对象本身不变。如果请求无法从存储类型生成的类型,则结果取决于该类型;有关详细信息,请参阅函数文档。
QDataStream out(...);
QVariant v(123); // The variant now contains an int
int x = v.toInt(); // x = 123
out << v; // Writes a type tag and an int to out
v = QVariant(tr("hello")); // The variant now contains a QString
int y = v.toInt(); // y = 0 since v cannot be converted to an int
QString s = v.toString(); // s = tr("hello") (see QObject::tr())
out << v; // Writes a type tag and a QString to out
QDataStream in(...); // (opening the previously written stream)
in >> v; // Reads an Int variant
int z = v.toInt(); // z = 123
qDebug("Type is %s", // prints "Type is int"
v.typeName());
v = v.toInt() + 100; // The variant now holds the value 223
v = QVariant(QStringList()); // The variant now holds a QStringList
QVariant值存储在变体中,可以轻松构建任意类型的任意复杂数据结构。这是非常强大和通用的,但可能证明比在标准数据结构中存储特定类型更少的内存和速度效率。
QVariant x; // x.isNull() == true
QVariant y = QVariant::fromValue(nullptr); // y.isNull() == true
因为QVariant是Qt Core模块的一部分,它不能提供Qt GUI中定义的数据类型的转换函数,可以使用QVariant::value()或qvariant_cast()模板函数,也就是说,没有toColor()
功能。相反,您可以使用QVariant::value()或qvariant_cast()模板函数。举例来说:
QVariant variant;
QColor color = variant.value<QColor>();
QVariant::setValue(T &&value)
QVariant v;
v.setValue(5);
int i = v.toInt(); // i is now 5
QString s = v.toString(); // s is now "5"
MyCustomStruct c;
v.setValue(c);
MyCustomStruct c2 = v.value<MyCustomStruct>();
QVariant QVariant::fromValue(const T &value)
返回一个包含value副本的QVariant。在其他方面与setValue()完全相同。
MyCustomStruct s;
return QVariant::fromValue(s);
今天总结一下对QTreeView节点重命名的操作
在QtreeView中有一个void eidt(const QModelIndex &index)的槽函数,当你想要对某个节点进行重命名操作时,将索引传给该槽函数,该节点状态就能变成可编辑。
另外如果不是通过右键菜单进行重命名操作,通过双击也能实现相应操作,只需要通过setEditTriggers(EditTriggers triggers)函
在Qt开发过程中,多次遇到要对QTreeWidget中的节点重命名,网上有很多可参考的代码,但在拿过来用后发现还是有很多问题。目前本人认为比较靠谱的参考链接如下:
https://www.cnblogs.com/ling123/p/5503465.html
这个参考我在实现的过程中也发现了问题:第一次启动程序重命名时没有问题,但是在程序启动后,进行第二次、第三次重命名时,就会出现问题。经过测试...
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) //节点鼠标单击事件
treeView1.LabelEdit = true; //允许出现编辑状态
downnum = e.Clicks; //记录按下次数
if (downnum >= 2)
treeV
要隐藏 QTreeView 中的根节点,您可以使用 `setRootIsDecorated()` 函数,并将其参数设置为 `false`。这将隐藏根节点的展开/折叠图标和缩进。
以下是一个示例:
```cpp
QTreeView* treeView = new QTreeView;
treeView->setRootIsDecorated(false);
如果您使用的是默认的 QStandardItemModel,还可以通过设置根节点的标志来实现隐藏。根节点默认是可展开的,因此您可以将其标志设置为 `Qt::ItemNeverHasChildren`,这样它就不会显示展开/折叠图标。
以下是一个示例:
```cpp
QStandardItemModel* model = new QStandardItemModel;
QStandardItem* rootItem = model->invisibleRootItem();
rootItem->setFlags(rootItem->flags() & ~Qt::ItemIsDropEnabled);
rootItem->setFlags(rootItem->flags() | Qt::ItemNeverHasChildren);
通过以上方法,您可以隐藏 QTreeView 中的根节点。