Can I get mouse events in a QGraphicsItem? - qt

I have subclassed QGraphicsRectItem, and it's not receiving any mouse events. I've seen other questions similar to this say I need to enable mouse tracking, but setMouseTracking is in QWidget, and QGraphicsItem does not appear to be a QWidget.
I've implemented paint, and that's working. In my subclassed QGraphicsView I am getting mouse events.
The docs seem to think I should just override the mousePressEvent function (for example) and I should start getting the events. Whether I forward the mousePressEvent to the superclass of my QGraphicsView or not doesn't seem to make any difference.

In your subclassed QGraphicsView, you need to call the default implementations of overridden mouse event methods if you want them to propagate down to the items. For example:
CustomView::mousePressEvent(QMouseEvent *event)
{
// handle the event as you like
QGraphicsView::mousePressEvent(event); // then call default implementation
}
If you want to accept hover events, you need to call QGraphicsItem::setAcceptHoverEvents(true);. Otherwise you do not need to enable any particular mouse tracking.
EDIT: Here is a full working example:
#include <QtGui>
class CustomView : public QGraphicsView
{
protected:
void mousePressEvent(QMouseEvent *event)
{
qDebug() << "Custom view clicked.";
QGraphicsView::mousePressEvent(event);
}
};
class CustomItem : public QGraphicsRectItem
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "Custom item clicked.";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomItem item;
item.setRect(20, 20, 60, 60);
QGraphicsScene scene(0, 0, 100, 100);
scene.addItem(&item);
CustomView view;
view.setScene(&scene);
view.show();
return a.exec();
}

I went through the same problems you have encountered and I wanted to add some insights on top of Anthony's really good answer. Here is an example I wrote showing some features that can be implemented using the mouse events and the keyboard events.
Note that the events do not propagate to QGraphicsItems in a QGraphicsItemGroup or in a QList<QGraphicsItem> (it took me a while to figure that out).
#include <QtGui>
#include <QGraphicsRectItem>
#include <QGraphicsView>
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
class CustomItem : public QGraphicsEllipseItem
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton) {
if(event->modifiers() == Qt::ShiftModifier) {
qDebug() << "Custom item left clicked with shift key.";
// add the item to the selection
setSelected(true);
} else if(event->modifiers() == Qt::AltModifier){
qDebug() << "Custom item left clicked with alt key.";
// resize the item
double radius = boundingRect().width() / 2.0;
_center = QPointF(boundingRect().topLeft().x() + pos().x() + radius, boundingRect().topLeft().y() + pos().y() + radius);
QPointF pos = event->scenePos();
qDebug() << boundingRect() << radius << this->pos() << pos << event->pos();
double dist = sqrt(pow(_center.x()-pos.x(), 2) + pow(_center.y()-pos.y(), 2));
if(dist / radius > 0.8) {
qDebug() << dist << radius << dist / radius;
_isResizing = true;
} else {
_isResizing = false;
}
} else {
qDebug() << "Custom item left clicked.";
QGraphicsItem::mousePressEvent(event);
event->accept();
}
} else if(event->button() == Qt::RightButton) {
qDebug() << "Custom item right clicked.";
event->ignore();
}
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(event->modifiers() == Qt::AltModifier && _isResizing){
QPointF pos = event->scenePos();
double dist = sqrt(pow(_center.x()-pos.x(), 2) + pow(_center.y()-pos.y(), 2));
setRect(_center.x()-this->pos().x()-dist, _center.y()-this->pos().y()-dist, dist*2, dist*2);
} else if(event->modifiers() != Qt::AltModifier) {
qDebug() << "Custom item moved.";
QGraphicsItem::mouseMoveEvent(event);
qDebug()<<"moved"<<pos();
}
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if(event->modifiers() == Qt::AltModifier && _isResizing) {
_isResizing = false;
} else if(event->modifiers() != Qt::ShiftModifier) {
QGraphicsItem::mouseReleaseEvent(event);
}
}
int type() const
{
// Enable the use of qgraphicsitem_cast with this item.
return UserType+1;
}
private:
QPointF _center;
bool _isResizing;
};
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "Custom scene clicked.";
QGraphicsScene::mousePressEvent(event);
if(!event->isAccepted()) {
if(event->button() == Qt::LeftButton) {
// add a custom item to the scene
QPointF pt = event->scenePos();
CustomItem * item = new CustomItem();
item->setRect(pt.x()-25, pt.y()-25, 50, 50);
item->setFlags(QGraphicsItem::ItemIsSelectable|
QGraphicsItem::ItemIsMovable);
addItem(item);
} else if(event->button() == Qt::RightButton) {
// check whether there is an item under the cursor
QGraphicsItem * itemToRemove = NULL;
foreach(auto item, items(event->scenePos())) {
if(item->type() == QGraphicsItem::UserType+1) {
itemToRemove = item;
break;
}
}
if(itemToRemove) {
// remove the item from the graphicsScene
removeItem(itemToRemove);
}
}
}
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "Custom scene moved.";
QGraphicsScene::mouseMoveEvent(event);
}
void keyPressEvent(QKeyEvent * event) {
if(event->key() == Qt::Key_Backspace) {
// remove all selected items
qDebug() << "selected items" << selectedItems().size();
while(!selectedItems().isEmpty()) {
removeItem(selectedItems().front());
}
} else {
QGraphicsScene::keyPressEvent(event);
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CustomItem item;
item.setRect(20, 20, 60, 60);
item.setFlags(QGraphicsItem::ItemIsSelectable|
QGraphicsItem::ItemIsMovable);
CustomScene scene;
scene.setSceneRect(0, 0, 500, 500);
scene.addItem(&item);
QGraphicsView view;
view.setScene(&scene);
view.show();
return a.exec();
}
Hope it helps too!

I had a similar problem with a view not accepting mouse clicks. The problem was that I needed to enable the view ( ui->view->setEnabled(true) ).

Related

Testing Qt application with Qt Test

I have looked at the 5 Qt testing examples including the one about GUI events, but these examples are way too simple.
I want to test my program by launching it, simulating some clicks, and checking the value of instance variables that have been changed by those clicks.
I assume that this test below is illogical: a.exec() blocks the thread until the program is closed, and when the program is closed w has been deleted I think (or will be deleted later?).
So how to write system/GUI tests?
My test:
void LaunchProgramTest::LaunchProgramTestFunction() {
QApplication a(argc, argv);
MainWindow *w = new MainWindow();
w->show();
a.exec();
int testResult = w->myTestFunction();
qDebug() << testResult; //Prints big numbers like "-17891602" or "1753770528" as if testResult was not initialized
QVERIFY2(testResult == 3, "Incorrectly changed");
}
In mainWindow.h I declared a variable:
int testValue;
Mainwindow.cpp is the class for the main GUI of the program. In the constructor I added
testValue = 2;
Then in a function that is executed upon events I wrote
void MainWindow::on_actionTest_clicked() {
testValue = 3;
}
enter code hereSo)) you need to add QTest, add .pro
QT += testlib
and
#include <QTest>
I will show an example of my implementation for MousePress, the rest you can do yourself))
struct EventMK
{
int type;
QString m_widPath;
int _timer;
int width;
int height;
QPoint p;
QPoint g;
int button;
int buttons;
int modifiers;
int _key;
QString text;
void print(){
qDebug()<<"{ \n"
<<"type "<< type<<","<<"\n"
<<"Widget_Path "<< m_widPath<<","<<"\n"
<<"Timer "<< _timer<<","<<"\n"
<<"Width "<< width<<","<<"\n"
<<"Height "<< height<<","<<"\n"
<<"Pos_x "<< p.x()<<","<<"\n"
<<"Pos_y "<< p.y()<<","<<"\n"
<<"Global_x "<< g.x()<<","<<"\n"
<<"Global_y "<< g.y()<<","<<"\n"
<<"Button "<< button<<","<<"\n"
<<"Buttons "<< buttons<<","<<"\n"
<<"Modifiers "<< modifiers<<","<<"\n"
<<"Key "<< _key<<","<<"\n"
<<"Text "<< text<<"\n"
<<"}\n";
}
};
QWidget * _getWidget(EventMK ev)
{
QString wname = ev.m_widPath;
QStringList wpath = wname.split ( "/" );
return QWidgetUtils::getAWidget(&wpath);
}
void _postExecution(EventMK ev, QWidget *widget)
{
if (widget){
//set focus
QWidgetUtils::setFocusOnWidget(widget);
//end simulation
widget->setUpdatesEnabled ( true );
widget->update();
}
}
QPoint adaptedPosition(EventMK ev, QWidget *w)
{
if (w == nullptr)
return QPoint(ev.p.x(), ev.p.y());
int orig_w = ev.width;
int orig_h = ev.height;
int curr_w = w->width();
int curr_h = w->height();
int new_x = ev.p.x() * curr_w / orig_w;
int new_y = ev.p.y() * curr_h / orig_h;
return QPoint(new_x, new_y);
}
and function implementation
void executeMousePressEvent(EventMK ev)
{
QWidget* widget = _getWidget(ev);
if ( widget == nullptr )
{
qDebug()<<"error: "<<__LINE__<<__FUNCTION__;
return;
}
// _preExecutionWithMouseMove(ev, widget);
if (widget){
QTest::mousePress ( widget, (Qt::MouseButton)ev.button ,
(Qt::KeyboardModifier) ev.modifiers,
adaptedPosition(ev,widget));
}
_postExecution(ev, widget);
}
now left to fill struct EventMK , you need to populate it from MouseButtonPress events.
Here is my example
bool eventFilter(QObject *obj, QEvent *event)
{
///
/// process control
///
//window events
if (event->type() == QEvent::KeyPress)
{
handleKeyPressEvent(obj, event);
}
//mouse events
else if (event->type() == QEvent::MouseButtonPress)
{
handleMousePressEvent(obj, event);
}
else if (event->type() == QEvent::MouseButtonRelease)
{
handleMouseReleaseEvent(obj, event);
}
else if (event->type() == QEvent::MouseButtonDblClick)
{
handleMouseDoubleEvent(obj, event);
}
else if (event->type() == QEvent::Wheel)
{
handleWheelEvent(obj, event);
}
//keyboard events
else if (event->type() == QEvent::Close)
{
handleCloseEvent(obj, event);
}
///the event should continue to reach its goal...
return false;
}
and
void handleMousePressEvent(QObject *obj, QEvent *event)
{
QWidget *widget = isValidWidget(obj);
if (!widget){
return;
}
QMouseEvent *me = dynamic_cast< QMouseEvent*> ( event );
//create the event
if (widget != nullptr){
EventMK evkm;
evkm.type = QOE_MOUSE_PRESS; // set your type
evkm._timer = _timer.restart(); // add your QElapsedTimer
evkm.m_widPath = QWidgetUtils::getWidgetPath(widget);
evkm. width = widget->width();
evkm. height = widget->height();
QPoint p ( me->pos() );
QPoint g = widget->mapToGlobal ( p );
evkm. p = p;
evkm. g = g;
evkm. button = me->button();
evkm. buttons = me->buttons();
evkm. modifiers = me->modifiers();
evkm.print();
}
//send event if EventMK is valid
}
so, it turns out we can write a scenario and run what you wanted, thanks

Using both MouseMoveEvent for QGraphicsScene and HoverEnterEvent for QGraphicsItem

I'm trying to create a program in which you can connect points together with lines. I instantiate QGraphicsEllipseItem into a QGraphicsScene and I use HoverEnterEvent and HoverLeaveEvent to change the color and the size of the ellipses when the mouse is over them. To draw a temporary line between the point clicked and the mouse cursor I have to use MouseMoveEvent into the scene. However, when I do that, the HoverEvents of the items don't work anymore ! How can I use both MouseMoveEvent of the scene and HoverEvents of the items ?
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
for (int i = 0; i < pointList.size(); ++i) {
if (pointList[i]->over == 1){
pointList[i]->press();
lineActivated=true;
tempLine.setLine(pointList[i]->x(),pointList[i]->y(),pointList[i]->x(),pointList[i]->y());
}
}
}
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(lineActivated){
const QPointF pos = event->scenePos();
tempLine.setP2(pos);
}
}
void Point::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
pen.setColor(Qt::green);
pen.setWidth(2);
this->setPen(pen);
over=true;
qDebug("enter");
update();
}
void Point::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
if(isClicked==false){
pen.setColor(Qt::lightGray);
pen.setWidth(1);
over=false;
this->setPen(pen);
qDebug("leave");
update();
}
}
By default QGraphicsScene::mouseMoveEvent sends the necessary information to handle the hover event of the items but override that method you eliminate that behavior. The solution is to call the parent method.
#include <QtWidgets>
class GraphicsScene: public QGraphicsScene{
public:
using QGraphicsScene::QGraphicsScene;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event){
if(!m_lineitem){
m_lineitem = new QGraphicsLineItem;
addItem(m_lineitem);
}
QLineF l(event->scenePos(), event->scenePos());
m_lineitem->setLine(l);
QGraphicsScene::mousePressEvent(event);
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
if(m_lineitem){
QLineF l(m_lineitem->line().p1(), event->scenePos());
m_lineitem->setLine(l);
}
QGraphicsScene::mouseMoveEvent(event);
}
private:
QGraphicsLineItem *m_lineitem = nullptr;
};
class Point: public QGraphicsEllipseItem{
public:
Point(QGraphicsItem *parent=nullptr): QGraphicsEllipseItem(parent){
setRect(QRectF(-5, -5, 10, 10));
QPen pen;
pen.setColor(Qt::lightGray);
pen.setWidth(1);
setPen(pen);
setAcceptHoverEvents(true);
}
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event){
QPen pen;
pen.setColor(Qt::green);
pen.setWidth(2);
setPen(pen);
QGraphicsEllipseItem::hoverEnterEvent(event);
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event){
QPen pen;
pen.setColor(Qt::lightGray);
pen.setWidth(1);
setPen(pen);
QGraphicsEllipseItem::hoverLeaveEvent(event);
}
};
int main(int argc, char *argv[]){
QApplication a(argc, argv);
GraphicsScene *scene = new GraphicsScene;
QGraphicsView w(scene);
w.setRenderHint(QPainter::Antialiasing, true);
w.fitInView(QRectF(0, 0, 100, 100), Qt::KeepAspectRatio);
for(const QPointF & p: {QPointF(10.0, 10.0), QPointF(90.0, 20.0), QPointF(30.0, 40.0)}){
Point *it = new Point();
scene->addItem(it);
it->setPos(p);
}
w.resize(640, 480);
w.show();
return a.exec();
}

How to align Graphics items in Graphic scene based on first selected item?

I have Graphic scene in that i have to alight right,left,top or bottom based on first selected item(Reference Item). i searched i got some code but in this its aligning to scene right position. I have to align items based on first selected item. how can i do this?
void GraphicScene::ItemsRightAlign()
{
if (selectedItems().isEmpty())
return;
QRectF refRect = selectedItems().first()->boundingRect();
QList<QGraphicsItem*> sel =selectedItems(); // for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
QRectF itemRect = selItem->mapToScene(selItem->boundingRect()).boundingRect();
//if(align_right)
dx = refRect.right() - itemRect.right();
qDebug() << "item pos "<< dx << dy << selItem->mapToScene(selItem->boundingRect()).boundingRect() ;
selItem->moveBy(dx, dy);
}
}
For more details
Output should be like this output
The resolution method is to map the point that determines the right, left, up, down to the scene of the first item and the other items obtaining the difference that must be compensated.
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsItem>
class GraphicsScene: public QGraphicsScene{
Q_OBJECT
public:
GraphicsScene(QObject *parent=nullptr);
void moveSelecteds(Qt::Alignment aligment);
private slots:
void onSelectionChanged();
private:
void move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment);
QGraphicsItem *mRef;
};
#endif // GRAPHICSSCENE_H
graphicsscene.cpp
#include "graphicsscene.h"
GraphicsScene::GraphicsScene(QObject *parent):
QGraphicsScene(parent),
mRef(nullptr)
{
connect(this, &GraphicsScene::selectionChanged, this, &GraphicsScene::onSelectionChanged);
}
void GraphicsScene::moveSelecteds(Qt::Alignment aligment){
QList<QGraphicsItem *> its= selectedItems();
if(its.size() < 2)
return;
if(!its.removeOne(mRef))
return;
move(mRef, its, aligment);
}
void GraphicsScene::onSelectionChanged(){
if(selectedItems().isEmpty()){
mRef = nullptr;
}
else if(selectedItems().size() == 1){
mRef = selectedItems().first();
}
}
void GraphicsScene::move(QGraphicsItem *ref, QList<QGraphicsItem *> others, Qt::Alignment aligment){
QPointF p;
switch (aligment) {
case Qt::AlignLeft:
p = QPointF(ref->mapToScene(ref->boundingRect().topLeft()).x(), 0);
break;
case Qt::AlignRight:
p = QPointF(ref->mapToScene(ref->boundingRect().topRight()).x(), 0);
break;
case Qt::AlignTop:
p = QPointF(0, ref->mapToScene(ref->boundingRect().topLeft()).y());
break;
case Qt::AlignBottom:
p = QPointF(0, ref->mapToScene(ref->boundingRect().bottomLeft()).y());
break;
}
for(QGraphicsItem *o: others){
QPointF delta;
switch (aligment) {
case Qt::AlignLeft:{
delta = p - QPointF(o->mapToScene(o->boundingRect().topLeft()).x(), 0);
break;
}
case Qt::AlignRight:{
delta = p - QPointF(o->mapToScene(o->boundingRect().topRight()).x(), 0);
break;
}
case Qt::AlignTop:{
delta = p - QPointF(0, o->mapToScene(o->boundingRect().topLeft()).y());
break;
}
case Qt::AlignBottom:{
delta = p - QPointF(0, o->mapToScene(o->boundingRect().bottomLeft()).y());
break;
}
}
o->moveBy(delta.x(), delta.y());
}
}
In this example you can use the up, down, left, right keys to move the items.
main.cpp
#include "graphicsscene.h"
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QShortcut>
#include <random>
static void create_items(QGraphicsScene & scene){
std::default_random_engine generator;
std::uniform_int_distribution<int> dist_size(30, 40);
std::uniform_int_distribution<int> dist_pos(-50, 50);
for(const QString & colorname : {"red", "green", "blue", "gray", "orange"}){
QRectF r(QPointF(dist_pos(generator), dist_pos(generator)),
QSizeF(dist_size(generator), dist_size(generator)));
auto item = new QGraphicsRectItem(r);
item->setPos(QPointF(dist_pos(generator), dist_pos(generator)));
item->setBrush(QColor(colorname));
item->setFlag(QGraphicsItem::ItemIsSelectable);
scene.addItem(item);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GraphicsScene scene;
create_items(scene);
QGraphicsView view(&scene);
const QList<QPair<Qt::Key, Qt::Alignment>> k_a {
{Qt::Key_Up, Qt::AlignTop},
{Qt::Key_Down, Qt::AlignBottom},
{Qt::Key_Left, Qt::AlignLeft},
{Qt::Key_Right, Qt::AlignRight}
};
for(const QPair<Qt::Key, Qt::Alignment> & p : k_a){
QShortcut *shorcut = new QShortcut(p.first, &view);
QObject::connect(shorcut, &QShortcut::activated, std::bind(&GraphicsScene::moveSelecteds, &scene, p.second));
}
view.resize(640, 480);
view.show();
return a.exec();
}
The complete example can be found in the following link.
Replace topLeft.x -- for Right align with topRight.x ,For Top Align replace topLeft.y and dx with dy,For Bottom Align replace bottomLeft.y and dx with dy
void GraphicScene::ItemsLeftAlign
{
if (selectedItems().isEmpty())
return;
QGraphicsItem *FirstSelItem = selectedItems().first();
QList<QGraphicsItem*> sel =selectedItems(); // for example
foreach(QGraphicsItem* selItem, sel)
{
qreal dx = 0, dy = 0;
dx = (FirstSelItem->mapToScene(FirstSelItem->boundingRect().topLeft()).x()) -
(selItem->mapToScene(selItem->boundingRect().topLeft()).x());
selItem->moveBy(dx, dy);
}
}

Qt mouse movement/action combination

How to manage combined mouse movement (for example left click + midle click + left click release)
I have the method mousePressEvent and mouseReleaseEvent but I did not find the way to combine them.
If you need to combine the information from mousePress and mouseRelease you need to somehow keep track which button is still pressed and which one has already been released again.
Below is a rather simply example which seems to do what you describe (actions indicated by printouts)
Let me know if this helps
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QMouseEvent>
#include <QtCore>
class MyGraphicsView: public QGraphicsView
{
public:
MyGraphicsView( QGraphicsScene *scene, QWidget *parent = 0) : QGraphicsView(scene,parent)
{
active[Qt::LeftButton] = false;
active[Qt::RightButton] = false;
active[Qt::MiddleButton] = false;
}
public slots:
virtual void mousePressEvent( QMouseEvent * event );
virtual void mouseReleaseEvent( QMouseEvent * event );
std::map< Qt::MouseButton, bool > active;
};
void MyGraphicsView::mousePressEvent( QMouseEvent * event )
{
active[event->button()] = true;
if( event->button() == Qt::LeftButton && !active[Qt::MiddleButton] && !active[Qt::RightButton]) {
qDebug() << "select";
} else if (event->button() == Qt::RightButton && !active[Qt::LeftButton] && !active[Qt::MiddleButton] ) {
qDebug() << "property";
} else if ( event->button() == Qt::LeftButton && active[Qt::MiddleButton] && !active[Qt::RightButton]) {
qDebug() << "zoom";
} else if ( event->button() == Qt::MiddleButton && !active[Qt::LeftButton] && !active[Qt::RightButton] ) {
qDebug() << "pan";
}
}
void MyGraphicsView::mouseReleaseEvent( QMouseEvent * event )
{
if( event->button() == Qt::LeftButton && active[Qt::LeftButton] && active[Qt::MiddleButton] ){
qDebug() << "move";
}
active[event->button()] = false;
}
int main( int argc, char **argv )
{
QApplication app(argc, argv);
QGraphicsScene scene;
scene.setSceneRect( -100.0, -100.0, 200.0, 200.0 );
MyGraphicsView view( &scene );
view.show();
return app.exec();
}

QgraphicsView rubberbanddrag effects zooming behavior

I am stuck with this weird behavior where, if I enable rubberband drag, the wheel event doesn't zoom under mouse anymore. It does zooming but irrespective of mouse position. And if I disable other events, then wheelEvent works properly.
I have a custom class inheriting QGraphicsView as :
class MyGraphics : public QGraphicsView{
public :
MyGraphics(QWidget *parent = NULL);
public slots:
void zoomIn() { scale(1.2, 1.2); }
void zoomOut() { scale(1 / 1.2, 1 / 1.2); }
protected :
QRubberBand *rubberBand;
QPoint origin;
QPointF InitialCenterPoint;
QPointF CurrentCenterPoint;
QPoint rubberBandOrigin;
bool rubberBandActive;
QPoint LastPanPoint;
int _numScheduledScalings;
//if I uncomment these three event handlers, the wheelevent doesnt zoom properly!
// virtual void mousePressEvent(QMouseEvent *event);
// virtual void mouseMoveEvent(QMouseEvent *event);
// virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *);
virtual void resizeEvent(QResizeEvent *event);
};
The constructor :
MyGraphics::MyGraphics(QWidget *parent) : QGraphicsView(parent){
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
this->installEventFilter(this);
this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
this->setResizeAnchor( QGraphicsView::AnchorUnderMouse );
QGraphicsScene *graphScene = new QGraphicsScene(this);
this->setScene(graphScene);
QGraphicsItem* pEllipse = graphScene->addEllipse(0,100,50,50);
QGraphicsItem* pRect = graphScene->addRect(200,100,50,50);
pEllipse->setFlag(QGraphicsItem::ItemIsSelectable, true);
pRect->setFlag(QGraphicsItem::ItemIsSelectable, true);
setSceneRect(0, 0, 1000, 1000);
rubberBandOrigin = QPoint(0,0);
}
Event handlers :
void MyGraphics::wheelEvent(QWheelEvent *event){
if(event->delta() > 0){
//Zoom in
this->zoomIn();
} else {
this->zoomOut();
}
}
/*
void MyGraphics::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton) {
rubberBandOrigin = event->pos();
rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
rubberBand->setGeometry(event->x(),event->y(),0, 0);
rubberBand->show();
rubberBandActive = true;
}
if(event->button() == Qt::LeftButton){
LastPanPoint = event->pos();
}
}
void MyGraphics::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() == Qt::MiddleButton && rubberBandActive == true){
rubberBand->resize( event->x()-rubberBandOrigin.x(), event->y()-rubberBandOrigin.y() );
}
else{
if(!LastPanPoint.isNull()) {
//Get how much we panned
QGraphicsView * view = static_cast<QGraphicsView *>(this);
QPointF delta = view->mapToScene(LastPanPoint) - view->mapToScene(event->pos());
LastPanPoint = event->pos();
}
}
}
void MyGraphics::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::MiddleButton){
QGraphicsView * view = static_cast<QGraphicsView *>(this);
QPoint rubberBandEnd = event->pos();
QRectF zoomRectInScene = QRectF(view->mapToScene(rubberBandOrigin), view->mapToScene(rubberBandEnd));
QPointF center = zoomRectInScene.center();
view->fitInView(zoomRectInScene, Qt::KeepAspectRatio);
rubberBandActive = false;
delete rubberBand;
}
else{
LastPanPoint = QPoint();
}
}
*/
Any idea where I am doing wrong or how do I fix it ?
QGraphicsView::scale function's behaviour depends on the mouse position. It's performing automatically and internally by QGraphicsView. Since you don't pass mouse position to the scale function, I think QGraphicsView tracks the mouse and remembers the last position on its own.
By reimplementing mouse event handlers you have taken this ability from it. The view can't determine the mouse position anymore because its original handlers aren't called.
Luckily this issue can be easily fixed. You need to call base class implementation before your own:
void MyGraphics::mousePressEvent(QMouseEvent *event) {
QGraphicsView::mousePressEvent(event);
// your implementation goes here
}
It's an example for mousePressEvent but you should add similar statements to all your event handlers unless you need to disable some part of default behavior.

Resources