I have made a GUI in Qt that is basically a widget with a QGraphicsView on it i have a function:
void GUI::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
QPointF mousePoint = ui->graphicsView->mapToScene(event->pos());
qDebug() << mousePoint;
}
}
which links to a public slot:
void mousePressEvent(QMouseEvent *event);
this shows me on the console the x,y coordinate of where i have clicked, however currently this is working on the entire widget and i ideally would like x,y(0,0) to be the top left of the QGraphicsView instead of the top left of the entire widget. does anyone have any idea how to make this work, i thought from my code that this is what it was doing but it turns out this is not so, iv had a look around for a while now but im coming up with nothing
any help would be really appreciated thanks.
Reimplement the mousePressEvent(QMouseEvent *event) of the QGraphicsView not your widget would be the 'proper' way of doing it. Otherwise you can hack it with:
// Detect if the click is in the view.
QPoint remapped = ui->graphicsView->mapFromParent( event->pos() );
if ( ui->graphicsView->rect().contains( remapped ) )
{
QPointF mousePoint = ui->graphicsView->mapToScene( remapped );
}
This assumes that the widget is the parent of the QGraphicsView.
Related
I have a QGraphicsView inside the MainWindow that has implemented a QGraphicScene. I need to pop up a widget when I right click the mouse on a certain portion of the QGraphicScene. The parent of the widget needs to be MainWindow.
My problem is that I need to verify the validity of the portion on witch I clicked inside a mousePressEvent in QGraphicScene and to pop up the widget at the exact same location but the coordinates of the QGraphicScene and MainWindow are obvious not the same. For that I use a custom signal that trigger a slot inside MainWindow and get the coordinates from the mousePressEvent of the MainWindow. The problem is that the mouseEvent from the QGraphicsScene is triggered before the mouseEvent from MainWindow. This makes perfect sense and works if I right click twice but I need it to work from the first right click.
I can not implement a filter or change the focus because I have tons of events in the application.
QGraphicScene:
void CGraphicScene :: mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event -> button() == Qt::RightButton)
{
//test stuff
emit signalChangeContextualMenuPosition();
m_contextualMenu -> show();
}
}
MainWindow:
CGraphicScene *scene;
CContextualMenu *m_contextualMenu;
m_contextualMenu = new CContextualMenu(this);
m_contextualMenu ->close();
scene = new CGraphicScene(m_contextualMenu);
ui->gvInterface -> setScene(scene);
connect(scene, SIGNAL(signalChangeContextualMenuPosition()), this, SLOT(openPopUp()));
void MainWindow :: openPopUp()
{
m_contextualMenu ->move(m_xCoordPopMenu, m_yCoordPopMenu);
}
void MainWindow :: mousePressEvent(QMouseEvent *event)
{
if(event -> button() == Qt::RightButton)
{
m_xCoordPopMenu = event -> x();
m_yCoordPopMenu = event -> y();
}
}
Use the QGraphicsView::mapFromScene() to map scene coordinates to the view widget coordinates and then QWidget::mapToParent() to map the coordinates to it's parent widget, which is probably your main window. You can also find useful the method QWidget::mapTo().
I have grid of QFrames in QGridLayout and a popup menu with some actions, which are targeted on the cell where mouse right click happens. On the implementation of ContextMenuEvent I get clicked QPoint using common event->pos() but how I get access to my correct cell object by that point? Or is there some better alternative solution path for this purpose?
void X::contextMenuEvent(QContextMenuEvent* event)
{ // QPoint target = event->pos();
// TODO: m_gridLayout-> ...
// myDerivedCell->setSomething();
}
There are a bunch of solutions here. The simplest is to go through your widgets, calling bool QWidget::underMouse () const. My favorite is this:
frame_i->setContextMenuPolicy(Qt::CustomContextMenu);
connect(frame_i, SIGNAL(customContextMenuRequested(QPoint))
, SLOT(onContextMenu(QPoint)));
...
void X::onContextMenu(const QPoint &pos)
{
QFrame *w = qobject_cast < QFrame * >(sender());
...
}
I have a QGraphicsView window on my widget and have just put in an event for mouse wheel which zooms in on the image.
However as soon as i zoom in scroll bars are displayed and the scroll functionality on the mouse wheel overrides the zoom function i have.
i was wondering if there is any way that i can remove scrolling all together and add a drag to move option or maybe a CTRL and mouse wheel to zoom and mouse wheel alone would control scrolling
here is my zoom function (Which im aware isnt perfect) but if anyone could shed some light on that it would be a bonus
cheers in advance
void Test::wheelEvent(QWheelEvent *event)
{
if(event->delta() > 0)
{
ui->graphicsView->scale(2,2);
}
else
{
ui->graphicsView->scale(0.5,0.5);
}
}
You reimplemented wheelEvent for QWidget/QMainWindow that contains your QGraphicsView, however, wheelEvent of QGraphicsView remains intact.
You can derive from QGraphicsView, reimplement wheelEvent for derived class and use derive class instead of QGraphicsView - this way you won't even need wheelEvent in your QWidget/QMainWindow, and you can customize reimplemented wheelEvent to do what you want. Something like that:
Header file:
class myQGraphicsView : public QGraphicsView
{
public:
myQGraphicsView(QWidget * parent = nullptr);
myQGraphicsView(QGraphicsScene * scene, QWidget * parent = nullptr);
protected:
virtual void wheelEvent(QWheelEvent * event);
};
Source file:
myQGraphicsView::myQGraphicsView(QWidget * parent)
: QGraphicsView(parent) {}
myQGraphicsView::myQGraphicsView(QGraphicsScene * scene, QWidget * parent)
: QGraphicsView(scene, parent) {}
void myQGraphicsView::wheelEvent(QWheelEvent * event)
{
// your functionality, for example:
// if ctrl pressed, use original functionality
if (event->modifiers() & Qt::ControlModifier)
{
QGraphicsView::wheelEvent(event);
}
// otherwise, do yours
else
{
if (event->delta() > 0)
{
scale(2, 2);
}
else
{
scale(0.5, 0.5);
}
}
}
Scrolling can be disabled with the following code:
ui->graphicsView->verticalScrollBar()->blockSignals(true);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->horizontalScrollBar()->blockSignals(true);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
I think your question has a bit simpler answer.. To disable scroll bars just set scroll bar policy (QGraphicsView is just QScrollView), so step 1)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
that will disable scroll bars..
step 2) (if you want to keep it simple)
QGraphicsView * pView; // pointer to your graphics view
pView->setInteractive(true);
pView->setDragMode(QGraphicsView::ScrollHandDrag);
thats the fastest way to get results you want
I am working on a C++ application with Qt 4.8 as the GUI framework:
A subclassed QGraphicsView rendering a subclassed QGraphicsScene containing one or more subclassed QGraphicsPixMapItem(s) in which I need to know the mouse cursor's relative pixel position.
Following How to show pixel position and color from a QGraphicsPixmapItem I subclass QGraphicsPixmapItem:
class MyGraphicsPixmapItem : public QObject, public QGraphicsPixmapItem
{
Q_OBJECT
public:
explicit MyGraphicsPixmapItem(QGraphicsItem *parent = 0);
signals:
public slots:
void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
};
and implement constructor and handler:
MyGraphicsPixmapItem::MyGraphicsPixmapItem(QGraphicsItem *parent) : QGraphicsPixmapItem(parent)
{
qDebug() << "constructor mygraphicspixmapitem";
setCacheMode(NoCache);
setAcceptHoverEvents(true);
setFlag(QGraphicsItem::ItemIsSelectable,true);
}
void MyGraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
QPointF mousePosition = event->pos();
qDebug() << "mouseMoveEvent";
qDebug() << mousePosition;
QGraphicsPixmapItem::mouseMoveEvent(event);
}
I have also subclassed QGraphicsView and QGraphicsScene with respective mouseMoveEvent handlers that pass the event further down successfully:
void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << "QGraphicsScene: mouseMoveEvent";
QGraphicsScene::mouseMoveEvent(event);
}
However, although MyGraphicsView is configured with ...
setMouseTracking(true);
setInteractive(true);
... MyGraphicsPixmapItem only executes the mouseMoveEvent handler when being clicked or after setting it as grabber:
void MyGraphicsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
qDebug() << QString("item clicked at: %1, %2").arg( event->pos().x()).arg( event->pos().y() );
event->accept();
grabMouse();
}
I would like to know whether I am making a mistake or the solution given in the above-mentioned answer is flawed or incomplete. Further, I would like to know whether the mouseMoveEvent handler can be triggered without the QGraphicsPixmapItem being configured as 'grabber'. It is also acceptable if the configuration as grabber occurs automatically when the mouse first moves onto the item.
Here is a thread where a very similar problem seems to be related to the item's reference, but since I add the item to the scene like this:
thepixMapItem = new MyGraphicsPixmapItem();
scene->addItem(thepixMapItem);
I suppose this is not related.
The solution is to reimplement or filter the item's hoverMoveEvent, as mouseMoveEvent only triggers when receiving a mouse press:
If you do receive this event, you can be certain that this item also
received a mouse press event, and that this item is the current mouse
grabber.
I am currently able to load my image into a grahpics scene, and then again into a QGraphicsViewer.
I am able to implement a zoom feature by dtecting a QEvent::Wheel and then calling the graphicsViews's scale() function.
However, I can't seem to figure out how to get the pan functionality working. I basically want to detect when a mouse has clicked down on the image, and then move the image left, right, up or down along with the mouse.
As of right now, I basically have a MouseFilter class that is detecting events, and doing different things depending on the event type. I attached that listener to the QGraphicsView object
In case someone is wondering how to do it on their own, it's actually quite simple. Here's the code from my app:
class ImageView : public QGraphicsView
{
public:
ImageView(QWidget *parent);
~ImageView();
private:
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
bool _pan;
int _panStartX, _panStartY;
};
You need to store the start position of the drag, for example like this (I used the right button):
void ImageView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton)
{
_pan = true;
_panStartX = event->x();
_panStartY = event->y();
setCursor(Qt::ClosedHandCursor);
event->accept();
return;
}
event->ignore();
}
Also, you need to clear the flag and restore the cursor once the button is released:
void ImageView::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton)
{
_pan = false;
setCursor(Qt::ArrowCursor);
event->accept();
return;
}
event->ignore();
}
To actually manage the drag, you need to override the mouse move event. QGraphicsView inherits a QAbstractScrollArea, and its scrollbars are easily accessible. You also need to update the pan position:
void ImageView::mouseMoveEvent(QMouseEvent *event)
{
if (_pan)
{
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - _panStartX));
verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - _panStartY));
_panStartX = event->x();
_panStartY = event->y();
event->accept();
return;
}
event->ignore();
}
QGraphicsView has build-in mouse-panning support. Set correct DragMode and it will handle the rest. You do need the enable scroll bars for that to work.
neuviemeporte solution requires to subclass a QGraphicsView.
Another working drag implementation can be obtained without subclassing the view using eventFilter. If you don't need to customize other behaviors of the QGraphicsView, this technique will save you some work.
Let's say your GUI logic is maintained by a QMainWindow subclass and the QGraphicsView & QGraphicsScene are declared as a private members of this subclass. You would have to implement the eventFilter function as follows:
bool MyMainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == scene && event->type() == Event::GraphicsSceneMouseMove)
{
QGraphicsSceneMouseEvent *m = static_cast<QGraphicsSceneMouseEvent*>(event);
if (m->buttons() & Qt::MiddleButton)
{
QPointF delta = m->lastScreenPos() - m->screenPos();
int newX = view->horizontalScrollBar()->value() + delta.x();
int newY = view->verticalScrollBar()->value() + delta.y();
view->horizontalScrollBar()->setValue(newX);
view->verticalScrollBar()->setValue(newY);
return true;
}
}
return QMainWindow::eventFilter(obj, event);
}
To filter events from the QGraphicsScene, you'll have to install MyMainWindow as an eventFilter of the scene. Perhaps you could do this in the same function where you setup your GUI.
void MyMainWindow::setupGUI()
{
// along with other GUI stuff...
scene->installEventFilter(this);
}
You can extend this idea to replace the cursor with the drag "hand" as previously shown.