I know how to pass events from qgraphics scene to q graphics item ,but the problem is for the item,the mouse events for the scene is being executed.
for example in the code below when pressing on the item the output is "custom scene is pressed"
#include <QtGui>
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(itemAt(event->pos()))
QGraphicsScene::mousePressEvent((event));
else
qDebug() << "Custom scene clicked.";
}
};
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);
CustomScene scene;
//scene().set
scene.addItem(&item);
QGraphicsView view;
view.setScene(&scene);
view.show();
return a.exec();
}
See the documentation of QGraphicsSceneMouseEvent::pos:
Returns the mouse cursor position in item coordinates.
This means if the mouse is 10 pixels away from the top and left border of your item, you will get (10,10) as coordinates no matter where on the scene the item is.
What you need is QGraphicsSceneMouseEvent::scenePos:
Returns the mouse cursor position in scene coordinates.
Change your if-statement to:
if(itemAt(event->scenePos()))
QGraphicsScene::mousePressEvent((event));
else
qDebug() << "Custom scene clicked.";
Related
I have a widget with this structure:
main->QMainWindow->QFrame->QGraphicsView->QScene->QGraphicsPixMapItem->QPixmap
I had to do it this way because im not using Qt creator or QML, just widgets. Anyway I added an event filter to my QMainindow to be movable when clicking and dragging whenever side of the window And it worked. But due to the QGraphicsView implementation if I try to drag it doesnt work but still receives input (a menu opens when i click). What makes QGraphicsview so stubborn and how do i make the window to be draggable when i click and drag on the QGraphics view, Even installing the eventFilter on the view and frame but with no results. Thanks in advance and this is my code.
This is the problem, the red square is the QGrapicsView, the white one is the MainWindow/Qframe. See how i still can make right clic on the red one and the menu still appears. I made a debug and see i get 2 objs when i click, the mainwindow and the Qgraphicview so i dont know why the move functionality just doesn work.
enter image description here
Main.cpp
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return QApplication::exec();
}
MainWindow.h
protected:
bool eventFilter(QObject *obj, QEvent *event) override{
if (event->type() == QEvent::MouseButtonPress ) {
qDebug() << event;
auto *ev = (QMouseEvent *) event;
if (ev->button() == Qt::RightButton) {
//Opens menu, it works well when i click on the QGraphics as well
auto *menu = new QMenu(this);
auto *idle = new QAction("Idle", this);
auto *close = new QAction("Quit", this);
menu->addAction(idle);
menu->popup(ev->globalPos());
connect(close, &QAction::triggered, [](){QCoreApplication::quit();});
connect(idle, &QAction::triggered, [this](){changeDance(0);});
}
else{
// "grab" the mainWindow
this->oldPos = ev->globalPos();
}
}
if (event->type() == QEvent::MouseMove) {
//drag the window
auto *ev = (QMouseEvent *) event;
const QPoint delta = ev->globalPos() - oldPos;
move(x() + delta.x(), y() + delta.y());
oldPos = ev->globalPos();
}
}
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent) {
setWindowTitle("waifu"); //waifu
setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
setFixedSize(screenWidth, screenHeight);
//setAttribute(Qt::WA_TranslucentBackground, true);
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setWindowFlags(Qt::BypassGraphicsProxyWidget);
//mainWindow->frame->view->scene->pixmap
installEventFilter(this);
frame = new Frame(this);
setCentralWidget(frame);
view = new View(frame);
view->setFixedSize(50, 50);
scene = new Scene(view);
view->setScene(this->scene); // That connects the view with the scene
frame->layout()->addWidget(view);
myItem = new QGraphicsPixmapItem(*new QPixmap());
//scene->setBackgroundBrush(QBrush(Qt::yellow));
scene->addItem(myItem);
view->show();
Frame.h
class Frame : public QFrame {
Q_OBJECT
public:
explicit Frame(QMainWindow *parent = 0) : QFrame(parent) {
setMouseTracking(true);
setStyleSheet("background-color: red;"); //delete
setLayout(new QVBoxLayout);
layout()->setContentsMargins(0, 0, 0, 0);
setWindowFlags(Qt::FramelessWindowHint); //No windowing
setAttribute(Qt::WA_TranslucentBackground); // No background
setStyleSheet("background-color: transparent;");
};
};
QGraphicsView.h
class View : public QGraphicsView {
Q_OBJECT
public:
explicit View(QFrame *parent) : QGraphicsView(parent) {
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setMouseTracking(true);
};
protected:
private:
};
Tried to install the event filter located in the main window, in the Qframe and QGraphicsView class. with the parameter event filter being *parent.
I'm making a side view drag racing game in QT c++. I want to move my view around my scene from left to right. I have the scene set to 3600x800, but i want the view the be at the far left of my scene not at the center at the start. When i press W on my keyboard I want the view to move to the left for 1px. How do I do that? I can't find anything online
scene=new QGraphicsScene(this);
view = new QGraphicsView;
scene->setSceneRect(0,0,3600,800);
view->setScene(scene);
You will never find something so particular on the internet, you should look for each part separately:
If you want it to appear on the left side then you must use horizontalScrollBar() of the GraphicsView when it is displayed, we can do that with the showEvent method.
if you want to do an action when you press any key you could overwrite the keyPressEvent method.
To move the sceneRect() you must make a copy, move it and set it again.
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QKeyEvent>
#include <QScrollBar>
class GraphicsView: public QGraphicsView{
public:
using QGraphicsView::QGraphicsView;
protected:
void keyPressEvent(QKeyEvent *event){
if(event->key() == Qt::Key_W){
if(scene()){
QRectF rect = scene()->sceneRect();
rect.translate(1, 0);
scene()->setSceneRect(rect);
}
}
}
void showEvent(QShowEvent *event){
QGraphicsView::showEvent(event);
if(isVisible()){
horizontalScrollBar()->setValue(0);
}
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
GraphicsView view;
scene.setSceneRect(0,0,3600,800);
view.setScene(&scene);
scene.addRect(0, 200, 400, 400, Qt::NoPen, Qt::red);
view.show();
return a.exec();
}
I have QGraphicsView, QGraphicsScene and QGraphicsRectItem.
QGraphicsRectItem in the QGraphicsScene and the last one in the QGraphicsView. I want to move QGraphicsRectItem with mouse by clicking on it only! But in my implementation it moves if I click on any position on my QGraphicsScene. Whether it is my QGraphicsRectItem or some other place. And the second issue. The item has been moved to the center of the scene. Clicking on it again it starts to move from the home location.
void Steer::mousePressEvent(QMouseEvent *click)
{
offset = click->pos();
}
void Steer::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
p1->setPos(event->localPos() - offset); //p1 movable item
}
}
What do I do wrong?
UPDATE:
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Steer w;
w.show();
return a.exec();
}
widget.h
#ifndef STEER_H
#define STEER_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QMouseEvent>
#include <QPoint>
#include <QGraphicsRectItem>
class Steer : public QGraphicsView
{
Q_OBJECT
private:
QGraphicsScene *scene;
QGraphicsRectItem *p1;
QPoint offset;
public:
explicit Steer(QGraphicsView *parent = 0);
~Steer(){}
public slots:
void mousePressEvent(QMouseEvent * click);
void mouseMoveEvent(QMouseEvent * event);
};
#endif // STEER_H
widget.cpp
#include "widget.h"
#include <QBrush>
Steer::Steer(QGraphicsView *parent)
: QGraphicsView(parent)
{
scene = new QGraphicsScene;
p1 = new QGraphicsRectItem;
//add player
p1->setRect(760, 160, 10, 80);
//add scene
scene->setSceneRect(0, 0, 800, 400);
//add moveable item
scene->addItem(p1);
//set scene
this->setScene(scene);
this->show();
}
void Steer::mousePressEvent(QMouseEvent *click)
{
offset = click->pos();
}
void Steer::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
p1->setPos(event->localPos() - offset);
}
}
I'd try a different approach that is a little easier to understand:
#include <QtWidgets>
class Steer : public QGraphicsView
{
public:
Steer()
{
scene = new QGraphicsScene;
p1 = new QGraphicsRectItem;
//add player
p1->setRect(0, 0, 10, 80);
p1->setX(760);
p1->setY(160);
//add scene
scene->setSceneRect(0, 0, 800, 400);
//add moveable item
scene->addItem(p1);
//set scene
this->setScene(scene);
this->show();
}
protected:
void mousePressEvent(QMouseEvent * click)
{
if (p1->contains(p1->mapFromScene(click->localPos()))) {
lastMousePos = click->pos();
} else {
lastMousePos = QPoint(-1, -1);
}
}
void mouseMoveEvent(QMouseEvent * event)
{
if(!(event->buttons() & Qt::LeftButton)) {
return;
}
if (lastMousePos == QPoint(-1, -1)) {
return;
}
p1->setPos(p1->pos() + (event->localPos() - lastMousePos));
lastMousePos = event->pos();
}
private:
QGraphicsScene *scene;
QGraphicsRectItem *p1;
QPoint lastMousePos;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Steer w;
w.show();
return a.exec();
}
There are a few things to point out here:
Don't use setRect() to set the position of a QGraphicsRectItem. It doesn't work the way you think it might. Always use setPos() to change the position of an item.
Rename offset to something more descriptive. I chose lastMousePos. Instead of just updating it once when the mouse is pressed, also update it whenever the mouse is moved. Then, it's simply a matter of getting the difference between the two points and adding that to the position of the item.
Check if the mouse is actually over the item before reacting to move events. If the mouse isn't over the item, you need some way of knowing that, hence the QPoint(-1, -1). You may want to use a separate boolean flag for this purpose. This solves the problem that you saw, where it was possible to click anywhere in the scene to get the item to move.
Also, note the mapFromScene() call: the contains() function works in local coordinates, so we must map the mouse position which is in scene coordinates before testing if it's over the item.
The event functions are not slots, they're virtual, protected functions.
You could also consider handling these events in the items themselves. You don't need to do it from within QGraphicsView, especially if you have more than one of these items that need to be dragged with the mouse.
Let's say I have a widget in main window, and want to track mouse position ONLY on the widget: it means that left-low corner of widget must be local (0, 0).
Q: How can I do this?
p.s. NON of functions below do that.
widget->mapFromGlobal(QCursor::pos()).x();
QCursor::pos()).x();
event->x();
I am afraid, you won't be happy with your requirement 'lower left must be (0,0). In Qt coordinate systems (0,0) is upper left. If you can accept that. The following code...
setMouseTracking(true); // E.g. set in your constructor of your widget.
// Implement in your widget
void MainWindow::mouseMoveEvent(QMouseEvent *event){
qDebug() << event->pos();
}
...will give you the coordinates of your mouse pointer in your widget.
If all you want to do is to report position of the mouse in coordinates as if the widget's lower-left corner was (0,0) and Y was ascending when going up, then the code below does it. I think the reason for wanting such code is misguided, though, since coordinates of everything else within said widget don't work this way. So why would you want it, I can't fathom, but here you go.
#include <QtWidgets>
class Window : public QLabel {
public:
Window() {
setMouseTracking(true);
setMinimumSize(100, 100);
}
void mouseMoveEvent(QMouseEvent *ev) override {
// vvv That's where the magic happens
QTransform t;
t.scale(1, -1);
t.translate(0, -height()+1);
QPoint pos = ev->pos() * t;
// ^^^
setText(QStringLiteral("%1, %2").arg(pos.x()).arg(pos.y()));
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
I know how to pass events from qgraphicsview to qgraphicsitem.
But the problem is after executing the mouseEvent of item ,i have to execute the mouse event of the view which is not desirable in my case.
so,the question is: "Is there a smart way to know if mousePress is on an item or on an empty space?"
Edit:The working code:
#include <QtGui>
class CustomView : public QGraphicsView
{
protected:
void mousePressEvent(QMouseEvent *event)
{
QGraphicsView::mousePressEvent(event);
qDebug() << "Custom view clicked.";
}
};
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();
}
Appling the same concept in a qgraphics scene instead of view
#include <QtGui>
class CustomScene : public QGraphicsScene
{
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(itemAt(event->scenePos()))
QGraphicsScene::mousePressEvent((event));
else
qDebug() << "Custom view clicked.";
}
};
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);
CustomScene scene;
//scene().set
scene.addItem(&item);
QGraphicsView view;
view.setScene(&scene);
view.show();
return a.exec();
}
There are 3 correct ways to solve your task:
1. Reimplement QGraphicsView::mousePressEvent and use QGraphicsView::itemAt to find clicked item.
2. Subclass QGraphicsScene and reimplement QGraphicsScene::mousePressEvent. Its argument event contains position in scene coordinates, and you can use QGraphicsScene::itemAt to determine which item has been clicked.
3. Subclass QGraphicsItem (or any derived class) and reimplement QGraphicsItem::mousePressEvent. It will be called only if this element was clicked.
To determine wether mouse event occured on item or not you can use QGraphicsView::itemAt:
void CustomView::mousePressEvent(QMouseEvent *event)
{
if (itemAt(event->pos()))
// Click on item
else
// Click on empty space
...
}