Название: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: vovan1982 от Июнь 17, 2009, 18:42
Привет всем!
Народ кто реализовал mimeData() и dropMimeData() для treemodel поделитель пожалуйста, ни как не получается с ними разобраться. В ассистанте смотрел вроде всё понятно но там пример для обычного списка, а что нужно делать для дерева ни как не пойму.
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: ритт от Июнь 18, 2009, 00:32
смотри код QFileSystemModel - можно код вообще без модификации утянуть...
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: vovan1982 от Июнь 18, 2009, 01:13
Спасибо, основную идею уловил. Остался один вопрос, в QFileSystemModel через mimeData() передаётся ссылка на файл, а в случае с обычной моделью с данными в виде дерева, что лучше передавать указатель на элемент дерева или непосредственно данные?
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: ритт от Июнь 18, 2009, 02:11
задачу обрисуй?
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: vovan1982 от Июнь 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 и всех его потомков, после чего вставлять выбранные данные в нужное место в дереве.
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: ритт от Июнь 18, 2009, 10:20
первое, что в голову пришло - передавай список ид (примари) примерно так (new_parent_id, id1, id2, id3), а в дропе этот new_parent_id и установишь строкам с ид id1, id2, id3.
зы. только название метаданных придумай оригинальное.
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: vovan1982 от Июнь 22, 2009, 09:45
Передать айдишкин получилось.
Передаю, получаю, нахожу по id нужную мне ветку, но ни как не соображу как переместить полученную ветку вместе со всеми потомками в другую ветку, уже мозги сломал.
Подскажите пожулайста кто знает, а ещё лучше если у кого есть пожелитесь примером!!!
Клас дерева такой же как в "..\examples\itemviews\editabletreemodel".
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: vovan1982 от Июнь 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; }
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: vyacheti от Декабрь 16, 2010, 16:20
Разобрался с перемещением, работает. Но наткнулся на другую проблему, после завершения dropMimeData() выполняется removeRows() элемета который перемещался, но дело в том что удалять его не нужно, в результате перемещений под индексом перемещаемого элемента находится уже другой элемет, либо индекс уже не валидный.
Что нужно переопределить чтоб removeRows() не вызывался?
Если еще актуально ;D Вызывается он в QAbstractItemView ::startDrag -> QAbstractItemViewPrivate::clearOrRemove()
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: fte от Июнь 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() не выполняется!
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: break от Апрель 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; }
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: break от Апрель 08, 2014, 04:30
Свойство вьюва dragDropOverWriteMode влияет на поведение модели. При true removeRows не вызывается после dropMimeData
Название: Re: Drag&Drop: mimeData(), dropMimeData() в treemodel нужны примеры
Отправлено: break от Апрель 08, 2014, 18:37
И можно реализовать успешно дроп прямо в dropMimeData !
|