Russian Qt Forum
Ноябрь 23, 2024, 04:26 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры  (Прочитано 11052 раз)
vovan1982
Гость
« : Июнь 17, 2009, 18:42 »

Привет всем!

Народ кто реализовал mimeData() и dropMimeData() для treemodel
поделитель пожалуйста, ни как не получается с ними разобраться.
В ассистанте смотрел вроде всё понятно но там пример для обычного списка,
а что нужно делать для дерева ни как не пойму.
Записан
ритт
Гость
« Ответ #1 : Июнь 18, 2009, 00:32 »

смотри код QFileSystemModel - можно код вообще без модификации утянуть...
Записан
vovan1982
Гость
« Ответ #2 : Июнь 18, 2009, 01:13 »

Спасибо, основную идею уловил.
Остался один вопрос, в QFileSystemModel через mimeData() передаётся ссылка на файл, а в случае с обычной моделью
с данными в виде дерева, что лучше передавать указатель на элемент дерева или непосредственно данные?
Записан
ритт
Гость
« Ответ #3 : Июнь 18, 2009, 02:11 »

задачу обрисуй?
Записан
vovan1982
Гость
« Ответ #4 : Июнь 18, 2009, 09:49 »

Вообщем есть своя модель унаследованная от QAbstractItemModel
Данные загружаются из БД MySql из таблицы типа (id, parent_id, name)
В итоге строется дерево
all
 |
  ----note1
 |        |
 |         -----n11
 |        |
 |         -----n12
 |
  -----note2
 |        |
 |         -----n21
 |        |
 |         -----n22
 |       
  -----note3
           |
            -----n31

Хочу чтоб при перетаскивании одного из элементов на другой перетаскиваемый элемент и все его дети становилились
потомками элемента на который его перетащили.

Пример:
Перемещаем note3 на n21 в итоге получаем дерево

all
 |
  ----note1
 |        |
 |         -----n11
 |        |
 |         -----n12
 |
  -----note2
           |
            -----n21
           |         |
           |           -----note3
           |                    |
           |                     -----n31
           |
            -----n22
         
При перемещении note3 между n21 и n22 получаем дерево

all
 |
  ----note1
 |        |
 |         -----n11
 |        |
 |         -----n12
 |
  -----note2
          |
           -----n21
          |
           ----note3
          |       |
          |        -----n31
          |
           -----n22
В итоге ни как не могу понять что мне надо передавать через mimeData(), dropMimeData() указатель на note3 или же делать выборку данных из note3 и всех его потомков, после чего вставлять выбранные данные в нужное место в дереве.
 
Записан
ритт
Гость
« Ответ #5 : Июнь 18, 2009, 10:20 »

первое, что в голову пришло - передавай список ид (примари) примерно так (new_parent_id, id1, id2, id3), а в дропе этот new_parent_id и установишь строкам с ид id1, id2, id3.

зы. только название метаданных придумай оригинальное.
Записан
vovan1982
Гость
« Ответ #6 : Июнь 22, 2009, 09:45 »

Передать айдишкин получилось.

Передаю, получаю, нахожу по id нужную мне ветку, но ни как не соображу как переместить полученную ветку вместе со всеми потомками в другую ветку, уже мозги сломал.

Подскажите пожулайста кто знает, а ещё лучше если у кого есть пожелитесь примером!!!

Клас дерева такой же как в "..\examples\itemviews\editabletreemodel".
Записан
vovan1982
Гость
« Ответ #7 : Июнь 22, 2009, 18:55 »

Разобрался с перемещением, работает.
Но наткнулся на другую проблему, после завершения dropMimeData() выполняется removeRows() элемета который перемещался, но дело в том что удалять его не нужно, в результате перемещений под индексом перемещаемого элемента находится уже другой элемет, либо индекс уже не валидный.

Что нужно переопределить чтоб removeRows() не вызывался?

вот dropMimeData()
Код
C++ (Qt)
bool SqlTreeModel::dropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
   if (action == Qt::IgnoreAction)
       return true;
 
   if (action != Qt::MoveAction)
       return false;
 
   if (!data->hasFormat("sqltreemodel/data.list"))
       return false;
 
   if (column > 0)
       return false;
 
   int beginRow;
 
   if (row != -1)
       beginRow = row;
   else if (parent.isValid())
       beginRow = rowCount(parent);
   else
       beginRow = rowCount(index(0,0,QModelIndex()));
 
   TreeItem *item;
   QModelIndex ind;
 
   QByteArray encodedData = data->data("sqltreemodel/data.list");
   QDataStream stream(&encodedData, QIODevice::ReadOnly);
 
   QList<int> moveIds;
 
   while (!stream.atEnd()) {
       int id;
       stream >> id;
       moveIds << id;
   }
 
   foreach (int id, moveIds) {
       item = search(rootItemData, id);            
       ind = indexFromItemData(item);            
       if(parent == QModelIndex())                  
           moveItem(ind,index(0,0,QModelIndex()),beginRow);  
       else
           moveItem(ind,parent,beginRow);    
       beginRow++;
   }
   return true;
}

После этого вызывается removeRows(). Где и как, не могу найти.

Вот moveItem() в модели
Код
C++ (Qt)
void SqlTreeModel::moveItem(const QModelIndex& indexFrom, const QModelIndex& indexTo, int position)
{
   TreeItem *newParent;
   TreeItem *item;
   newParent = itemDataFromIndex(indexTo);
   item = itemDataFromIndex(indexFrom);
   emit layoutAboutToBeChanged();
   if(item->moveTo(position,newParent))
       changePersistentIndex(index(position,0,indexTo),indexFrom);
   emit layoutChanged();
}

Вот moveTo() в TreeItem
Код
C++ (Qt)
bool TreeItem::moveTo(int position, TreeItem *newParent)
{
   if (position < 0 || position > newParent->children.size())
       return false;
 
   int num;
   num = childNumber();
   parentItem->children.removeAt(num);
   parentItem = newParent;
   newParent->children.insert(position,this);
 
   return true;
}
Записан
vyacheti
Гость
« Ответ #8 : Декабрь 16, 2010, 16:20 »

Разобрался с перемещением, работает.
Но наткнулся на другую проблему, после завершения dropMimeData() выполняется removeRows() элемета который перемещался, но дело в том что удалять его не нужно, в результате перемещений под индексом перемещаемого элемента находится уже другой элемет, либо индекс уже не валидный.

Что нужно переопределить чтоб removeRows() не вызывался?
Если еще актуально Смеющийся
Вызывается он в QAbstractItemView ::startDrag -> QAbstractItemViewPrivate::clearOrRemove()
Записан
fte
Гость
« Ответ #9 : Июнь 26, 2012, 10:35 »

Цитировать
Цитировать
Разобрался с перемещением, работает.
Но наткнулся на другую проблему, после завершения dropMimeData() выполняется removeRows() элемета который перемещался, но дело в том что удалять его не нужно, в результате перемещений под индексом перемещаемого элемента находится уже другой элемет, либо индекс уже не валидный.
Что нужно переопределить чтоб removeRows() не вызывался?
Если еще актуально:)
Вызывается он в QAbstractItemView ::startDrag -> QAbstractItemViewPrivate::clearOrRemove()
Если еще актуально:)
Посмотрел исходники...., вот один из вариантов решения....
1.опеределяем у модели сигнал, например: void dropNotSuccess()
2.коннектим его со view'шкой: connect(model,SIGNAL(dropNotSuccess()),view->selectionModel(),SLOT(clearSelection()));
3.выдаем сигнал в там, где нужно:
Код:
bool Tmodel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
      ..........
      if(action == Qt::MoveAction && !success)
      {
emit dropNotSuccess();
return false;
      }
}

 removeRows() не выполняется!
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #10 : Апрель 07, 2014, 19:49 »

Проблема присутствует!

Пишу модель дерево. Предыдущий вариант как решение не  подходит, вообще не понятно, что автор хотел сказать. Если мы в dropMimeData возвращаем false, то removeRows так и так не вызовется. Исходники модели прилагаю. Повторю проблему.

В модели классического SQL дерева (ROW_ID, TITLE, PARENT_ID) хочется реализовать перемещиние, естественно просто сменой значения PARENT_ID, а не удалением и вставкой записей. Реализовывая dropMimeData и возвращая true - получаем вызов removeRows ....

Код:

#pragma once

#include <QAbstractItemModel>
#include <QTreeView>
#include <QKeyEvent>
#include <QSqlRecord>
#include "SqlTreeModel_BranchProxy.h"
#include "DebugLog.h"

class CSqlTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
    // Construction/destruction
CSqlTreeModel();
~CSqlTreeModel();

void setTableName( const QString& sVal );
    // Object linkage
void setRootID( int nRootID );

    // Model hierarchy
    virtual QModelIndex index( int row, int column, const QModelIndex& parent ) const;
    virtual QModelIndex parent( const QModelIndex& index ) const;

    // Index enumeration
    virtual int rowCount( const QModelIndex& parent ) const;
    virtual int columnCount( const QModelIndex& parent ) const;

    // Data retrieval
    virtual QVariant data( const QModelIndex& index, int role ) const;
virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const;

virtual bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex() );
virtual bool removeRows ( int row, int count, const QModelIndex & parent = QModelIndex() );

    // Data set
virtual Qt::ItemFlags flags( const QModelIndex& index ) const;
virtual bool setData( const QModelIndex& index, const QVariant& value, int role );

// Simple Helper Function
QModelIndex addChild( const QModelIndex& index );
QModelIndex addSibling( const QModelIndex& index );
void delRow( const QModelIndex& index );

    // Data proxy from index retrieval
QModelIndex getIndex( CSqlTreeModel_BranchProxy * pData ) const;
CSqlTreeModel_BranchProxy * getProxy( const QModelIndex& index ) const;
CSqlTreeModel_BranchProxy * getProxyOrRoot( const QModelIndex& index ) const;

QSqlQuery& select_Root_Q()  { return m_qSelectRoot; }
QSqlQuery& select_Child_Q() { return m_qSelectChild; }
QSqlQuery& remove_Child_Q() { return m_qRemoveChild; }
QSqlQuery& insert_Child_Q() { return m_qInsertChild; }
QSqlQuery& update_Q()       { return m_qUpdate; }
QSqlQuery& gen_ROW_ID_Q()   { return m_qGen_ROW_ID; }

inline QHash<int, QString>& columnsHash() { return m_columnsHash;}

virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction; }

QStringList mimeTypes() const;
QMimeData * mimeData(const QModelIndexList &indexes) const;
virtual bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent);

private:
QSqlQuery m_qSelectRoot;
QSqlQuery m_qSelectChild;
QSqlQuery m_qRemoveChild;
QSqlQuery m_qInsertChild;
QSqlQuery m_qChangeParent;
QSqlQuery m_qUpdate;
QSqlQuery m_qGen_ROW_ID;

mutable CSqlTreeModel_BranchProxy m_Root;

QHash<int, QString> m_columnsHash;
};

class CSqlTreeView_EventFilter : public QObject
{
CSqlTreeModel * m_pSqlTreeModel = 0;
QTreeView * m_pView = 0;
public:
CSqlTreeView_EventFilter( QObject * parent = 0 ) : QObject (parent)
{
m_pView = qobject_cast<QTreeView*>( parent );
m_pSqlTreeModel = qobject_cast<CSqlTreeModel*>( m_pView->model() );
}
protected:
bool eventFilter( QObject * obj, QEvent *event )
{
if (event->type() == QEvent::KeyPress)
{
switch ( event->type() )
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
QKeyEvent * keyEvent = static_cast<QKeyEvent *>( event );
switch ( keyEvent->key() )
{
case ( Qt::Key_Insert ):
{
QModelIndex index = m_pView->selectionModel()->currentIndex();
QModelIndex newIndex;

if ( index.isValid() )
{
if ( keyEvent->modifiers() & Qt::ShiftModifier )
{
m_pView->expand( index );
newIndex = m_pSqlTreeModel->addChild( index );
}
else
{
newIndex = m_pSqlTreeModel->addSibling( index );
}
};

if ( newIndex.isValid() )
m_pView->selectionModel()->setCurrentIndex( newIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );

return true;
};
case ( Qt::Key_Delete ):
{
QModelIndex index = m_pView->selectionModel()->currentIndex();

if ( index.isValid() )
{
m_pView->expand( index );
m_pSqlTreeModel->delRow( index );
}
return true;
};
default:;
};
};
default:;
}
};

return QObject::eventFilter(obj, event);
}
};


Код:

#include "SqlTreeModel.h"

#include <QDebug>
#include <QFont>
#include <QBrush>
#include <QMimeData>
#include <QSqlError>

#include "SqlUtils.h"

#define ROW_ID_LIST_MIME_TYPE "application/row_id.list"

CSqlTreeModel::CSqlTreeModel() :
m_Root(this)
{
m_columnsHash.insert( 0, "TITLE" );
m_columnsHash.insert( 1, "CHILD_COUNT" );
m_columnsHash.insert( 2, ROW_ID_FIELD_NAME );
}

CSqlTreeModel::~CSqlTreeModel()
{

}

void CSqlTreeModel::setTableName( const QString& sVal )
{
m_qSelectRoot.prepare( "SELECT a.ROW_ID, a.TITLE, a.PARENT_ID, ( select count(*) from " + sVal + " b where b.PARENT_ID = :PARENT_ID ) CHILD_COUNT "
"FROM " + sVal + " a WHERE a.ROW_ID = :PARENT_ID" );

m_qSelectChild.prepare( "select p.ROW_ID, p.TITLE, p.PARENT_ID, (select count(*) from " + sVal + " fp where fp.PARENT_ID = p.ROW_ID ) CHILD_COUNT "
  "from " + sVal + " p where PARENT_ID = :PARENT_ID "
  "order by p.TITLE");

m_qRemoveChild.prepare( "delete from " + sVal + " where ROW_ID = :ROW_ID");

m_qInsertChild.prepare( "insert into " + sVal + "(ROW_ID, TITLE, PARENT_ID) values(:ROW_ID, :TITLE, :PARENT_ID)");

m_qChangeParent.prepare( "update " + sVal + " set PARENT_ID = :PARENT_ID where ROW_ID = :ROW_ID" );

m_qUpdate.prepare( "update " + sVal + " p set p.TITLE = :TITLE where p.ROW_ID = :ROW_ID" );

m_qGen_ROW_ID.prepare( "select gen_id( GEN_" + sVal + "_ID, 1 ) from RDB$DATABASE" );
}

void CSqlTreeModel::setRootID(int nRootID )
{
m_Root.setID( nRootID );
}

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

CSqlTreeModel_BranchProxy * pParent = getProxyOrRoot( parent );
CSqlTreeModel_BranchProxy * pChild = pParent->child(row);

    if( pChild )
        return createIndex( row, column, pChild );
    else
        return QModelIndex();
}

QModelIndex CSqlTreeModel::parent( const QModelIndex& index ) const
{
    if( !index.isValid() ) return QModelIndex();

CSqlTreeModel_BranchProxy * pProxy = getProxy( index );
    if( !pProxy ) return QModelIndex();

CSqlTreeModel_BranchProxy * pParent = pProxy->parent();
    if( !pParent ) return QModelIndex();

return getIndex( pParent );
}

int CSqlTreeModel::rowCount( const QModelIndex& parent ) const
{
return getProxyOrRoot( parent )->childrenCount();
}

int CSqlTreeModel::columnCount( const QModelIndex& parent ) const
{
int nCount = m_columnsHash.count();
return nCount > 0 ? nCount : 4;
}

QVariant CSqlTreeModel::data( const QModelIndex& index, int role ) const
{
    if( !index.isValid() ) return QVariant();

CSqlTreeModel_BranchProxy * pProxy = getProxy( index );

    switch( role )
    {

// case Qt::CheckStateRole:
// {
// if ( index.column() == 0 )
// if ( getProxy( index )->childrenCount() == 0 )
// return Qt::PartiallyChecked;
// break;
// }

    case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole:
{
switch( index.column() )
{
case 0: return pProxy->title();
case 1: return pProxy->childrenCount();
case 2: return pProxy->rowID();
default: return QVariant();
}
}

// case Qt::FontRole:
// {
// QFont font;
// font.setItalic( pProxy->isPrivate() );
// font.setBold( !pProxy->isStatic() );
// return font;
// }

// case Qt::ForegroundRole:
// {
// if( pProxy->isStatic() )
// return QBrush( Qt::black );
// else
// return QBrush( Qt::darkRed );
// }

default:
return QVariant();
}
return QVariant();
}

QVariant CSqlTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if( orientation != Qt::Horizontal )
return QVariant();

switch( role )
{
case Qt::DisplayRole:
{
if ( m_columnsHash.contains( section ) )
return m_columnsHash.value( section );
else
return QVariant();

break;
}
default:
return QVariant();
}
}

bool CSqlTreeModel::insertRows(int row, int count, const QModelIndex & parent )
{
CSqlTreeModel_BranchProxy * pParent = getProxyOrRoot( parent );

beginInsertRows( parent, row, row + (count-1) );

bool b = true;
for ( int i=0; i < count; ++i )
b = b && pParent->insertChild( row + i );

endInsertRows();

//emit layoutChanged();
emit dataChanged( parent, parent );

return b;
}

bool CSqlTreeModel::removeRows ( int row, int count, const QModelIndex & parent )
{
qDebug() << "111";
CSqlTreeModel_BranchProxy * pParent = getProxyOrRoot( parent );
beginRemoveRows( parent, row, row + (count-1) );

bool b = true;
for ( int i=0; i < count; ++i )
b = b && pParent->removeChild( row + i );

endRemoveRows();

emit dataChanged( parent, parent );

return b;
}

Qt::ItemFlags CSqlTreeModel::flags( const QModelIndex& index ) const
{
if( !index.isValid() ) return 0;

Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;

//    CModelObjDataProxy * pProxy = getProxy( index );
// OldBaseType nType = pProxy->type();
bool bEditable = index.column() == 0;

// if( bEditable )
// bEditable  &=
// ( ( nType != btObject ) &&
//   ( nType != btUndefined ) &&
//   ( nType != btQByteArray ) );

if( bEditable )
flags |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;

return flags;
}

bool CSqlTreeModel::setData( const QModelIndex& index, const QVariant& value, int role )
{
if( !index.isValid() ) return false;
if( role != Qt::EditRole ) return false;
if ( index.column() != 0 ) return false;

CSqlTreeModel_BranchProxy * pProxy = getProxy( index );
pProxy->setTitle( value.toString() );

emit dataChanged( index, index );
return true;
}

QModelIndex CSqlTreeModel::addChild( const QModelIndex& index )
{
CSqlTreeModel_BranchProxy * pProxy = getProxy( index );
int nRow = pProxy->childrenCount();

bool b = insertRow( nRow, index );

return b ? getIndex( pProxy->child( nRow ) ) : QModelIndex();
}

QModelIndex CSqlTreeModel::addSibling( const QModelIndex& index )
{
int nRow = index.row() + 1;
QModelIndex parent = index.parent();

bool b = insertRow( nRow, parent );

return b ? getIndex( getProxyOrRoot( parent )->child( nRow ) ) : QModelIndex();
}

void CSqlTreeModel::delRow( const QModelIndex& index )
{
removeRow( index.row(), index.parent() );
}

QModelIndex CSqlTreeModel::getIndex( CSqlTreeModel_BranchProxy * pData ) const
{
    if( pData == &m_Root ) return QModelIndex();
return createIndex( pData->row(), 0, pData );
}

CSqlTreeModel_BranchProxy *CSqlTreeModel::getProxy( const QModelIndex& index ) const
{
return static_cast<CSqlTreeModel_BranchProxy*>( index.internalPointer() );
}

CSqlTreeModel_BranchProxy * CSqlTreeModel::getProxyOrRoot( const QModelIndex& index ) const
{
return index.isValid() ?
static_cast<CSqlTreeModel_BranchProxy*>( index.internalPointer() ) :
&m_Root;
}

QStringList CSqlTreeModel::mimeTypes() const
{
QStringList types;
types << ROW_ID_LIST_MIME_TYPE;
return types;
}

QMimeData *CSqlTreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;

QDataStream stream(&encodedData, QIODevice::WriteOnly);

foreach (const QModelIndex &index, indexes)
{
if (index.isValid())
{
CSqlTreeModel_BranchProxy * pProxy = getProxy( index );
//QString text = data(index, Qt::DisplayRole).toString();
stream << pProxy->rowID();
}
}

mimeData->setData( ROW_ID_LIST_MIME_TYPE, encodedData);
return mimeData;
}

bool CSqlTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if ( action != Qt::MoveAction ) return false;

if ( !parent.isValid() ) return false;

CSqlTreeModel_BranchProxy * pNewParent_Proxy = getProxy( parent );

QByteArray encodedData = data->data( ROW_ID_LIST_MIME_TYPE );
QDataStream stream(&encodedData, QIODevice::ReadOnly);
QList<int> listROW_ID;

while (!stream.atEnd()) {
QString text;
int nROW_ID;
stream >> nROW_ID;
listROW_ID << nROW_ID;
}

bool b = true;

foreach( int nROW_ID, listROW_ID )
{
m_qChangeParent.bindValue( ":PARENT_ID", pNewParent_Proxy->rowID() );
m_qChangeParent.bindValue( ":ROW_ID", nROW_ID );
m_qChangeParent.exec();

if ( m_qChangeParent.lastError().type() != QSqlError::NoError )
{
qCritical() << m_qChangeParent.lastError().text();
b = b && false;
continue;
};

}


// QByteArray encoded = data->data( mimeTypes().at(0) );
// QDataStream stream(&encoded, QIODevice::ReadOnly);
//qDebug() << listROW_ID;
return true;
}

Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #11 : Апрель 08, 2014, 04:30 »

Свойство вьюва dragDropOverWriteMode влияет на поведение модели. При true removeRows не вызывается после dropMimeData
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #12 : Апрель 08, 2014, 18:37 »

И можно реализовать успешно дроп прямо в dropMimeData !
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.212 секунд. Запросов: 22.