【Qt入门第16篇】 2D绘图(六)坐标系统

导语

前面一节我们讲解了图片的显示,其中很多地方都用到了坐标的变化。这一节我们将讲解Qt的坐标系统,分为两部分来讲解:第一部分主要讲解前面一节的那几个函数,它们分别是translate()平移变换、scale()比例变换、rotate()旋转变换、shear()扭曲变换。最后还会介绍两个有用的函数save()restore(),利用它们来保存和弹出坐标系的状态,从而实现快速利用几个变换函数来绘图。

第二部分会和大家一起来研究一下Qt的坐标系统,其中可能会涉及到多个坐标,大家一定要亲自动手操作感悟一下,不然很难理解的!

环境:Windows Xp + Qt 4.8.4+Qt Creator2.6.2

目录

  • 第一部分 Qt坐标系统应用
    • 一、坐标系统简介
    • 二、坐标系统变换
    • 三、坐标系统的保存
  • 第二部分 坐标系统深入研究
    • 一、获得坐标信息
    • 二、研究变换后的坐标系统
    • 三、研究绘图设备的坐标系统

正文

第一部分 Qt坐标系统应用

一、坐标系统简介

Qt中每一个窗口都有一个坐标系统,默认的,窗口左上角为坐标原点,水平向右依次增大,水平向左依次减小,垂直向下依次增大,垂直向上依次减小。原点即为(0,0)点,以像素为单位增减。

下面仍然在上一节的程序中进行代码演示,更改paintEvent()的内容如下:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setBrush(Qt::red);painter.drawRect(0, 0, 100, 100);painter.setBrush(Qt::yellow);painter.drawRect(-50, -50, 100, 100);
}

我们先在原点(0,0)绘制了一个长宽都是100像素的红色矩形,又在(-50,-50)点绘制了一个同样大小的黄 色矩形。可以看到,我们只能看到黄 色矩形的四分之一部分。运行程序,效果如下图所示。

二、坐标系统变换

默认的,QPainter在相关设备的坐标系统上进行绘制,在进行绘图时,可以使用QPainter::scale()函数缩放坐标系统;使用QPainter::rotate()函数顺时针旋转坐标系统;使用QPainter::translate()函数平移坐标系统;还可以使用QPainter::shear()围绕原点来扭曲坐标系统。如下图所示。

坐标系统的2D变换由QTransform类实现,我们可以使用前面提到的那些便捷函数进行坐标系统变换,当然也可以通过QTransform类实现。

1.平移变换。将paintEvent()函数内容更改如下:

void Dialog::paintEvent(QPaintEvent *)
{// 平移变换QPainter painter(this);painter.setBrush(Qt::yellow);painter.drawRect(0, 0, 50, 50);painter.translate(100, 100);  //将点(100,100)设为原点painter.setBrush(Qt::red);painter.drawRect(0, 0, 50, 50);painter.translate(-100, -100);painter.drawLine(0, 0, 20, 20);
}

运行程序,效果如下图所示。

这里先在原点(0, 0)绘制了一个宽、高均为50的正方形,然后使用translate()函数将坐标系统进行了平移,使(100, 100)点成为了新原点,所以我们再次进行绘制的时候,虽然drawRect()中的逻辑坐标还是(0, 0)点,但实际显示出来的却是在(100, 100)点的红色正方形。可以再次使用translate()函数进行反向平移,使原点重新回到窗口左上角。

2.缩放变换。将paintEvent()函数中的内容更改如下:

void Dialog::paintEvent(QPaintEvent *)
{// 缩放QPainter painter(this);painter.setBrush(Qt::yellow);painter.drawRect(0, 0, 100, 100);painter.scale(2, 2); //放大两倍painter.setBrush(Qt::red);painter.drawRect(50, 50, 50, 50);
}

运行程序,效果如下图所示。

可以看到,当我们使用scale()函数将坐标系统的横、纵坐标都放大两倍以后,逻辑上的(50, 50)点变成了窗口上的(100, 100)点,而逻辑上的长度50,绘制到窗口上的长度却是100。

3.扭曲变换。将paintEvent()函数更改如下:

void Dialog::paintEvent(QPaintEvent *)
{    // 扭曲    QPainter painter(this);    painter.setBrush(Qt::yellow);   painter.drawRect(0, 0, 50, 50);    painter.shear(0, 1); //纵向扭曲变形    painter.setBrush(Qt::red);    painter.drawRect(50, 0, 50, 50);
}

运行程序,效果如下图所示。

shear()有两个参数,第一个是对横向进行扭曲,第二个是对纵向进行扭曲,而取值就是扭曲的程度。比如程序中对纵向扭曲值为1,那么就是红色正方形左边的边下移一个单位,右边的边下移两个单位,值为1就表明右边的边比左边的边多下移一个单位。大家可以更改取值,测试效果。

4.旋转变换。将paintEvent()函数更改如下:

void Dialog::paintEvent(QPaintEvent *)
{// 旋转QPainter painter(this);painter.drawLine(0, 0, 100, 0);painter.rotate(30); //以原点为中心,顺时针旋转30度painter.drawLine(0, 0, 100, 0);painter.translate(100, 100);painter.rotate(30);painter.drawLine(0, 0, 100, 0);
}

运行程序,效果如下图所示。

这里先绘制了一条水平的直线,然后将坐标系统旋转了30度,又绘制了一条直线。可以看到,默认是以原点(0, 0)为中心旋转的。如果想改变旋转中心,可以使用translate()函数,比如这里将中心移动到了(100, 100)点,然后旋转了30度,又绘制了一条直线。我们的本意是想在新的原点从水平方向旋转30度进行绘制,可是实际效果却超过了30度。这是由于前面已经使用rotate()函数旋转过坐标系统了,后面的旋转会在前面的基础上进行。

下面我们再次更改paintEvent()函数:

void Dialog::paintEvent(QPaintEvent *)
{    // 旋转    QPainter painter(this);   painter.drawLine(0, 0, 100, 0);   painter.rotate(30); //以原点为中心,顺时针旋转30度    painter.drawLine(0, 0, 100, 0);     painter.rotate(-30); // 反向旋转     painter.translate(100, 100);    painter.rotate(30);    painter.drawLine(0, 0, 100, 0);
}

运行程序,效果如下图所示。

这次我们在移动原点以前先将坐标系统反向旋转,可以看到,第二次旋转也是从水平方向开始的。

其实,前面讲到的这几个变换函数都是如此,他们改变了坐标系统以后,如果不进行逆向操作,坐标系统是无法自动复原的。针对这个问题,下面我们将讲解两个非常实用的函数来实现坐标系统的保存和还原。

三、坐标系统的保存

我们可以先利用save()函数来保存坐标系现在的状态,然后进行变换操作,操作完之后,再用restore()函数将以前的坐标系状态恢复,其实就是一个入栈和出栈的操作。下面来看一个具体的例子,更改paintEvent()函数如下:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.save(); //保存坐标系状态painter.translate(100,100);painter.drawLine(0, 0, 50, 50);painter.restore(); //恢复以前的坐标系状态painter.drawLine(0, 0, 50, 50);
}

运行程序,效果如下图所示。利用好这两个函数,可以实现坐标系快速切换,绘制出不同的图形。

第二部分 坐标系统深入研究

在第一部分,我们主要学习了常用的一些坐标变换,虽然在编程中,这些变换已经可以满足大部分的应用需求。不过,大家是否也感觉到现在对坐标的变换依然很模糊,没有一个透彻的认识。下面咱们就一点一点来研究一下坐标系统的变换。

一、获得坐标信息

前面图形的变换都是我们眼睛看到的,为了更具有说服力,下面将获取具体的坐标数据,通过参考数据来进一步了解坐标变换。

1.首先在dialog.h文件中添加头文件包含:

#include <QMouseEvent>

然后添加一个protected鼠标事件处理函数声明:

void mousePressEvent(QMouseEvent *);

2.到dialog.cpp文件中,先添加头文件包含:

#include <QDebug>

然后添加函数定义:

void Dialog::mousePressEvent(QMouseEvent *event)
{    qDebug() << event->pos();
}

这里应用了qDebug()函数,该函数可以在程序运行时将程序中的一些信息输出到控制面板,在QtCreator中会将信息输出到其下面的“应用程序输出”窗口。这个函数很有用,在进行简单的程序调试时,都可以利用该函数进行。我们这里利用它将鼠标指针的坐标值输出出来。

3.将paintEvent()函数更改如下:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.drawRect(0, 0, 50, 50);
}

现在运行程序,然后将鼠标在绘制的正方形右下角顶点处点击,在QtCreator的应用程序输出窗口就会输出相应点的坐标信息。如下图所示。大家也可以点击一下其他的地方,查看输出信息。

二、研究变换后的坐标系统

1.首先研究放大后的坐标系统,将paintEvent()函数更改如下:

void Dialog::paintEvent(QPaintEvent *)
{// 放大QPainter painter(this);painter.drawRect(0, 0, 50, 50);painter.scale(1, 2);painter.drawRect(50, 50, 50, 50);
}

这里,我们将纵坐标放大了两倍,而横坐标没有改变。运行程序,效果如下图所示。

大家可以查看一下第二个矩形的各个顶点的坐标,左上角是(50, 100)也就是说纵坐标扩大了两倍,查看其它点,会发现左右两条边长都变成了100。

2.研究旋转后的坐标系统。修改paintEvent()函数如下:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.drawLine(0, 0, 100, 0);painter.rotate(45);painter.setPen(Qt::red);painter.drawLine(0, 0, 100, 0);
}

这里我们先绘制了一条水平的直线,然后将坐标系统旋转45度,再次绘制了一条相同的红色直线。运行程序,效果如下图所示。

大家可以查看一下各处的坐标,虽然旋转后直线位置发生了变化,但是坐标其实是没有变化的。 我们也可以利用这种方法来测试一下应用其他变换函数后坐标的变化,这里就不再敖述。

三、研究绘图设备的坐标系统

除了可以在QWidget等窗口部件上进行绘制以外,还可以在QPixmapQImage等上面进行绘制,这些均称为绘图设备。下面我们就以QPixmap为例,来研究一下它的坐标系统。

1.首先更改paintEvent()函数如下:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);QPixmap pix(200, 200);pix.fill(Qt::red);   //背景填充为红色painter.drawPixmap(0, 0, pix);
}

在前面我们已经讲过,QPixmap可以用来显示图片。其实QPixmap本身就是一个绘图设备,可以在它上面直接绘图。这里先生成了一个宽和高都是200像素的QPixmap类对象(注意,必须在构建时指定其大小),然后为其填充了红色,最后在窗口的原点进行了绘制。为了表述方面,下面将QPixmap对象称为画布,这里就是先绘制了一个红色画布。

我们运行程序,并在红色画布的左上角和右下角分别点击,查看输出的坐标。如下图所示。因为点击位置的误差,所以两个点可能不是顶点。

2.下面我们接着更改paintEvent()的代码:

void Dialog::paintEvent(QPaintEvent *)
{    QPainter painter(this);    QPixmap pix(200, 200);    pix.fill(Qt::red);   //背景填充为红色    painter.drawPixmap(100, 100, pix);
}

这次我们在(100, 100)点重新绘制了画布,现在运行程序,发现画布左上角坐标确实为(100,100),这个就是我们窗口中的坐标,是没有什么疑问的。效果如下图所示。

窗口和画布都是绘图设备,那么画布本身有没有自己的坐标系统呢?我们接着研究!

3.我们继续更改paintEvent()函数:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);QPixmap pix(200, 200);pix.fill(Qt::red);//新建QPainter类对象,在pix上进行绘图QPainter pp(&pix);//在pix上的(0,0)点和(50,50)点之间绘制直线pp.drawLine(0, 0, 50, 50);painter.drawPixmap(100, 100, pix);
}

这里我们为画布pix创建了一个QPainter对象pp,注意这个pp只能在画布上绘画,然后我们在画布上绘制了一条从原点(0, 0)开始的直线。运行程序,效果如下图所示。

可以看到,直线是从画布的左上角开始绘制的,也就是说,画布也有自己的坐标系统,坐标原点在画布的左上角。

下面补充说明一下:QPainter painter(this)this就表明了是在窗口上进行绘图,所以利用painter进行的绘图都是在窗口部件上的,painter进行的坐标变换,是变化的窗口的坐标系;而利用pp进行的绘图都是在画布上进行的,如果它进行坐标变化,就是变化的画布的坐标系。

而通过坐标数值,我们可以得出下面两条结论:

第一,QWidgetQPixmap各有一套坐标系统,它们互不影响。可以看到,无论画布在窗口的什么位置,它的坐标原点依然在左上角,为(0,0)点,没有变。

第二,我们所得到的鼠标指针的坐标值是窗口坐标系统的,不是画布的坐标。

4.下面这个例子将对比分析扩大窗口坐标或画布坐标的异同。

首先将paintEvent()函数更改如下:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);QPixmap pix(200,200);//放大前输出画布的大小qDebug() << pix.size();pix.fill(Qt::red);QPainter pp(&pix);//画布的坐标扩大2倍pp.scale(2, 2);//在画布上的(0,0)点和(50,50)点之间绘制直线pp.drawLine(0, 0, 50, 50);//放大后输出画布的大小qDebug() << pix.size();painter.drawPixmap(0, 0, pix);
}

这里我们将画布坐标系统放大了两倍,然后从原点开始绘制了一条直线,并分别输出了画布放大前后的大小。运行程序,效果如下图所示。

下面再次更改paintEvent()函数:

void Dialog::paintEvent(QPaintEvent *)
{QPainter painter(this);QPixmap pix(200,200);qDebug() << pix.size();//窗口坐标扩大2倍painter.scale(2,2);pix.fill(Qt::red);QPainter pp(&pix);pp.drawLine(0, 0, 50, 50);qDebug() << pix.size();painter.drawPixmap(0, 0, pix);
}

这里与前面唯一的不同是:这里放大了窗口的坐标系统,而前面放大的是画布的坐标系统。运行程序,效果如下图所示。

可以看到,整个画布的可见面积变大了。直线虽然长度依然是100,但是这次的效果跟前面明显不同,因为是窗口坐标变大,所以在上面绘出的线条有了明显的颗粒感。

上面两个程序虽然最终输出的数据是一样的,但实际效果还是有很大不同的。大家可以根据需要进行选择性应用。

结语

在这一节中我们讲述了坐标相关的多个知识点,经过本节的学习,大家应该已经对Qt的2D绘图有了一个浅显的认识,下一节我们将做一个比较实用的涂鸦板例子。

Qt的坐标系统是很有必要好好研究的,它对深入学习应用Qt绘图很有帮助。如果大家想更系统的学习Qt坐标系统,可以参考《Qt Creator快速入门》的第10章相关内容。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/52052.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Qt进行程序开发的过程记录

目录 一、qt的安装 二、resources文件的使用 三、Qt中各种设置问题 四、在子页面的简单图表显示 五、Qt发布独立运行的exe的方法 六、Qt创建子页面的方法 七、Qt中使用菜单栏的方法 八、Qt联合C进行文件读取、写入操作 九、Qt关闭窗口函数 十、提示窗口与调试功能的使…

Qt开发技术:Qt绘图系统(二)QPainter详解

若该文为原创文章&#xff0c;未经允许不得转载 原博主博客地址&#xff1a;https://blog.csdn.net/qq21497936 原博主博客导航&#xff1a;https://blog.csdn.net/qq21497936/article/details/102478062 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/…

Qt绘图与信号事件

Qt应用开发的基本模式(面向对象) 继承QDailog gkdialog.h #ifndef GK_DIALOG_H #define GK_DIALOG_H #include <QtWidgets/QDialog>class GKDialog: public QDialog{ public:GKDialog(QWidget *parent0);~GKDialog(); }; #endifgkdialog.cpp #include "gkdialog…

我的Qt作品(9)Qt使用Cairo绘图引擎提升抗锯齿效果【开源】

之前写了一篇文章《Qt使用GDI绘图&#xff08;仅Windows平台&#xff09;》 Qt使用GDI绘图&#xff08;仅Windows平台&#xff09;_libaineu2004的博客-CSDN博客 本篇重点介绍Cairo绘图引擎&#xff0c;Qt自带的QPainter的绘图效率和抗锯齿效果都没有cairo的好。cairo 的目标…

基于Qt的多媒体综合应用程序设计(2)

框架结构图等 1.多媒体综合应用程序设计思路 首先创建一个Qt Widgets Application程序,在头文件中完成类、函数及变量的设置,在程序窗口中添加相应的控件,利用控件的属性和方法分别实现文字处理程序跳转、绘图程序跳转、图像处理程序跳转、音频播放器跳转、视频播放器跳转和…

VS+Qt+鼠标框选后掩膜去除背景

前言&#xff1a; 实际采集的图片背景比较复杂&#xff0c;所以我想着采用框选矩形框掩膜的方式简单粗暴的消除背景对图片有用区域的干扰。网上搜索了一圈尝试了几种不同的方式去框选&#xff0c;最后发现还是Qt的鼠标事件比较容易上手。 效果图&#xff1a; 主要思路&#x…

QT—3D绘图

OpenGL是一个跨平台的、用来渲染3D图形的标准API,Qt对OpenGL提供了强大的支持。Qt4时代的QtOpenGL模块在Qt5中已经不再建议使用,OpenGL相关的类被移到了Qt GUI模块。Qt Widgets模块中的QOpenGLWidget类提供了一个可以渲染OpenGL图形的部件&#xff0c;通过该部件可以轻松地将Op…

【Qt】Qt环境配置与入门案例

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Qt环境配置与入门。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&…

(三)NI采集卡应用学习:在Qt中实现多路数据的同时采集、显示、绘图及存储

1、使用NI MAX创建多路输入的测量任务 本处设置Dev1/ai0:3四路电压输入&#xff0c;采样率均为100&#xff0c;差分连接。 2、代码 // 设置地址 void Widget::on_add_pushButton_clicked() {address_txt QFileDialog::getExistingDirectory(this, tr("Open Directory&q…

linux程序框架设计,《Linux与Qt程序设计》知识框架

本文主要是通过一本书来大致了解Qt开发的框架,不对具体内容做详细分析。 1.首先弄清楚概念:定义->以自己的话理解是什么-> 实现的是什么功能->用在哪些地方 2.前面认识到的知识点的特点-> 代码实现-> 工程代码分析 第一部分 Linux基础知识 第二部分 Qt程序基础…

QT开发应用程序(11)--图形绘制和文本输出

代码演示&#xff1a; #include <QPainter> MyDraw::MyDraw(QWidget *parent) :QWidget(parent) {QLinearGradient linearGradient(0,0,400,400);linearGradient.setColorAt(0.0,Qt::white);linearGradient.setColorAt(0.2,QColor(0,0,0));linearGradient.setColorAt(1.…

chatgpt赋能python:使用Python捕获错误:为您的代码添加可靠性

使用Python捕获错误&#xff1a;为您的代码添加可靠性 在编写Python代码时&#xff0c;错误很常见。您可能会因输入无效参数而收到TypeError&#xff0c;或者因无法连接到数据库而收到ConnectionError。当这些错误发生时&#xff0c;您的代码可能会崩溃或产生不正确的结果。尽…

chatgpt赋能python:关闭Python:如何优雅地退出一个Python程序

关闭Python&#xff1a;如何优雅地退出一个Python程序 Python作为一门开发语言&#xff0c;可以广泛应用于众多领域&#xff0c;如数据科学、人工智能、Web应用开发等等。但有时候&#xff0c;我们需要关闭一个正在运行的Python程序。本文将介绍如何优雅地退出一个Python程序&…

chatgpt赋能python:Python异常捕获存在的问题

Python 异常捕获存在的问题 作为一门广受欢迎、应用广泛的编程语言&#xff0c;Python 在处理异常方面有着比较完善的设计。Python 提供了 try…except…finally 这样的异常处理机制&#xff0c;通过这些机制&#xff0c;开发者可以捕获、处理程序中产生的异常&#xff0c;从而…

yagmail——快速发送邮件

yagmail——快速发送邮件 目标&#xff1a;如何利用python中的yagmail包&#xff0c;快速发送邮件。 python写的一个简单demo脚本代码如下&#xff1a; import yagmail import pandas as pd from loguru import logger# 保存excel文件 contents pd.DataFrame() contents[&quo…

电脑和微信怎么发邮件到别人邮箱,企业邮箱如何发email给别人

邮箱已成为主流的办公软件&#xff0c;除了日常的电脑办公使用以外&#xff0c;你知道还有哪些便捷的方式使用邮箱吗&#xff1f;现在是互联网时代&#xff0c;许多邮箱都研发了手机邮箱配合使用&#xff0c;那TOM企业邮箱有哪些便捷的服务呢&#xff1f;一起看看吧&#xff01…

怎么发送电子邮件到别人邮箱?手把手教你!

在日常生活中&#xff0c;我们在工作学习的时候都离不开电子邮件&#xff0c;无论两人的距离有多远&#xff0c;都是可以通过电子邮件的方式完成信息的即时传递。这样不仅可以传输文字信息&#xff0c;还能传输图片、视频等多种格式的信息。那么&#xff0c;怎么发送电子邮件到…

chatgpt赋能Python-pythonapp自动化

Python App自动化&#xff1a;优化SEO的终极解决方案 随着互联网的发展&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;变得日益重要。对于任何网站或应用程序开发人员来说&#xff0c;SEO应该是一个非常重要的考虑因素。为了帮助开发人员和企业提高其在线可见性&#x…

chatgpt赋能python:Python自动化断言介绍

Python 自动化断言介绍 软件业的各种应用程序不可避免地需要与用户进行交互。测试团队应该确保这些应用程序快速&#xff0c;可靠地响应用户交互&#xff0c;并注重高质量测试的策略和工具。Python自动化断言是一种测试策略&#xff0c;可帮助团队实现高品质和高效率的测试。P…

chatgpt赋能python:Python自动化操作桌面的方法和应用

Python自动化操作桌面的方法和应用 Python是一种高级编程语言&#xff0c;越来越多的研究人员和程序员将其应用在自动化操作任务上。本文将向您介绍如何利用Python自动化操作桌面并且展示一些实际应用。 桌面自动化操作是什么&#xff1f; 桌面自动化操作是使用编程语言编写…