PDA

View Full Version : Moving of QGraphicsItem



Erlendhg
2nd May 2007, 21:47
Hi.

I am making a timeline widget based on Qt's amazing Graphics View framework.
Now as you an see on the picture attached, I have three lines where I am putting QGraphicsItems.

Now, I have made them movable with the ItemIsMovable flag, but I only want each item to be movable on the X axis (from left to right). Currently, you can move them in every direction.

What is the best way to achive this?

Many thanks,
Erlend Graff

marcel
2nd May 2007, 21:52
The best way is to loose the ItemIsMovable flag and implement your own item moving functionality ( using the item mouse events in conjunction with scene coordinates ).

Then, all you have to do is restrict the Y position of the item and alow only X to be modified.

Regards

Bitto
3rd May 2007, 06:23
There's a better way. In your item, reimplement the itemChange() virtual function. Handle the ItemPositionChange case to fixate one of your axises. You can keep QGraphicsItem's default move implementation, and still only allow moving along one axis.



QVariant MyItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange)
return QPointF(pos().x(), value.toPointF().y());
return QGraphicsItem::ItemChange(change, value);
}


Neat? ;-)

marcel
3rd May 2007, 06:29
Neat? ;-)


Indeed it is:)

Erlendhg
3rd May 2007, 07:11
Wow.
That was a great solution. Got to keep so much as possible of Qt's fantastic native implementation.

Thanks :D

Erlendhg
7th May 2007, 16:22
Hi.
Now I am here again to annoy you with another question.

This is what I want to achieve:
When I move an item in my timeline widget, I want to have a tooltip showing the current position where the item starts, and where it stops.

This, I first wrote like this:


void eventItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseMoveEvent(event);
QToolTip::showText(event->screenPos(), QString("This item goes from position %1 to %2").arg(startOfItem).arg(startOfItem+lengthOfItem));
}

where startOfItem and lengthOfItem is variables in the items class, telling the start and length of the item. These are updated in itemChange(), therefore the call to QGraphicsItem::mouseMoveEvent(event); before the tooltip.

But earlier, when I moved my items around, the program could suddenly freeze, and nothing would then happen.

This, I guess, may have been a problem that I have fixed somehow, 'cause later on now, I can't re-create it. (I am still quite n00b in Qt :P)

But my real question is:
Is there a better way to acheive this?
If possible, I would like to see the tooltip less "flashy", if you understand what I mean.

Many thanks,
Erlend Graff

marcel
7th May 2007, 18:01
Why a tooltip? Why not use a "floating" widget that you custom-draw( you could make it look better than a tooltip - transparent, etc ).

To keep the widget glued to the graphics item you can call QWidget::move, setGeometry, etc. You have the mouse position, anyway.

It really should be easy - just fill it, draw some text and set transparency.

regards

Bitto
8th May 2007, 18:24
Have you seen this blog? http://labs.trolltech.com/blogs/2006/12/15/interact-with-volatile-graphics-items/

Like Marcel says, you probably want to do something like what this example shows. When you move the cursor over certain parts of the item, your "tooltips" could hover up just like the circular indicators show (in this example it's not even a separate item). If you want it to be resolution independent, you could also set QGraphicsItem::ItemIgnoresTransformations on the tooltip-ish item.

Erlendhg
11th May 2007, 23:42
Sorry, I didn't get that.
But I think I might have made me a bit unclear.
What I want, is that when I click and drag items in my timeline, a tooltip will always show the current start and stop position of the item, until the mouse button is released.

That is what my code does, except every time I move the mouse (while dragging an item), the tooltip is redrawn, and this makes it "flashy".

I want to make it as simple as possible, so I thought a tooltip would not be too hard, but it seems to be more difficult than expected.

Erlendhg
14th May 2007, 17:41
Hi again.
I am here with another problem.
I have stripped my timeline code, to show you the problem.

Attached is the code and a video (very crappy) trying to explain what happens.

When I add an item (click outside the QGraphicsView, and click "Q"), slides it to the beginning of the view, resizes it down quite a bit, and deletes it (with "W"), it doesn't disappear, but is resized up.

I can't understand why this happens, so I hope you guys can help me solve it.

(I'm pretty sure it must be something I have done wrong, because the process of the program also uses very much CPU!)

If it is relevant: I am using Qt 4.2.2

Many thanks,
Erlend Graff

spud
15th May 2007, 10:08
I can't reproduce your bug, it works fine for me. However, you have forgotten to delete the eventItems after you remove them. This might be the source of your problem.

Try


void EventView::removeItem(eventItem *item)
{
int index = getIndexFromItem(item);

eventScene->removeItem(eventList[index]);
delete eventList.takeAt(index);
}

Erlendhg
15th May 2007, 12:13
Hi.
Good spotted, but when I update the code to delete the item, and do the same thing as I did to "produce" the bug, I get this:

Unhandled exception at 0x6554f2b1 (QtGuid4.dll) in EventEditor.exe: 0xC0000005: Access violation reading location 0xfeeeff26.

And the program breaks here:


class QGraphicsSceneFindItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor
{
public:
QList<QGraphicsItem *> *foundItems;

void visit(QList<QGraphicsItem *> *items)
{
for (int i = 0; i < items->size(); ++i) {
QGraphicsItem *item = items->at(i);
if (!item->d_func()->itemDiscovered && item->isVisible()) { <--- Here it stops
item->d_func()->itemDiscovered = 1;
foundItems->prepend(item);
}
}
}
};

Weird that you couldn't reproduce the problem.
Did you add one more item, slide the first to the very beginning, and resize it down a lot, before you pressed "w"?

Are you using Qt 4.2.2 as well?
Could this be a bug in Qt?

spud
15th May 2007, 12:24
I'm using Qt 4.2.0 open source on windows. I did exactly what you did in the movie clip and it worked just like it should. Your exception confirms my belief that the item isn't removed properly. I can't tell you why, but you might simplify your code by removing the repeated calls to itemToIndex and indexToItem.
Deleting objects in an event handler can be hairy. Try letting your items be subclasses of QObject as well and call QObject::deleteLater(). That might prevent the crash, but doesn't answer why the items aren't removed as they should.
Good Luck!

Erlendhg
15th May 2007, 15:30
Can QGraphicsItems be subclassed by QObject?
I tried that earlier, but then my program wouldn't compile.
It didn't look like it fancyed QObject...

I have seen the problem of deleting an item in an event handler, but I thought that was only for the items itself's event handlers. These items are deleted in the mainwindow's event handler.

Thanks anyway,
Erlend Graff

Gopala Krishna
15th May 2007, 19:43
Can QGraphicsItems be subclassed by QObject?
I tried that earlier, but then my program wouldn't compile.
It didn't look like it fancyed QObject...

Yes it can be done using multiple inheritance as follows. I guess someone had already asked this before. Probably you can find more info by forum search :)
Anyway here is quick one


class Item : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
..

public slots:
..
};

Erlendhg
16th May 2007, 18:52
That doesn't seem to help either :(
Even when I subclass QObject and use QObject::deleteLater() instead of deleting the item, I get the same bug.

Erlendhg
27th June 2007, 13:19
Ok, finally I found the solution of my quite annoying problem.
It was me that hadn't read the documentation carefully enoughed, so I had missed that QGraphicsItem::prepareGeometryChange() had to be called whenever I resize my timeline items :P

After adding that, everything seems to work just fine :)
Just wanted to tell, in case someone experiences similar problems:
Remember to call QGraphicsItem::prepareGeometryChange() before you change the boundingRect() of a QGraphicsItem :D

Matthew.J
5th November 2013, 16:04
There's a better way. In your item, reimplement the itemChange() virtual function. Handle the ItemPositionChange case to fixate one of your axises. You can keep QGraphicsItem's default move implementation, and still only allow moving along one axis.



QVariant MyItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionChange)
return QPointF(pos().x(), value.toPointF().y());
return QGraphicsItem::ItemChange(change, value);
}


Neat? ;-)

Awesome!!!!