Hi !
I have a treeview with a custom model (read only) working perfectly...
but now I want to implement drag&drop support (but only inside the treeview! not to other widgets or from other widgets)
I have read all the documentation and books... but I think that I don't understand it well.
This is only a simplified example to expose my doubts and questions about that.
Important Note: The treeview cannot be edited by user.
(I can change de model programatically... but not directly in the view)
These are my classes:
class Teacher
{
...
...
private:
QString m_name;
// this will be a column in the treeview QString m_phone;
// this will be a column in the treeview School * m_school // "parent" school of the teacher
// much more complex data that is not going to be showed in the treeview
MyOtherDataTeacher m_otherData;
etc
...
...
}
class School
{
...
...
private:
QString m_name;
// this will be a column in the treeview QString m_address;
// this will be a column in the treeview // much more complex data that is not going to be showed in the treeview
MyOtherDataSchool m_otherData;
etc
...
...
}
{
...
...
private:
QList<School *> m_schools; // each school will be a "top level item" in the treeview
QList<Teacher *> m_teachers; // each teacher will be a child of his school
// the model contains other data not needed by the view
MyOtherData m_whatever;
etc
...
...
}
class Teacher
{
...
...
private:
QString m_name; // this will be a column in the treeview
QString m_phone; // this will be a column in the treeview
School * m_school // "parent" school of the teacher
// much more complex data that is not going to be showed in the treeview
QPicture m_photo;
MyOtherDataTeacher m_otherData;
etc
...
...
}
class School
{
...
...
private:
QString m_name; // this will be a column in the treeview
QString m_address; // this will be a column in the treeview
// much more complex data that is not going to be showed in the treeview
QDate m_openingDate;
MyOtherDataSchool m_otherData;
etc
...
...
}
class SchoolTeacherModel : public QAbstractItemModel
{
...
...
private:
QList<School *> m_schools; // each school will be a "top level item" in the treeview
QList<Teacher *> m_teachers; // each teacher will be a child of his school
// the model contains other data not needed by the view
QDate m_whateverdate;
MyOtherData m_whatever;
etc
...
...
}
To copy to clipboard, switch view to plain text mode
[First step]
With that data model I want to create a read-only tree like this:
+ School1 street A, 3-3
teacher1 555-644-247
teacher2 555-641-577
+ School2 street B, 4-3
teacher3 555-644-247
teacher4 555-641-577
+ School3 street C, 2-6
(under each school we can see their teachers)
That's pretty easy...
I only need to reimplement these methods in SchoolTeacherModel:
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
To copy to clipboard, switch view to plain text mode
Everything very easy...
Note that my "columnCount(...)" method is implemented in this way:
int SchoolTeacherModel
::columnCount(const QModelIndex &parent
) const {
return 2; // because this is the max number of columns that is going to be showed
}
int SchoolTeacherModel::columnCount(const QModelIndex &parent) const
{
return 2; // because this is the max number of columns that is going to be showed
}
To copy to clipboard, switch view to plain text mode
And that "data(...)" is implemented in this way (a bit simplified for brevity):
{
if (!index.isValid())
{
}
if (role != Qt::DisplayRole)
{
}
// Important Note: I only support here data that is to be shown... but remember that internally School and Teacher has much more data that the view doesnt need...
if (index.isSchool()) // <- this is a simplification
{
School * school = static_cast<School*>(index.internalPointer());
switch (index.column)
{
case 0:
return school->m_name; break;
case 1:
return school->m_address; break;
default:
}
}
else if (index.isTeacher()) // <- this is a simplification
{
Teacher * teacher = static_cast<Teacher*>(index.internalPointer());
switch (index.column)
{
case 0:
return teacher->m_name; break;
case 1:
return teacher->m_phone; break;
default:
}
}
}
QVariant SchoolTeacherModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role != Qt::DisplayRole)
{
return QVariant();
}
// Important Note: I only support here data that is to be shown... but remember that internally School and Teacher has much more data that the view doesnt need...
if (index.isSchool()) // <- this is a simplification
{
School * school = static_cast<School*>(index.internalPointer());
switch (index.column)
{
case 0:
return school->m_name; break;
case 1:
return school->m_address; break;
default:
return QVariant();
}
}
else if (index.isTeacher()) // <- this is a simplification
{
Teacher * teacher = static_cast<Teacher*>(index.internalPointer());
switch (index.column)
{
case 0:
return teacher->m_name; break;
case 1:
return teacher->m_phone; break;
default:
return QVariant();
}
}
return QVariant();
}
To copy to clipboard, switch view to plain text mode
That is my read-only model...
and it is working perfectly. And I think that is corretcly implemented. (do you agree? or do you think that my columnCount(...) and data(...) are not correct?)
In my imaginary window I have a button... "Create school with teachers"... (Insert a new school in my model)
So in my model added this imaginary method:
void SchoolTeacherModel::insertSchool(School * school)
{
beginInsertRows
(QModelIndex(),m_schools.
count(),m_schools.
count());
m_schools.insertItem(m_schools.count(), school);
endInsertRows();
}
void SchoolTeacherModel::insertSchool(School * school)
{
beginInsertRows(QModelIndex(),m_schools.count(),m_schools.count());
m_schools.insertItem(m_schools.count(), school);
endInsertRows();
}
To copy to clipboard, switch view to plain text mode
This is working perfectly too. Now I can add new schools programatically.
Note: I have not reimplemented these methods ->
bool QAbstractItemModel::insertRows ( int row, int count, const QModelIndex & parent = QModelIndex() );
bool QAbstractItemModel::setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole )
To copy to clipboard, switch view to plain text mode
For the moment I don't need them ! (correct?)
[Second step]
Now I want to add support for "drag and drop" rows of the treeview
(only drag and drop INSIDE the treeview... I only want that the user can move -for example- Teacher1 from School1 to School2. I don't want support for drad&drop to/from other widgets)
What methods should I reimplement?
Qt::DropActions DragDropListModel::supportedDropActions() const -> YES (move action)
Qt
::ItemFlags DragDropListModel
::flags(const QModelIndex &index
) const -> YES
QStringList DragDropListModel
::mimeTypes() const -> Not sure
! QMimeData *DragDropListModel
::mimeData(const QModelIndexList
&indexes
) const -> Not sure
! bool DragDropListModel
::dropMimeData(const QMimeData *data,
Qt
::DropAction action,
int row,
int column,
const QModelIndex &parent
) -> Not sure
!bool QAbstractItemModel::setData(...
) -> Not sure
!! and...
how?? only columns that are going to be showed? or all the data??
Qt::DropActions DragDropListModel::supportedDropActions() const -> YES (move action)
Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const -> YES
QStringList DragDropListModel::mimeTypes() const -> Not sure !
QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const -> Not sure !
bool DragDropListModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int column, const QModelIndex &parent) -> Not sure !
QAbstractItemModel::removeRows(...) -> Not sure !!
bool QAbstractItemModel::insertRows(...) -> Not sure !!
bool QAbstractItemModel::setData(...) -> Not sure !! and... how?? only columns that are going to be showed? or all the data??
To copy to clipboard, switch view to plain text mode
....
I need to reimplement any other method???
....
and...
Do I need to change my columnCount(...) and data(...) methods???
For the moment, remember, they only "see" data that is going to be showed (name and address in School, and name a phone in Teacher)... but they don't see all the rest of data that is inside school and teacher classes.
Of course i added this:
myTreeView->setDragEnabled(TRUE);
myTreeView->setAcceptDrops(TRUE);
myTreeView->setDropIndicatorShown(TRUE);
myTreeView->setSelectionBehavior(QAbstractItemView::SelectRow s);
another side question:
I don't like how the drop indicator looks...is possible to alter it??
Thank you very much.
Bookmarks