带有 QTreeView 模型的 Qt itemChanged 信号仅适用于第一级项目

Qt itemChanged signal with QTreeView model works only on first level items

不知道是不是我的qt代码哪里做错了。我只需要每次项目数据更改时发出 itemChanged 信号。 我使用以下代码制作模型:

QStandardItemModel* model = new QStandardItemModel;
QStandardItem *parentItem = model->invisibleRootItem();
QList<QStandardItem*> itemList1;
QList<QStandardItem*> itemList2;
QList<QStandardItem*> itemList3;
QStandardItem* item1;
QStandardItem* item2;
QStandardItem* item3;

for (int i = 0; i < 3; ++i)
{
    item1 = new QStandardItem;
    item1->setText("item1-" + QString::number(i));

    for (int i = 0; i < 3; ++i)
    {
        item2 = new QStandardItem;
        item2->setText("item2-" + QString::number(i));

        for (int i = 0; i < 3; ++i)
        {
            item3 = new QStandardItem;
            item3->setText("item3-" + QString::number(i));
            itemList3 << item3;
        }
        item2->appendRows(itemList3);
        itemList3.clear();
        itemList2 << item2;
    }
    item1->appendRows(itemList2);
    itemList2.clear();
    itemList1 << item1;
}
parentItem->appendRows(itemList1);
itemList1.clear();

ui.treeView->setModel(model);

QObject::connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(onChanged(QStandardItem*)));

我想,每次更改项目时都会调用 onChanged - 例如,编辑项目文本或单击复选框。 但在每种情况下,我都只在 "item1-..." 级项目(第一级项目)上触发了 itemChanged 信号,而不是 item2/3 级项目。 为什么?我怎样才能让它正确?

PS: 与 QTreeWidget 相同的代码工作完美,但我在我的应用程序中使用多线程,我需要划分模型和视图。 QTreeWidget 项目不能在非gui 线程中创建,qtreewidget 不能使用自创建模型。这就是为什么我必须将 QTreeView 与 QStandardItem 一起使用的原因。

我不确定为什么子项 mod 的默认值不会导致父项成为 "changed"。我想作为一种解决方法,您可以将每个子 DataChanged() 信号连接到其父 DataChanged() 信号,以便信号向上传播层次结构,例如:

for (int i = 0; i < 3; ++i)
{
    item1 = new QStandardItem;
    item1->setText("item1-" + QString::number(i));

    for (int i = 0; i < 3; ++i)
    {
        item2 = new QStandardItem;
        item2->setText("item2-" + QString::number(i));

        for (int i = 0; i < 3; ++i)
        {
            item3 = new QStandardItem;
            item3->setText("item3-" + QString::number(i));
            itemList3 << item3;
            connect(item3, SIGNAL(DataChanged()), item2, SIGNAL(DataChanged()));
        }
        item2->appendRows(itemList3);
        itemList3.clear();
        itemList2 << item2;
        connect(item2, SIGNAL(DataChanged()), item1, SIGNAL(DataChanged()));
    }
    item1->appendRows(itemList2);
    itemList2.clear();
    itemList1 << item1;
}

应该发生的是,如果您更改 item3 级别记录,信号应该一直转发到 item1(您建议它正在工作)然后继续正常:

item3 ---DataChanged()---> item2
item2 ---DataChanged()---> item1
item1 ---DataChanged()---> model
model ---itemChanged()---> onChanged()

我没有对此进行测试,但假设 item1 dataChanged() 信号对您有效(您的评论表明)那么这应该有效。

编辑

啊,我想这可能并不像你想要的那样有效。我想你总是会得到一个 item1 指针发送到你的 onChanged() 插槽。如果您想要发送 item3 指针,那么您可能必须子 class QStandardItem(创建一个继承 QStandardItem 的 class),然后执行以下操作:

  • 添加发出的信号:itemChanged(QStandardItem*)
  • 添加一个插槽:void itemHasChanged() {emit itemChanged(this);}
  • 在构造函数中将 dataChanged() 连接到 itemHasChanged()。

然后在你的循环中你可以item1 = new myQStandardItem; 然后对于您添加的每个新项目,直接将它们连接到您的 onChanged() 插槽:

QObject::connect(itemX, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(onChanged(QStandardItem*)));

如果你不能让模型为你做这件事(即计划 B),这会有点额外的努力(但不是太多)...

我刚调试了下,你收不到信号的原因如下:当item的数据发生变化时,你在Qt源码中是这样的:

void QStandardItem::setData(...)
{
   /* ... */
   if (d->model)
       d->model->d_func()->itemChanged(this);
}

另一方面,将 child 项目添加到 parent 时,您有

bool QStandardItemPrivate::insertRows(int row, const QList<QStandardItem*> &items)
{
   /* ... */
   for (int i = 0; i < items.count(); ++i) {
     /* ... */
       item->d_func()->model = model;
   }
}

所以这意味着,项目需要一个指向模型的指针来通知模型有关更改,并且 child 项目的模型设置为 parent 的项目 在插入时。现在你先把children加到parent上,再把parent加到它们的parent上,不可见的根就是level的parent -1 项。因为隐根有模型,所以level-1的item发出信号,其他的没有。

一个简单的更改即可解决此问题:只需先将项目添加到其 parent,然后附加 children,如下所示:

for (int i = 0; i < 3; ++i)
{
    item1 = new QStandardItem;
    item1->setText("item1-" + QString::number(i));
    parentItem->appendRow(item1);

    for (int i = 0; i < 3; ++i)
    {
        item2 = new QStandardItem;
        item2->setText("item2-" + QString::number(i));
        item1->appendRow(item2);

        for (int i = 0; i < 3; ++i)
        {
            item3 = new QStandardItem;
            item3->setText("item3-" + QString::number(i));
            item2->appendRow(item3);
        }
    }
}