Dragging object around a QWidget in Qt - c++

I am trying to make a custom widget in Qt Creator that supports dragging objects around. At its simplest form, the widget has a QRect (or any other shape), on which I can click and then drag it around the widget. Once I release the mouse button, the QRect should stop being dragged.
In my QWidget class, I have this method
void ImageArea::mouseMoveEvent(QMouseEvent *event)
{
QPoint mousePos = event->pos();
qDebug() << mousePos.x();
qDebug() << mousePos.y();
qDebug() << "---------";
}
that can get the coordinates of the mouse as the pointer is moved around the screen. I have tried updating member variables for x and y, and then painting the QRect via the paintEvent method, but this doesn't work.
Does anyone have any suggestions?

To get mouse move events, you must set the QWidget::mouseTracking property to true:
ImageArea::ImageArea( QWidget* p ) : QWidget( parent ) {
...
setMouseTracking( true );
}

Implement paintEvent(QPaintEvent *) to draw the object(s) at the positions indicated by the current value(s) of their corresponding member variables.
After you've changed the values of one or more member variables (in mouseMoveEvent or wherever), call this->update(). That will tell Qt that it needs to call your paintEvent method again in the near future.
That should be all you need to do.

Be sure to use the moveTo method to move the rectangle.
Setting the x,y position directly may affect the size of the rectangle.
I don't see what you aren't doing, based on your question.
Are you sure that the rectangles are in new positions when you paint them?
Maybe you are missing the update step Jeremy Friesner told to implement.
It seems that you are missing mouse button tracking.
The easy way might be to get the mouse button states from QApplication::mouseButtons(). Although it might be slightly less efficient.

Related

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.

QGraphicsItem::SetCursor Won't Deselect

All,
I have a QGraphicsEllipseItem with setFlags(Qt::ItemIsSelectable | Qt::ItemIsMovable). This allows me drag and move ellipses quickly in a QGraphicsView.
I decided to be fancy, and setCursor(Qt::OpenHandCursor) to let the user know they can move it by clicking. However, now it won't let go when I let go of the left mouse button? What am I doing wrong?
Example code: Custom QGraphicItem and Repaint Issues
Note: I removed the update() calls, and added prepareGeometryChange() calls.
Now modify the MakeNewPoint function:
QGraphicsEllipseItem * InteractivePolygon::MakeNewPoint(QPointF & new_point)
{
QGraphicsEllipseItem * result = 0;
result = new QGraphicsEllipseItem();
result->setPos(new_point);
result->setRect(-4, -4, 8, 8);
result->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable)
result->setCursor(Qt::OpenHandCursor); //Setting this removes my ability to let go of an item. NOTE: result is parented by this.
return result;
}
later:
QGraphicsEllipseItem * new_item = MakeNewPoint(bla);
new_item->setParent(this);
//add it to my QList<QGraphicsEllipseItem *> m_points;
I would like to note that my QGraphicsEllipseItem is parented by a custom QGraphicsItem. I don't change the parents/Custom Item cursor, only the ellipse's. I do not experience this problem with non parented ellipses...
Interesting result: So my class custom QGraphicsItem class (the parent of the ellipses) is a QObject so I can filter incoming mouse events from the scene. I did a setCursor(Qt::ArrowCursor) in my custom class's constructor... and here's where it gets interesting:
The eventFilter now catches (event->type() == QEvent::GraphicsSceneMouseMove) even if a mouse button isn't pressed down. If I don't have the setCursor call, that event only fires while a mouse button is pressed... thoughts?
Okay got it, here's what's happening, it's intuitive once you realize it:
When you set that a QGraphicsItem to a unique cursor, the QGraphicsView has to setMouseTracking(true) otherwise the QGraphicsScene will never know when to change the cursor (ie when it's over the graphics item with the unique cursor.) The mouse move events were affecting my ellipse.
Normally, the QGraphicsScene only gets mouse move events when a button is held down.

Force update drawForeground on QGraphicsScene

I want to show a little image on my mouse position.
So i did that:
void AreaScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
MapData::pEnd.setX(event->scenePos().x());
MapData::pEnd.setY(event->scenePos().y());
this->update(0, 0, this->width(), this->height());
}
The pEnd is my point.
On the drawForeground i did that:
void AreaScene::drawForeground(QPainter *painter, const QRectF &rect){
qDebug() << "called";
if(MapData::tileIndex!=-1&&MapData::pEnd.x()!=-1){
painter->drawPixmap(MapData::pEnd.x(),MapData::pEnd.y(), *MapData::tileImage, (((int)(MapData::tileIndex%(MapData::tileImage->width()/MapData::tileSize.x())))*MapData::tileSize.y()),
(((int)(MapData::tileIndex/(MapData::tileImage->width()/MapData::tileSize.x())))*MapData::tileSize.x()),
MapData::tileSize.x(), MapData::tileSize.y());
}
}
Note:
The tile index is the position of the subrectangle on the tileImage (QPixelMap)
So i get the points, the image and the subrectange inside it.
It works if i keep pressing the right or the left mouse buttons it updates, but i want to update it when i move the mouse, i know the drawForeground is not even called at all.
Is there a way to call it, force to update so i can show the little tile on the screen?
The another option (i think) is change the mouse icon to the tile image, but i did a little research and didn't find a way to do that.
Thanks ppl
Call setMouseTracking(true); on the QGraphicsView that is displaying the scene. That will tell the view to generate mouse move events whenever the mouse is hovered over the view. Otherwise, the view will only generate mouse move events when you click and drag while holding down a mouse button.

Accessing the the coordinates in a QPushbutton clicked slot

I have a QPushButton with an image that has two areas that I want to handle differently when clicked. Due to the positioning on the image, I cannot really use separate buttons for the two images.
What I'd like to do is, in the slot where I am handling the click, have it check the coordinates of the click to determine which area was clicked.
Is there a way to access this?
This is what first comes to mind. It should work, although there may be a simpler way:
Create your own class that derives from QPushButton.
Override the necessary mouse events (mousePressEvent and mouseReleaseEvent) and, before calling the base class implementation, set a property in your object using setProperty with the position of the mouse click. (The position is available from the event parameter.)
In the slot handling the event, use sender() to get the button, and read the property using property().
If you don't need to treat your object as the base class (QPushButton*) you could just create a new signal that includes the mouse event and attach that to your slot. Then you wouldn't need the property at all.
You can get the current mouse position using QCursor::pos() which returns the position of the cursor (hot spot) in global screen coordinates.
Now screen coordinates are not easy to use, and probably not what you want. Luckily there is a way to transform screen coordinates to coordinates relative to a widget.
QPoint _Position = _Button->mapFromGlobal(QCursor::pos());
This should tell you where on the button the mouse was when the user clicked. And you can take it from there.
Building on #Liz's simple mechanism, here's what I did; this is in a slot method that is called on pressed() but generalizes to other situations. Note that using pushButton->geometry() gives you coordinates that are already in global space so you don't need to mapFromGlobal.
void MainWindow::handlePlanButtonPress()
{
int clickX = QCursor::pos().x();
int middle = m_buttonPlan->geometry().center().x();
if ( clickX < middle ) {
// left half of button was pressed
m_buttonPlan->setStyleSheet(sStyleLargeBlueLeft);
} else {
// right half of button was pressed
m_buttonPlan->setStyleSheet(sStyleLargeBlueRight);
}
}

QT mouse event handling problem

Greetings all,
As seen in the picture
I have an extended QWidget object (which draws the cell images and some countour data) inside a QScrollBar.
User can zoom in/out the Image (QWidget size is changed according to the zoomed size of the QImage ) using mouse wheel.
I process the events (mouseMoveEvent(),wheelEvent()..etc) by implementing the listener methods in QWidget.
My problem is ,I can only perform zooming (and other events) when the mouse pointer is over the QWidget.
If the mouse point is over the QScrollBar (the gray area in the image) ,those events are consumed by the QScroolBar.
Any tips,
[Edit] Sorry I was refering to QScrollArea , not QScrollBar.
thanks,
umanga
I'm uncertain if you want the scroll wheel to only ever be used for zooming the image or if you want the scroll wheel to control zooming when the image is smaller than the scroll area viewport and then use the scroll wheel to do scrolling when the image is larger than the scroll area viewport. In either case, you should be able to customize how the wheel is handled with the following:
Since I've not actually tried this one, I'm not sure if it will work. The hope is that if you install an event filter and set ignore on the event, the event will still be propagated back to your image widget. This will allow you to leave your current mouse handling in the image widget intact.
bool YourImageWidget::eventFilter(QObject *obj, QEvent *event)
{
if((obj == scrollAreaPointer) && (event->type() == QEvent::Wheel))
{
if(!scrollAreaShouldHandleWheel)
{
event->ignore();
}
}
return false; // always pass the event back to the scroll area
}
The scrollAreaShouldHandleWheel flag is a boolean you would set from your image widget based on whether or not you want the scroll area to handle the wheel events.
Somewhere in your code you would install your image widget as an event filter for the scrollarea.
scrollArea->installEventFilter(imageWidget);
If this doesn't work, you can always use this filter to make sure your widget gets the event and handle it directly and then return true so that the scroll area won't be able to receive the event.
I recommend you use QGraphicsScene and QGraphicsView. The graphics framework already provides a lot of useful features (including viewport transformation). And the QGraphicsView is a scroll area.
have you done grabMouse() for Qwidget i.e for the one which you display image?