QGraphicsItem setPos() not triggered - c++

And again, trying to implement bezier curves redactor. There is
class BezierNode : public QGraphicsItem
BezierNode::BezierNode(QPointF point, Type type) : QGraphicsItem()
{
setPos(point);
setFlags(ItemIsMovable | ItemSendsScenePositionChanges | ItemSendsGeometryChanges);
}
It properly moves around in scene on mousePress + mouseMove, and I can catch events in itemChange() for some additional acting with control points. In QGraphicsItem::mouseMoveEvent() (according to Qt source on gitorious) there is a call to item->setPos(...). However, if I try to reimplement BezierNode::setPos(..), it's never triggered on moving object.
void BezierNode::setPos(const QPointF &pos) {
qDebug() << "setPos " << pos;
m_point = pos;
QGraphicsItem::setPos(pos);
}
In my case setPos() triggers only in constructor (there I call it manually). Yes, it moves in scene properly, I can get its position with pos() and use it everywhere instead of m_point, but I want to understand, what happens there.
Thanks in advance.

QGraphicsItem::setPos() is not virtual, so you can't override it. That's why BezierNode::setPos() will never be called.

Related

Is there any better way to move a window?

I am working on an application with Qt Framework for desktop. Since I remove every window decoration I had to implement the main window to receive move event from when the user click on it and move the mouse around.
I tried the following code but I am not satisfied. I wonder if there is any better way to this with more elegance.
QPoint* mouseOffset; //global variable that hold distance of the cursor from
the top left corner of the window.
void ArianaApplication::mouseMoveEvent(QMouseEvent* event)
{
move(event->screenPos().x() - mouseOffset->x(),
event->screenPos().y() - mouseOffset->y());
}
void ArianaApplication::mousePressEvent(QMouseEvent*)
{
mouseOffset = new QPoint(QCursor::pos().x() - pos().x(),
QCursor::pos().y() - pos().y());
}
Would you suggest me something else?
The method is correct, but the implementation can be improved in the following points:
mouseOffset is not necessary to be a pointer, since you are creating dynamic memory unnecessarily and you have the responsibility to eliminate it.
It is not necessary to obtain each component, QPoint supports subtraction.
*.h
QPoint mouseOffset;
*.cpp
void ArianaApplication::mouseMoveEvent(QMouseEvent * event)
{
move(event->globalPos() - mouseOffset);
}
void ArianaApplication::mousePressEvent(QMouseEvent * event)
{
mouseOffset = event->globalPos() - frameGeometry().topLeft();
}

Qt moving a QWidget without triggering the moveEvent method?

I am trying to set a widget to remain fixed at the location where it is created initially and never move. I am doing so by setting its position to its old position in an overridden moveEvent method. The problem is that the only way to move it is to...call move, which triggers another moveEvent, entering an infinite loop. Is there a way to set the position directly and bypass any events?
bool m_firstTimeMove; // class member for QMyWidget
QMyWidget::QMyWidget(QObject* parent) : QWidget(parent),
m_firstTimeMove(true)
{
}
void QMyWidget::moveEvent(QMoveEvent* event)
{
if (m_firstTimeMove)
{
onlyForFirstTimeMove(); // do one time move action
m_firstTimeMove = false;
}
event->accept();
}

Qt mouseReleaseEvent() not trigggered?

I got a library to display pictures, lets call it PictureGLWidget, with:
class PictureGLWidget: public QGLWidget {
so PictureGLWidget extends QGLWidget. In PictureGlWidget the
void PictureGlWidget::mouseReleaseEvent(QMouseEvent* releaseEvent);
is already implemented.
I started an own project, lets say class MyMainWindow, where I just use a PictureGlWidget as a Pointerobject:
PictureGlWidget * myPictureGLWidget = new PictureGlWidget(...);
//..
layout->addWidget(myPictureGLWidget , 0, 1);
Here at this point, I already can see the PictureGlWidget and the corresponding picture in my MainwindowWidget. When I click in that PictureGlWidget, hold the mouse, I can move the picture (like 2D-scrolling), since it is much bigge than my little MainWindow.
Further on PictureGlWidget provides a function
bool PictureGlWidget::getPictureLocation(double& xPos, double& yPos);
which just tells me the Pictures center position, where I released the current clipping of the picture. Remeber my picture is much bigger than my little MainWindowWidget and thus much much more bigger than my PictureGLWidget. Imagine the picture has 4000x4000px (0,0 upper left). The PictureGLWidget is only to display lets say 800x800px. So the getPictureLocation() sets the center cooridinates of the current displayed picture part and it would return something like (400, 400), which might be somewhere in the midldle upper left corner.
I would like to grab the current displayed pictureparts (just a little part of that big picture) center position, after scrolling in that Widget and I released the mouse. I thought I do that by overwriting the
MyMainWindow::mouseReleaseEvent(QMouseEvent *event){ qDebug() << "Mouse released!"; }
method, but did not connected it anywhere yet. Currently it is not reacting on my mouseReleases and that text is not displayed.
The virtual protected methods in QWidget that you can override to react on some events don't need to be "connected". These are not Qt slots but classical functions Qt automatically calls when necessary.
As explained in Qt Event system doc, if the implementation PictureGlWidget::mouseReleaseEvent(QMouseEvent*) accept the event, it is not propagated to the parent widget. But you can install an event filter to your PictureGLWidget and receive events before they are sent to it.
PictureGlWidget * myPictureGLWidget = new PictureGlWidget(...);
layout->addWidget(myPictureGLWidget , 0, 1);
myPictureGLWidget->installEventFilter(this);
Then implements the right method in your main window:
bool MyMainWindow::eventFilter(QObject *object, QEvent *event)
{
if (object == myPictureGLWidget && event->type() == QEvent::MouseButtonRelease) {
QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event);
// Do what you need here
}
// The event will be correctly sent to the widget
return false;
// If you want to stop the event propagation now:
// return true
}
You can even decide if, after doing what you have to do, you want to stop the event, or send it to the PictureQLWidget instace (the normal behavior).
Doc:
http://doc.qt.io/qt-4.8/qobject.html#installEventFilter
http://doc.qt.io/qt-4.8/qobject.html#eventFilter
Do not forget the Q_OBJECT keyword in your MyGLwidget custom class declaration

qt drag and drop in qgraphicsScene

I have 2 custom qgraphicsitems on a qgraphicsScene, rendered by a qgraphicsview. Now I want to be able to drag and drop one of the 2 items to the other kind. But which events should I reimplement for this? The documentation is a bit confusing on this.
also I want the qgraphicsitem to jump back to its original position if the user drags it to another area than the qgraphicsitem it should be dropped on.
As far as i know this is not implemented in the QGraphicsScene itself.
You must derive your own class from QGraphicsView or QGraphicsScene and then overload:
class MyGraphicsView : public QGraphicsView
{
Q_OBJECT;
protected:
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
...
private:
QGraphicsItem *currentDraggedItem;
};
QGraphicsView gives works with view/window coordinates while QGraphicsScene works with Scene coordinates.
Add code like:
void MyGraphicsView::mousePressEvent(QMouseEvent* event)
{
currentDraggedItem = itemAt(event->pos());
QGraphicsView::mousePressEvent(event);
}
void MyGraphicsView::mouseReleaseEvent(QMouseEvent* event)
{
QGraphicsItem *foundItem = itemAt(event->pos());
if(foundItem && currentDraggedItem &&
foundItem != currentDraggedItem)
{
// Handle DragDrop Here
}
QGraphicsView::mouseReleaseEvent(event);
}
This does the job for one QGaphicsScene. If you have two of them - the both have to know each other and you must translate coordinates from the one QGraphicsView to the other QGraphicsView. using mapTo...().
The key to this is checking the QGraphicsItems rect and seeing if they intersect.
So, when the mouse down is pressed on an item, store its current position. You can now move it and wait for the mouse release. On the release of the mouse button, check if the bounding rects of the items intersect with QRect::contains(const QRectF). If they do, then you've dropped one onto the other. If not, then animate the graphics item back to the previously stored position.
Just make sure that when you're checking the bounding rects for intersection that you're doing this with both of them in scene space coordinates. Either convert them, or use QGraphicsItem::sceneBoundingRect().

QGraphicsItem paint not called

I use QGraphicsView, QGrapichsScene and QGraphicsItem for drawing some charts. I have implemented QGraphicsItem::paint function for drawing text (values of charts), but it is not being called every time that is must draw something new. My paint function
void CLabelItem::paint(QPainter *painter,
const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget = 0*/)
{
if ( GetValue() < 0 )
{
return;
}
painter->drawText(boundingRect(), m_value.toString());
}
So my question is - why QGraphicsItem::paint can be not called and how may I make it to be called?
You need to call QGraphicsItem::update() from the function that modify the m_value variable to trigger a repaint.
In general with Qt graphics items are drawn when paintEvent is called, and then you can handle painting inside a paint function.