qt - El cambio de tamaño y la rotación de un QGraphicsItem da como resultado una forma extraña
rotation resize (2)
Pude hacer ese trabajo al usar dos QTransforms separados y multiplicarlos juntos. Tenga en cuenta que el orden de las transformaciones es importante:
QTransform transform1;
transform1.scale(10, 1);
QTransform transform2;
transform2.rotate(45);
t->setTransform(transform1 * transform2);
No puedo entender cómo se aplican la escala y la rotación a QGraphicsItem
.
Necesito poder aplicar rotación y escalado ( no necesariamente manteniendo la relación de aspecto ) y obtengo resultados completamente inesperados.
La rotación debe estar alrededor del centro del elemento. Parece que no tengo problemas para hacerlo, pero si trato de depurar el rectángulo delimitador, obtengo valores aparentemente incorrectos.
Si no mantengo la relación de aspecto, en lugar de rotación obtengo un sesgo muy raro, y he estado luchando durante bastante tiempo para encontrar la causa y corregirla. Espero que cualquiera pueda encontrar una solución.
Para muchos elementos, como rectángulos, mi solución era renunciar al cambio de tamaño, y simplemente reemplazar el artículo por uno nuevo de un tamaño determinado. Incluso hice eso para pixmap (aunque probablemente afecte mucho al rendimiento).
Pero no sé cómo hacer eso para texto o algunos otros tipos (svg ...).
Estoy tratando de entender cómo se aplica la escala, en elementos rotados y cómo aplicarlo correctamente.
El siguiente código es un experimento en el que escalo y giro un elemento de texto, y el resultado ... ver imagen adjunta
#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();
}
El texto de referencia (rojo) giró 45 grados, el texto giró 45 grados y se redimensionó 10,1:
El texto redimensionado (negro) debe tener la misma altura que la referencia (rojo); sin embargo, es mucho más alto;
El rectángulo delimitador ya no es un rectángulo; está sesgado;
El ángulo parece mucho más pequeño que 45;
Se agregó una referencia redimensionada pero no rotada también:
Ayúdame a comprender por qué ocurre este comportamiento y qué puedo hacer al respecto.
He intentado buscar en QGraphicsRotation
pero no sé cómo aplicarlo ... Todo lo que obtengo es un movimiento en lugar de rotación.
Como se documenta, las transformaciones del elemento se aplican matemáticamente en un orden determinado: este es el orden en el que se multiplican las matrices de transformación y, conceptualmente, es el orden inverso al que normalmente se piensa.
- La
transform
se aplica. El punto de origen debe incluirse en la transformación en sí, al aplicar traducciones durante la transformación. - Las
transformations
se aplican, cada una de ellas puede especificar su propio centro. - Se aplican la
rotation
y lascale
, ambas relativas atransformOriginPoint
.
Cuando configura la transform
a escala, y establece la rotation
, la rotación se realiza antes de escalar. La escala se aplica al resultado girado: simplemente estira la versión girada horizontalmente en su caso.
Necesitas de alguna manera hacer cumplir el orden inverso de las operaciones. Las únicas dos formas de hacerlo son:
Apila las transformaciones en el orden correcto y pásalas para
transform
, o.Pase una lista de las transformaciones correctas a las
transformations
.
Demostraré cómo hacerlo de cualquier manera, de forma interactiva, donde puede ajustar los parámetros de transformación con controles deslizantes.
Para obtener el resultado correcto con la 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);
Para obtener el resultado correcto usando 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);
Finalmente, el ejemplo:
// https://github.com/KubaO/n/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();
}