Synchronizing mouse wheel and MarbleWidget (ZoomIn(), ZoomOut() ) - c++

I use MarbleWidget with OpenStreetMap on Qt.
Wheel zoom shows blurry images on the map. Therefore, I want to synchronize the mouse wheel with ZoomIn() and ZoomOut() inorder user to get sharp images on the map.
I want to do something like this:
QObject::connect( MarbleWidget, SIGNAL(??????), this, SLOT(wheelEvent(wheelEvent)) );
void MainWindow::wheelEvent(QWheelEvent *event){
//....
}
Is there any signal or event that I can use from MarbleWidget for ??????? above line?
And, how can I disable the mouse zoom on the MarbleWidget?

You can make your own input handler and tell MarbleWidget to use it. This will allow you to intercept mouse wheel events in the way you are asking.
Create a custom input handler
MarbleWidget uses a default input handler. Inside of MarbleInputHandler.cpp there is a function eventFilter(QObject*, QEvent*) that handles (among other things) the QEvent::Wheel event. Derive from this class and override eventFilter:
class MyMarbleInputHandler : public MarbleWidgetDefaultInputHandler
{
Q_OBJECT
public:
explicit MyMarbleInputHandler(MarbleWidget* mw) :
MarbleWidgetDefaultInputHandler(mw) {}
virtual bool eventFilter(QObject *o, QEvent *e);
signals:
void wheelEvent(QWheelEvent *event);
};
Basically, you want to intercept QEvent::Wheel and emit your own signal. Anything you don't handle yourself should be passed along to the base class.
bool MyMarbleInputHandler::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::Wheel)
{
emit wheelEvent(static_cast<QWheelEvent*>(e));
return true;
}
return MarbleWidgetDefaultInputHandler::eventFilter(o, e);
}
Create a custom MarbleWidget
The constructor below shows how you can set the input handler defined above. You'll also have to wire the signal/slot.
class MyMarbleWidget : public MarbleWidget
{
Q_OBJECT
public:
explicit MyMarbleWidget()
{
MyMarbleInputHandler *myMarbleInputHandler = new MyMarbleInputHandler(this);
setInputHandler(myMarbleInputHandler);
connect(myMarbleInputHandler, SIGNAL(wheelEvent(QWheelEvent*)),
this, SLOT(handleWheelEvent(QWheelEvent*)));
}
public slots:
void handleWheelEvent(QWheelEvent *event)
{
if (event->delta() > 0) zoomIn();
else zoomOut();
}
};
handleWheelEvent() provides the code to zoom in/out. Not all scroll wheels work the same, so you'll have to figure out how much movement of the mouse wheel it will take to zoom in/out by one step. In this example, it zooms in/out one step based on each event, paying attention only to the sign of delta() and ignoring its magnitude.
You might also check out MarbleDefaultInputHandler::handleWheel() to see what's going on with the default behavior. They use interpolated/stretched bitmap images between vector layers to provide a smoother animation when zooming. Note that the plus+ and minus- keys on the keyboard will allow you to zoom to non-interpolated map levels, whereas the mouse wheel zooms using animated ("blurry") interpolated layers. This behavior is documented in a bug report.

Related

How to make perform many functions in mousepressevent

I want to make some functions on a dicom serie (with qt and vtk) , and I want to make some connections between the qt window and the mouse.
This is my primary design:
For example, if I click on zoombutton, then I click on my image with the left button of the mouse, I want that the image will be zoomed,
I know that we must use the function mousePressEvent but I have seen that we must use this name for any connection with the mouse, or I want to do 4 or 5 functions like this one, each one for one pushbutton.
How can I do this ?
As you suggested correctly, you should use mousePressEvent to capture a mouse press action. To perform the correct action on a mouse press (zoom, pan, ...), you should remember the last pressed button and call the appropriate method accordingly. This can be implemented as follows:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow ()
{
connect(ui->panButton, &QPushButton::clicked, this, &MainWindow::onPan)
connect(ui->zoomButton, &QPushButton::clicked, this, &MainWindow::onZoom)
...
}
protected slots:
enum Action {None, Pan, Zoom, ...};
void onPan () {currentAction = Pan;}
void onZoom () {currentAction = Zoom;}
protected:
void mousePressEvent(QMouseEvent *event)
{
switch(currentAction)
{
case Pan:
// perform Pan operation
break;
case Zoom:
// perform Zoom operation
break;
}
}
protected:
Action currentAction;
};

Qt mouse events not working in QGraphicsScene

I am using Qt 5.7 (the latest version). I can't get the mouse events to work in QGraphicsScene, but they work in window outside of my scene. I have followed this question.
So I have overwritten QWidget::mouseMoveEvent() in my main widget's subclass like this:
// header:
class MyWidget {
...
void mouseMoveEvent( QMouseEvent * event );
};
// source:
MyWidget::MyWidget() {
setMouseTracking();
}
void MyWidget::mouseMoveEvent( QMouseEvent * event ) {
}
It doesn't work for: mouseMoveEvent, mouseGrabber, mousePressEvent, mouseReleaseEvent, or mouseDoubleClickEvent. But somehow it only works for mousePressEvent.
Could this be a bug in Qt?
SOURCE CODE:
In objectloader.cpp
ObjectLoader::ObjectLoader(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::ObjectLoader)
{
ui->setupUi(this);
scene=new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->setMouseTracking(true);
setMouseTracking(true);
}
Thats were i set mouse tracking twice
In objectloader.h
Then i define that method in objectloader.h
class ObjectLoader : public QMainWindow
{
Q_OBJECT
public:
explicit ObjectLoader(QWidget *parent = 0);
~ObjectLoader();
private slots:
void mouseMoveEvent(QMouseEvent *event);
protected:
private:
};
#endif // OBJECTLOADER_H
And implementation of that method in objectloader.cpp
void ObjectLoader::mouseMoveEvent(QMouseEvent *event){
qDebug()<<"Mouse moved";
}
When a mouse event is generated by Qt it is generally passed initially to the QWidget that was under the mouse pointer when the event was generated. If that QWidget accepts the event then no further processing will take place. If the event isn't accepted then Qt may propogate the event to that QWidget's parent and so on.
In your particular case the mouse move events you are interested in are being sent to the QGraphicsView/QGraphicsScene conponents where they are being accepted and, hence, no further processing takes place. In a case like that you generally need to install an event filter to intercept and process the events of interest.
Mouse move events will occur only when a mouse button is pressed down, unless mouse tracking has been enabled with QWidget::setMouseTracking().
So, I think you should check whether mouseTracking is really enabled or not, by using `bool hasMouseTracking() const'.

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

Paint over top of label, not behind it in Qt

I am creating a simple gauge in Qt 4.7.4, and everything is working wonderfully. Except for the fact that, for the life of me, I cannot get the dial shape to paint over the text labels when it passes over them. It always paints it behind the label. I am just using a simple drawpolygon() method.
I'm thinking this has something to do about paint events? I am drawing everything inside a QFrame inside a MainWindow. I am using QFrame's paintEvent.
Edit:
The QLabels are created on start up with new QLabel(this). They are only created once, and never touched again ( Similar to manually adding them on the Ui with Designer). The drawpolygon() is in the QFrame's Paint event.
"myclass.h"
class gauge : public QFrame
{
Q_OBJECT
public:
explicit gauge(QWidget *parent = 0);
~gauge();
void setValues(int req, int Limit, bool extra=false);
private:
void drawDial();
protected:
void paintEvent(QPaintEvent *e);
};
"myclass.cpp"
void gauge::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
drawDial();
return;
}
void gauge::drawDial()
{
QPainter Needle(this);
Needle.save();
Needle.setRenderHint(Needle.Antialiasing, true); // Needle was Staggered looking, This will make it smooth
Needle.translate(centrePt); // Center of Widget
Needle.drawEllipse(QPoint(0,0),10,10);
Needle.restore();
Needle.end();
}
If the gauge widget and the QLabels are siblings, then you can move the gauge widget to the front by calling its raise() method.
If the QLabels are children of the gauge widget, on the other hand, then they will always display in front of it. In that case you can either reorganize your widget hierarchy so that they are siblings instead, or you can get rid of the QLabels and simply call drawText() from your paintEvent() method instead (after drawDial() returns)

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().