调整和旋转 QGraphicsItem 会导致形状奇怪 [英] Resizing and rotating a QGraphicsItem results in odd shape

查看:92
本文介绍了调整和旋转 QGraphicsItem 会导致形状奇怪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解如何将缩放和旋转应用于 QGraphicsItem.

我需要能够应用旋转和缩放(不一定保持纵横比)并且我得到了完全出乎意料的结果.

旋转必须围绕项目中心.我这样做似乎没有问题 - 但是如果我尝试调试边界矩形,我会得到看似错误的值.

如果我不保持纵横比,而不是旋转,我会得到一个非常奇怪的歪斜,我一直在努力寻找原因并纠正它.我希望任何人都可以找到解决方案.

对于许多项目 - 如矩形 - 我的解决方案是放弃调整大小 - 只需用给定大小的新项目替换该项目.

调整大小的(黑色)文本应该与参考(红色)具有相同的高度 - 但要高得多;
边界矩形不再是矩形 - 它是倾斜的;
角度看起来比45小很多;

还添加了调整大小但未旋转的参考:

请帮助我理解为什么会发生这种行为以及我能做些什么.

我曾尝试研究 QGraphicsRotation,但我不知道如何应用它......我得到的只是移动而不是旋转.

解决方案

如文档所述,项目的变换以特定顺序在数学上应用 - 这是您将变换矩阵相乘的顺序,并且从概念上讲,与您通常想到的顺序相反.

  1. 应用了 transform.通过在转换期间应用翻译,原点必须包含在转换本身中.
  2. 应用转换 - 每个转换都可以指定自己的中心.
  3. rotation 然后 scale 被应用,两者都相对于 transformOriginPoint.

当您将transform设置为缩放,并设置rotation时,在缩放之前执行旋转.缩放适用于旋转结果 - 它只是在您的情况下水平拉伸旋转版本.

您需要以某种方式强制执行相反的操作顺序.唯一的两种方法是:

  1. 以正确的顺序堆叠转换并将它们传递给transform,或者.

  2. 将正确转换的列表传递给 transformations.

我将演示如何以交互方式执行此操作,您可以使用滑块调整变换参数.

使用transform获得正确的结果:

QGraphicsItem * item = ....;Q变换t;QPointF xlate = item->boundingRect().center();t.translate(xlate.x(), xlate.y());t.旋转(角度);t.scale(xScale, yScale);t.translate(-xlate.x(), -xlate.y());项目-> setTransform(t);

使用transformations获得正确的结果:

QGraphicsItem * item = ....;QGraphicsRotation 腐烂;QGraphicsScale 比例尺;自动居中 = item->boundingRect().center();rot.setOrigin(QVector3D(center));scale.setOrigin(QVector3D(center()));item->setTransformations(QList<QGraphicsTransform*>() << &rot << &scale);

最后,例子:

//https://github.com/KubaO/stackoverflown/tree/master/questions/graphics-transform-32186798#include 结构控制器{上市:QSlider 角度、xScale、yScale;控制器(QGridLayout 和网格,int col){角度.setRange(-180, 180);xScale.setRange(1, 10);yScale.setRange(1, 10);grid.addWidget(&angle, 0, col + 0);grid.addWidget(&xScale, 0, col + 1);grid.addWidget(&yScale, 0, col + 2);}模板 void connect(F && f) { connect(f, f, std::forward(f));}template 无效连接(Fa&& a,Fx&& x,Fy&& y){QObject::connect(&angle, &QSlider::valueChanged, std::forward(a));QObject::connect(&xScale, &QSlider::valueChanged, std::forward(x));QObject::connect(&yScale, &QSlider::valueChanged, std::forward(y));}QTransform xform(QPointF xlate) {Q变换t;t.translate(xlate.x(), xlate.y());t.rotate(angle.value());t.scale(xScale.value(), yScale.value());t.translate(-xlate.x(), -xlate.y());返回 t;}};int main(int argc, char **argv){auto text = QStringLiteral("Hello, World!");QApplication app(argc, argv);QGraphicsScene场景;QWidget w;QGridLayout布局(&w);QGraphicsView 视图(&scene);控制器左(布局,0),右(布局,4);layout.addWidget(&view, 0, 3);auto ref = new QGraphicsTextItem(text);//一个引用,未调整大小ref->setDefaultTextColor(Qt::red);ref->setTransformOriginPoint(ref->boundingRect().center());ref->setRotation(45);场景.addItem(ref);auto leftItem = new QGraphicsTextItem(text);//从左边控制leftItem->setDefaultTextColor(Qt::green);场景.addItem(leftItem);auto rightItem = new QGraphicsTextItem(text);//从右侧控制rightItem->setDefaultTextColor(Qt::blue);场景.addItem(rightItem);QGraphicsRotation 腐烂;QGraphicsScale 比例尺;rightItem->setTransformations(QList<QGraphicsTransform*>() << &rot << &scale);rot.setOrigin(QVector3D(rightItem->boundingRect().center()));scale.setOrigin(QVector3D(rightItem->boundingRect().center()));left.connect([leftItem, &left]{ leftItem->setTransform(left.xform(leftItem->boundingRect().center()));});right.connect([&rot](int a){ rot.setAngle(a); },[&scale](int s){ scale.setXScale(s);}, [&scale](int s){ scale.setYScale(s);});right.angle.setValue(45);right.xScale.setValue(3);right.yScale.setValue(1);view.ensureVisible(scene.sceneRect());w.show();返回 app.exec();}

I can't understand how scaling and rotation are applied to QGraphicsItem.

I need to be able to apply rotation and scaling (not necessarily keeping aspect ratio) and I get fully unexpected results.

Rotation must be around the item center. I seem to have no problem doing that - yet if I try to debug the bounding rectangle, I get seemingly wrong values.

If I don't keep aspect ratio, instead of rotation I get a very weird skew, and I have been struggling for quite a while to find the cause and correct it. I hope anybody can find a solution.

For many items - like rectangles - my solution was to give up on resize - and just replace the item with a new one of given size. I even did that for pixmap (though it probably will affect performance a lot).
But I don't know how to do that for text, or a few other types (svg...).
I am trying to understand how scaling is applied, on rotated items, and how to get it applied correctly.

The code below is an experiment where I scale and rotate a text item, and the result... see attached image

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsTextItem>

void experimentScaling(QGraphicsScene* s)
{
    QGraphicsTextItem* ref = new QGraphicsTextItem();  // a reference, not resized
    ref->setPlainText("hello world");
    s->addItem(ref);
    ref->setDefaultTextColor(Qt::red);
    ref->setRotation(45);

    QGraphicsTextItem* t = new QGraphicsTextItem();    // text item to be experimented on
    t->setPlainText("hello world");
    s->addItem(t);

    QTransform transform;                    // scale
    transform.scale(10, 1);
    t->setTransform(transform);
    t->update();

    QPointF _center = t->boundingRect().center();
    qDebug("%f %f %f %f", t->boundingRect().left(), t->boundingRect().top(), t->boundingRect().right(), t->boundingRect().bottom());   // seems to be unscaled...

    t->setTransformOriginPoint(_center);    // rotation must be around item center - and seems to work even though the bounding rect gives wrong values above
    t->setRotation(45);        // skewed
    t->update();
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QGraphicsScene s;
    QGraphicsView view(&s);
    s.setSceneRect(-20, -20, 800, 600);
    view.show();
    experimentScaling(&s);
    return app.exec();
}

Reference (red) text rotated 45 degrees, text rotated 45 degrees and resized 10,1:

The resized (black) text should have the same height as the reference (red) - yet is much taller;
The bounding rectangle is no longer a rectangle - it is skewed;
The angle looks much smaller than 45;

Added a resized but not rotated reference as well:

Please help me understand why this behavior is happening and what can I do about it.

I have tried looking into QGraphicsRotation but I can't figure out how to apply it... All I get is a move instead of rotation.

解决方案

As documented, the item's transformations are mathematically applied in a certain order - this is the order you'd be multiplying the transform matrices in and is, conceptually, the reverse of the order you'd normally think of.

  1. The transform is applied. The origin point must be included in the transform itself, by applying translations during the transform.
  2. The transformations are applied - each of them can specify its own center.
  3. rotation then scale are applied, both relative to transformOriginPoint.

When you set transform to scaling, and set rotation, the rotation is performed before scaling. The scaling applies to the rotated result - it simply stretches the rotated version horizontally in your case.

You need to somehow enforce the reverse order of operations. The only two ways to do that are:

  1. Stack the transforms in correct order and pass them to transform, or.

  2. Pass a list of correct transformations to transformations.

I'll demonstrate how to do it either way, in an interactive fashion where you can adjust the transform parameters using sliders.

To obtain the correct result using transform:

QGraphicsItem * item = ....;
QTransform t;
QPointF xlate = item->boundingRect().center();
t.translate(xlate.x(), xlate.y());
t.rotate(angle);
t.scale(xScale, yScale);
t.translate(-xlate.x(), -xlate.y());
item->setTransform(t);

To obtain the correct result using transformations:

QGraphicsItem * item = ....;
QGraphicsRotation rot;
QGraphicsScale scale;
auto center = item->boundingRect().center();
rot.setOrigin(QVector3D(center));
scale.setOrigin(QVector3D(center()));
item->setTransformations(QList<QGraphicsTransform*>() << &rot << &scale);

Finally, the example:

// https://github.com/KubaO/stackoverflown/tree/master/questions/graphics-transform-32186798
#include <QtWidgets>

struct Controller {
public:
   QSlider angle, xScale, yScale;
   Controller(QGridLayout & grid, int col) {
      angle.setRange(-180, 180);
      xScale.setRange(1, 10);
      yScale.setRange(1, 10);
      grid.addWidget(&angle, 0, col + 0);
      grid.addWidget(&xScale, 0, col + 1);
      grid.addWidget(&yScale, 0, col + 2);
   }
   template <typename F> void connect(F && f) { connect(f, f, std::forward<F>(f)); }
   template <typename Fa, typename Fx, typename Fy> void connect(Fa && a, Fx && x, Fy && y) {
      QObject::connect(&angle, &QSlider::valueChanged, std::forward<Fa>(a));
      QObject::connect(&xScale, &QSlider::valueChanged, std::forward<Fx>(x));
      QObject::connect(&yScale, &QSlider::valueChanged, std::forward<Fy>(y));
   }
   QTransform xform(QPointF xlate) {
      QTransform t;
      t.translate(xlate.x(), xlate.y());
      t.rotate(angle.value());
      t.scale(xScale.value(), yScale.value());
      t.translate(-xlate.x(), -xlate.y());
      return t;
   }
};

int main(int argc, char **argv)
{
   auto text = QStringLiteral("Hello, World!");
   QApplication app(argc, argv);
   QGraphicsScene scene;
   QWidget w;
   QGridLayout layout(&w);
   QGraphicsView view(&scene);
   Controller left(layout, 0), right(layout, 4);
   layout.addWidget(&view, 0, 3);

   auto ref = new QGraphicsTextItem(text);         // a reference, not resized
   ref->setDefaultTextColor(Qt::red);
   ref->setTransformOriginPoint(ref->boundingRect().center());
   ref->setRotation(45);
   scene.addItem(ref);

   auto leftItem = new QGraphicsTextItem(text);    // controlled from the left
   leftItem->setDefaultTextColor(Qt::green);
   scene.addItem(leftItem);

   auto rightItem = new QGraphicsTextItem(text);   // controlled from the right
   rightItem->setDefaultTextColor(Qt::blue);
   scene.addItem(rightItem);

   QGraphicsRotation rot;
   QGraphicsScale scale;
   rightItem->setTransformations(QList<QGraphicsTransform*>() << &rot << &scale);
   rot.setOrigin(QVector3D(rightItem->boundingRect().center()));
   scale.setOrigin(QVector3D(rightItem->boundingRect().center()));

   left.connect([leftItem, &left]{ leftItem->setTransform(left.xform(leftItem->boundingRect().center()));});
   right.connect([&rot](int a){ rot.setAngle(a); },
                 [&scale](int s){ scale.setXScale(s); }, [&scale](int s){ scale.setYScale(s); });
   right.angle.setValue(45);
   right.xScale.setValue(3);
   right.yScale.setValue(1);

   view.ensureVisible(scene.sceneRect());
   w.show();
   return app.exec();
}

这篇关于调整和旋转 QGraphicsItem 会导致形状奇怪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆