QGraphicsitem sceneEvent handler - c++

What I am trying to implement is the following:
I have a QGraphicsitem I could use mouse or finger to press and drag on it, so its color will change gradually depend on the drag.
Also I would like to resize it using touch event.
I have already done all the mouse related event handler like mousePressEvent, mouseMoveEvent and mouseReleaseEvent. And it seems the touch event is translated into a mouse event by default under Windows.
Now I add the following code to re-implement the graphicItem's sceneEvent function:
bool MapGraphicItem::sceneEvent(QEvent * event)
{
switch (event->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
if (touchPoints.count() == 2) {
//do the zoom
}
return true;
}
return QGraphicsItem::sceneEvent(event);
}
The zoom also works, the problem is during pinch zoom, the mouse Events are also triggered so the color is changed which I don't want.
How to deal with this?

Related

How to disable scrolling in QGraphicsView?

I am trying to implement mouse wheel zoom in/out. It works BUT when I zoom in/out, the image is getting smaller AND scrolls up/down at the same time with zoom function. Look like events exist at the same time and work together.
I cannot find how to disable scrolling with mouse wheel. May be there is a way to make possible scrolling only with mouse cursor (by clicking on scrollbar).
I was overriding the main method of mouse wheel but it was causing the effect I wrote above
void MainWindow::wheelEvent( QWheelEvent* event )
Solved by using event filter. The code below provides zoom in/out with holding ctrl button.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::GraphicsSceneWheel)
{
ui->GV_image->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
double scaleFactor = 1.15;
bool ok = QApplication::keyboardModifiers() & Qt::ControlModifier;
if (ok)
{
QGraphicsSceneWheelEvent *scrollevent = static_cast<QGraphicsSceneWheelEvent *>(event);
if (scrollevent->delta() > 0)
{
ui->GV_image->scale(scaleFactor, scaleFactor);
}
else
{
ui->GV_image->scale(1/scaleFactor, 1/scaleFactor);
}
}
event->accept();
return true;
}
return false;
}
Put this line into your constructor or other your init function
QGraphicsView *GV_image;
...
ui->GV_image->scene()->installEventFilter(this);

Qt: accept mouseRelease only when there was no drag and drop

I have a subclass of QGraphicsView that should accept two kinds of mouse events: drag and drop for scrolling and simple clicks for item selection/highlight.
So I use
setDragMode(QGraphicsView::ScrollHandDrag);
to enable scrolling the view with the "Hand". And I have a function like this:
void GraphView::mouseReleaseEvent(QMouseEvent* e)
{
if (e->button() == Qt::LeftButton)
emit leftClicked(mapToScene(e->pos()));
else
emit rightClicked(mapToScene(e->pos()));
QGraphicsView::mouseReleaseEvent(e);
}
which creates signal whenever the user clicks on the scene.
However, the problem is: when I stop dragging and release the mouse button, the mouseReleaseEvent function is called, and if the cursor happens to be over some element of the scene, it will get highlighted.
How can I changed the mouseReleaseEvent function so that the signals are created only if there was no previous drag of the mouse?
If you use mousePress and mouseMove in combination with mouseRelease, then you can determine what mouse action the user just performed.
If you have mousePress then mouseRelease, then it must be a simple click.
If you have mousePress, mouseMove, and then mouseRelease, then it must be a drag.
The Qt documentation contains an example of interpreting combinations of mouse events in action in a scribbling program.
http://doc.qt.io/qt-4.8/qt-widgets-scribble-example.html
You can extend the principle to something like this:
private bool MyWidget::dragging = false;
private bool MyWidget::pressed = false;
void MyWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
pressed=true;
}
QGraphicsView::mousePressEvent(event)
}
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && pressed)
{
dragging=true;
}
QGraphicsView::mouseMoveEvent(event)
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && pressed) {
pressed = false;
if (dragging)
{
dragging = false;
}
else
{
// plain mouse click
// do something here
}
}
QGraphicsView::mouseReleaseEvent(event)
}
Note that this does not address edge cases where a user's mouse action is performed only partially inside the widget. I must also admit that I am relatively new to Qt and have not yet used ScrollHandDrag, but this is how one would go about identifying a certain combination of mouse events.

Qt - mouse events on a QLabel

I've got this test piece of code in mainwindow.cpp:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y());
}
return false;
}
I just want to get mouse position when clicked, and send the coordinates to another thread with an OpenCV loop that'll pull HSV information and do things accordingly. I'm using mouse over just for testing.
The problem is that I have no idea how to attach this (tracking, clicking) to a QLabel labelKalibracja, one I use to display video frames, instead of the whole window.
ui->labelKalibracja->installEventFilter(this);
is supposed to work, but doesn't, but
qApp->->installEventFilter(this);
Will make the whole window a mouse track zone.
You should check the object of the event filter :
if (qobject_cast<QLabel*>(obj)==ui->labelKalibracja && event->type() == QEvent::MouseMove)
{
...
}
Now you can make sure that the event is for the label. Note that the event filter could be installed on multiple objects and it's your duty to identify the combination of objects and events.

qt rubberband selection with specific keyboard key

I have a QGraphicsView and a QGraphicsScene and I enabled
this->setDragMode(QGraphicsView::RubberBandDrag);
for a Rubberband selection. However, in my application it would make sense that you need to press the CTRL key and then move the mouse to start the rubberband selection. Can I accomplish this without making my own QRubberBand? If not, how can I reimplement it?
If you have say a QMainWindow that contains your QGraphicsView and Scene, one way to do this would be to overload the keyPressEvent and keyReleaseEvent methods of QMainWindow like this:
void MyMainWindow::keyPressEvent( QKeyEvent * event )
{
if( event->key() == Qt::Key_Control ) {
graphicsView->setDragMode(QGraphicsView::RubberBandDrag);
}
QMainWindow::keyPressEvent(event);
}
void MyMainWindow::keyReleaseEvent( QKeyEvent * event )
{
if( event->key() == Qt::Key_Control ) {
graphicsView->setDragMode(QGraphicsView::NoDrag);
}
QMainWindow::keyReleaseEvent(event);
}
This will set the selection mode to RubberBandDrag as long as CTRL is being pressed. When the key is released again, the drag mode is set back to the default NoDrag and no selection is performed.
In both cases the event is also forwarded to the QMainWindow base class implementation which may or may not be of relevance for you.
Erik's answer didn't work well for me. If I release the key while still dragging, the rubber band is not cleared and remains visible on the screen until the next selection.
Because QT only clears the rubber band on mouse release, my workaround was to force an artificial mouse release event while still in Rubberband mode to have it properly cleared:
void MyQGraphisView::keyReleaseEvent( QKeyEvent * event )
{
if( event->key() == Qt::Key_Control ) {
if(QApplication::mouseButtons() & Qt::LeftButton)
mouseReleaseEvent(new QMouseEvent(QApplicationStateChangeEvent::MouseButtonRelease, mousePosOnScene, Qt::LeftButton, Qt::NoButton, Qt::NoModifier));
setDragMode(QGraphicsView::NoDrag);
}
QMainWindow::keyReleaseEvent(event);
}
Update: Qt fixed this bug (https://bugreports.qt.io/browse/QTBUG-65186) and will be deployed in 5.15

QIcon inside combobox

I want to include a "remove" icon on entries in my QComboBox, but I am having trouble catching the mouse press event. I've tried to catch it on the combobox, and I've tried reimplemting the QIcon class to catch the mousepress there. No dice. Does anybody know how to do this?
-D
I've written code a bit like this, where I wanted to put a tree view inside a combo box and I needed to take an action when the check box on the tree was clicked. What I ended up doing was installing an event filter on the combo box to intercept mouse clicks, figure out where the mouse click was happening, and then take an action. Probably you can do the same kind of thing with your icon. Here is the code:
bool TreeComboBox::eventFilter(QObject* object, QEvent* event)
{
if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent* m = static_cast<QMouseEvent*>(event);
QModelIndex index = view()->indexAt(m->pos());
QRect vrect = view()->visualRect(index);
if(event->type() == QEvent::MouseButtonPress &&
(model()->flags(index) & Qt::ItemIsUserCheckable) &&
vrect.contains(m->pos()))
{
// Your action here
ToggleItem(index);
UpdateSelectionString();
}
if (view()->rect().contains(m->pos()))
skipNextHide = true;
}
return QComboBox::eventFilter(object, event);
}
Maybe you can reimplement QComboBox::mousePressEvent(QMouseEvent *e) and use e.x() together with QComboBox::iconSize() to find if the event occurred over the icon.
This will off cause break if a Qt style decides to switch label and icon position in combo boxes. Don't know if that is possible?