PDA

View Full Version : use custom mimedata between two windows



prasad_N
4th March 2016, 14:26
HI,
I wanted to drag & drop custom mime data between two windows (basically 2 QTreeview's in the same application), But when I use this custom mimedata I am not able to drop any data (I could see + sign while dragging in 1st window & the moment I leave the 1st winnow the + sign is changing to ForbiddenCursor & I am not able to drop data in 2nd window)

My classs:


class MyMimedata : public QMimeData
{
Q_OBJECT
public:
MyMimedata () : QMimeData() { }

bool hasFormat(const QString &mimetype) const { return QMimeData::hasFormat(mimetype); }
QStringList formats() const{ return QMimeData::formats(); }
QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const { QMimeData::retrieveData(mimetype, preferredType); }
};

I am not able to drag & drop above custome data type.
When I use
Q_DECLARE_METATYPE(MyMimedata ); I am getting compile time error: 'QMimeData::QMimeData(const QMimeData&)' is private

What am I doing wrong here.... any hint ??

anda_skoa
4th March 2016, 18:21
Your mime data doesn't offer any custom format, at least not as far as you code snippet goes.

Cheers,
_

prasad_N
6th March 2016, 09:39
Hi Thanks for the replay.


Your mime data doesn't offer any custom format, at least not as far as you code snippet goes._
Yes & I have not showed full implementation because I do n't want to dump all the code here & make you to read all the stuff, instead what i showed you is my basic requirement (passing custom mimedata to other window).

Actually I am holding reference to some object & i am trying to access that object in other window with the help of mime data.

class MyMimedata : public QMimeData
{
Q_OBJECT

QObject* parent;

public:
MyMimedata (QObject* parent) : QMimeData() { }

bool hasFormat(const QString &mimetype) const { return QMimeData::hasFormat(mimetype); }
QStringList formats() const{ return QMimeData::formats(); }
QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const { QMimeData::retrieveData(mimetype, preferredType); }

QObject* getParent() { return parent; }
};

And initialization of mime data:

QMimeData* myModel::mimedata(const QModelIndexList& indexs) const // myModel is inherited from QAbstractItemModel
{
MyMimedata* mime = new MyMimedata (this);

return mime;
}

I am accessing it in other view:

void myView::dropEvent ( QDropEvent * event ) //My view is from QTreeView
{
MyMimedata* myMime = qobject_cast<MyMimedata*> (event->mimedata());

if(myMime )
{
myModel* myModel = qobject_cast<myModel*> (myMime ->getParent());

//I wan to have my model here
}
}

This is what I am trying to do, but I am not actually interested in what is the implementation (We can do more customization in side) but wanted to know how can I transfer my custom mime data to the other window (this is the basic requirement once if I can do this we can do many more things which we do with c++ class ex: holding some pointer reference).

Thanks a lot.

anda_skoa
6th March 2016, 10:02
Yes & I have not showed full implementation because I do n't want to dump all the code here & make you to read all the stuff, instead what i showed you is my basic requirement (passing custom mimedata to other window).

So instead of showing the relevant code you though it would be helpful to show irrelevent code?So which of the things doesn't work?

Does the qobject_cast fail?

Cheers,
_

prasad_N
6th March 2016, 13:06
So instead of showing the relevant code you though it would be helpful to show irrelevent code?So which of the things doesn't work? _

I hope its not irrelevant code, Its relevant code just to explain what is the problem & The original code is in my replay (#3)


Does the qobject_cast fail? _

drop event does not even getting called in this case. but if I change my mime data function to below, drop event is getting called, So I thought problem is with MyMimedata class.


QMimeData* myModel::mimedata(const QModelIndexList& indexs) const // myModel is inherited from QAbstractItemModel
{
QMimeData* mime = new QMimeData(); // Or QMimedata* mime = QAbstractItemModel::mimedata(indexs);

return mime;
}

anda_skoa
6th March 2016, 13:21
Do you get the dragEnter event?
Does the cursor indicate that dropping is allowed?

Cheers,
_

prasad_N
6th March 2016, 15:09
Do you get the dragEnter event?
Does the cursor indicate that dropping is allowed?

Cheers,
_

Enter event is getting called & drop event is not getting called & during dropevent the cursor becomes forbidden cursor which signifies drop not allowed.

anda_skoa
6th March 2016, 16:20
So you are saying that this works


QMimeData* myModel::mimedata(const QModelIndexList& indexs) const
{
QMimeData* mime = new QMimeData();
return mime;
}

but this


QMimeData* myModel::mimedata(const QModelIndexList& indexs) const
{
QMimeData* mime = new MyMimedata();
return mime;
}

doesn't?
I.e. an empty mime data object, default construced, in both cases?

Cheers,
_

prasad_N
6th March 2016, 18:07
So you are saying that this works


QMimeData* myModel::mimedata(const QModelIndexList& indexs) const
{
QMimeData* mime = new QMimeData();
return mime;
}

but this


QMimeData* myModel::mimedata(const QModelIndexList& indexs) const
{
QMimeData* mime = new MyMimedata();
return mime;
}

doesn't?
I.e. an empty mime data object, default construced, in both cases?

Cheers,
_

Yes, absolutely..

anda_skoa
7th March 2016, 09:37
Ok, this is strange.

Have you tried with a minimal subclass? One that only has a constructor?
And just to be sure: you did not call QAbstractItemModel::mimedata(indexs), right?

Cheers,
_

prasad_N
7th March 2016, 16:01
Ok, this is strange.
Have you tried with a minimal subclass? One that only has a constructor? _

Sorry but which class you are talking about, QMimedata class ??



Ok, this is strange.
And just to be sure: you did not call QAbstractItemModel::mimedata(indexs), right? _

Yes, I have not called, I tried putting that statement event & no change in the results.



Just to check: I have changed the simple tree model example & I made below changes to get the minimal example.



Model:


#ifndef TREEMODEL_H
#define TREEMODEL_H

#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QDebug>
#include <QMimeData>

class myMidedata : public QMimeData
{
Q_OBJECT

public:
myMidedata() : QMimeData() {}
};

class TreeItem;

//! [0]
class TreeModel : public QAbstractItemModel
{
Q_OBJECT

public:
explicit TreeModel(const QString &data, QObject *parent = 0);
~TreeModel();

QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
//bool hasChildren(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;

int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QMimeData* mimeData(const QModelIndexList &indexes) const;


private:
void setupModelData(const QStringList &lines, TreeItem *parent);

TreeItem *rootItem;
};
//! [0]

#endif // TREEMODEL_H




#include "treeitem.h"
#include "treemodel.h"

#include <QStringList>

//! [0]
TreeModel::TreeModel(const QString &data, QObject *parent)
: QAbstractItemModel(parent)
{
QList<QVariant> rootData;
rootData << "Title" << "Summary";
rootItem = new TreeItem(rootData);
setupModelData(data.split(QString("\n")), rootItem);
}
//! [0]

//! [1]
TreeModel::~TreeModel()
{
delete rootItem;
}
//! [1]

//! [2]
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}

QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QAbstractItemModel::mimeData(indexes);

myMidedata* mime = new myMidedata();
return mime;
}

//! [2]

//! [3]
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();

if (role != Qt::DisplayRole)
return QVariant();

TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

return item->data(index.column());
}
//! [3]

//! [4]
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;

return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
//! [4]

//! [5]
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);

return QVariant();
}
//! [5]

//! [6]
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();

TreeItem *parentItem;

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

TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
//! [6]

//! [7]
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();

TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parentItem();

if (parentItem == rootItem)
return QModelIndex();

return createIndex(parentItem->row(), 0, parentItem);
}
//! [7]

//! [8]
int TreeModel::rowCount(const QModelIndex &parent) const
{
// return 0;
TreeItem *parentItem;
if (parent.column() > 0)
return 0;

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

return parentItem->childCount();
}
//! [8]

//bool TreeModel::hasChildren(const QModelIndex &parent) const
//{
// TreeItem *parentItem;
// if (parent.column() > 0)
// return false;

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

// return parentItem->childCount();
//}

void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;

int number = 0;

while (number < lines.count()) {
int position = 0;
while (position < lines[number].length()) {
if (lines[number].mid(position, 1) != " ")
break;
position++;
}

QString lineData = lines[number].mid(position).trimmed();

if (!lineData.isEmpty()) {
// Read the column data from the rest of the line.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData;
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];

if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.

if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount()-1);
indentations << position;
}
} else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}

// Append a new item to the current parent's list of children.
parents.last()->appendChild(new TreeItem(columnData, parents.last()));
}

++number;
}
}





view :


#ifndef TREEVIEW_H
#define TREEVIEW_H

#include <QTreeView>
#include <QDebug>
#include <QDropEvent>


class treeView : public QTreeView
{
Q_OBJECT
public:
treeView(QString title, QWidget *parent = 0);
~treeView();

QString m_title;

void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);

void dropEvent(QDropEvent *event);
};

#endif // TREEVIEW_H


#include "treeview.h"

treeView::treeView(QString title, QWidget *parent) : QTreeView(parent), m_title(title)
{
setDragEnabled(true);
setAcceptDrops(true);
setDragDropMode(DragDrop);
}

treeView::~treeView()
{

}

void treeView::dragEnterEvent(QDragEnterEvent *event)
{
//qDebug() << "Enter event of = " << m_title;
return QTreeView::dragEnterEvent(event);
}

void treeView::dragMoveEvent(QDragMoveEvent *event)
{
return QTreeView::dragMoveEvent(event);
}

void treeView::dragLeaveEvent(QDragLeaveEvent *event)
{
return QTreeView::dragLeaveEvent(event);
}

void treeView::dropEvent(QDropEvent *event)
{
event->acceptProposedAction();
qDebug() << "Enter event of = " << m_title;
QTreeView::dropEvent(event);
}






#include "treemodel.h"
#include "treeview.h"

#include <QApplication>
#include <QFile>
#include <QTreeView>

int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(simpletreemodel);

QApplication app(argc, argv);

QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();

treeView view("view1");
view.setModel(&model);
view.setWindowTitle(QObject::tr("Simple Tree Model"));
view.show();

treeView view1("view2");
view1.setModel(&model);
view1.setWindowTitle(QObject::tr("Simple Tree Model"));
view1.show();


return app.exec();
}




I have not changed the treeitem class. & this is also not working when I try drag items from view1 to view2 or in other way .

anda_skoa
7th March 2016, 16:17
Can you also post the code that gets the dropEvent called?

Cheers,
_

prasad_N
7th March 2016, 17:15
Can you also post the code that gets the dropEvent called?_



QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
//This is working fine
QMimeData* mime = QAbstractItemModel::mimeData(indexes);
return mime;


//Just now realized below both cases are not working in the simplified example. but QMimeData* mime = new QMimeData();
is working in my project, strange
//myMidedata* mime = new myMidedata();
//QMimeData* mime = new QMimeData();


//this is also not working
//QAbstractItemModel::mimeData(indexes);
//myMidedata* mime = new myMidedata(); OR QMimeData* mime = new QMimeData();

return mime;
}

anda_skoa
7th March 2016, 22:46
I asked you twice(!) if it worked for an empty QMimeData.
Twice(!) you wrote that, yes, that was the case.

So, why are you posting questions if you ignore any attempt in helping you?

Anyway, the problem is obviously not your subclass.

Cheers,
_

prasad_N
8th March 2016, 07:18
I asked you twice(!) if it worked for an empty QMimeData.
Twice(!) you wrote that, yes, that was the case.
So, why are you posting questions if you ignore any attempt in helping you?_

I think you misunderstood, It worked in my project where I have huge code & I can n't put all that code here so, I just created simplified version from simple tree model example just to check & I realized its not working in this simplified version.
When you stress on that particular statement I got you point & I double checked it in my project .


Anyway, the problem is obviously not your subclass._
then where is the problem, exactly this is what i am asking.. can't I pass customized mimedata OR is there a problem with my mimedata subclass OR do I need to re implement any other functions in subclass/model/view when I subclass mimedata ?

anda_skoa
8th March 2016, 10:16
I think you misunderstood, It worked in my project where I have huge code & I can n't put all that code here so, I just created simplified version from simple tree model example just to check & I realized its not working in this simplified version.

Well, yes, which is why I was specifically asking for whether two very concrete code options would show different behavior (comment #8) to which you replied


Yes, absolutely..




When you stress on that particular statement I got you point & I double checked it in my project .

Luckly for you I assumed you were really that lazy not to actually check it so I made you post the code you had.



then where is the problem, exactly this is what i am asking.. can't I pass customized mimedata OR is there a problem with my mimedata subclass OR do I need to re implement any other functions in subclass/model/view when I subclass mimedata ?

Well, since you can't use an empty QMimeData either, the problem is most likely the emptiness.
Which, if you think about it, is quite obvious. If there is nothing to drop, why call the drop method?

So you either put something into your mime data object or you use the conventional creation method and get the model through different means at the drop location.
E.g. through the QDropEvent::source().

Cheers,
_

prasad_N
8th March 2016, 17:03
Well, yes, which is why I was specifically asking for whether two very concrete code options would show different behavior (comment #8) to which you replied _
they were not actually when I tested in project & when I tested on my windows version with simplified example I could see its not working, that is the reason for my answer.


Luckly for you I assumed you were really that lazy not to actually check it so I made you post the code you had._
Not actually, But luckly you can assume what ever you want.


Well, since you can't use an empty QMimeData either, the problem is most likely the emptiness.
Which, if you think about it, is quite obvious. If there is nothing to drop, why call the drop method?
So you either put something into your mime data object or you use the conventional creation method and get the model through different means at the drop location.
E.g. through the QDropEvent::source()._
In this case, I hope below code should work but still I could see same result.


QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData* mime = new myMidedata();
mime->setText("Dummy Data");

return mime;
}

prasad_N
9th March 2016, 15:12
If somebody looking for the solution, Not for exactly what I have asked here but If you want to pass pointer or pointers to particular objects in dragdrop.

Model:

QMimeData* myModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData* l_mimedata = QAbstractItemModel::mimeData(indexes);

//send pointer to the view in mimedata
if(this->parent())
{
quintptr viewPtr = reinterpret_cast<quintptr>(this->parent()); // this->parent() is view in my case, but we can pass other pointers also

QByteArray encode;
QDataStream stram(&encode, QIODevice::WriteOnly);
stram << viewPtr;
l_mimedata->setData("application/view_pointer", encode);
}

return l_mimedata;
}


View:

void myView::dropEvent(QDropEvent *event)
{
const QMimeData* mimedata = event->mimeData();

//Decode view pointer
QByteArray decode = mimedata->data("application/view_pointer");
QDataStream stream(&decode, QIODevice::ReadOnly);
quintptr viewPtr = 0;
stream >> viewPtr;

//viewPtr will be 0, If no one sets while draging
if(viewPtr)
{
event->acceptProposedAction();

myView* view = reinterpret_cast<myView*>(viewPtr); // you have your pointer here
}
}


I think, this is kind of bruit-force way & not a safe way as we are using reinterpret_cast to get back the pointer. It is safe If we can get the pointer through customized QMimedata.

As anda_skoa suggested QDropEvent::source() is one more way but limited to particular pointer (source pointer only);

anda_skoa
9th March 2016, 16:32
I think, this is kind of bruit-force way & not a safe way as we are using reinterpret_cast to get back the pointer. It is safe If we can get the pointer through customized QMimedata.

You will of course still check QDropEvetn::source() to determine if this is an application internal drag as otherwise anyone can make that crashed your app or makes it cast a pointer to something that isn't there.



As anda_skoa suggested QDropEvent::source() is one more way but limited to particular pointer (source pointer only);
I would have assumed that, in the case of a drag from a QTreeView, that view is the source.
Is it something different?

Cheers,
_

prasad_N
10th March 2016, 06:48
I would have assumed that, in the case of a drag from a QTreeView, that view is the source.
Is it something different? _

Yes, initially when I started I only needed view, but then I came across a situation where I need some more handlers also. So I thought of some generic approach where I can have all the handlers as part of custom mime data (may use shared pointers just to avoid crashes if that pointers got deleted) & in dropevent get all those handlers with the help of this mime data. still could not do that because I am not able to pass custom mime data.

anda_skoa
10th March 2016, 14:06
If the view or the model has access to these pointers, then you can access them through the source/view.

If you really need your own mime data subclass, you'll have to fill it.
Either by copying the content of the mime data from QAbstractItemModel::mimeData() or by looking at that method and filling your mime data the same way.

Cheers,
_