QGraphicsPixmapItem, alternative methods of connecting to slots - qt

I'm aware I need to derive from QObject in order to connect to a slot if I am using QGraphicsPixmapItem, but I am struggling to do this. I have tried alternative ways to achieve what I want, I have tried onMousePress and isSelectable i.e.
run->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
if (run->isSelected())
{
qDebug() << "selected";
}
else if (!run->isSelected())
{
qDebug() << "not selected";
}
although run is selectable, the first argument is never true, it is always "not selected"
This is my code, I am working on the slot method;
mainwindow.cpp
int MainWindow::sim()
{
...
QGraphicsPixmapItem* run = new QGraphicsPixmapItem(QPixmap::fromImage(image6));
run->scale(0.3,0.3);
run->setPos(-200,-200);
run->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
run->setCursor(Qt::PointingHandCursor);
connect(run, SIGNAL(selectionChanged()), this, SLOT(runClicked()));
scene->addItem(run);
//pause
QGraphicsPixmapItem* pause = new QGraphicsPixmapItem(QPixmap::fromImage(image7));
pause->scale(0.3,0.3);
pause->setPos(-160,-197);
pause->setFlag(QGraphicsPixmapItem::ItemIsSelectable);
pause->setCursor(Qt::PointingHandCursor);
connect(pause, SIGNAL(selectionChanged()), this, SLOT(pauseClicked()));
scene->addItem(pause);
...
}
void MainWindow::runClicked()
{
qDebug() << "run Clicked";
}
void MainWindow::pauseClicked()
{
qDebug() << "pause Clicked";
}
mainwindow.h
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
int sim();
...
public slots:
void runClicked();
void pauseClicked();
...
So obviously I get the error when connecting to the slots. Could anyone help please? Thank you.

To find out if your item is selected, do this:
QVariant MyItem::itemChange( GraphicsItemChange change, const QVariant& value )
{
if ( change == QGraphicsItem::ItemSelectedHasChanged ) {
qDebug() << ( isSelected() ? "selected" : "not selected" );
}
return QGraphicsItem::itemChange( change, value );
}

If you want to use signals and slots, you need to subclass both QObject and QGraphicsPixmapItem.
Because QObject doesn't contain clicked() signal, you need to implement that, too, by re-implementing
void mousePressEvent ( QGraphicsSceneMouseEvent *e ) and void mouseReleaseEvent ( QGraphicsSceneMouseEvent *e ).
MyItem:
#pragma once
#include <QGraphicsPixmapItem>
#include <qobject.h>
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
class MyItem: public QObject, public QGraphicsPixmapItem
/* moc.exe requires to derive from QObject first! */
{
Q_OBJECT
public:
MyItem(QGraphicsItem *parent = 0): QObject(), QGraphicsPixmapItem(parent)
{
}
MyItem(const QPixmap & pixmap, QGraphicsItem * parent = 0 ): QObject(),
QGraphicsPixmapItem(pixmap, parent)
{
}
signals:
void clicked();
protected:
// re-implement processing of mouse events
void mouseReleaseEvent ( QGraphicsSceneMouseEvent *e )
{
// check if cursor not moved since click beginning
if ((m_mouseClick) && (e->pos() == m_lastPoint))
{
// do something: for example emit Click signal
emit clicked();
}
}
void mousePressEvent ( QGraphicsSceneMouseEvent *e )
{
// store click position
m_lastPoint = e->pos();
// set the flag meaning "click begin"
m_mouseClick = true;
}
private:
bool m_mouseClick;
QPointF m_lastPoint;
};
And simple example of usage:
#include <qgraphicsview.h>
#include <qgraphicsscene.h>
#include "reader.h"
#include <qdebug.h>
class MainAppClass: public QObject
{
Q_OBJECT
public:
MainAppClass()
{
QGraphicsScene *scene = new QGraphicsScene();;
scene->setSceneRect( -100.0, -100.0, 200.0, 200.0 );
MyItem *item = new MyItem(QPixmap("about.png"));
connect(item, SIGNAL(clicked()), this, SLOT(pixmapClicked()));
scene->addItem(item);
QGraphicsView * view = new QGraphicsView( scene );
view->setRenderHints( QPainter::Antialiasing );
view->show();
}
public slots:
void pixmapClicked()
{
qDebug() << "item clicked!" ;
}
};

Related

MouseMoveEvent stops being called

I'm using QCustomPlot (plot_ object) on QQuickPaintedItem (SinePlot class) so I can use it in QML. In mousePressEvent I collect initial point and in mouseMoveEvent I'm making calculations to add new points and updating cursor point:
void SinePlot::mousePressEvent(QMouseEvent* event)
{
prevPoint_ = event->globalPos();
}
void SinePlot::mouseMoveEvent(QMouseEvent* event)
{
QPointF tmp = event->globalPos();
qreal prop = (prevPoint_.x() - tmp.x()) / width();
if(prop > 0)
{
data_->shiftLeft(prop);
} else {
data_->shiftRight(prop);
}
plot_->xAxis->setRange(data_->minX, data_->maxX);
...
prevPoint_ = tmp;
update();
}
I have also trying to use pos() and localPos() but it does not make any difference, here is what I got:
As you can see mouseMoveEvent stops being called after some time(before releasing) and moving cursor does not call it.
Here is minimal reproducible example:
#ifndef SINEPLOT_H
#define SINEPLOT_H
#include "qcustomplot.h"
#include <QtQuick>
#include <QDebug>
class SinePlot : public QQuickPaintedItem
{
Q_OBJECT
public:
explicit SinePlot(QQuickItem* parent=nullptr)
{
setAcceptedMouseButtons(Qt::AllButtons);
plot_ = new QCustomPlot();
plot_->setInteractions(QCP::iRangeDrag);
plot_->addGraph();
}
virtual ~SinePlot()
{
delete plot_;
}
void paint(QPainter* painter)
{
QPicture picture;
QCPPainter qcpPainter;
qcpPainter.begin(&picture);
plot_->toPainter(&qcpPainter, width(), height());
qcpPainter.end();
picture.play(painter);
};
protected:
virtual void mousePressEvent(QMouseEvent* event) {};
virtual void mouseMoveEvent(QMouseEvent* event)
{
qDebug() << "mouse move";
};
private:
QCustomPlot* plot_;
};
#endif
In my case I got "mouse move" ~10 times.

How to install event filter on a custom Qt class?

I want to install an event filter on an object of a custom class in Qt. So I created a project such as QtGuiApplication1 on the Qt Designer and created a simple class as myClass as which has a widget and a QGraphicsView for drawing a colored rectangle.
in header file:
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QtGuiApplication1.h"
#include "myClass.h"
class QtGuiApplication1 : public QMainWindow
{
Q_OBJECT
public:
QtGuiApplication1(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplication1Class ui;
bool eventFilter(QObject *obj, QEvent *ev);
myClass* myClass_;
};
in .cpp
#include "QtGuiApplication1.h"
QtGuiApplication1::QtGuiApplication1(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
myClass_ = new myClass(this, QRect(100, 100, 200, 200));
myClass_->installEventFilter(this);
}
bool QtGuiApplication1::eventFilter(QObject * obj, QEvent * ev)
{
if (obj == myClass_)
{
bool hi = true;
}
return false;
}
and the myClass code is here:
header file of myClass:
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QGraphicsView>
class myClass : public QObject
{
Q_OBJECT
public:
explicit myClass(QObject *parent = 0);
myClass();
myClass(QWidget* parent, QRect inRect);
private:
QWidget * widget;
QGraphicsView* qGraph_back;
QGraphicsScene* scene_back;
};
#endif /
cpp file of myClass:
#include "myClass.h"
myClass::myClass(QObject *parent) :
QObject(parent)
{
}
myClass::myClass()
{
}
myClass::myClass(QWidget* parent, QRect inRect)
{
widget = new QWidget(parent);
qGraph_back = new QGraphicsView(widget);
scene_back = new QGraphicsScene(qGraph_back);
widget->setGeometry(inRect);
scene_back->setSceneRect(0,0,inRect.width(),inRect.height());
qGraph_back->setBackgroundBrush(QColor(0, 0, 255, 80));
qGraph_back->setScene(scene_back);
qGraph_back->show();
}
I want to get all the events of myClass_ object such as mouse event, But I can't and the eventfilter doesn't work. how to install eventfilter on the object?
The event filter will work only for events in your MyClass instance, only. Not for its children.
So, events, such as a mouse click or move, in your qGraph_back will be not visible in your eventFilter method.
When you add a child in a widget, an QChildEvent event is raised. You can use it to install the event filter on all children (and grandchildren, etc.). But, you have to install the event filter on your MyClass before adding the children.
A quick example:
class Listener: public QObject
{
public:
Listener(): QObject()
{}
bool eventFilter(QObject* object, QEvent* event)
{
qDebug() << Q_FUNC_INFO << object << event;
if (event->type() == QEvent::ChildAdded)
{
QChildEvent* ev = dynamic_cast<QChildEvent*>(event);
ev->child()->installEventFilter(this);
}
return false;
}
};
class Widget: public QWidget
{
public:
Widget(QObject* parent) : QWidget()
{
installEventFilter(parent);
QGraphicsView* view = new QGraphicsView(this);
auto layout = new QHBoxLayout(this);
layout->addWidget(view);
layout->addWidget(new QLabel("Foobar"))
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Listener* listener = new Listener();
Widget* w = new Widget(listener);
w->show();
return app.exec();
}
As you can see, the events in the QLabel are now sent to the listener. But, you can't see the events from the view because they are caught by the viewport widget in the QGraphicsView...
You have to handle the case where the added child has a viewport (inherits from QAbstractItemView, etc.) and it becomes more complicated.
So, if you have to know when the user clicks on your view, it would be easier to use signals/slots and not an event filter.

Drawing Line at current position using QGraphicsLineItem

I want to draw a line using QGraphicsLineItem. What exactly I want is that on clicking at GraphicsView, after second click Line must be drawn. I am confused with the syntax of QGraphicsLineItem and also how to use it. I am new to Qt. Please help me out to solve this problem.
You can use this code snippet.
*h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QStack>
#include <QPoint>
#include <QMouseEvent>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
signals:
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
public slots:
private:
QStack<QPoint> stack;
};
#endif // GRAPHICSSCENE_H
*.cpp
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPoint pos = mouseEvent->scenePos().toPoint();
if(stack.isEmpty())
stack.append(pos);
else if(stack.count() == 1)
{
stack.append(pos);
addLine(QLine(stack.pop(),stack.pop()),QPen(Qt::green));
}
}
}
Usage:
GraphicsScene *scene = new GraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
Edit: more beautiful solution which works as you need.
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
qDebug() << "in";
if (mouseEvent->button() == Qt::LeftButton)
{
QPoint pos = mouseEvent->scenePos().toPoint();
if(stack.isEmpty())
stack.append(pos);
else
addLine(QLine(pos,stack.pop()),QPen(Qt::green));
}
}
You can derive the graphics view/scene and override the mousePressEvent
Below is example using derived QGraphicsScene and overridden mousePressEvent
Class Definition :
class MyScene : public QGraphicsScene
Data Members :
QList<QPointF> m_clickPositions;
int m_mode;
Code :
void MyScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(false == sceneRect().contains(event->scenePos()))
{
QGraphicsScene::mousePressEvent(event);
}
else if(Qt::LeftButton == event->button() && m_mode == ConstructMode)
{
m_clickPositions.append(event->scenePos());
if(m_clickPositions.size() == 2)
{
QLineF lineF(m_clickPositions[0], m_clickPositions[1]);
QGraphicsLineItem* item = this->addLine(lineF);
m_clickPositions.clear();
m_mode = ScrollMode;
}
}
}
I had used something similar in my project and extracted the code. Hope this helps.
Please comment is this is not working.
Edit ::
ConstructMode and Scroll mode are used in the above program so that I can distinguish whether I want to Draw/Construct or just scroll the scene. You can remove them and the declaration of m_mode if not required by you.
If you want to use the modes you can define some public constants and add a method setMode(). Please see the code below.
MyScene.h or some Constant file if you have one
#define ConstructMode 100
#define ScrollMode 101
And add the following function
void MyScene::setMode(int mode)
{
m_mode = mode;
}
After this if you want to enter the construction mode you will need to call myScene->setMode(ConstructMode) everytime, as after the item is constructed the mode is reset to ScrollMode.

invalid static_cast from type 'QCell*' to type 'QWidget*'

this error appears in line 4:
void QPiece::setPosition( QPoint value )
{
_position = value;
QWidget* parentWidget = static_cast<QWidget *>( _board->Cells[value.x() ][ value.y() ]);
if (parentWidget->layout()) {
parentWidget->layout()->addWidget( this ); }
else {
QHBoxLayout *layout = new QHBoxLayout( parentWidget );
layout->setMargin(0);
layout->addWidget(this);
parentWidget->setLayout(layout);
}
this->setParent( _board->Cells[ value.x() ][ value.y() ] );
}
Here is definition of function Cells():
class QBoard : public QWidget
{
Q_OBJECT
public:
QCell *Cells[8][8];
QBoard(QWidget *parent = 0);
void drawCells();
private:
void positionCells();
};
I think I do something wrong, but what? Thanks in advance.
Here is type of QCell, and i think QWidget is parent to QLabel
class QCell:public QLabel
{
Q_OBJECT
public:
QCell( QPoint position, QWidget *parent = 0 );
private:
QGame *_game;
QPoint _position;
protected:
void mousePressEvent( QMouseEvent *ev );
};
This should work without a cast at all, the conversion from derived to base is implicit.
A likely cause of this error would be that you only have a forward declaration of QCell visible in that compilation unit, which would trigger this error. You need to have the complete class declaration visible for the compiler to know whether that conversion is legal or not.
Example:
#include <QWidget>
class QCell;
int main(int argc, char **argv)
{
QCell *w = 0;
QWidget *q = static_cast<QWidget*>(w);
}
main.cpp: In function ‘int main(int, char**)’:
main.cpp:8:41: error: invalid static_cast from type ‘QCell*’ to type ‘QWidget*’

Why multi-touch events using touchpad (laptop) is not working in this Qt program?

I am trying to handle mult-itouch events in this simple QWidget based program but not able to receive any touch events.
"MyWidget.h"
#include <QWidget>
class QPaintEvent;
class QEvent;
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *);
bool event ( QEvent * event );
};
"MyWidget.cpp"
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent)
{
setAttribute(Qt::WA_AcceptTouchEvents);
}
void MyWidget::paintEvent(QPaintEvent *evt) {
QPainter painter(this);
painter.fillRect(rect(),QColor(0,255,0));
// painter.drawText(QPoint(rect().left(),rect().top()),"Hello world");
}
bool MyWidget::event(QEvent *event){
if(event->type() == QEvent::TouchBegin ||
event->type() == QEvent::TouchEnd ||
event->type() == QEvent::TouchUpdate ){
qDebug() <<"Touch events";
}
else if(event->type() == QEvent::MouseButtonDblClick) {
qDebug() <<"double click";
}
return QWidget::event(event);
}
Am I missing anything here ?
To make touch events work add the following to your MainWindow:
MyWidget *myWidget = ...;
setCentralWidget(myWidget);
In the MyWidget constructor add:
setAttribute(Qt::WA_AcceptTouchEvents);
//grabGesture(Qt::PinchGesture);
//setAttribute(Qt::WA_InputMethodEnabled);
//setFocusPolicy(Qt::WheelFocus);
setAttribute(Qt::WA_StaticContents);

Resources