QT坐标变(转)换(QTransform 类)

使用 QPainter 类中的基本变换函数进行坐标变换

QPainter 类中的基本坐标变换函数
  • 以下函数都是在 QPainter 的变换矩阵(QTransform 类)上进行的变换,可使用QPainter::worldTransform()函数获取该变换矩阵。
    void QPainter::translate(const QPointF &offset); //平移坐标系。
    void QPainter::translate(const QPoint &offset); //平移坐标系。
    void QPainter::translate(qreal dx, qreal dy); //平移坐标系。
    void QPainter::scale(qreal sx, qreal sy); //缩放坐标系
    void QPainter::rotate(qreal angle); //顺时针旋转坐标系,angle 以度为单位。
    void QPainter::shear(qreal sh, qreal sv); //使用(sh, sv)错切坐标系。
绘制状态
  1. void QPainter::save();
    保存当前绘制器状态(把状态压入堆栈),包括当前的坐标系。一个 save()函数必须跟随一个 restore()函数
  2. void QPainter::restore();
    恢复当前绘制器的状态(从堆栈中弹出保存的状态)。
  3. void QPainter::resetTransform();
    重置使用以下函数进行的任何转换:translate()、scale()、shear()、rotate()、setWorldTransform()、setViewport()、setWindow()
  4. const QTransform &QPainter::deviceTransform() const;
    返回从逻辑坐标转换为平台相关绘图设备的设备坐标的矩阵,只有在平台相关句柄(Qt::HANDLE)上使用平台绘图命令时,才需要此函数。
示例
void paintEvent(QPaintEvent *e)
{
    QPainter pr(this);
    QBrush bs(QColor(255, 255, 1));
    pr.setBrush(bs);
    QRectF r(0, 11, 22, 55);
    pr.drawRect(r);
    pr.translate(50, 10);
    pr.drawRect(r); // 变换 1:平移(50,10)
    pr.save();      // 保存状态
    // 变换 2:平移后位于(100,60),缩放(Y 向 2 倍),旋转(逆时针)
    pr.translate(50, 50);
    pr.scale(1, 2);
    pr.rotate(-60);
    pr.drawRect(r);
    pr.restore(); // 恢复状态,此时坐标系位于(50,10)处。
    pr.translate(100, 0);
    pr.drawRect(r); // 变换 3:平移后位于(150,10)
    // 变换 5:平移后位于(200,10),旋转(逆时针),缩放(Y 向 2 倍),注意连续变换与变换 2 的顺序。
    pr.translate(50, 0);
    pr.rotate(-60);
    pr.scale(1, 2);
    pr.drawRect(r);
    pr.resetTransform(); // 重置为初始状态
    pr.translate(200, 50);
    pr.drawRect(r); // 变换4
}

使用变换矩阵(QTransform 类)进行坐标变换

QPainter 类中与 QTransform 类有关的函数

void QPainter::setWorldMatrixEnabled(bool enable);
若 enable 为 true 则启用世界转换,否则禁用世界转换,世界变换矩阵不会改变。
bool worldMatrixEnabled() const;
若启用了世界转换,则返回 true。
void QPainter::setWorldTransform(const QTransform &matrix, bool combine = false);
void QPainter::setTransform(const QTransform &transform, bool combine = false);
设置世界变换矩阵,若 combine 为 true,则把当前矩阵与 transform 组合,否则 transform会取代当前矩阵。
const QTransform &QPainter::transform() const;
返回世界变换矩阵
const QTransform &QPainter::worldTransform() const;
返回世界变换矩阵

QTransform 类中的函数

QTransform 类主要用于创建一个 3*3 的变换矩阵,该矩阵用于坐标系的 2D 变换。该类取代了QMatrix类(此类已过时)。QTransform类通过操控变换矩阵来实现坐标变换。

  • 把 QTransform 类的变换矩阵与坐标变换矩阵相对比,可得出如下规律
    其中 m31 和 m32 用于平移,m11 和 m22 用于缩放,m21 和 m12 用于错切(shear),m13 和 m23 用于投影变换,m33 是一个额外的投影因子,设置 m11,m12,m21,m22 可实现旋转变换
构造函数

QTransform(); //构造一个单位矩阵,即 m11=m22=m33=1;其余元素全为 0。
QTransform(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33 = 1.0);
QTransform(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy);
QTransform(const QMatrix &matrix);
QTransform(QTransform &&other);
QTransform(const QTransform &other);

设置和获取变换矩阵的元素

void setMatrix(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33);

QTransform 内置的基本坐标变换函数

QTransform &rotate(qreal angle, Qt::Axis axis = Qt::ZAxis);
把坐标系沿轴 axis 逆时针旋转角度 angle,并返回该矩阵的引用
QTransform &rotateRadians(qreal angle, Qt::Axis axis = Qt::ZAxis);
该函数与 rotate()函数相同,但角度 angle 是以弧度指定的。
QTransform &scale(qreal sx, qreal sy);
缩放坐标系,并返回该矩阵的引用。
static QTransform fromScale(qreal sx, qreal sy);
与 scale();相同,但速度更快。静态的
QTransform &translate(qreal dx, qreal dy);
平移
static QTransform fromTranslate(qreal sx, qreal sy);
与 translate();相同,但速度更快。静态的
QTransform &shear(qreal sh, qreal sv);
错切变换。

对变换矩阵的判断

bool isRotating() const;
若矩阵表示旋转变换,则返回 true。180 度或 360 度的旋转被视为缩放。
bool isScaling() const;
若矩阵表示缩放变换,则返回 true。
bool isTranslating() const;
若矩阵表示平移变换,则返回 true。
bool isAffine() const;
若矩阵表示的是仿射(affine)变换,则返回 true,否则返回 false。
TransformationType type() const;返回此矩阵的转换类型

类型说明
QTransform::TxNone
QTransform::TxTranslate平移
QTransform::TxScale缩放
QTransform::TxRotate旋转
QTransform::TxShear错切
QTransform::TxProject投影
构建变换矩阵

void reset();
把矩阵重置为单位矩阵。
static bool squareToQuad(const QPolygonF &quad, QTransform &trans); //静态的
创建一个把单位正方形映射到一个四边多边形 quad 的变换矩阵 trans。若构造了QTransform 则返回 true,否则返回 false。
static bool quadToSquare(const QPolygonF &quad, QTransform &trans); //静态的
创建一个把四边多边形quad映射到单位正方形的变换矩阵trans。若构造了QTransform则返回 true,否则返回 false。
static bool quadToQuad(const QPolygonF &one, const QPolygonF &two, QTransform &trans); //静态的
创建一个把四边多边形 one 映射到另一个四边多边形 two 的变换矩阵 trans。若构造了QTransform 则返回 true,否则返回 false。
const QMatrix &toAffine() const;
把此矩阵作为仿射矩阵返回,注意:若为透视转换,则转换后将导致数据丢失。

与线性代数有关的函数

QTransform adjoint() const;
返回该矩阵的伴随(adjoint)矩阵
qreal determinant() const;
返回该矩阵的行列式
QTransform transposed() const;
返回此矩阵的转置矩阵
QTransform inverted(bool *invertible = Q_NULLPTR) const;
返回此矩阵的逆矩阵,若矩阵是奇异的(非可逆的),则返回的矩阵是单位矩阵,若参数invertible 有效(即不为 0),则若矩阵可逆,则将其设置为 true,否则将其设置为 false。
bool isIdentity() const;
若矩阵是单位矩阵,则返回 true。
bool isInvertible() const;
若矩阵是可逆的,则返回 true。

示例,使用 QTransform 类(变换矩阵)进行坐标变换
void paintEvent(QPaintEvent *e)
{
    QPainter pr(this);
    QBrush bs(QColor(255, 255, 1));
    pr.setBrush(bs);
    QRectF r(0, 55, 22, 55);
    // 变换 1
    QTransform t(1, 0, 0, 1, 20, 0); // x 方向平移 20
    t.rotate(-60);                   // 使 t 再逆时针旋转 60 度。
    pr.setTransform(t);              // 设置变换矩阵为 t
    qDebug() << t;                   // 输出内容为:QTransform(type=TxRotate,11=0.5 12=-0.866025 13=0 21=0.866025 22=0.5 23=0 31=20 32=0 33=1)
    pr.drawRect(r);                  // 绘制矩形
    // 变换 2
    QTransform t1;
    t1.setMatrix(1, 0, 0, 0, 2, 0, 0, 0, 1); // Y 方向放大两倍。
    pr.setTransform(t1);
    pr.drawRect(r);
    // 变换 3
    t1 = t1 * t; // 使用 QTransform 重载的*运算符进行矩阵乘运算,此时 t1 的变换如下:
                    // 首先向 X 方向平移 20,再逆时针旋转 60 度,最后把 Y 轴方向放大两倍。
    pr.setTransform(t1);
    qDebug() << t1; // 输出内容为:QTransform(type=TxShear,11=0.5 12=-0.866025 13=0 21=1.73205 22=1 23=0 31=20 32=0 33=1)
    pr.drawRect(r);
    t1.reset();          // 重置矩阵为单位矩阵。
    pr.setTransform(t1); // 变换矩阵的值更改后需要使用 QPainter 重新设置变换矩阵,
    qDebug() << t1;      // 输出内容为:QTransform(type=TxNone,11=1 12=0 13=0 21=0 22=1 23=0 31=0 32=0 33=1)
    pr.drawRect(r);
}
示例:QTransform::map()函数的使用
void paintEvent(QPaintEvent *e)
{
    QPainter pr(this);
    QBrush bs(QColor(255, 255, 1));
    pr.setBrush(bs);
    QRectF r(0, 55, 22, 55);
    QLineF n(50, 0, 50, 555);
    pr.drawLine(n);
    pr.drawRect(r);
    QTransform t;
    t.translate(50, 0);       // x 方向平移 50
    QRectF r1 = t.mapRect(r); // 使用 t 转换 r 的坐标。
    QLineF n1 = t.map(n);     // 使用 t 转换 n 的坐标
    // n1 也可使用如下等效语句创建,更简洁
    // QLineF n1=n*t; //注意 t*n 是错误的
    pr.drawRect(r1);
    pr.drawLine(n1);
}
示例:使用变换矩阵计算变换后的坐标值
void paintEvent(QPaintEvent *e)
{
    QPainter pr(this);
    QBrush bs(QColor(255, 255, 1));
    pr.setBrush(bs);
    QRect r(0, 55, 22, 55);
    pr.drawRect(r);
    QTransform t;
    t.translate(50, 0);
    t.rotate(-60);
    QPolygon g1 = t.mapToPolygon(r);
    pr.drawPolygon(g1);
    qDebug() << t;
    qDebug() << g1;
}

窗口视口变换原理及其使用

需要用到的 QPainter 类中的函数

QTransform combinedTransform() const;
返回当前窗口/视口和世界变换的变换矩阵组合。
void setViewTransformEnabled(bool enable);
若 enable 为 true,则启用视口的转换,否则禁用。
bool viewTransformEnabled() const;
若视口转换已启用则返回 true。
QRect viewport() const;
返回视口矩形
QRect window() const;
返回窗口矩形
void setViewport(const QRect &rectangle);
void setViewport(int x, int y, int width, int height);
设置视口矩形,并启用视口转换。
void setWindow(const QRect &rectangle);
void setWindow(int x, int y, int width, int height);
设置窗口矩形,并启用视口转换。

  • 示例
void paintEvent(QPaintEvent *e)
{
    QPainter pr(this);
    QBrush bs(QColor(255, 255, 1));
    // 绘制一个与视口一样大小的矩形,以便于观察
    pr.drawRect(100, 100, 500, 500);
    QPen pn;
    pn.setColor(QColor(111, 1, 1));
    pn.setWidth(10);
    pn.setCapStyle(Qt::FlatCap);
    pr.setPen(pn);
    pr.setViewport(100, 100, 500, 500);
    pr.setWindow(0, 0, 1836, 1836);
    pr.drawRect(200, 200, 100, 100);
    pr.drawLine(0, 100, 200, 100);
}
  • 示例
    void paintEvent(QPaintEvent *e)
    {
        QPainter pr(this);
        QBrush bs(QColor(255, 255, 1));
        // 以下绘制的交叉直线用于标示平移后的坐标位置,以方便观察
        pr.drawLine(0, 100, 555, 100);
        pr.drawLine(100, 0, 100, 555);
        QPen pn;
        pn.setColor(QColor(111, 1, 1));
        pn.setWidth(10);
        pn.setCapStyle(Qt::FlatCap);
        pr.setPen(pn);
        // 设置窗口/视口,以下代码相当于把设备坐标移至(100,100)处。
        pr.setViewport(50, 50, 200, 200);
        pr.setWindow(-50, -50, 200, 200);
        pr.drawLine(0, 100, 100, 100);
        pr.drawRect(-50, -50, 100, 100);
    }

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐