2015-09-14 71 views
3

我想在將它添加到場景後移動一個QGraphicsRectItem。它移動,但與鼠標指針有一定的偏移量。我認爲這只是將鼠標指針位置添加到其原始位置。我對如何解決這個問題感到不知所措。用鼠標移動一個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); 
    } 

}; 

的ucFilter對象在場景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(); 
} 

可能在哪裏這個問題呢? 這裏的什麼我實際看到的截圖:enter image description here

我想繪製矩形鼠標的位置..

+0

也許2個座標(鼠標和物品)有不同的起源,如果我記得物品的座標是相對於場景的,而鼠標的座標正好基於 – Marco

+0

!我怎麼能改變這個? –

+1

Woohoo'std :: shared_ptr'瘋狂。您不需要將任何成員存儲爲指針。你的物品擁有它的孩子以及你擁有的任何其他參數。按價值存儲它們。你在做什麼是我見過的最邪惡的貨物崇拜節目。有人說服你,共同的指針是要走的路,但你不明白他們爲什麼要走(以及何時),並不加區別地使用它們。這真是淫穢。你的初始化程序列表在哪裏?通過const引用傳遞? UURGH。 –

回答

3

你有幾個問題:

  1. 採用共享指針來保存一切完全沒有根據。場景充當物品的容器 - 就像QObject是對象的容器。

  2. ucFilter沒有孩子。它持有指向其他項目的指針,但這是不必要的。基本項目本身可以是矩形,並且可以將文本作爲子項。這樣你就不需要以特殊的方式處理定位。

  3. ucFilter可以移動。不要自己重新實現該功能。

  4. 當您通過引用傳遞內容時,請將它們作爲常量引用傳遞,除非您打算將修改後的值傳遞出去。如果你想改變函數體內部的值,你可以用值來傳遞它。

  5. 當您拖動項目時,鼠標已被抓取。

讓我們從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(); 
} 

您可以將項目從右側的列表拖到左側的場景中。您還可以移動場景中的項目,以重新定位它們。

+0

熱烈感謝你從我的心臟底部,不僅你解決了我的問題,而是幫助我提高我的編程。你的答案是傳奇。 –