Qt 更改后需要QGraphicsCENE信号或事件

Qt 更改后需要QGraphicsCENE信号或事件,qt,events,signals,qgraphicsscene,Qt,Events,Signals,Qgraphicsscene,我使用Qt框架的qgraphicscene。在场景中,我有一些用户可以选择和移动的QGraphicsItems。 我想有一个信息标签,其中显示当前移动选择的当前x和y坐标(可以由许多项目组成) 我已尝试使用qgraphicscene的已更改的信号。但它是在项目的x()和y()属性设置为新值之前激发的。因此,标签始终显示从第二个到最后一个坐标。如果你慢慢地移动鼠标,显示就不是很错误。但由于快速移动和突然停止,标签是错误的。我需要一个在场景改变后发射的信号 我还尝试覆盖QGraphicsItem的i

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

我已尝试使用
qgraphicscene
已更改的
信号。但它是在项目的x()和y()属性设置为新值之前激发的。因此,标签始终显示从第二个到最后一个坐标。如果你慢慢地移动鼠标,显示就不是很错误。但由于快速移动和突然停止,标签是错误的。我需要一个在场景改变后发射的信号

我还尝试覆盖
QGraphicsItem
itemChange
方法。但这是一样的。它是在换衣服之前发射的。(新坐标位于此方法的参数内,但我需要立即获得所有选定项的新坐标)

我还尝试覆盖
qgraphicscene
QGraphicsView
mouseMove
事件,但它们也在设置新坐标之前

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

我能做什么?
使所有项目不可移动,并由我自己处理所有事情?

解决方案是将您已经在做的各种事情结合起来。仪器
itemChange
,查找并计算具有更新几何图形的项目。当您计算了当前选择中的项目数后,发出一个信号,该信号将为更新您的状态做好准备。确保已在所有项目上设置了
QGraphicsItem::itemssendsgeometrychanges
标志

编辑此代码是为了消除使用零定时器方法时固有的滞后。下面是一个例子来说明这一点

通过在窗口中单击,可以创建任意半径的圆。通过Ctrl或单击切换选择⌘-点击移动项目时,质心菱形将跟随选定组的质心。这提供了代码确实工作的视觉确认。选择为空时,不会显示质心

我免费添加了一些代码来展示如何利用Qt的属性系统,以便这些项目可以是通用的,并利用场景的
notifier
属性(如果有)。在它不存在的情况下,这些项目只是不通知,仅此而已

//https://github.com/KubaO/stackoverflown/tree/master/questions/scenemod-11232425
#包括
常量字符打结器[]=“通知程序”;
类通知程序:公共QObject
{
Q_对象
int m_count={};
公众:
int count()常量{返回m_count;}
void inc(){m_count++;}
void notify(){m_count={};emit notification();}
Q_信号无效通知();
};
typedef QPointer通知器;
Q_DECLARE_元类型(inter)
模板类NotifyingItem:public T
{
受保护的:
QVariant项更改(QGraphicsItem::GraphicsItemChange、常量QVariant和值)重写{
qv变异体;
如果(更改==T::ItemPositionHasChanged&&
这个->场景()&&
(v=此->场景()->属性(打结器)).isValid())
{
自动通知程序=v.value();
通知程序->inc();
如果(通知程序->计数()>=此->场景()->selectedItems().count()){
通知程序->通知();
}
}
返回T::itemChange(change,value);
}
};
//请注意,将Circle设为通知项所需的全部内容是从
//通知项。
类循环:公共通知项
{
毛刷;
公众:
圆(常数QPointF&c):m_画笔(Qt::浅灰色){
常数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});
立根刷(毛刷);
}
};
类视图:公共QGraphicsView
{
Q_对象
QsCENE场景;
QGraphicsSimpleTextItem文本;
qgraphicsrecitem质心{-5,-5,10,10};
通知者通知者;
int deltaCounter={};
公众:
显式视图(QWidget*parent={});
受保护的:
Q_插槽无效更新();
作废MousePresseEvent(QMouseEvent*事件)覆盖;
};
视图::视图(QWidget*父级):QGraphicsView(父级)
{
形心隐藏();
质心旋转(45.0);
质心setPen({Qt::blue});
质心设定值(2);
场景.附加项(&质心);
text.setPos(5470);
text.setZValue(1);
场景。添加项(&text);
SetRenderInt(QPaint::抗锯齿);
场景(和场景);
设定值(0,0500);
setProperty(kNotifier,QVariant::fromValue(notifierInter(¬ifier));
连接(¬ifier,¬ifier::notification,this,&View::gotUpdates);
连接(&scene,&qgraphicscene::selectionChanged,¬ifier,¬ifier::notification);
}
void视图::gotUpdates()
{
if(scene.selectedItems().isEmpty()){
形心隐藏();
返回;
}
质心显示();
QPointF质心;
qreal面积={};
对于(自动项:scene.selectedItems()){
const QRectF r=item->boundingRect();
常数a=r.宽度()*r.高度();
质心+=项目->位置()*a;
面积+=a;
}
如果(面积>0)质心/=面积;
auto st=qstringlateral(“增量#%1,包含%2项,质心位于%3,%4”)
.arg(deltaCounter++).arg(scene.selectedItems().count())
.arg(形心.x(),0,'f',1).arg(形心.y(),0,'f',1);
此->形心.setPos(形心);
text.setText(st);
}
void视图::MousePresseEvent(QMouseEvent*事件)
{
const auto center=maptosene(事件->位置());
if(!scene.itemAt(center,{}))scene.addItem(new Circle{center});
QGraphicsView::MousePresseEvent(事件);
}
int main(int argc,char*ar
// 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"
QVariant::myGraphicsItem( GraphicsItemChange change, const QVariant &value )
{
  if( change == QGraphicsItem::ItemPositionHasChanged )
  {
     // ...
  }
}