需要QGraphicsScene信号或事件_after_变化(Need QGraphicsScene

2019-06-25 19:46发布

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

我曾尝试与信号changedQGraphicsScene 。 但它是X()和项目的Y()属性之前解雇被设置为新值。 所以标签始终显示第二到最后一个坐标。 如果一个人慢慢地移动鼠标,显示器是不是非常错误的。 但随着快速移动和突然停止,标签是错误的。 我需要现场竟改变触发的信号。

我也试图重写itemChange的方法QGraphicsItem 。 但它是相同的。 它改变之前被解雇。 (新坐标是该方法的参数里面,但我需要一次所有选定项的新坐标)

我也试图重写mouseMove事件QGraphicsSceneQGraphicsView ,但他们也都被设定的新坐标

我做了一个测试:我用一个单稳态定时器,以使标签的信号后100毫秒更新一次。 然后一切工作正常。 但计时器对我来说是无解的。

我能做什么? 让所有项目未动,并通过我自己处理一切?

Answer 1:

QGraphicsItem::itemChange()是正确的做法,你可能只是检查了错误的标志。 这样的事情应该很好地工作:

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

注意使用QGraphicsItem::ItemPositionHasChanged而非QGraphicsItem::ItemPositionChange ,位置变化之后 ,而不是之前,前者被调用。



Answer 2:

解决的办法是,你已经在做各种各样的事情结合起来。 仪器itemChange ,寻找并计算与更新的几何形状的物品。 一旦你因为有在当前选择算作很多项目,断火,将一切准备就绪的关于更新状态的信号。 请确保您已设置QGraphicsItem::ItemSendsGeometryChanges标志在所有的项目!

这个代码被编辑以消除在使用零计时器方法中固有的滞后。 下面是一个SSCCE演示它。

您可以通过点击窗口创建随机半径的圆。 该选择切换与按住Ctrl键或⌘点击。 当您移动的项目,重心钻石如下选定组的重心。 这给人以视觉上的确认,该代码确实工作。 当选择是空的,不显示质心。

我无偿添加代码来显示如何利用Qt的属性系统,这样的项目可以是通用的,并充分利用notifier场景的财产,如果它有一个。 在缺席,项目根本就没有通知,这就是它。

// 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"


文章来源: Need QGraphicsScene signal or event for _after_ change