我使用Qt框架的QGraphicsScene
。在场景中我有一些QGraphicsItem
s,用户可以选择和移动。
我想要一个信息标签,其中显示当前移动的选择的当前x和y坐标(可以包含许多项目)。
我尝试使用changed
的信号QGraphicsScene
。但是在之前触发,将项目的x()和y()属性设置为新值。因此标签始终显示倒数第二个坐标。如果慢慢移动鼠标,则显示不是很错误。但随着快速移动和突然停止,标签是错误的。我需要一个在场景改变之后被触发的信号。
我还尝试覆盖itemChange
的{{1}}方法。但它是一样的。在更改之前触发。 (新坐标位于此方法的参数内,但我需要一次所有选定项的新坐标)
我还试图覆盖 我做了一个测试:我使用了单拍计时器,以便在信号发出后100毫秒更新标签。一切正常。但计时器对我来说无法解决。 我该怎么办?
让所有物品都不可移动并由我自己处理所有物品?QGraphicsItem
和mouseMove
的{{1}}事件,但是在设置新坐标之前,它们也是。< / p>
答案 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
属性(如果有)。在它缺席的情况下,这些项目根本就不会通知,就是这样。
// 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(¢roid);
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(¬ifier)));
connect(¬ifier, &Notifier::notification, this, &View::gotUpdates);
connect(&scene, &QGraphicsScene::selectionChanged, ¬ifier, &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"