首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用鼠标移动QGraphicsRectItem

用鼠标移动QGraphicsRectItem
EN

Stack Overflow用户
提问于 2015-09-14 21:56:32
回答 1查看 889关注 0票数 3

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

这是我的代码:

代码语言:javascript
复制
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中创建的:

代码语言:javascript
复制
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();
}

问题在哪里?下面是我所看到的截图:

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-09-15 16:43:38

你有几个问题:

  1. 使用共享指针来保存所有内容是完全没有道理的。场景充当项目的容器--就像QObject是对象的容器一样。
  2. ucFilter没有孩子。它保存指向其他项目的指针,但这是不必要的。基项可以是矩形本身,并且可以将文本作为子项。这样你就不需要以一种特殊的方式来处理定位了。
  3. ucFilter可以移动。不要自己重新实现这个功能。
  4. 通过引用传递事物时,将它们作为const引用传递,除非您打算将修改后的值传递出去。如果希望更改函数体内的值,则可以通过值传递它。
  5. 当您拖动项目时,鼠标已经被抓取。

让我们从ucFilter项开始。它真的很简单,可以做你需要的一切。请注意,m_text是按值持有的,并且是矩形父级的子元素。

代码语言:javascript
复制
// 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类型中解码文本:

代码语言:javascript
复制
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中。如果拖动离开现场(或中止),则将项目从场景中简单地删除。

代码语言:javascript
复制
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();
   }
};

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

代码语言:javascript
复制
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();
}

您可以将项目从右边的列表拖到左边的场景。你也可以移动场景中的物品,重新安置它们。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32574576

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档