_after_更改需要QGraphicsScene信号或事件

时间:2012-06-27 18:09:09

标签: qt events signals qgraphicsscene

我使用Qt框架的QGraphicsScene。在场景中我有一些QGraphicsItem s,用户可以选择和移动。 我想要一个信息标签,其中显示当前移动的选择的当前x和y坐标(可以包含许多项目)。

我尝试使用changed的信号QGraphicsScene。但是在之前触发,将项目的x()和y()属性设置为新值。因此标签始终显示倒数第二个坐标。如果慢慢移动鼠标,则显示不是很错误。但随着快速移动和突然停止,标签是错误的。我需要一个在场景改变之后被触发的信号。

我还尝试覆盖itemChange的{​​{1}}方法。但它是一样的。在更改之前触发。 (新坐标位于此方法的参数内,但我需要一次所有选定项的新坐标)

我还试图覆盖QGraphicsItemmouseMove的{​​{1}}事件,但是在设置新坐标之前,它们也是。< / p>

我做了一个测试:我使用了单拍计时器,以便在信号发出后100毫秒更新标签。一切正常。但计时器对我来说无法解决。

我该怎么办? 让所有物品都不可移动并由我自己处理所有物品?

2 个答案:

答案 0 :(得分:4)

QGraphicsItem::itemChange()是正确的方法,你可能只是检查错误的标志。这样的事应该可以正常工作:

QVariant::myGraphicsItem( GraphicsItemChange change, const QVariant &value )
{
  if( change == QGraphicsItem::ItemPositionHasChanged )
  {
     // ...
  }
}

请注意使用 QGraphicsItem::ItemPositionHasChanged 而不是QGraphicsItem::ItemPositionChange,前者在位置更改后称为,而不是之前。

答案 1 :(得分:3)

解决方案是结合您已经在做的各种事情。使用工具itemChange,查找并计算具有更新几何图形的项目。一旦您计算了当前选择中的项目数量,就会触发一个信号,该信号将为更新您的状态做好准备。确保您已在所有商品上设置QGraphicsItem::ItemSendsGeometryChanges标记!

编辑此代码以消除使用零定时器方法时固有的延迟。以下是展示它的sscce

您可以通过单击窗口创建随机半径的圆圈。使用Ctrl-单击或⌘-单击切换选择。移动项目时,质心菱形会跟随所选组的质心。这给出了代码确实有效的视觉确认。当选择为空时,不显示质心。

我无偿地添加了代码来展示如何利用Qt的属性系统,以便项目可以是通用的,并利用场景的notifier属性(如果有)。在它缺席的情况下,这些项目根本就不会通知,就是这样。

screenshot of the example

// https://github.com/KubaO/stackoverflown/tree/master/questions/scenemod-11232425
#include <QtWidgets>

const char kNotifier[] = "notifier";

class Notifier : public QObject
{
   Q_OBJECT
   int m_count = {};
public:
   int count() const { return m_count; }
   void inc() { m_count ++; }
   void notify() { m_count = {}; emit notification(); }
   Q_SIGNAL void notification();
};

typedef QPointer<Notifier> NotifierPointer;
Q_DECLARE_METATYPE(NotifierPointer)

template <typename T> class NotifyingItem : public T
{
protected:
   QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override {
      QVariant v;
      if (change == T::ItemPositionHasChanged &&
          this->scene() &&
          (v=this->scene()->property(kNotifier)).isValid())
      {
         auto notifier = v.value<NotifierPointer>();
         notifier->inc();
         if (notifier->count() >= this->scene()->selectedItems().count()) {
            notifier->notify();
         }
      }
      return T::itemChange(change, value);
   }
};

// Note that all you need to make Circle a notifying item is to derive from
// NotifyingItem<basetype>.

class Circle : public NotifyingItem<QGraphicsEllipseItem>
{
   QBrush m_brush;
public:
   Circle(const QPointF & c) : m_brush(Qt::lightGray) {
      const qreal r = 10.0 + (50.0*qrand())/RAND_MAX;
      setRect({-r, -r, 2.0*r, 2.0*r});
      setPos(c);
      setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable |
               QGraphicsItem::ItemSendsGeometryChanges);
      setPen({Qt::red});
      setBrush(m_brush);
   }
};

class View : public QGraphicsView
{
   Q_OBJECT
   QGraphicsScene scene;
   QGraphicsSimpleTextItem text;
   QGraphicsRectItem centroid{-5, -5, 10, 10};
   Notifier notifier;
   int deltaCounter = {};
public:
   explicit View(QWidget *parent = {});
protected:
   Q_SLOT void gotUpdates();
   void mousePressEvent(QMouseEvent *event) override;
};

View::View(QWidget *parent) : QGraphicsView(parent)
{
   centroid.hide();
   centroid.setRotation(45.0);
   centroid.setPen({Qt::blue});
   centroid.setZValue(2);
   scene.addItem(&centroid);
   text.setPos(5, 470);
   text.setZValue(1);
   scene.addItem(&text);
   setRenderHint(QPainter::Antialiasing);
   setScene(&scene);
   setSceneRect(0,0,500,500);
   scene.setProperty(kNotifier, QVariant::fromValue(NotifierPointer(&notifier)));
   connect(&notifier, &Notifier::notification, this, &View::gotUpdates);
   connect(&scene, &QGraphicsScene::selectionChanged, &notifier, &Notifier::notification);
}

void View::gotUpdates()
{
   if (scene.selectedItems().isEmpty()) {
      centroid.hide();
      return;
   }
   centroid.show();
   QPointF centroid;
   qreal area = {};
   for (auto item : scene.selectedItems()) {
      const QRectF r = item->boundingRect();
      const qreal a = r.width() * r.height();
      centroid += item->pos() * a;
      area += a;
   }
   if (area > 0) centroid /= area;
   auto st = QStringLiteral("delta #%1 with %2 items, centroid at %3, %4")
         .arg(deltaCounter++).arg(scene.selectedItems().count())
         .arg(centroid.x(), 0, 'f', 1).arg(centroid.y(), 0, 'f', 1);
   this->centroid.setPos(centroid);
   text.setText(st);
}

void View::mousePressEvent(QMouseEvent *event)
{
   const auto center = mapToScene(event->pos());
   if (! scene.itemAt(center, {})) scene.addItem(new Circle{center});
   QGraphicsView::mousePressEvent(event);
}

int main(int argc, char *argv[])
{
   QApplication app{argc, argv};
   View v;
   v.show();
   return app.exec();
}
#include "main.moc"