Сигнал Qt itemChanged с моделью QTreeView работает только для элементов первого уровня

Я не знаю, делаю ли я что-то не так в своем коде 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 будет вызываться каждый раз, когда элемент изменяется — например, текст элемента отредактирован или установлен флажок.
Но в каждом случае я вызвал itemChanged Сигнал только для предметов уровня «item1 -…» (предметы первого уровня), но не для предметов уровня item2 / 3.
Зачем? И как я могу сделать это правильно?

PS: тот же код с QTreeWidget работает отлично, но я использую многопоточность в своем приложении, и мне нужно разделить модель и представление. Элементы QTreeWidget не могут быть созданы в потоке, отличном от графического интерфейса, и qtreewidget не может использовать самостоятельно созданную модель. Вот почему я должен использовать QTreeView с QStandardItem.

2

Решение

Я только что отладил ситуацию, причина, по которой вы не получаете сигнал, заключается в следующем: когда данные элемента изменяются, вы получаете это в источнике Qt:

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

С другой стороны, при добавлении дочернего элемента к родительскому

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

Таким образом, это означает, что элементам нужен указатель на модель, чтобы уведомить модель об изменении, а модель дочернего элемента установлена ​​для элемента родителя. во время вставки. Теперь вы сначала добавляете детей к родителю, а затем родителей к их родителям с невидимым корнем в качестве родителя элементов уровня 1. Поскольку у невидимого корня есть модель, элементы уровня 1 посылают сигнал, а другие — нет.

Простое изменение исправляет это: просто сначала добавьте элемент к его родительскому элементу, а затем добавьте дочерние элементы следующим образом:

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);
}
}
}
3

Другие решения

Я не уверен, почему по умолчанию моды дочерних элементов не вызывают «изменение» родительского элемента. Я полагаю, что в качестве обходного пути вы могли бы соединить каждого ребенка 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()

Я не проверял это, но предполагая, что сигнал itemChanged () item1 работает для вас (что предлагают ваши комментарии), тогда это должно сработать.

редактировать

Ах, я думаю, что это может на самом деле не работать, как вы хотите. Я думаю, что вы всегда будете получать указатель item1 на ваш слот onChanged (). Если вы хотите, чтобы указатель item3 был отправлен, вам, возможно, придется подкласс QStandardItem (создать класс, который наследует QStandardItem), а затем выполнить следующее:

  • Добавьте сигнал, который излучает: itemChanged(QStandardItem*)
  • добавить слот: void itemHasChanged() {emit itemChanged(this);}
  • подключите dataChanged () к itemHasChanged () в конструкторе.

Тогда в вашем цикле вы можете item1 = new myQStandardItem;
А затем для каждого добавляемого вами нового элемента напрямую подключите его к вашему слоту onChanged ():

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

Это немного дополнительные усилия (но не слишком много), если вы не можете заставить модель сделать это за вас (то есть план-B) …

1

По вопросам рекламы [email protected]