用鼠标移动QGraphicsRectItem

时间:2015-09-14 21:56:32

标签: c++ qt

我在将QGraphicsRectItem添加到场景后尝试移动它。它会移动,但与鼠标指针有一定的偏移。我认为只是将鼠标指针位置添加到其原始位置。我不知道如何解决这个问题。

这是我的代码:

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);
    }

};

在场景dropEvent:

中创建了ucFilter对象
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();
}

问题出在哪里? 这是我实际看到的屏幕截图:enter image description here

我希望在鼠标所在的位置绘制矩形..

1 个答案:

答案 0 :(得分:3)

您有几个问题:

  1. 使用共享指针来保存所有内容是完全没有根据的。场景充当项目的容器 - 就像QObject是对象的容器一样。

  2. ucFilter没有孩子。它包含指向其他项目的指针,但这是不必要的。基本项本身可以是一个矩形,它可以将文本作为子项。这样你就不需要以特殊的方式处理定位。

  3. ucFilter可以移动。不要自己重新实现这些功能。

  4. 当您通过引用传递内容时,将它们作为const引用传递,除非您打算将修改后的值传递出去。如果您希望更改函数体内的值,可以改为按值传递。

  5. 拖动项目时已抓住鼠标。

  6. 让我们从ucFilter项开始。它非常简单,可以满足您的一切需求。请注意,m_text由值保存,并且是矩形父项的子项。

    // 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();
       }
    };
    

    由于我们从便捷小部件(QListWidget)中删除,我们需要解码来自application/x-qabstractitemmodeldatalist mime类型的文本:

    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();
    }
    

    一旦拖动进入场景,场景就会创建一个项目,并在拖动过程中移动项目。

    项目的所有权仍然保留在场景中:除非我们想要从场景中明确删除它们,否则我们不需要删除任何项目。 m_dragItem用于引用当前拖动的项目,只需移动它并在完成删除后将其添加到m_filters。拖动离开场景(或中止),只需从场景中删除该项目。

    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();
       }
    };
    

    测试工具非常简单:我们的场景,显示它的视图,以及包含两个可拖动到场景中的项目的列表。

    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();
    }
    

    您可以将项目从右侧列表拖动到左侧的场景。您还可以移动场景中的项目,以重新定位它们。