调整大小和旋转 QGraphicsItem 会导致奇怪的形状
Resizing and rotating a QGraphicsItem results in odd shape
我无法理解如何将缩放和旋转应用于 QGraphicsItem
。
我需要能够应用旋转和缩放(不一定保持纵横比),但我得到了完全出乎意料的结果。
旋转必须围绕项目中心。我这样做似乎没有问题 - 但是如果我尝试调试边界矩形,我会得到看似错误的值。
如果我不保持纵横比,而不是旋转,我会得到一个非常奇怪的倾斜,我已经花了很长时间来寻找原因并纠正它。我希望任何人都可以找到解决方案。
对于许多项目(如矩形),我的解决方案是放弃调整大小 - 只需将项目替换为给定大小的新项目。 (虽然它可能会对性能产生很大影响)。
但我不知道如何为文本或其他一些类型 (svg...) 执行此操作。
我正在尝试了解如何在旋转的项目上应用缩放,以及如何正确应用它。
下面的代码是我缩放和旋转文本项的实验,结果...见附图
#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();
}
参考(红色)文本旋转 45 度,文本旋转 45 度并调整大小 10,1:
调整后的(黑色)文本应与参考文本(红色)具有相同的高度 - 但要高得多;
边界矩形不再是矩形 - 它是倾斜的;
角度看起来远小于45;
添加了一个调整大小但未旋转的参考:
请帮助我理解为什么会发生这种行为以及我能做些什么。
我已经尝试研究 QGraphicsRotation
但我不知道如何应用它...我得到的只是移动而不是旋转。
我能够通过使用两个单独的 QTransform 并将它们相乘来完成这项工作。请注意,转换顺序很重要:
QTransform transform1;
transform1.scale(10, 1);
QTransform transform2;
transform2.rotate(45);
t->setTransform(transform1 * transform2);
如文档所述,项目的转换在数学上按特定顺序应用 - 这是您乘以转换矩阵的顺序,从概念上讲,是 reverse您通常会想到的顺序。
- 应用
transform
。通过在变换期间应用平移,原点必须包含在变换本身中。
- 应用了
transformations
- 每个都可以指定自己的中心。
rotation
然后 scale
被应用,两者都相对于 transformOriginPoint
.
当你设置transform
缩放,设置rotation
时,旋转先于缩放。缩放适用于旋转后的结果 - 它只是在您的情况下水平拉伸旋转后的版本。
您需要以某种方式强制执行相反的操作顺序。只有两种方法可以做到这一点:
以正确的顺序堆叠转换并将它们传递给 transform
,或者。
将正确转换列表传递给 transformations
。
我将演示如何以交互方式进行操作,您可以在其中使用滑块调整变换参数。
使用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);
使用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);
最后,例子:
// https://github.com/KubaO/Whosebugn/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
。
我需要能够应用旋转和缩放(不一定保持纵横比),但我得到了完全出乎意料的结果。
旋转必须围绕项目中心。我这样做似乎没有问题 - 但是如果我尝试调试边界矩形,我会得到看似错误的值。
如果我不保持纵横比,而不是旋转,我会得到一个非常奇怪的倾斜,我已经花了很长时间来寻找原因并纠正它。我希望任何人都可以找到解决方案。
对于许多项目(如矩形),我的解决方案是放弃调整大小 - 只需将项目替换为给定大小的新项目。
但我不知道如何为文本或其他一些类型 (svg...) 执行此操作。
我正在尝试了解如何在旋转的项目上应用缩放,以及如何正确应用它。
下面的代码是我缩放和旋转文本项的实验,结果...见附图
#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();
}
参考(红色)文本旋转 45 度,文本旋转 45 度并调整大小 10,1:
调整后的(黑色)文本应与参考文本(红色)具有相同的高度 - 但要高得多;
边界矩形不再是矩形 - 它是倾斜的;
角度看起来远小于45;
添加了一个调整大小但未旋转的参考:
请帮助我理解为什么会发生这种行为以及我能做些什么。
我已经尝试研究 QGraphicsRotation
但我不知道如何应用它...我得到的只是移动而不是旋转。
我能够通过使用两个单独的 QTransform 并将它们相乘来完成这项工作。请注意,转换顺序很重要:
QTransform transform1;
transform1.scale(10, 1);
QTransform transform2;
transform2.rotate(45);
t->setTransform(transform1 * transform2);
如文档所述,项目的转换在数学上按特定顺序应用 - 这是您乘以转换矩阵的顺序,从概念上讲,是 reverse您通常会想到的顺序。
- 应用
transform
。通过在变换期间应用平移,原点必须包含在变换本身中。 - 应用了
transformations
- 每个都可以指定自己的中心。 rotation
然后scale
被应用,两者都相对于transformOriginPoint
.
当你设置transform
缩放,设置rotation
时,旋转先于缩放。缩放适用于旋转后的结果 - 它只是在您的情况下水平拉伸旋转后的版本。
您需要以某种方式强制执行相反的操作顺序。只有两种方法可以做到这一点:
以正确的顺序堆叠转换并将它们传递给
transform
,或者。将正确转换列表传递给
transformations
。
我将演示如何以交互方式进行操作,您可以在其中使用滑块调整变换参数。
使用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);
使用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);
最后,例子:
// https://github.com/KubaO/Whosebugn/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();
}