this is my first try trying to use Drag&Drop feature of Qt. I'm a beginner, I made my first subclassing this week although I have made other 2 Qt programs in the past.
I need a movable by Drag&Drop QGraphicsTextItem to show on a QGraphicsView that is connected to the corresponding QGraphicScene. So I can retrieve the new position of the item.
I have looked at the animated robot example and this link: http://www.qtcentre.org/threads/50028-Drag-and-Drop-QGraphicsTextItem
The code of the link above, looked well for my. So I reimplemented it but when building, the compiler shows all kind of errors I´m not sure how to overcome. I don't know where to start, and don't know what piece of code is incorrect...
examples of errors appearing:
error: no matching function for call to 'GraphicsTextItem::setCursor(Qt::CursorShape)'
setCursor(Qt::OpenHandCursor);
^
error: invalid use of incomplete type 'class QGraphicsSceneDragDropEvent'
if(event->mimeData()->hasText())
^
error: forward declaration of 'class QGraphicsSceneDragDropEvent'
class QGraphicsSceneDragDropEvent;
^
I'll leave the code:
Header:
#ifndef GRAPHICSTEXTITEM_H
#define GRAPHICSTEXTITEM_H
#include <QGraphicsTextItem>
class GraphicsTextItem : public QGraphicsTextItem
{
Q_OBJECT
public:
GraphicsTextItem(QGraphicsItem *parent = 0);
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
private:
bool dragOver;
};
//! [0]
#endif // GRAPHICSTEXTITEM_H
and the Implementation:
#include <graphicstextitem.h>
#include <QDrag>
//#include <QGraphicsScene>
GraphicsTextItem::GraphicsTextItem(QGraphicsItem *parent)
:QGraphicsTextItem(parent)
{
//setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
setTextInteractionFlags(Qt::TextEditorInteraction);
setAcceptedMouseButtons(Qt::LeftButton);
setAcceptDrops(true);
setCursor(Qt::OpenHandCursor);
}
void GraphicsTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
if(event->mimeData()->hasText())
{
event->setAccepted(true);
update();
dragOver = true;
}
else
event->setAccepted(false);
}
void GraphicsTextItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
Q_UNUSED(event);
dragOver = false;
update();
}
void GraphicsTextItem::dropEvent(QGraphicsSceneDragDropEvent *event)
{
event->setAccepted(true);
//qDebug() << "I dropped it";
dragOver = false;
update();
}
void GraphicsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
setCursor(Qt::ClosedHandCursor);
}
void GraphicsTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length()
< QApplication::startDragDistance())
return;
QDrag *drag = new QDrag(event->widget());
QMimeData *mime = new QMimeData;
mime->setText(this->toPlainText());
drag->setMimeData(mime);
drag->exec();
setCursor(Qt::OpenHandCursor);
}
void GraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
setCursor(Qt::OpenHandCursor);
}
The error message is telling you that there is a forward declaration somewhere in your code, although that is not true based on the code you have shown so far.
Either way, for accessingt members of your heap object, you need more than just forward declaration. You are missing this include:
#include <QGraphicsSceneDragDropEvent>
Related
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.
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.
I'm using Qt Creator to create a gui for a mineseeper game.
How can I know a QpushButton clicked with rightclick? for flag in the game.
In other word, which signal used for rightclick?
Create your own button with filter at mousePressEvent slot.
qrightclickbutton.h
#ifndef QRIGHTCLICKBUTTON_H
#define QRIGHTCLICKBUTTON_H
#include <QPushButton>
#include <QMouseEvent>
class QRightClickButton : public QPushButton
{
Q_OBJECT
public:
explicit QRightClickButton(QWidget *parent = 0);
private slots:
void mousePressEvent(QMouseEvent *e);
signals:
void rightClicked();
public slots:
};
#endif // QRIGHTCLICKBUTTON_H
qrightclickbutton.cpp
#include "qrightclickbutton.h"
QRightClickButton::QRightClickButton(QWidget *parent) :
QPushButton(parent)
{
}
void QRightClickButton::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::RightButton)
emit rightClicked();
}
Now connect like this
QRightClickButton *button = new QRightClickButton(this);
ui->gridLayout->addWidget(button);
connect(button, SIGNAL(rightClicked()), this, SLOT(onRightClicked()));
Create a slot in MainWindow.cpp.
void MainWindow::onRightClicked()
{
qDebug() << "User right clicked me";
}
It works for me!
I think QPushButton is internally implemented to listen to left mouse clicks only. But you can easily extend QPushButton and re-implement let's say the mouse release event and do your thing if the right mouse button was pressed, e.g. emit a custom rightClicked() signal for example:
signals:
void rightClicked();
protected:
void mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton) emit rightClicked();
else if (e->button() == Qt::LeftButton) emit clicked();
}
... or you can create an overload of the clicked signal that forwards the mouseEvent pointer so you can do the same check outside of the button.
signals:
void clicked(QMouseEvent *);
protected:
void mouseReleaseEvent(QMouseEvent *e) {
emit clicked(e);
}
Then you do the check in the slot you connect the button's clicked(QMouseEvent *) signal to and proceed accordingly.
I just wrote this little helper adapter to make any existing button right-clickable with no need to subclass it:
class CRightClickEnabler : public QObject
{
public:
CRightClickEnabler(QAbstractButton * button): QObject(button), _button(button) {
button->installEventFilter(this);
};
protected:
inline bool eventFilter(QObject *watched, QEvent *event) override {
if (event->type() == QEvent::MouseButtonPress)
{
auto mouseEvent = (QMouseEvent*)event;
if (mouseEvent->button() == Qt::RightButton)
_button->click();
}
return false;
}
private:
QAbstractButton* _button;
};
Usage:
connect(ui->pushButton, &QPushButton::clicked, [](){qDebug() << "Button clicked";});
new CRightClickEnabler(ui->pushButton);
From now on, the clicked signal will be triggered by the right click as well as left click. There's no need to delete this object - it uses ui->pushButton as parent and will be auto-deleted by Qt when the parent is destroyed.
Obviously, you can write 2 lines of code (literally) to declare a new signal here and emit that signal upon right click instead of clicked, if desired.
I'd like to suggest this option as well, without need for event filter/other stuffs...
self.button.released.connect(self.doStuff)
self.button.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.button.customContextMenuRequested.connect(partial(self.doStuff, False))
def doStuff(self,state=True,p=QPoint()):
print("True for left, False for right!",state)
I want to subclass QLineEdit to add it some signals like mouseEnter() and mouseExit() to become informed when the mouse courser is over the widget and when leaves it.
I could write mouseEnter() like below.
class MyLineEdit:public QLineEdit{
//
// ..
//
protected:
void mouseMoveEvent(QMouseEvent *e);
signals:
void mouseEnter();
}
void MyLineEdit::mouseMoveEvent(QMouseEvent *e)
{
emit mouseEnter();
QLineEdit::mouseMoveEvent(e);
}
It works correctly.
How can I write mouseExit()?
leaveEvent is not a very good name!
#include <QtGui>
#include <QEvent>
class Editor : public QLineEdit
{
Q_OBJECT
public:
void leaveEvent(QEvent *);
signals:
void mouseLeave();
};
void Editor::leaveEvent(QEvent *e);
{
qDebug() << "Mouse has left the building..";
emit mouseLeave();
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
How can I develop drag and drop functionality in QML? I want to drag and drop one image to another.
At this point in time, you will probably need to use C++, especially if you want to accept drops from outside the QML application (e.g. the user drags a file from a file manager to your app). Here's an example component class to implement a DropArea item:
DropArea.h:
#ifndef DropArea_H
#define DropArea_H
#include <QDeclarativeItem>
/**
An oversimplified prototype Item which accepts any drop that includes
data with mime type of text/plain, and just emits the text.
*/
class DropArea : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(bool acceptingDrops READ isAcceptingDrops WRITE setAcceptingDrops NOTIFY acceptingDropsChanged)
public:
DropArea(QDeclarativeItem *parent=0);
bool isAcceptingDrops() const { return m_accepting; }
void setAcceptingDrops(bool accepting);
signals:
void textDrop(QString text);
void acceptingDropsChanged();
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
private:
bool m_accepting;
};
#endif
DropArea.cpp:
#include <QGraphicsSceneDragDropEvent>
#include <QMimeData>
#include "DropArea.h"
DropArea::DropArea(QDeclarativeItem *parent)
: QDeclarativeItem(parent),
m_accepting(true)
{
setAcceptDrops(m_accepting);
}
void DropArea::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event->acceptProposedAction();
setCursor(Qt::DragMoveCursor);
}
void DropArea::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
unsetCursor();
}
void DropArea::dropEvent(QGraphicsSceneDragDropEvent *event)
{
emit textDrop(event->mimeData()->text());
unsetCursor();
}
void DropArea::setAcceptingDrops(bool accepting)
{
if (accepting == m_accepting)
return;
m_accepting = accepting;
setAcceptDrops(m_accepting);
emit acceptingDropsChanged();
}
your QML:
DropArea {
onTextDrop: ...
}
And you can implement a DragSourceArea similarly.
I know it's been a while but I struggled so much with this I wanted to share the Qt5 version:
Based on ecloud an Michael's example:
DropArea.h:
#ifndef DropArea_H
#define DropArea_H
#include <QQuickItem>
/**
An oversimplified prototype Item which accepts any drop that includes
data with mime type of text/plain, and just emits the text.
*/
class DropArea : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(bool acceptingDrops READ isAcceptingDrops WRITE setAcceptingDrops NOTIFY acceptingDropsChanged)
public:
DropArea(QQuickItem *parent=0);
bool isAcceptingDrops() const { return m_accepting; }
void setAcceptingDrops(bool accepting);
signals:
void textDrop(QString text);
void acceptingDropsChanged();
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dropEvent(QDropEvent *event);
private:
bool m_accepting;
};
#endif
DropArea.cpp:
#include <QGraphicsSceneDragDropEvent>
#include <QMimeData>
#include "DropArea.h"
DropArea::DropArea(QQuickItem *parent)
: QQuickItem (parent),
m_accepting(true)
{
setFlag(QQuickItem::ItemAcceptsDrops, m_accepting);
}
void DropArea::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}
void DropArea::dragLeaveEvent(QDragLeaveEvent *event)
{
unsetCursor();
}
void DropArea::dropEvent(QDropEvent *event)
{
qDebug() << event->mimeData()->text();
unsetCursor();
}
void DropArea::setAcceptingDrops(bool accepting)
{
if (accepting == m_accepting)
return;
m_accepting = accepting;
setFlag(QQuickItem::ItemAcceptsDrops, m_accepting);
emit acceptingDropsChanged();
}
Qml:
Drop2 {
id: myDropArea
}