I've the following situation:
Basically I've a QTreeWidget with some items, and a canvas that's a subclass of a QGraphicsView. What I want to accomplish is to drag a QTreeWidgetItem in the QGraphicsView subclass. When the mouse is released in the QGraphicsView subclass I want to create a new shape (let's say a circle) with some data of the dragged item (let's say the name itself written inside the circle).
I was able to enable the drag from the QTreeWidget by using the setDragEnabled(true) method, and I'm able to receive the event in the QGraphicsView, because when I enter its area it calls the dragEnterEvent.
In my subclass I've a QGraphicsScene, like this:
#ifndef SEQUENCECANVAS_HPP_
#define SEQUENCECANVAS_HPP_
#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
class SequenceCanvas : public QGraphicsView {
Q_OBJECT
public:
SequenceCanvas(QWidget* parent = nullptr);
virtual ~SequenceCanvas() = default;
protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
private:
void createWidgets();
void createLayout();
private:
QGraphicsScene m_scene;
};
#endif // !SEQUENCECANVAS_HPP_
#include "SequenceCanvas.hpp"
#include <QString>
#include <QHBoxLayout>
#include <QDragEnterEvent>
#include <QMimeData>
SequenceCanvas::SequenceCanvas(QWidget* parent) :
QGraphicsView(parent) {
createWidgets();
createLayout();
setScene(&m_scene);
setAcceptDrops(true);
}
void SequenceCanvas::dragEnterEvent(QDragEnterEvent* event) {
event->acceptProposedAction(); // no filter at the moment I just want to test
}
void SequenceCanvas::dropEvent(QDropEvent* event) {
int i = 0; // just for putting a breakpoint now. Its breakpoint is never raised
}
void SequenceCanvas::createWidgets() {
}
void SequenceCanvas::createLayout() {
}
Even if the dragEnterEvent is called when my mouse enter my widget during the drag, the dropEvent method is not called, so when I release the mouse inside my SequenceCanvas class nothing happens.
Basically I've two questions:
How can I raise the drop event inside my SequenceCanvas class that's a subclass of QGraphicsView?
How can I pass custom data (let's say a vector of strings) with the drop event, so I can pass all needed information for managing the event?
EDIT:
I missed to reimplement the void dragMoveEvent(QDragMoveEvent* event) method. I've added it and now I receive drop events. I need to know how to pass custom data with a drag-drop event now... Thanks to zeFrenchy.
My class body now is:
SequenceCanvas::SequenceCanvas(QWidget* parent) :
QGraphicsView(parent) {
createWidgets();
createLayout();
setScene(&m_scene);
setAcceptDrops(true);
bool ok = acceptDrops();
}
///////////////////////////////////////////////////////////////////////////////
// VIRTUAL PROTECTED SECTION //
///////////////////////////////////////////////////////////////////////////////
void SequenceCanvas::dragEnterEvent(QDragEnterEvent* event) {
event->acceptProposedAction();
}
void SequenceCanvas::dragMoveEvent(QDragMoveEvent* event) {
event->acceptProposedAction();
}
void SequenceCanvas::dropEvent(QDropEvent* event) {
int i = 0;
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE SECTION //
///////////////////////////////////////////////////////////////////////////////
void SequenceCanvas::createWidgets() {
}
void SequenceCanvas::createLayout() {
}
The documentation says the following about DragMoveEvent:
A widget will receive drag move events repeatedly while the drag is within its boundaries, if it accepts drop events and enter events. The widget should examine the event to see what kind of data it provides, and call the accept() function to accept the drop if appropriate.
So you should be provide an override for dragMoveEvent(QDragMoveEvent* event) in which you simply accept the proposed action.
Related
I have a QMainwindow class, in the cpp file I have certain QGroupBox, I have resize event for the mainwindow when I resize the mainwindow it fires. But I want the resizeevent for the QGroupBox, When I resize the QGroupBox I want to write some custom logic to place a button on the top right corner of the QGroupBpox. I have no idea how to implement the QGroupBox class again and put it into mainwindow so I can write some custom logic in the resize event of QGroupBox.
I tried implementing a class inheriting from QWidget. But this doesnt work.
#ifndef SRIBBLEAREA_H
#define SRIBBLEAREA_H
#include <QGroupBox>
#include <QPushButton>
class ScribbleArea : public QWidget {
Q_OBJECT
public:
ScribbleArea(QWidget* parent = nullptr);
protected:
void resizeEvent(QResizeEvent* event) override;
QGroupBox* sensorBox;
QPushButton* fullScreenButton;
};
#endif
#include <iostream>
#include "ScribbleArea.h"
#include <QtDebug>
ScribbleArea::ScribbleArea(QWidget* parent) : QGroupBox(parent) {
this->setMinimumSize(1, 1);
}
ScribbleArea::~ScribbleArea() {
}
void ScribbleArea::resizeEvent(QResizeEvent* e) {
qDebug() << "resize event called for QGroupBox";
QWidget::resizeEvent(e);
}
I want to make one QGraphicsItem in a QGraphicsScene move (or change size) when another one moves. But, trying to access either QGraphicsItem from the QGraphicsScene object causes crashes.
Here's a dummy example. I want to automatically:
Resize BlueItem's width when I click and drag RedItem.
Move RedItem when I click and drag BlueItem.
The dummy code:
redItem.h (A pixmap item)
#include <QGraphicsPixmapItem>
class RedItem : public QGraphicsPixmapItem
{
public:
RedItem();
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
redItem.cpp
RedItem::RedItem()
{
setPixmap(QPixmap(redPixMap));
setFlags(ItemIsMovable);
setCacheMode(DeviceCoordinateCache);
}
void RedItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
setPos(event->scenePos(),0); //I can override this part just fine.
}
blueItem.h (A resizable rectangle item)
#include <QGraphicsRectItem>
class BlueItem : public QGraphicsRectItem
{
public:
BlueItem();
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};
blueItem.cpp is similar to redItem.cpp
graphicsViewWidget.h (The GraphicsView)
#include <QGraphicsView>
#include "blueItem.h"
#include "redItem.h"
class GraphicsViewWidget : public QGraphicsView
{
Q_OBJECT
public:
explicit GraphicsViewWidget(QWidget *parent = 0);
protected:
virtual void mouseMoveEvent(QMouseEvent *event);
private:
RedItem *red;
BlueItem *blue;
};
graphicsViewWidget.cpp
#include "graphicsViewWidget.h"
#include <QRectF>
void Qx::createItem(int frame)
{
red = new RedItem;
red->setPos(0, 0);
scene()->addItem(red);
blue = new BlueItem;
blue->setPos(10, 10);
scene()->addItem(blue);
}
void GraphicsViewWidget::mouseMoveEvent(QMouseEvent *event) //QGraphicsSceneMouseEvent
{
QGraphicsView::mouseMoveEvent(event);
//if(redItem moves)
// resize blueItem;
//if(blueItem moves)
// move redItem;
}
Any help or advice would be greatly appreciated.
If you make item B a child of item A, then it will live in its coordinate space. This means all transformations - moving, scaling, rotating... everything you apply to the parent item will also be applied to all its children and their children and so on...
I am trying to write a simple program (nothing special) which has a QListView and some Buttons.
My question is:
How do I specifically tell the QListView to accept Drag and Drop from the filesystem?
I currently have
setAcceptDrops(true)
Which is OK, but the drag and drop works on the whole (Main)Window. I just want it to work when the file is dragged into the QListView.
Why doesn't this work?:
ui->listView->setAcceptDrops(true);
The whole code:
#include "player.h"
#include "ui_player.h"
#include <QListView>
Player::Player(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Player)
{
ui->setupUi(this);
setAcceptDrops(true);
//This doesnt work:
//ui->listView->setAcceptDrops(true);
}
Player::~Player()
{
delete ui;
}
void Player::dropEvent(QDropEvent *ev)
{
QList<QUrl> urls = ev -> mimeData() -> urls();
foreach(QUrl url, urls)
{
qDebug() << url.toString();
}
ev->acceptProposedAction();
}
void Player::dragEnterEvent(QDragEnterEvent *ev)
{
ev->acceptProposedAction();
}
You should override these event functions for QListView, not QMainWindow. When you do ui->listView->setAcceptDrops(true);, QListView is now the widget that reacts to the drop events, by calling its virtual dropEvent and dragEnterEvent functions.
Make your own class that inherits QListView and define dropEvent and dragEnterEvent in there:
class MyListView
{
public:
MyListView(QWidget *parent); // implement
protected:
void dropEvent(QDropEvent *ev) override; // implement
void dragEnterEvent(QDragEnterEvent *ev) override; // implement
};
You might also want to override dragMoveEvent as the reference says.
I'm having trouble getting events in QGraphicsView working. I've subclassed QGraphicsView and tried to overload the mousePressEvent and wheelEvent. But neither mousePressEvent nor wheelEvent get called.
Here's my code (Edited a few things now):
Declaration:
#include <QGraphicsView>
#include <QGraphicsScene>
class rasImg: public QGraphicsView
{
public:
rasImg(QString file);
~rasImg(void);
initialize();
QGraphicsView *view;
QGraphicsScene *scene;
private:
virtual void mousePressEvent (QGraphicsSceneMouseEvent *event);
virtual void wheelEvent ( QGraphicsSceneMouseEvent * event );
}
Definition:
#include "rasImg.h"
void rasImg::initialize()
{
view = new QGraphicsView();
scene = new QGraphicsScene(QRect(0, 0, MaxRow, MaxCol));
scene->addText("HELLO");
scene->setBackgroundBrush(QColor(100,100,100));
view->setDragMode(QGraphicsView::ScrollHandDrag);
view->setScene(scene);
}
void rasImg::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
qDebug()<<"Mouse released";
scene->setBackgroundBrush(QColor(100,0,0));
}
void rasImg::wheelEvent ( QGraphicsSceneMouseEvent * event )
{
qDebug()<<"Mouse released";
scene->setBackgroundBrush(QColor(100,0,0));
}
So, what am I doing wrong?.Why don't I see a message or background color change when I click the view or use the mouse wheel?
You're not getting the events because they're being handled by the scene object you're creating, not your class.
Remove the QGraphicsScene *scene; from your rasImg and try something like this for the constructor:
rasImg::rasImg(QString file)
: QGraphicsScene(QRect(0, 0, MaxRow, MaxCol))
{
addText("HELLO");
setBackgroundBrush(QColor(100,100,100));
setDragMode(QGraphicsView::ScrollHandDrag);
view = new QGraphicsView();
view->setScene(this);
}
If you want that in two steps, you could do:
rasImg::rasImg(QString file)
: QGraphicsScene()
{
// any constructor work you need to do
}
rasImg::initialize()
{
addText("HELLO");
setSceneRect(QRect(0, 0, MaxRow, MaxCol));
setBackgroundBrush(QColor(100,100,100));
setDragMode(QGraphicsView::ScrollHandDrag);
view = new QGraphicsView();
view->setScene(this);
}
The point is that the scene that is displayed must be an actual instance of your rasImg, not an instance of QGraphicsScene.
If it's the view you're subclassing, do the same thing. The view you're displaying must be an instance of your class, not a plain QGraphicsView.
rasImg::rasImg(QString file)
: QGraphicsView()
{
// constructor work
}
void rasImg::initialize()
{
scene = new QGraphicsScene(QRect(0, 0, MaxRow, MaxCol));
scene->addText("HELLO");
scene->setBackgroundBrush(QColor(100,100,100));
setDragMode(QGraphicsView::ScrollHandDrag);
setScene(scene);
}
QGraphicsView is derived from QWidget. Therefore it receives mouse events like regular widgets. If your code really is
void rasImg::mousePressEvent (QGraphicsSceneMouseEvent *event)
It can not receive events since it should be
void rasImg::mousePressEvent ( QMouseEvent *event )
QGraphicsSceneMouseEvent is for items in QGraphicsScene that receive mouse input.
If you would like to handle clicks on a specific GUI element rather than handle click on the whole scene, you should derive your own class either from QGraphicsItem (see example of SimpleClass here) or derive from one of existing elements, e.g. QGraphicsPixmapItem.
In both cases in your derived class you can override void mousePressEvent(QGraphicsSceneMouseEvent *event);
I'm trying for half an eternity now overriding QWidgets keyPressEvent function in QT but it just won't work. I've to say i am new to CPP, but I know ObjC and standard C.
My problem looks like this:
class QSGameBoard : public QWidget {
Q_OBJECT
public:
QSGameBoard(QWidget *p, int w, int h, QGraphicsScene *s);
signals:
void keyCaught(QKeyEvent *e);
protected:
virtual void keyPressEvent(QKeyEvent *event);
};
QSGameBoard is my QWidget subclass and i need to override the keyPressEvent and fire a SIGNAL on each event to notify some registered objects.
My overridden keyPressEvent in QSGameBoard.cpp looks like this:
void QSGameBoard::keyPressEvent(QKeyEvent *event) {
printf("\nkey event in board: %i", event->key());
//emit keyCaught(event);
}
When i change QSGameBoard:: to QWidget:: it receives the events, but i cant emit the signal because the compiler complains about the scope. And if i write it like this the function doesn't get called at all.
What's the problem here?
EDIT:
As pointed out by other users, the method I outlined originally is not the proper way to resolve this.
Answer by Vasco Rinaldo
Use Set the FocusPolicy to Qt::ClickFocus to get the keybordfocus by
mouse klick. setFocusPolicy(Qt::ClickFocus);
The previous (albeit imperfect) solution I gave is given below:
Looks like your widget is not getting "focus". Override your mouse press event:
void QSGameBoard::mousePressEvent ( QMouseEvent * event ){
printf("\nMouse in board");
setFocus();
}
Here's the source code for a working example:
QSGameBoard.h
#ifndef _QSGAMEBOARD_H
#define _QSGAMEBOARD_H
#include <QWidget>
#include <QGraphicsScene>
class QSGameBoard : public QWidget {
Q_OBJECT
public:
QSGameBoard(QWidget *p, int w, int h, QGraphicsScene *s);
signals:
void keyCaught(QKeyEvent *e);
protected:
virtual void keyPressEvent(QKeyEvent *event);
void mousePressEvent ( QMouseEvent * event );
};
#endif /* _QSGAMEBOARD_H */
QSGameBoard.cpp
#include <QKeyEvent>
#include <QLabel>
#include <QtGui/qgridlayout.h>
#include <QGridLayout>
#include "QSGameBoard.h"
QSGameBoard::QSGameBoard(QWidget* p, int w, int h, QGraphicsScene* s) :
QWidget(p){
QLabel* o = new QLabel(tr("Test Test Test"));
QGridLayout* g = new QGridLayout(this);
g->addWidget(o);
}
void QSGameBoard::keyPressEvent(QKeyEvent* event){
printf("\nkey event in board: %i", event->key());
}
void QSGameBoard::mousePressEvent ( QMouseEvent * event ){
printf("\nMouse in board");
setFocus();
}
main.cpp
#include <QtGui/QApplication>
#include <QtGui/qmainwindow.h>
#include "QSGameBoard.h"
int main(int argc, char *argv[]) {
// initialize resources, if needed
// Q_INIT_RESOURCE(resfile);
QApplication app(argc, argv);
QMainWindow oM;
QGraphicsScene o;
QSGameBoard a(&oM, 1, 2, &o);
oM.setCentralWidget(&a);
a.show();
oM.show();
// create and show your widgets here
return app.exec();
}
You don't have to reimplement mousePressEvent yourself just to call setFocus. Qt planed it already.
Set the FocusPolicy to Qt::ClickFocus to get the keybordfocus by mouse klick.
setFocusPolicy(Qt::ClickFocus);
As said in the manual:
This property holds the way the widget accepts keyboard focus.
The policy is Qt::TabFocus if the widget accepts keyboard focus by tabbing, Qt::ClickFocus if the widget accepts focus by clicking, Qt::StrongFocus if it accepts both, and Qt::NoFocus (the default) if it does not accept focus at all.
You must enable keyboard focus for a widget if it processes keyboard events. This is normally done from the widget's constructor. For instance, the QLineEdit constructor calls setFocusPolicy(Qt::StrongFocus).
If the widget has a focus proxy, then the focus policy will be propagated to it.
Set the FocusPolicy to Qt::ClickFocus to get the keybordfocus by mouse klick.
setFocusPolicy(Qt::ClickFocus);