图形项目的QGraphicsScene中的可拖动像素图 [英] Draggable pixmaps inside a QGraphicsScene of graphic items

查看:149
本文介绍了图形项目的QGraphicsScene中的可拖动像素图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个场景,其中有一个12 * 4的网格,其中包含QGraphicsItems块,当我右键单击这些块时,我会看到一个contexmenu 可以在块内添加图标,我的建议是 我无法确定如何使这些图标可拖动到图形场景内的其他块,我知道有可拖动图标示例",但如何将代码实现到图形场景.

这是主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsPathItem>
class QGraphicsSceneMouseEvent;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
bool eventFilter(QObject *, QEvent *);
~MainWindow();

private slots:
void showContextMenu(const QPoint&);
void addPixBlock();

private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QGraphicsItem *itemAt(const QPointF&);

int x;
int y;

QMenu *Menu;
QMenu *Submenu;
QAction *Picture;
QGraphicsPixmapItem* pix;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "block.h"
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QPainter>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new QGraphicsScene(this) ;
for(int row=-4;row<8;++row)
  for(int column=0;column<4;++column)
{
  Block *b = new Block;
  scene->addItem(b);
  b->setPos(row* 95,column*85);
}
ui->graphicsView->setScene(scene);
scene->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
QGraphicsItem* MainWindow::itemAt(const QPointF &pos)
{
QList<QGraphicsItem*> items = scene->items(QRectF(pos - QPointF(1,1), 
QSize(3,3)));

foreach(QGraphicsItem *item, items)
     if (item->type() > QGraphicsItem::UserType)
     return item;
 return 0;
  }
 bool MainWindow::eventFilter(QObject *o, QEvent *e)
  {
 QGraphicsSceneMouseEvent *me = (QGraphicsSceneMouseEvent*) e;

 switch ((int) e->type()){

 case QEvent::GraphicsSceneMousePress:{

 switch ((int) me->button()){

  case Qt::RightButton:{

     QGraphicsItem *item = itemAt(me->scenePos());

     if (item && item->type() == Block::Type){
         x=item->scenePos().x();
         y=item->scenePos().y();
        showContextMenu(item->scenePos().toPoint());
      }
   break;
  }
  }
  break;
  }
 }
 return QObject::eventFilter(o, e);
 }
void MainWindow::showContextMenu(const QPoint &pos)
{
Menu= new QMenu("Menu");
Submenu=Menu->addMenu(QIcon(":/img/pix.png"),"Pix");
Picture =Submenu->addAction(QIcon(":/img/pix.png"),"Pix");
connect(Picture, SIGNAL(triggered()), this, SLOT(addPixBlock()));
Menu->exec(QCursor::pos());
}
void MainWindow::addPixBlock()
{
QPixmap pixmap(":/img/pix.png");
pix = scene->addPixmap(pixmap.scaled(70,50));
pix->setPos(x,y);
}

block.h

#ifndef BLOCK_H
#define BLOCK_H
#include <QGraphicsPathItem>
class QGraphicsSceneMouseEvent;
class Block : public QGraphicsPathItem
{
public:
enum { Type = QGraphicsItem::UserType + 3 };
int type() const { return Type; }
Block(QGraphicsItem *parent = 0);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget 
*widget);
bool eventFilter(QObject *, QEvent *);
};
#endif // BLOCK_H

Block.cpp

#include "block.h"
#include <QPainter>
#include <QtWidgets>
class QGraphicsSceneMouseEvent;
Block::Block(QGraphicsItem *parent)
         : QGraphicsPathItem(parent)
{
QPainterPath p;
//<->,|,<->,|,roundness
p.addRoundedRect(0,0,80,50, 5, 5);
setPath(p);
setAcceptDrops(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
void Block::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 
QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->setPen(QPen(QColor(67, 141, 220)));
painter->setBrush(QColor(67, 141, 220,100));
painter->drawPath(path());
}

解决方案

首先,如果您想将QGraphicsPixmapItem放在另一个项目的顶部,则更好的选择是将其设置为您的parentItem.

另一方面,我们可以使用事件过滤器,但是在这种情况下,更好的选择是实现自定义QGraphicsScene,并在用左键按下时允许拖动该项目,为此我们使用QDrag然后传递项目的数据,然后覆盖事件dropEvent,我们将在其中获取该项目并建立一个新的父级.

graphicsscene.h

#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H

#include <QGraphicsScene>

class QMenu;
class QAction;

class GraphicsScene : public QGraphicsScene
{
public:
    using QGraphicsScene::QGraphicsScene;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

private:
    QGraphicsPixmapItem *findPixmapItem(QGraphicsItem *item);
    void createDrag(const QPointF &pos, QWidget *widget, QGraphicsItem *item);
    void showContextMenu(const QPointF &pos);
    void addPixBlock(QGraphicsItem *item);

    QMenu *menu;
    QMenu *submenu;
    QAction *picture;
    QGraphicsPixmapItem *pix;
};

#endif // GRAPHICSSCENE_H

graphicsscene.cpp

#include "graphicsscene.h"

#include <QDrag>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QMimeData>
#include <QWidget>

void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    auto its =  items(QRectF(event->scenePos() - QPointF(1,1), QSize(3,3)));
    auto val = std::find_if(its.constBegin(), its.constEnd(), [](auto const& it){
        return it->type() > QGraphicsItem::UserType;
    });
    if(val == its.constEnd())
        return;
    if(event->button() == Qt::RightButton){
        showContextMenu(event->scenePos());
    }
    else{
        createDrag(event->scenePos(), event->widget(), *val);
    }

}

void GraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    QByteArray byteArray = event->mimeData()->data("Item");
    QGraphicsPixmapItem * item = *reinterpret_cast<QGraphicsPixmapItem**>(byteArray.data());
    QGraphicsItem *item_parent = itemAt(event->scenePos(), QTransform());
    item->setParentItem(item_parent);
}

QGraphicsPixmapItem *GraphicsScene::findPixmapItem(QGraphicsItem *item){
    auto chs = item->childItems();
    auto  val = std::find_if(chs.constBegin(), chs.constEnd(), [](auto const& it){
            return static_cast<QGraphicsPixmapItem *>(it) != Q_NULLPTR;
});
    return val == chs.constEnd() ? Q_NULLPTR : static_cast<QGraphicsPixmapItem *>(*val);
}

void GraphicsScene::createDrag(const QPointF &pos, QWidget *widget, QGraphicsItem *item){
    QGraphicsPixmapItem *pix = findPixmapItem(item);
    if(pix == Q_NULLPTR)
        return;
    QByteArray byteArray(reinterpret_cast<char*>(&pix),sizeof(QGraphicsPixmapItem*));
    QDrag *drag = new QDrag(widget);
    QMimeData * mimeData = new QMimeData;
    mimeData->setData("Item",byteArray);
    drag->setMimeData(mimeData);
    drag->setHotSpot(pos.toPoint()-pix->scenePos().toPoint());
    drag->setPixmap(pix->pixmap());
    drag->start();

}

void GraphicsScene::showContextMenu(const QPointF &pos)
{
    QGraphicsItem *item = itemAt(pos, QTransform());
    menu= new QMenu("Menu");
    submenu = menu->addMenu(QIcon(":/img/pix.png"),"Pix");
    picture = submenu->addAction(QIcon(":/img/pix.png"),"Pix");
    connect(picture, &QAction::triggered, [item, this](){
        addPixBlock(item);
    });
    menu->exec(QCursor::pos());
}

void GraphicsScene::addPixBlock(QGraphicsItem *item)
{
    if(findPixmapItem(item))
        return;
    QPixmap pixmap(":/img/pix.png");
    pix = addPixmap(pixmap.scaled(70,50));
    if(pix->parentItem() != item)
        pix->setParentItem(item);
}

然后我们建立新场景并添加Block.

完整的示例可以在以下链接

I have a scene with a 12*4 grid with blocks of QGraphicsItems ,when i right click on the blocks I have a contexmenu that can add icons inside the blocks my proplem is that I can't fingure out how can I make those icons draggable to the other blocks inside the graphic scene ,I know there is the "Draggable Icons Example" but how can I implement that code to a graphic scene.

this is the mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsPathItem>
class QGraphicsSceneMouseEvent;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
bool eventFilter(QObject *, QEvent *);
~MainWindow();

private slots:
void showContextMenu(const QPoint&);
void addPixBlock();

private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QGraphicsItem *itemAt(const QPointF&);

int x;
int y;

QMenu *Menu;
QMenu *Submenu;
QAction *Picture;
QGraphicsPixmapItem* pix;
};
#endif // MAINWINDOW_H

the mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "block.h"
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QPainter>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new QGraphicsScene(this) ;
for(int row=-4;row<8;++row)
  for(int column=0;column<4;++column)
{
  Block *b = new Block;
  scene->addItem(b);
  b->setPos(row* 95,column*85);
}
ui->graphicsView->setScene(scene);
scene->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
QGraphicsItem* MainWindow::itemAt(const QPointF &pos)
{
QList<QGraphicsItem*> items = scene->items(QRectF(pos - QPointF(1,1), 
QSize(3,3)));

foreach(QGraphicsItem *item, items)
     if (item->type() > QGraphicsItem::UserType)
     return item;
 return 0;
  }
 bool MainWindow::eventFilter(QObject *o, QEvent *e)
  {
 QGraphicsSceneMouseEvent *me = (QGraphicsSceneMouseEvent*) e;

 switch ((int) e->type()){

 case QEvent::GraphicsSceneMousePress:{

 switch ((int) me->button()){

  case Qt::RightButton:{

     QGraphicsItem *item = itemAt(me->scenePos());

     if (item && item->type() == Block::Type){
         x=item->scenePos().x();
         y=item->scenePos().y();
        showContextMenu(item->scenePos().toPoint());
      }
   break;
  }
  }
  break;
  }
 }
 return QObject::eventFilter(o, e);
 }
void MainWindow::showContextMenu(const QPoint &pos)
{
Menu= new QMenu("Menu");
Submenu=Menu->addMenu(QIcon(":/img/pix.png"),"Pix");
Picture =Submenu->addAction(QIcon(":/img/pix.png"),"Pix");
connect(Picture, SIGNAL(triggered()), this, SLOT(addPixBlock()));
Menu->exec(QCursor::pos());
}
void MainWindow::addPixBlock()
{
QPixmap pixmap(":/img/pix.png");
pix = scene->addPixmap(pixmap.scaled(70,50));
pix->setPos(x,y);
}

the block.h

#ifndef BLOCK_H
#define BLOCK_H
#include <QGraphicsPathItem>
class QGraphicsSceneMouseEvent;
class Block : public QGraphicsPathItem
{
public:
enum { Type = QGraphicsItem::UserType + 3 };
int type() const { return Type; }
Block(QGraphicsItem *parent = 0);
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget 
*widget);
bool eventFilter(QObject *, QEvent *);
};
#endif // BLOCK_H

the Block.cpp

#include "block.h"
#include <QPainter>
#include <QtWidgets>
class QGraphicsSceneMouseEvent;
Block::Block(QGraphicsItem *parent)
         : QGraphicsPathItem(parent)
{
QPainterPath p;
//<->,|,<->,|,roundness
p.addRoundedRect(0,0,80,50, 5, 5);
setPath(p);
setAcceptDrops(true);
setAcceptedMouseButtons(Qt::LeftButton);
}
void Block::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 
QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->setPen(QPen(QColor(67, 141, 220)));
painter->setBrush(QColor(67, 141, 220,100));
painter->drawPath(path());
}

解决方案

First of all if you want to place a QGraphicsPixmapItem on top of another item, a better option is to set it as your parentItem.

On the other hand we can use an event filter but a better option in this case is to implement a custom QGraphicsScene, and when pressing with the left key it allows to drag the item, for that we use QDrag and we pass the data of the item, then we overwrite the event dropEvent where we will obtain the item and establish a new parent.

graphicsscene.h

#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H

#include <QGraphicsScene>

class QMenu;
class QAction;

class GraphicsScene : public QGraphicsScene
{
public:
    using QGraphicsScene::QGraphicsScene;

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

private:
    QGraphicsPixmapItem *findPixmapItem(QGraphicsItem *item);
    void createDrag(const QPointF &pos, QWidget *widget, QGraphicsItem *item);
    void showContextMenu(const QPointF &pos);
    void addPixBlock(QGraphicsItem *item);

    QMenu *menu;
    QMenu *submenu;
    QAction *picture;
    QGraphicsPixmapItem *pix;
};

#endif // GRAPHICSSCENE_H

graphicsscene.cpp

#include "graphicsscene.h"

#include <QDrag>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QMenu>
#include <QMimeData>
#include <QWidget>

void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    auto its =  items(QRectF(event->scenePos() - QPointF(1,1), QSize(3,3)));
    auto val = std::find_if(its.constBegin(), its.constEnd(), [](auto const& it){
        return it->type() > QGraphicsItem::UserType;
    });
    if(val == its.constEnd())
        return;
    if(event->button() == Qt::RightButton){
        showContextMenu(event->scenePos());
    }
    else{
        createDrag(event->scenePos(), event->widget(), *val);
    }

}

void GraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    QByteArray byteArray = event->mimeData()->data("Item");
    QGraphicsPixmapItem * item = *reinterpret_cast<QGraphicsPixmapItem**>(byteArray.data());
    QGraphicsItem *item_parent = itemAt(event->scenePos(), QTransform());
    item->setParentItem(item_parent);
}

QGraphicsPixmapItem *GraphicsScene::findPixmapItem(QGraphicsItem *item){
    auto chs = item->childItems();
    auto  val = std::find_if(chs.constBegin(), chs.constEnd(), [](auto const& it){
            return static_cast<QGraphicsPixmapItem *>(it) != Q_NULLPTR;
});
    return val == chs.constEnd() ? Q_NULLPTR : static_cast<QGraphicsPixmapItem *>(*val);
}

void GraphicsScene::createDrag(const QPointF &pos, QWidget *widget, QGraphicsItem *item){
    QGraphicsPixmapItem *pix = findPixmapItem(item);
    if(pix == Q_NULLPTR)
        return;
    QByteArray byteArray(reinterpret_cast<char*>(&pix),sizeof(QGraphicsPixmapItem*));
    QDrag *drag = new QDrag(widget);
    QMimeData * mimeData = new QMimeData;
    mimeData->setData("Item",byteArray);
    drag->setMimeData(mimeData);
    drag->setHotSpot(pos.toPoint()-pix->scenePos().toPoint());
    drag->setPixmap(pix->pixmap());
    drag->start();

}

void GraphicsScene::showContextMenu(const QPointF &pos)
{
    QGraphicsItem *item = itemAt(pos, QTransform());
    menu= new QMenu("Menu");
    submenu = menu->addMenu(QIcon(":/img/pix.png"),"Pix");
    picture = submenu->addAction(QIcon(":/img/pix.png"),"Pix");
    connect(picture, &QAction::triggered, [item, this](){
        addPixBlock(item);
    });
    menu->exec(QCursor::pos());
}

void GraphicsScene::addPixBlock(QGraphicsItem *item)
{
    if(findPixmapItem(item))
        return;
    QPixmap pixmap(":/img/pix.png");
    pix = addPixmap(pixmap.scaled(70,50));
    if(pix->parentItem() != item)
        pix->setParentItem(item);
}

Then we establish that new scene and add the Blocks.

The complete example can be found in the following link

这篇关于图形项目的QGraphicsScene中的可拖动像素图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆