PDA

View Full Version : QGraphicsItem .. Scene .. View question?



DirtyBrush
17th July 2009, 11:32
Hello All,

Not getting much response in my other thread, simple question...

Is there any examples of QGraphicsView / Scene / Item with
drag and drop functionality?

I've read the help pages again and again but not getting my events
through to the item.

Go on, please help. I'll give you a gold star, and if I get it all working
a certificate too!

thanks,

DB

wagmare
17th July 2009, 11:40
see diagramscene example
http://doc.trolltech.com/4.3/graphicsview-diagramscene.html

for drag drop of items .

but for learning QGraphicsView and QGraphicsScene padnavigator is best example to follow .
/Trolltech/Qt-4.4.3/examples/graphicsview/padnavigator.

and also take a look at this doc
http://doc.trolltech.com/4.2/graphicsview.html

especially

http://doc.trolltech.com/4.2/graphicsview.html#drag-and-drop

DirtyBrush
17th July 2009, 12:51
Hi,

Thanks for this wagmare, your gold star is in the post.

I'll go over these with a fine tooth pick, however on the surface they
dont quite do what I have.

I've got an item, in a scene in a view. I drag something onto the item,
I can see the drag and then drop in the view. I see the drop in the scene,
but I dont see it in the item.

The item has accept drops as true. It's got the dragEnterEvent, dropEvent
and dragMoveEvent defined. But I dont see these events.

Any ideas? (worth another star)

I do have mousePressEvent and mouseReleaseEvent define, there isn't some
weird clashing going on here? like you only get one event, a release, or a drop
and not both?

thanks agian,

DB

wagmare
17th July 2009, 13:01
better to use mouseReleaseEvent() because
"As the view receives a drag, it translates the drag and drop events into a QGraphicsSceneDragDropEvent, which is then forwarded to the scene. The scene takes over scheduling of this event, and sends it to the first item under the mouse cursor that accepts drops."
from http://doc.trolltech.com/4.2/graphicsview.html#drag-and-drop


for items drop event
http://doc.trolltech.com/4.2/qgraphicsitem.html#setAcceptDrops

DirtyBrush
17th July 2009, 22:39
Hello again,

Thanks for this.


better to use mouseReleaseEvent() because
"As the view receives a drag, it translates the drag and drop events into a QGraphicsSceneDragDropEvent, which is then forwarded to the scene. The scene takes over scheduling of this event, and sends it to the first item under the mouse cursor that accepts drops."
from http://doc.trolltech.com/4.2/graphic...#drag-and-drop

Why do you say better to use this?

I want what it says in quotes, I want the first item under the mouse to receive the
drop event. But it doesn't happen. The item setAcceptDrops(true) is called in the
item, the scene dropEvent() can see that there is an item under the mouse with
the itemAt() call.

With all this the item still doesn't get the event.

I tried to use mouseReleaseEvent() but that is only called if the mouseEnterEvent()
is called in that item before hand. I.e. a drag from somewhere else into this item
does not result in a mouseReleaseEvent() when you release the drop. Makes sense
I guess.

But it's the dropEvent() in the item which I'm missing. I'm thinking it's the fault of
the QGraphicsScene not passing it on to the item. Tried setting focus of both the
scene and item before exiting out of the scene drop event, but nothing.


void myScene::dropEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "scene: dropEvent";

// various things to try to get the event to the item...no luck
//event->ignore();
//event->accept();
//event->acceptProposedAction();

this->setFocus(Qt::NoFocusReason);

QGraphicsItem *gi;

gi = this->itemAt( event->scenePos() );

if (gi)
{
qDebug() << "Scene(dropEvent); there is an item thar";
gi->setFocus(Qt::NoFocusReason);
gi->setSelected(true);
//gi->setAcceptDrops(true);
}

QGraphicsScene::dropEvent(event);
}


DB

wysota
17th July 2009, 22:50
What do you mean that the item "doesn't get the event"?

DirtyBrush
18th July 2009, 11:21
In the scene it gets the event, and with the qDebug() line I see on the console output


void myScene::dropEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "scene: dropEvent";

'scene: drop Event'

I have a similar debug in the item dropEvent() method.


void MyNode::dropEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "Node: dropEvent";
QGraphicsItem::dropEvent(event);
}

But I do not see the 'Node: dropEvent' debug, so the QGraphicsItem doesn't get the event.

wysota
18th July 2009, 11:59
How is the shape() of the item defined? Is there a chance some other item gets the event? What does this return when ran as part of the scene's dropEvent?

qDebug() << items(event->scenePos()).count();

DirtyBrush
18th July 2009, 21:30
Hi,

Thanks for the reply.

It comes back with '1', which is what I'd expect, only got one item there.

--

Have found an interesting thing though...

In the view, I have dragMoveEvent of :-


void myView::dragMoveEvent(QDragMoveEvent *event) {
}

With this the dropEvent gets to the scene. However, if I add


QGraphicsView::dragMoveEvent(event);

to this method the dropEvent doesn't.

Also, if I dont have this function defined, it also doesn't get to the
scene.

Is this this the correct behaviour?

Usually when I redefine a method I call the base class method at
the end like 'Base:SomeMethod();'.

Thanks again, I appreciate your help,

DB

wysota
19th July 2009, 22:04
If you don't call the base class implementation, the scene shouldn't receive the event.

DirtyBrush
20th July 2009, 09:07
Hello Again!


If you don't call the base class implementation, the scene shouldn't receive the event.


OK, so there's the problem? I see the exact opposite of this.

Shall we have a quick overview of my code -

View header file


class myView : public QGraphicsView
{
public:
myView(QWidget *parent=0);
protected:

void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
void dragMoveEvent(QDragMoveEvent *event);

};

view methods


myView::myView(QWidget *parent) : QGraphicsView(parent) {
// setAcceptDrops(true);
// Comment the line below, and drop starts working
setScene(new QGraphicsScene(this));
}

void myView::dragEnterEvent(QDragEnterEvent *event) {
// event->accept();
qDebug() << "View: dragEnterEvent";
QGraphicsView::dragEnterEvent(event);
}

void myView::dropEvent(QDropEvent *event) {
QPointF pnt;
QPoint pos;
QGraphicsItem * itm;

qDebug() << "View sees drop";

pos = event->pos();
pnt = this->mapToScene( event->pos() );

if (itm = itemAt( pos ) )
{
qDebug("There is an item here");
QGraphicsView::dropEvent(event);
return;
}
else
{
qDebug("Blank area");
}
// Is there something underneath?

QByteArray ba = event->mimeData()->data("foo/bar");
QDataStream ds(&ba, QIODevice::ReadOnly);
while (!ds.atEnd()) {
QString name;
QString path;
QString monid;
MyNode * nd;
ds >> name >> path >> monid;
qDebug() << name << path << monid;

//
// This view is inside the central widget which is in the mainwindow, so node
// parent is the mainwindow so it can access the action functions. a bit
// untiy huh?
//
if (QString("Button")==name)
nd = new MyNode( (QWidget*)(this->parent())->parent(), name, path, monid, MyNode::BUTTON);
else
nd = new MyNode( (QWidget*)(this->parent())->parent(), name, path, monid, MyNode::POINT);
QGraphicsScene * sc;
sc = this->scene();

pnt = this->mapToScene( event->pos() );
qDebug() << pnt;
nd->setPos(pnt);
sc->addItem(nd);
}
}

void myView::dragMoveEvent(QDragMoveEvent *event) {
}



Scene header


class myScene : public QGraphicsScene
{
public:
myScene(QObject *parent);
void dropEvent(QGraphicsSceneDragDropEvent * event);
void dragEnterEvent(QGraphicsSceneDragDropEvent * event);
};

scene code



myScene::myScene(QObject*parent): QGraphicsScene(parent)
{
}

void myScene::dropEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "scene: dropEvent";
qDebug() << items(event->scenePos()).count();

this->setFocus(Qt::NoFocusReason);

QGraphicsItem *gi;

gi = this->itemAt( event->scenePos() );

if (gi)
{
qDebug() << "Scene(dropEvent); there is an item thar";
gi->setFocus(Qt::NoFocusReason);
gi->setSelected(true);
}

QGraphicsScene::dropEvent(event);
}

void myScene::dragEnterEvent(QGraphicsSceneDragDropEven t * event)
{
event->acceptProposedAction();
QGraphicsItem *gi;

gi = this->itemAt( event->scenePos() );

if (gi)
{
qDebug() << "Scene(dragEnterEvent); there is an item thar";
gi->setFocus(Qt::NoFocusReason);
gi->setSelected(true);
}

qDebug() << "scene: dragEnterEvent";
QGraphicsScene::dragEnterEvent(event);
}


And finally item header


class MyNode : public QGraphicsItem
{
public:
int MonId;
int ValInt;
int DataType;
QString endnode;
QString path;
QString monid;

enum MimicType { LAMP, BUTTON, POINT };
MimicType mimic;
int EditMode;


MyNode(QWidget * parent, QString endnode, QString path, QString monid, MimicType mt);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem * option, QWidget *widget);
void mousePressEvent(QGraphicsSceneMouseEvent * event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent * event);
void dropEvent(QGraphicsSceneDragDropEvent * event);
void dragEnterEvent(QGraphicsSceneDragDropEvent * event);
void dragMoveEvent(QGraphicsSceneDragDropEvent * event);
void myupdate();
void createActions();
QAction * myAct;
void passDrop(QDropEvent * event);

private slots:
void doMyAct();
QWidget * parent;
};

and the item drag methods...


void MyNode::dragEnterEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "Node: dragEnterEvent";
//event->setAccepted(true);
//event->accept();
event->acceptProposedAction();
QGraphicsItem::dragEnterEvent(event);
}
void MyNode::dragMoveEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "Node: dragMoveEvent";
event->accept();
QGraphicsItem::dragMoveEvent(event);
}
void MyNode::dropEvent(QGraphicsSceneDragDropEvent * event)
{
qDebug() << "Node: dropEvent";
QGraphicsItem::dropEvent(event);
}

wysota
20th July 2009, 09:56
What is exactly the point of intercepting those events in both the view and the scene? Do you have more than one view on the same scene and want them to behave differently?

DirtyBrush
20th July 2009, 10:55
Hi again,

I'll want to capture events in the view to resize the view, zoom, pan, etc.

In the scene I want to have items, which will allow drops and also
have context menu(edit mode). Then in (run mode) the items will have
mouse click events which result in some internal processing.

(These drops configure the items with various fields in a database,
then these items change colour or text when the values in the
database change)

Eventually, more complex behaviour, linking from one item to another,
moving items around, etc.

thanks again,

DB

wysota
20th July 2009, 11:54
But I'm asking about drop events specifically. I don't see the point in intercepting them in both the view and the scene unless you have more than one view on the same scene. In general in 90% of the cases it is enough to intercept any events just in the scene and items themselves.

DirtyBrush
20th July 2009, 12:54
OK, I'll try without catching these events in the view. So now I've removed
myview and am back to using the base QGraphicsView with view->setAcceptDrops(true);

However, when I drag into the view/scene pane I get the 'no entry' cursor
and cannot drop into the view/scene.

Any ideas?

DB

wysota
20th July 2009, 13:24
You need to reimplement dragEnterEvent on the scene and possibly dragMoveEvent as well. The same needs to be done for item classes you want to accept drops.

DirtyBrush
20th July 2009, 14:45
Hi,

Yes, I have dragMoveEvent, dragEnterEvent and dropEvent defined in
the scene.

I also have setAcceptDrops(true) for the scene.

However, it still has the 'no entry' cursor when trying a drag-drop into
this view/scene.

DB

wysota
20th July 2009, 14:51
Well, being defined and being defined properly are two different things. For instance if you call the base class implementation of dragEnterEvent() it will probably reject the event despite that you accepted it in your own code.

DirtyBrush
20th July 2009, 16:02
Well, being defined and being defined properly are two different things. For instance if you call the base class implementation of dragEnterEvent() it will probably reject the event despite that you accepted it in your own code.

Oooh, now that's a nice cryptic response. :-)

So I removed the 'QGraphicsScene::dragMoveEvent(event)' from my dragMoveEvent method and I can see the drop in the scene.

However, when there is an item underneath the cursor I still dont see the event in
my item dropEvent()?

Is there documentation which details when to call, and when not to call the base methods of the QGraphicsScene/QGraphicsItem?

wysota
20th July 2009, 16:14
However, when there is an item underneath the cursor I still dont see the event in
my item dropEvent()?
Are you calling the base class implementation of the item's dragEnterEvent() and/or dragMoveEvent()?


Is there documentation which details when to call, and when not to call the base methods of the QGraphicsScene/QGraphicsItem?

There is a simple rule of a thumb - if you want to replace what the base class does with your own logic, don't call the base class implementation. If you want to extend what the base class does, call the base class implementation. You can't accept and ignore the event at the same time so calling the base class implementation in case of handling drops makes sense only if you want to say "I don't want to make a decision about the drag, I'm leaving it up to Qt".

DirtyBrush
20th July 2009, 16:26
I think I've finally got it. For dragging and dropping....

Case 1 No Item underneath.

Scene dragMoveEvent does nothing
Scene dragEnterEvent accept event, calls base method
Scene dropEvent (adds a new Item to the scene), calls base method

Case 2 Item underneath mouse

Scene dragMoveEvent, sees there is an item and does ignore() and base method
Scene dragEnterEvent, sees there is an item and does ignore() and base method
Scene dropEvent, sees there is an item and does ignore() and base method

Item dragMoveEvent, calls accept()
Item dragEnterEvent, calls accept and base method
Item dropEvent, calls base method.

I now see the event in the item! wow! amazing! yay!

Thank you for your help, now I can get on with some code logic!

DB

xxxxxx
19th October 2010, 10:12
Hi,
Can you please help me with dropEvent for Scene?
I cannot catch it...

how can you setAcceptDrops for Scene?
do I have to reimplement events for View also?