Restrict movement area of QGraphicsItem inside of polygon area - c++

I am trying to limit the movement of QGraphicsItem inside of a parent object inherited from QGraphicsPathItem that has an arbitrary complex shape (not square).
As I understand, I should extract each point of the moving object and check whether each of them is not contained in the parent QPainterPath. Then split the parent QPainterPath into small rectangle-shaped polygons and restrict movement area in the bounding box of each sub polygon.
So, I would like to know if there are any other options to exist. Thanks.

Maybe you can check on QGraphicsItem move event if it is still inside QGraphicsPathItem.
The check itself, you can do it with this QGraphicsItem method:
bool QGraphicsItem::collidesWithItem(const QGraphicsItem * other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const
And to be sure that item is still fully enclosed inside its parent, set the mode to Qt::ContainsItemShape
If it is false, return QGraphicsItem to its previous position
EDIT: this check returns if QGraphicsItems shape is inside other QGraphicsItems shape, not its bounding box.

Related

Ignore transformation of QGraphicsItem's shape

My reimplemented QGraphicsItem draws a circle. It has flag ItemIgnoresTransformations, so it has always the same size when scaling. Should I set transformations manually for item's shape function to be accurate or there exists easy way? I need shape function for processing mouse events for this circle in QGraphicsScene.

Qt: Custom QGraphicsItem not showing when boundingRect() center is out of view

I'm making a Diagram (Fluxogram) program and for days I'm stuck with this issue:
I have a custom QGraphicsScene that expands horizontally whenever I place an item to it's rightmost area. The problem is that my custom arrows (they inherit QGraphicsPathItem) disappear from the scene whenever it's boundingRect() center is scrolled off the view. Everytime the scene expands, both it's sceneRect() and the view's sceneRect() are updated as well.
I've:
set ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate)
the item flags QGraphicsItem::ItemIgnoresTransformations and QGraphicsItem::ItemSendsGeometryChanges, setActive(true) on the item as well, and everytime I add an arrow to the scene i call the update(sceneRect()) method. Still, everytime I scroll the view, as soon as the arrow's boundingRect() center moves away from the view, all the arrow disappears. If I scroll back and the boundingRect() center enters the view, all the arrow appears again.
Can someone give me a tip of what I might be missing? I've been using Qt's example project diagramscene as reference, so a lot of my code is similar (the "press item toolButton -> click on the scene" relation to insert items, the way they place the arrows to connect the objects,...).
In the meanwhile I'll try to make a minimal running example that can show what my issue is.
Your Arrow object inherits from QGraphicsPathItem, which I expect also implements the QGraphicsItem::shape function.
Override the shape function in your Arrow class, to return the shape of the item. This, along with the boundingRect is used to collision detection and detection of an item on-screen.
In addition, before changing the shape of an item by changing its boundingRect, you need to call prepareGeometryChange.
As the docs state: -
Prepares the item for a geometry change. Call this function before changing the bounding rect of an item to keep QGraphicsScene's index up to date.
So, in the Arrow class, store a QRectF called m_boundingRect and in the constructor: -
prepareGeometryChange();
m_boundingRect = QRectF(-x, -y, x*2, y*2);
Then return m_boundingRect in the boundingRect() function.
If this is still an issue, I expect it's something in QGraphicsPainterPath that's causing the problem, in which case, you can simply inherit from QGraphicsItem and store a QPainterPath with which you draw in the item's paint function and also return the painter path in shape().
You are making your life too complicated. Do not subclass QGraphicsPathItem just use it and update its path value every time position of anchors (from to) changes.

qt create simple rectangular board

I want to create a rectangular board using QT. This board will be updated when a step is performed. For example on step x, the text in cell 5,6 updates from "not explored" to "explored".
I have looked through the QT documentation and found the class QGraphicsView. How can i use QGraphicsItem to simulate a cell where text can be written?
I am also open for alternatives.
Technically this could also be done by customizing a QTableView/Widget, but QGraphicsScene is more robust for custom graphics and performance.
From a high level view, you can either create a composite object representing a "Cell" item, or you can subclass a QGraphicsItem and do all the custom painting yourself.
When creating a composite object, that would just be a QGraphicsItem "Cell" subclass which contains maybe a QGraphicsRectItem and a QGraphicsTextItem as members, set to the cell instance as a parent. This will keep the child items translating with the parent cell item.
When creating a completely custom QGraphicsItem, you would define all the painting inside of the paint() method, which would draw a rectangle, and text taken from a value set on the instance.

Should QGraphicsItem::boundingRect() include child bounding rects?

Googling suggests that it should.
But the dragdroprobot example implementation (on the parent Robot object) suggests not:
QRectF Robot::boundingRect() const
{
return QRectF();
}
Which is correct, or is there something more subtle going on?
Child items are painted directly by the scene not by the parent, and according to the documentation about boundingRect():
QGraphicsView uses this to determine whether the item requires redrawing.
So, if there is no drawing to do in the parent, there is no need to return a non-null bounding rectangle, even if the parent has child items.
And if there is some drawing in the parent, it only needs to contain its own bounding rectangle.
Under normal usage the children of your QGraphicsItem are contained within its bounding rect, but depending on your implementation I don't believe that this is required.
If you need the bounding rect of an item's children you can simply use
QGraphicsItem::childrenBoundingRect();
Possibly related:
QGraphicItemGroup is different.
The documentation says:
The boundingRect() function of QGraphicsItemGroup returns the bounding rectangle of all items in the item group.
(However, the documentation does not say that boundingRect is reimplemented, although QGraphicsItemGroup inherits QGraphicsItem. Thats probably a flaw in the documentation.)
QGraphicItem.shape() seems to be similar to boundingRect() in that the view calls it for each instance (for purposes of picking i.e. QGraphicsView.items(), similarly as boundingRect() is called for purposes of determining what needs to be redrawn).
As far as I can tell, QGraphicItemGroup.shape() is never called by QGraphicsView.items(). The documentation does not say that it is reimplemented.

Overlapping labels in Qt fridge magnets example

I want to modify the fridge magnets example provided with Qt in a way that when I drag a label and drop it over another, it will push the label beneath the dragged label to the side, so they will never overlap one another.
I've seen how collision is detected in the colliding mice example, where it uses a QGraphicsScene to draw the QGraphicsItem mice on, and scene()->collidingItems(this) to see which mice are colliding.
The problem is that the fridge magnets example uses a class that inherits QWidget in place of QGraphicsScene, so there's no collidingItems() method to check when we have a collision.
How do I go about doing that?
You can get the location and size of each QWidget from geometry(), which returns a QRect. QRect has function intersects(), which will tell you if it intersects another QRect. After the drop is complete, iterate through all of the labels and check if any of them do intersect the new position.
(This will be easier if you modify dragwidget to keep a QList<DragLabel*> of each label on the dragwidget.)
QRect droppedRect = newLabel->geometry();
foreach(DragLabel* label, dragLabelList)
{
if (droppedRect.intersects(label->geometry())
{
// Add to the list of covered labels that need to be moved.
}
}
The harder part: If there is an intersection, move the old label out of the way.
Maybe try the following algorithm: Move the offending label out of the way in the direction that takes the least movement. Now check it against all the other labels. Any of those that are covered should be moved in the same direction. Repeat until all the labels are uncovered.