简体   繁体   中英

Moving a QGraphicsRectItem with mouse

I'm trying to move a QGraphicsRectItem after I add it to the scene. It moves, but appears with a certain offset from the mouse pointer. I think it is simply adding the mouse pointer position to its original position. I am at a loss as to how to work around this.

Here is my code:

class ucFilter : public QGraphicsItem {

    std::shared_ptr<QGraphicsRectItem> m_rect;
    std::shared_ptr<QGraphicsTextItem> m_text;
    std::shared_ptr<QString> m_name;
    std::shared_ptr<QPointF> m_pos;
    QGraphicsItem* selectedItem;

    bool m_mouseGrabbed;
public:
    static const int default_x = 80, default_y=40;
    ucFilter::ucFilter(QString &name, QPointF &pos){
        m_name  = shared_ptr<QString>(new QString(name));
        m_pos   = shared_ptr<QPointF>(new QPointF(pos));
        m_rect  = shared_ptr<QGraphicsRectItem>( new QGraphicsRectItem(pos.x()-default_x, pos.y()-default_y, 2*default_x, 2*default_y ));


        m_text  = shared_ptr<QGraphicsTextItem>( new QGraphicsTextItem(name));

        m_text->setPos(pos.x() - m_text->boundingRect().width()/2, pos.y()- 30);
        selectedItem = NULL;
        m_mouseGrabbed = false;
    }

    QGraphicsRectItem*  getRect()   { return m_rect.get();  }
    QGraphicsTextItem*  getText()   { return m_text.get();  }
    QString*            getName()   { return m_name.get();  }
    QPointF*            getPos()    { return m_pos.get();   }

    void                setPos(QPointF newPos)  { m_pos->setX(newPos.x()); m_pos->setY(newPos.y()); }

    QRectF ucFilter::boundingRect() const
    {
        return m_rect->boundingRect();
    }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        Q_UNUSED(widget);
        //QBrush brush(

        if (!m_mouseGrabbed){ grabMouse(); m_mouseGrabbed = true;   }

    }

    void mousePressEvent(QGraphicsSceneMouseEvent *event){
        selectedItem = this;
        QGraphicsItem::mousePressEvent(event);
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
        selectedItem = NULL;
        QGraphicsItem::mouseReleaseEvent(event);
    }

    void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
    {
        if(NULL != selectedItem){
            m_text->setPos(event->pos());
            m_rect->setPos(event->pos());
        }
        QGraphicsItem::mouseMoveEvent(event);
    }

};

the ucFilter object is created in the scene dropEvent:

void cGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent * event){

    QTreeView* source = static_cast<QTreeView*>(event->source());

    string name = event->mimeData()->text().toUtf8().constData();
    if(0 == name.length()){
        event->acceptProposedAction();
        return ; // nothing to do anymore 
    }
    QPointF pos = event->scenePos ();

    shared_ptr<ucFilter> newFilter = shared_ptr<ucFilter>(new ucFilter(event->mimeData()->text(),event->scenePos ()));
    m_filters.push_back(newFilter);
    this->addItem(newFilter->getRect());
    this->addItem(newFilter->getText());

    this->addItem(newFilter.get()); // also add the item to grab mouse events

    event->acceptProposedAction();
}

Where could the problem be ? Here a screenshot of what I'm actually seeing : 在此输入图像描述

I would like the rectangle to be drawn where the mouse is..

You have several issues:

  1. The use of shared pointers to hold everything is completely unwarranted. The scene acts as a container for items - just like QObject is a container for objects.

  2. ucFilter doesn't have children. It holds pointers to other items, but that is unnecessary. The base item can be a rectangle itself, and it can have the text as a child. That way you don't need to handle positioning in a special fashion.

  3. ucFilter can be movable. Don't reimplement that functionality yourself.

  4. When you pass things by reference, pass them as const references unless you are intending to pass the modified value out. If you wish to change the value inside of the body of the function, you can pass it by value instead.

  5. The mouse is already grabbed when you are dragging the item.

Let's start with the ucFilter item. It is really simple and does everything you need. Note that m_text is held by value, and is made a child of the rectangle parent.

// https://github.com/KubaO/stackoverflown/tree/master/questions/graphics-item-drop-32574576
#include <QtWidgets>

class ucFilter : public QGraphicsRectItem {
   QGraphicsTextItem m_text;
public:
   ucFilter(const QString &name, const QPointF &pos, QGraphicsItem * parent = 0) :
      QGraphicsRectItem(parent),
      m_text(this)
   {
      static const QRect defaultRect(0, 0, 160, 80);
      setPos(pos);
      setRect(QRect(-defaultRect.topLeft()/2, defaultRect.size()));
      setFlags(QGraphicsItem::ItemIsMovable);
      setName(name);
   }
   void setName(const QString & text) {
      m_text.setPlainText(text);
      m_text.setPos(-m_text.boundingRect().width()/2, -30);
   }
   QString name() const {
      return m_text.toPlainText();
   }
};

Since we're dropping from a convenience widget (a QListWidget ), we need to decode the text from a application/x-qabstractitemmodeldatalist mime type:

const char * kMimeType = "application/x-qabstractitemmodeldatalist";

QVariant decode(const QMimeData* data, Qt::ItemDataRole role = Qt::DisplayRole) {
   auto buf = data->data(kMimeType);
   QDataStream stream(&buf, QIODevice::ReadOnly);
   while (!stream.atEnd()) {
      int row, col;
      QMap<int, QVariant> map;
      stream >> row >> col >> map;
      if (map.contains(role)) return map[role];
   }
   return QVariant();
}

The scene creates an item as soon as a drag enters it, and moves the item during the drag.

Ownership of the items remains with the scene: we don't need to delete any items unless we want to explicitly remove them from the scene. The m_dragItem is used to refer to the currently dragged item simply to move it and add it to m_filters upon completion of the drop. It the drag leaves the scene (or is aborted), the item is simply deleted from the scene.

class cGraphicsScene : public QGraphicsScene {
   QList<ucFilter*> m_filters;
   ucFilter* m_dragItem;
public:
   cGraphicsScene(QObject * parent = 0) : QGraphicsScene(parent), m_dragItem(nullptr) {}
   void dragEnterEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
      if (!event->mimeData()->hasFormat(kMimeType)) return;
      auto name = decode(event->mimeData()).toString();
      if (name.isEmpty()) return;
      QScopedPointer<ucFilter> filter(new ucFilter(name, event->scenePos()));
      addItem(m_dragItem = filter.take());
      event->acceptProposedAction();
   }
   void dragMoveEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
      if (!m_dragItem) return;
      m_dragItem->setPos(event->scenePos());
      event->acceptProposedAction();
   }
   void dropEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
      if (!m_dragItem) return;
      m_dragItem->setPos(event->scenePos());
      m_filters << m_dragItem;
      event->acceptProposedAction();
   }
   void dragLeaveEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
      delete m_dragItem;
      m_dragItem = nullptr;
      event->acceptProposedAction();
   }
};

The test harness is very simple: our scene, a view displaying it, and a list with two items that you can drag onto the scene.

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   QWidget w;
   cGraphicsScene scene;
   QGraphicsView view(&scene);
   QListWidget list;
   QHBoxLayout l(&w);
   l.addWidget(&view);
   l.addWidget(&list);

   list.setFixedWidth(120);
   list.addItem("Item1");
   list.addItem("Item2");
   list.setDragDropMode(QAbstractItemView::DragOnly);
   view.setAcceptDrops(true);

   w.resize(500, 300);
   w.show();
   return app.exec();
}

You can drag items from the list on the right to the scene on the left. You can also move the items within the scene, to relocate them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM