Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Модель/Представление _ Поиск и выбор узла в QTreeView

Автор: JohnZ 3.8.2014, 22:28

Прошу сильно не пинать, может уже и избитый вопрос, но я в форуме не нашёл.
Если есть тема с решением, прошу ткнуть в ссыль ...
В общем траблема в выборе узла в QTreeView, из примера simpledommodel.
Добавил я в узел свой уникальный ключ Id, по которому ведётся поиск.

DomItem *DomItem::findIDD(int fIdd )
{
    int i, nCnt;
    DomItem *childItem;

    if (fIdd == Idd)    return this;

    nCnt = domNode.childNodes().count();
    for (i = 0;i < nCnt;i++)
    {
        if (childItem = child(i)->findIDD(fIdd ))
            return  childItem;
    }
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

Это из View

        qDebug() << fitem->row();
        QModelIndex index = model->index(fitem->row(), 0, QModelIndex());
        scrollTo(index, EnsureVisible);

//////////////////////////////////////////////////////////////////////////////////////////////////////

Узел находит, а вот выбрать я его не могу, т.к. row() найденного узла при свёрнутом дереве всегда 1,
вне зависимости где этот узел находится :blink: Ведь дерево из DomItem в момент поиска уже построено !?
Подскажите плз, как в QTreeView выбор установить на найденный узел ?
Узлов 3-6 тыс. и искать вручную сложновато ...

Автор: lanz 5.8.2014, 20:38

В TreeModel важно правильно указать родителя, если найденный узел ниже по уровню вложенности чем корневой. QModelIndex() означает рута как родителя.
Вот здесь подробнее:
http://qt-project.org/doc/qt-4.8/model-view-programming.html#parents-of-items

Автор: JohnZ 5.8.2014, 23:29

Цитата(lanz @ 5.8.2014, 20:38) *
В TreeModel важно правильно указать родителя, если найденный узел ниже по уровню вложенности чем корневой. QModelIndex() означает рута как родителя.
Вот здесь подробнее:
http://qt-project.org/doc/qt-4.8/model-view-programming.html#parents-of-items


Читал я это, и даже на русском, а не на пендосском ...
Каждый индекс имеет свой уникальный ключ, кроме row & col, IMHO которого должно быть достаточно
для прямого доступа к узлу внутри представления ?!
Задавал я и парента и про-парента, не позиционирует никак, только иногда курсор скачет на рута.
Вот кусок кода, который выводит мне весь путь к узлу, а затем я вручную к нему добираюсь ... :(

    do {
            if (fitem)
                qDebug() << fitem->node().nodeName()
                               << fitem->node().toElement().attribute("name")
                               << " IDD = " << fitem->Idd  << "\n";
            else
                 qDebug() << "End !";

            fitem = fitem->parent();
            if (fitem->Idc < 3) break; // ROOT

    } while(fitem);


Иначе пока никак :(

Автор: lanz 6.8.2014, 18:12

Если ваша модель возвращает этот id для какой нибудь роли, то можно получить готовый индекс через match:
http://qt-project.org/doc/qt-5/qabstractitemmodel.html#match

Если получится, выложите минимальный пример, попробую посмотреть что там не так.

Автор: JohnZ 10.8.2014, 22:13

Цитата(lanz @ 6.8.2014, 18:12) *
Если ваша модель возвращает этот id для какой нибудь роли, то можно получить готовый индекс через match:
http://qt-project.org/doc/qt-5/qabstractitemmodel.html#match

Если получится, выложите минимальный пример, попробую посмотреть что там не так.


Видимо я не точно выразился, либо меня не так поняли ...

Вот ф-я поиска по IDD которая выводит путь к узлу. Исполнителя я привёл в первом посту.
У меня не отрабатывает то, что между коментариями !

Например вызов QComboBox.setCurrentIndex ( 7 ) установит выбор на 7-ю строку,
и покажет его. Мне нужно аналогично для QTreeView !!!

//  Это переработанный  simpledommodel

class MdiChild : public QTreeView
{
    Q_OBJECT
.....................

}


void MdiChild::findIDD()
{
    QModelIndex index = selectedIndexes().first();  
    DomItem *item = static_cast<DomItem*>(index.internalPointer());

    int tfIdd = QInputDialog::getInt(this, tr("Find Idd Object"), tr("Idd: "));

    DomItem *fitem = item->findIDD(tfIdd);

///////////////////////////////////////////////////////////////////////////////////////////////

    QModelIndex index = model->index(fitem->row(), 0, QModelIndex());
     scrollTo(index, EnsureVisible);

///////////////////////////////////////////////////////////////////////////////////////////////

    if (fitem)
    {

    do {
                qDebug() << fitem->node().nodeName()
                         << fitem->node().toElement().attribute(getTagByClass(201))
                         << " IDC = " << fitem->Idc
                         << " IDD = " << fitem->Idd;

            fitem = fitem->parent();
            if (fitem->Idc < 3) break;

        } while(fitem);
     }
}


Заранее благодарен за помощь !

Цитата(lanz @ 6.8.2014, 18:12) *
Если ваша модель возвращает этот id для какой нибудь роли, то можно получить готовый индекс через match:
http://qt-project.org/doc/qt-5/qabstractitemmodel.html#match

Если получится, выложите минимальный пример, попробую посмотреть что там не так.


Видимо я не точно выразился, либо меня не так поняли ...

Вот ф-я поиска по IDD которая выводит путь к узлу. Исполнителя я привёл в первом посту.
У меня не отрабатывает то, что между коментариями !

Например вызов QComboBox.setCurrentIndex ( 7 ) установит выбор на 7-ю строку,
и покажет его. Мне нужно аналогично для QTreeView !!!

//  Это переработанный  simpledommodel

class MdiChild : public QTreeView
{
    Q_OBJECT
.....................

}

void MdiChild::findIDD()
{
    QModelIndex index = selectedIndexes().first();  
    DomItem *item = static_cast<DomItem*>(index.internalPointer());

    int tfIdd = QInputDialog::getInt(this, tr("Find Idd Object"), tr("Idd: "));

    DomItem *fitem = item->findIDD(tfIdd);

/////////////////////////////////////////////////////////////////////////////////////////////

    QModelIndex index = model->index(fitem->row(), 0, QModelIndex());
     scrollTo(index, EnsureVisible);

/////////////////////////////////////////////////////////////////////////////////////////////

    if (fitem)
    {
             do {
              qDebug() << fitem->node().nodeName()
                         << fitem->node().toElement().attribute("name")
                     << " IDC = " << fitem->Idc
                     << " IDD = " << fitem->Idd;

              fitem = fitem->parent();
              if (fitem->Idc < 3) break;  // Root

        } while(fitem);
     }
}


Заранее благодарен за помощь !


Автор: lanz 12.8.2014, 19:53

Имеете ввиду:
treeView->selectionModel()->select(index) ?
http://qt-project.org/doc/qt-4.8/qitemselectionmodel.html#select

Автор: JohnZ 13.8.2014, 13:25

Спасибо за ответ. lanz извиняюсь что морочу вам ... :rolleyes: , но не "догоняю" :(
Idd я вижу только когда дерево развёрнуто, как на снимке.
Поиск мне возвращает указатель на узел DomItem * (из примера QT, как писалось выше)

Видимо индекс найденного узла получаю НЕ правильный, поэтому и не позиционирует ?

QModelIndex index = model->index(fitem->row(), 0, QModelIndex());

// fitem->row() почему-то ВСЕГДА 1 :blink: Фактически вернулись к 1-й месаге темы :(

Тогда вопрос, - как его (QModelIndex) получить, правильный, имея указатель
на найденный объект DomItem * ???

Из объекта вообще возможно получить его индекс в модели ?

///////////////////////////////////////////////////////////////////////////////////////

QModelIndex DomModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    DomItem *parentItem;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<DomItem*>(parent.internalPointer());

    DomItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}


Видимо можно было-бы пере-делать поиск основываясь на получении
индекса из кода выше, начиная всегда от корня (хотя нужно ниже !)
но какие параметры row & column задавать, согласно снимка ?
Опять-же, когда дерево не развёрнуто, row в узле всегда == 1 !?

 

Автор: lanz 14.8.2014, 23:09

Не стоит искать элементы так. Воспользуйтесь интерфейсом модели.
Прикрепил пример проекта. Исправьте если что не так в нем, будем над ним работать.
Не очень удобно пытаться составить в голове куски кода.

 scratch1.zip ( 3.31 килобайт ) : 248
 

Автор: JohnZ 16.8.2014, 0:49

lanz - БлагоДарю ! Это действительно то, что мне нужно в проект.
Описание ф-ции match(...) для меня было не совсем понятным, (из-за индекса) поэтому "слепил" свой вариант
поиска Idd. Конечно-же "малость" придётся переделать то что уже есть, т.к. "структуры" различны (sdi<-->mdi) ...
В связИ с этим, есть несколько вопросов.
При добавлении и удалении строк у меня просто вставляется/удаляется узел в/из QDomDocument,
например для справочника

    if ((item->Idc == 100))  // New Catalogue
    {
        newobj = model->insertCatalogue(&item->node().toElement(),  text );
        collapse ( index );
        expand (  index ); // <-- ес-сно parent
        return(newobj);
    }

Я понимаю что это не совсем корректно (т.е. без beginInsert и т.д...) но пока работает, и обновляет :)

Как для Вашей модели должны выглядеть insertRows(...) и removeRows(...) для корректной работы view-а ?

Нужны-ли будут в класе Element Вашей модели

bool removeChildren(int position, int count)
bool insertChildren(int position, int count, int columns)

для поддержания механизма вставки / удаления строк ? (как в QT-шном примере)
Если да, то как должны выглядеть ?

Ещё раз спасибо за пример !

Удачи !

Автор: lanz 17.8.2014, 10:55

Обычно я не пользуюсь insert/removeRows. Как написано в документации:

Цитата
If you implement your own model, you can reimplement this function if you want to support insertions. Alternatively, you can provide your own API for altering the data. In either case, you will need to call beginInsertRows() and endInsertRows() to notify other components that the model has changed.


begin/endInsertRows нужно просто вызвать, это не так сложно :lol:
void MyModel::insertElement(const QModelIndex &parent, int id)
{
    Element * pEl = this->mRoot;
    if( parent.isValid() ) pEl = (Element*)parent.internalPointer();

    this->beginInsertRows( parent, pEl->children().size(), pEl->children().size() );
    Element * el = new Element( pEl );
    el->id = id;
    this->endInsertRows();
}


Если вы хотите использовать интерфейс insert/removeRows вместо своего, тогда да, вам понадобятся методы insert/removeChildren.

Автор: JohnZ 17.8.2014, 12:22

Это я спрашивал согласно примера editabletreemodel. Там демонстрируется поддержка
вставки-удаления в модели и элементе. Поэтому, я так понял, эта поддержка нужна
будет и здесь, в Вашей модели.

lanz, а от чего зависит обновление на экране в TreeView-е ?
Дело в том, что данные в узле, обновляемые из диалога, почему-то обновляются в TreeView-е
по-разному, в зависимости от колонки. Если в 1-й, обновляются мгновенно, если в 4-й (value)
не обновляется (на экране, хотя данные в реальности обновлены). Это видно при сохранении
конфигурации. Не хватат сигнала datachanged ?!

Автор: lanz 17.8.2014, 15:01

Не хватает :lol:
Если меняете данные, нужно посылать dataChanged.

Автор: JohnZ 1.9.2014, 14:56

Всем Здравствовать !

Дабы не плодить "лишних" тем, продолжу здесь, тем-более что вопрос(ы) по данной теме (проекта).
В прицепе небольшой проектик, с которого я начал свой, с добавленным поиском от lanz,
Примерная (тестовая) конфигурация в архиве. ехе-шник тоже там, если у Вас он запустится.
Собрано на либе 4.5.3 2008-й студией. Сейчас он выглядит ес-сно иначе, как на картинке
которую я давал выше, но для понимания задаваемого вопроса достачен IMHO, но если надо ...

Вопрос в следующем, - как можно избавиться в view-e от узла "xml" выше рута, и по возможности
узлов "#text" ? С минимальными переделками, ес-сно !
С поиском это конечно-же почти не связано, это нужно для правильной инициализации указателя
"DomItem *rootItem" в view-e для последующего поиска узлов DomItem * уже в модуле исполнителя.
Вот конструктор модели в реальном проекте ...

DomModel::DomModel(QDomDocument document, QObject *parent)
         : QAbstractItemModel(parent), xmlDocument(document)
{
    view = (MdiChild *) parent;
    setObjectName( "metadata" );   //   for qsa ?

    setCompressed( false );
    setModified( false );
    prjType = false;         // Anannas Config for  init

    rootnode = xmlDocument.documentElement();
    nextIdd = nextID();

    qDebug() << rootnode.nodeName();

    rootItem = new DomItem(xmlDocument, 0);
//     rootItem = new DomItem(rootnode, 0);

    qDebug() << rootItem->node().nodeName() << " --- " << rootItem->Idd;

    init();
}


В этом случае узел "xml" показывает, но если разремить вторую строку инициализации
rootItem, то показывает начиная с узла "Info" :blink: пропустив рутовый узел "config".
Подскажите плз, как это можно исправить ?


 simpledommodel.zip ( 87.39 килобайт ) : 136
 

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)