PDA

View Full Version : Drag and Drop, dropEvent not being called?



steg90
21st May 2007, 09:33
Hi,

I have a tree widget which allows items to be dragged. I have a derived tableview which accepts drops, I have reimplemented the dragEnterEvent and dropEvent in here, the dragEnterEvent gets called but the dropEvent doesn't. At the moment I am just accepting any types of drops.

Code in the tableview :



void DATableView::dropEvent(QDropEvent *e)
{
QString strText = e->mimeData()->text();
e->acceptProposedAction();
}

void DATableView::dragEnterEvent(QDragEnterEvent* e)
{
e->accept();
}


Any idea what I'm missing?

Regards,
Steve

marcel
21st May 2007, 09:37
You have to handle this in the model, with dropMimeData.

Regards

steg90
21st May 2007, 09:37
Hi,

Got this working now, I needed to also reimplement the dragMoveEvent. Just now need to figure out how to put the dragged item into my table model...

Regards,
Steve

steg90
21st May 2007, 09:43
Thanks,

My tree consists of objects of type CSignal ( I use set data on the items for this ). When you drag an item from the tree ( CSignal ) I want to get this data as type CSignal, how do I do this? Is this a custom mime type?

Regards,
Steve

wysota
21st May 2007, 10:11
Might I ask why do you reimplement methods from the view to handle this?

steg90
21st May 2007, 10:13
I have done this as I've been following an example which does this? Is there a better way?!

I'm now trying to subclass QMimeData in order to have my own custom data.

Regards,
Steve

wysota
21st May 2007, 10:17
Which example does that? I'm pretty sure all drag and drop examples that concern views reimplement mimeTypes(), mimeData() and dropMimeData() from the model.

steg90
21st May 2007, 10:21
Hi,

Well the example ( C++ GUI Programming with Qt4) doesn't use a model so I guess this is why it handles the events in the view/widget. I have just implemented the dropMimeData method in my model but it doesn't get called, I guess this is because I have the events implemented in my view?!

Kind regards,
Steve

wysota
21st May 2007, 10:26
Well the example ( C++ GUI Programming with Qt4) doesn't use a model so I guess this is why it handles the events in the view/widget.
Yes, it doesn't have the required infrastructure which is already present in views.

I have just implemented the dropMimeData method in my model but it doesn't get called, I guess this is because I have the events implemented in my view?!

Correct. You only need to reimplement the three methods mentioned, even if you want to carry custom data.

steg90
21st May 2007, 10:28
Thanks,

I have removed the drag/drop events from my view and implemented the three methods you mentioned into my model, but they don't get called? Do you have to set up the model to accept drops or something?

Regards,
Steve

steg90
21st May 2007, 10:31
got it - setSupportedDragActions !

mimeTypes gets called but dropMimeData and mimeData are not...

jpn
21st May 2007, 10:48
Do you return drag and drop flags in flags() (http://doc.trolltech.com/4.2/qabstractitemmodel.html#flags)?

steg90
21st May 2007, 10:53
Hi JPN,

I have the following flags function in my model code :



Qt::ItemFlags DACanTreeModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}


Still can't get the dropMimeData function to be called...

steg90
21st May 2007, 11:01
Also, the flags for the item in the treewidget I'm dragging are set to ItemIsDragEnabled. Can't understand why dropMimeData isn't being called...

Regards,
Steve

jpn
21st May 2007, 11:03
Presumably you are already following the instructions in Using Drag and Drop with Item Views (http://doc.trolltech.com/4.2/model-view-dnd.html#using-model-view-classes), because your flags() looks so similar. But does your view still have overridden drag/dragmove/drop event handlers? If so, I'd suggest commenting them out. For example accepting the event in dropEvent() and then calling the base class implementation makes it never drop..

wysota
21st May 2007, 11:04
Also if you drag between two widgets, make sure they understand each other's mime-types.

steg90
21st May 2007, 11:07
Hi,

Yes, following that example, I had removed the events from my view. Still not being called, got to be something simple?!

This is the code I have in my treewidget ( items in here I want to be draggable ) :



void DASignalTree::mousePressEvent ( QMouseEvent * event )
{
if( event->button() == Qt::LeftButton )
{
m_startPos = event->pos();
}
QTreeWidget::mousePressEvent( event );
}

void DASignalTree::mouseMoveEvent( QMouseEvent * event )
{
if( event->buttons() & Qt::LeftButton )
{
int distance = ( event->pos() - m_startPos).manhattanLength();
if( distance >= QApplication::startDragDistance() )
{
QTreeWidgetItem *item = itemAt( m_startPos );
if ( item )
{
startDrag();
}
}
}
// QTreeWidget::mouseMoveEvent( event );
}

void DASignalTree::startDrag()
{
QTreeWidgetItem* dragItem = currentItem();
DAMimeData* pMimeData = new DAMimeData;
pMimeData->setText( dragItem->text( 0 ) );
QDrag* pDrag = new QDrag( this );
pDrag->setMimeData( pMimeData );
pDrag->setPixmap( QPixmap("images/new.png") );

theApp->AddText( dragItem->text( 0 ) );

if( pDrag->start(Qt::MoveAction) == Qt::MoveAction)
delete dragItem;
}


and the code in my model :



bool DACanTreeModel::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
{
return true;
}

QStringList DACanTreeModel::mimeTypes () const
{
return QStringList();
}

QMimeData * DACanTreeModel::mimeData ( const QModelIndexList & indexes ) const
{
return new QMimeData();
}


Qt::DropActions DACanTreeModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}

Qt::ItemFlags DACanTreeModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}


And finally the view constructor :



DATableView::DATableView(QWidget *parent)
: QTableView(parent)
{
setAcceptDrops(true);
setDropIndicatorShown(true);
}


I'm at a loss?! :confused:

Regards,
Steve

wysota
21st May 2007, 11:28
The tree widget also has the before mentioned methods which you should reimplement.

steg90
21st May 2007, 11:37
Thanks,

So I need to also implement dropMimeData, mimeData and mimeTypes in my tree widget? I guess I can ignore the dropMimeData as my tree won't allow any drop events?

Regards,
Steve

steg90
21st May 2007, 11:51
Implemented the methods mimeData, dropmimeData and mimeTypes in the tree widget, but still no dropMimeData being called...

When I was doing the drag/drop in the view, it seemed a lot easier and seemed to work, just can't understand what is wrong.

Regards,
Steve

wysota
21st May 2007, 11:58
Here is a working example.

steg90
21st May 2007, 11:59
Many thanks, that is very kind of you.;)

steg90
21st May 2007, 12:22
Many thanks for the example, I think way back in the thread or a related one you did mention that you should only implement the dropMimeData, mimeData and mimeTypes functions, I should have listened!!! I had implemented a few mouse event functions and a few drag events in my tree widget which I shouldn't have done, this was stopping it working.

Is it possible to pass over the object associated with the item being dragged from the tree widget?

Kind regards,
Steve

wysota
21st May 2007, 12:49
Many thanks for the example, I think way back in the thread or a related one you did mention that you should only implement the dropMimeData, mimeData and mimeTypes functions, I should have listened!!! I had implemented a few mouse event functions and a few drag events in my tree widget which I shouldn't have done, this was stopping it working.
I think we told you a few times to remove the mouse event handlers :)


Is it possible to pass over the object associated with the item being dragged from the tree widget?
In general, yes. You can pass a pointer or something like that, you can subclass QMimeData if you want. But the proper way to go would be to serialize your object to QByteArray, transfer that array using QMimeData::setData and reconstruct the object on the other side.

steg90
21st May 2007, 13:01
Hi again,

This is my mimeData function within the treewidget :



QMimeData * DASignalTree::mimeData ( const QList<QTreeWidgetItem *> items ) const
{
if(items.isEmpty()) return 0;
QTreeWidgetItem* parent = items[0]->parent();
CDADcb::CSignal* pSignal = items[0]->data( 0, Qt::UserRole ).value<CDADcb::CSignal*>();
CDADcb::CMessage* pMess = parent->data( 0, Qt::UserRole ).value<CDADcb::CMessage*>();
theApp->m_dcb.m_oSignalMap.insert( pMess->m_strId, (QObject*)pSignal );

QMimeData *md = new QMimeData;
md->setData("application/x-example", items[0]->data(0, Qt::DisplayRole).toByteArray());
return md;
}


The maps are what the model uses for its data. Problem with the above is, the model code now crashes in the data method ( the model data is updated in a thread every 5 milliseconds ).

Your solution sounds better, how would I serialize an object of the following and I'm presuming it would be reconstructuted in the models dropMimedata method?

Many thanks for your invaluable help!
Steve

wysota
21st May 2007, 13:11
The easiest way is to register your types within QVariant using Q_DECLARE_METATYPE and qRegisterMetaTypeStreamOperators which in theory should allow you to just redirect your map to a byte array through QDataStream.

I certainly wouldn't use pointers if your data changes rapidly. Your code seems a bit complicated and I'm wondering why you even reveal the pointers to the outside world in your model... Don't these "signals" have some kind of textual representation you could use?

steg90
21st May 2007, 15:48
Hi,

I did do the register of my objects with Q_DECLARE_METATYPE as they are used in other places. The model uses a hash map for its 'internal' structure which gets updated on the fly. I have the following code now which works fine for the drag and drop operation :



QMimeData * DASignalTree::mimeData ( const QList<QTreeWidgetItem *> items ) const
{
if(items.isEmpty()) return 0;

QTreeWidgetItem* parent = items[0]->parent(); // message

if( parent )
{
QString strText = parent->text(0);

CDADcb::CSignal* pSigs = items[0]->data( 0, Qt::UserRole ).value<CDADcb::CSignal*>();
CDADcb::CMessage* pMess = parent->data( 0, Qt::UserRole ).value<CDADcb::CMessage*>();

if( pSigs ) // make sure we haven't selected message node
{
for( int nCount = 0; nCount < pMess->m_oSignals.count(); nCount++ )
{
CDADcb::CSignal* pSignal = new CDADcb::CSignal();
pSignal->m_strId = pMess->m_strId;
pSignal->m_strRawData = pMess->m_strRawData;
CDADcb::ASignal* pSig = (CDADcb::ASignal*)pMess->m_oSignals.at(nCount);
if( strcmp( pSigs->name, pSig->name ) == 0 )
{
strcpy_s( pSignal->name, pSig->name );
pSignal->m_nCount = 0;
strcpy_s( pSignal->unit, pSig->unit );
strcpy_s( pSignal->canname, pMess->name );
theApp->m_dcb.GetSignalList()->insert( 0, (QObject*)pSignal );

// we use this for fast look ups later in the model
theApp->m_dcb.m_oSignalMap.insert( pMess->m_strId, (QObject*)pSignal );
break;
}
}
}
}



The only problem I notice is that when I drag the item from the tree widget to the table view, the item is added before I've done a drop, but I guess this is because that is what I'm doing with the function above, if only I could do this on the models dropMimeData function...

Regards,
Steve

wysota
21st May 2007, 15:55
The code is indeed strange. Could you show your dropMimeData() method of the model?

steg90
21st May 2007, 16:02
Hi,

The code for my dropMimeData within my model doesn't actually do anything, this is where I would like to do the before mentioned function so the item is added on a drop event, but I have no way of getting the message/signal object from within that function, or is there away???

Thanks,
Steve

wysota
21st May 2007, 16:14
As I mentioned before, it'd be best if Signal was serializable through QDataStream. Then you could just transfer it through the existing capabilities of QMimeData. Alternatively you can do this:

class SignalMimeData : public QMimeData {
//...
setPointer(Signal *s){ ... }
signal() const { ... }
};
And in dropMimeData cast to SignalMimeData and retrieve the pointer.

steg90
21st May 2007, 16:22
Thanks for that,

So, within my tree widget mimeData, I can just get CSignal and just set the pointer to it in the derived QMimeData class? Would mimeData return SignalMimeData, I'm just confused as to how I set this up?

Kind regards,
Steve

wysota
21st May 2007, 16:28
You can, but I wouldn't do that unless you're sure the pointer will be valid after some time (after the model gets updated a few times).

Yes, mimeData should return the subclass instance then.

steg90
21st May 2007, 16:43
Hi,

I guess I also will have to return my derived mimeData from the mimeData functions?

For some reason, my dropMimeData doesn't get called now?

Regards,
Steve

wysota
21st May 2007, 16:46
Let's make it simple - everywhere where it says "QMimeData", use the subclass. Just don't change the function signatures, QMimeData has to remain there.

steg90
21st May 2007, 16:55
Ok, what I did was to change ALL the QMimeData within my functions to my own type and left the function signatures as QMimeData, still doesn't get called?

wysota
21st May 2007, 16:59
Could you show me the whole model class?

steg90
22nd May 2007, 08:03
Hi,

I have now got this working thanks, it was the fact that I had overridden the retrieveData method of QMimeData in my subclassed version of this...I'm learning every day :D

Many, many thanks for all your invaluable help, it is much appreciated.

Regards,
Steve