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);
10-15 22:31