欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt坐标系统

程序员文章站 2022-05-22 22:49:58
...

    Qt的坐标系统是由QPainter类控制的,而QPainter是在绘图设备上进行绘制的。一个绘图设备的默认坐标系统中,原点(0,0)在其左上角,x坐标向右增长,y坐标向下增长。

    QPainter的逻辑坐标与绘图设备的物理坐标之间的映射由QPainter的变换矩阵、视口和窗口处理。逻辑坐标和物理坐标默认是一致的。QPainter也支持坐标变换(比如旋转和缩放)。相关内容可在Qt帮助中输入Coordinate System关键字查看。


基本变换

     默认的,QPainter在相关设备的坐标系统上进行操作,但是它也完全支持仿射坐标变换。绘图时可以用QPainter::scale()函数缩放坐标系统,使用QPainter::rotate()函数顺时针旋转坐标系统,使用QPainter::translate()函数平移坐标系统,使用QPainter::shear()围绕原点扭曲坐标系统。

    坐标系统的2D变换由QTransform类实现,QTransform类对象可以存储多个变换操作,当同样的变换需要多次使用时,建议使用QTransform类对象。

QPainter的坐标变换方法的应用实例:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    // 填充界面背景为白色
    painter.fillRect(rect(), Qt::white);
    painter.setPen(QPen(Qt::red, 11));
    // 绘制一条线段
    painter.drawLine(QPoint(5, 6), QPoint(100, 99));
    // 将坐标系统进行平移,使(200, 150)点作为原点
    painter.translate(200, 150);
    // 开启抗锯齿
    painter.setRenderHint(QPainter::Antialiasing);
    // 重新绘制相同的线段
    painter.drawLine(QPoint(5, 6), QPoint(100, 99));

    // 保存painter的状态
    painter.save();
    // 将坐标系统旋转90度
    painter.rotate(90);
    painter.setPen(Qt::cyan);
    // 重新绘制相同的线段
    painter.drawLine(QPoint(5, 6), QPoint(100, 99));
    // 恢复painter的状态
    painter.restore();

    painter.setBrush(Qt::darkGreen);
    // 绘制一个矩形
    painter.drawRect(-50, -50, 100, 50);
    painter.save();
    // 将坐标系统进行缩放
    painter.scale(0.5, 0.4);
    painter.setBrush(Qt::yellow);
    // 重新绘制相同的矩形
    painter.drawRect(-50, -50, 100, 50);
    painter.restore();

    painter.setPen(Qt::blue);
    painter.setBrush(Qt::darkYellow);
    // 绘制一个椭圆
    painter.drawEllipse(QRect(60, -100, 50, 50));
    // 将坐标系统进行扭曲
    painter.shear(1.5, -0.7);
    painter.setBrush(Qt::darkGray);
    // 重新绘制相同的椭圆
    painter.drawEllipse(QRect(60, -100, 50, 50));

}

结果:

Qt坐标系统


QTransform类的应用实例

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setMouseTracking(true);

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(1000);
    angle = 0;

    this->resize(400,300);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *)
{
    angle += 10;
    if(angle == 360)
        angle = 0;
    int side = qMin(width(), height());
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    QTransform transform;
    //Moves the coordinate system width()/2 along the x axis and height()/2 along the y axis, and returns a reference to the matrix.
    transform.translate(width()/2, height()/2);
    //Scales the coordinate system by side/300.0 horizontally and side/300.0 vertically, and returns a reference to the matrix.
    transform.scale(side/300.0, side/300.0);
    transform.rotate(angle);
    painter.setWorldTransform(transform);
    painter.drawEllipse(-120, -120, 240, 240);
    painter.drawLine(0, 0, 100, 0);
}

结果:

Qt坐标系统


窗口-视口转换

    使用QPainter进行绘制时,会使用逻辑坐标进行绘制,然后再转换为绘图设备的物理坐标。逻辑坐标到物理坐标的映射由QPainter的worldTransform( )函数、QPainter的viewport( )以及window( )函数进行处理。其中,视口(viewport)表示物理坐标下指定的一个任意矩形,而窗口(window,与窗口部件的概念不同)表示逻辑坐标下的相同矩形。默认的,逻辑坐标和物理坐标是重合的,它们都相当于绘图设备上的矩形。

    使用窗口-视口转换可以使逻辑坐标系统适合应用要求,令绘图代码独立于绘图设备。如下代码可使逻辑坐标以(-50, -50)为原点,宽为100,高为100。

QPainter painter(this);
painter.setWindow(QRect(-50,-50,100,100));

    现在逻辑坐标的(-50, -50)对应绘图设备的物理坐标的(0,0)点。当设置窗口或者视口矩形时,实际上是执行了坐标的一个线性变换,窗口的4个角会映射到视口对应的4个角,反之亦然。因此,一个很好的一个办法是让视口和窗口维持相同的宽高比来防止形变。如果设置了逻辑坐标系统为一个正方形,那么也需要设置视口为一个正方形,如下代码将视口设置为适合绘图设备矩形的最大矩形:

int side = qMin(width(),height());
int x = (width()-side/2);
int y = (height()-side/2);
painter.setViewport(x,y,side,side);

一个应用实例:

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setMouseTracking(true);
    this->resize(400,300);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setWindow(-50, -50, 100, 100);
    painter.setBrush(Qt::green);
    painter.drawRect(0, 0, 20, 20);
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    QString pos = QString("%1,%2").arg(event->pos().x()).arg(event->pos().y());
    QToolTip::showText(event->globalPos(), pos, this);    
}
Qt坐标系统

    可以看出绘制的正方形发生了形变,成为了一个长方形。这是由于我们设置widget的宽为400,高为300,而我们设置逻辑坐标矩形为以(-50,-50)为起点,宽100,高100。按比例对应,在水平方向,逻辑坐标的一个单位对应物理坐标的4个单位;在垂直方向,逻辑坐标的一个单位对应物理坐标的3个单位。所以逻辑坐标中宽20、高20的矩形在物理坐标中就是宽80、高60的矩形。为了防止形变,需要将视口的宽和高的对应比例设置为相同值,即把视口也设置为一个正方形。更改paintEvent()函数如下:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    int side = qMin(width(), height());
    int x = (width() / 2);
    int y = (height() / 2);
    // 设置视口
    painter.setViewport(x, y, side, side);
    painter.setWindow(0, 0, 100, 100);
    painter.setBrush(Qt::green);
    painter.drawRect(0, 0, 20, 20);
}

结果:

Qt坐标系统