【QT】坐标系统和坐标变换

news2024/11/19 21:31:39

目录

1 坐标变换函数

1.1 坐标平移

1.2 坐标旋转

1.3 缩放

1.4 状态保存与恢复

2 坐标变换绘图实例

2.1 绘制3个五角星的程序

2.2 绘制五角星的PainterPath的定义

3 视口和窗口

3.1 视口和窗口的定义与原理

3.2 视口和窗口的使用实例

4 绘图叠加的效果


1 坐标变换函数

        QPainter在窗口上绘图的默认坐标系统如图8-1所示,这是绘图设备的物理坐标。为了绘图 的方便,QPainter提供了一些坐标变换的功能,通过平移、旋转等坐标变换,得到一个逻辑坐标 系统,使用逻辑坐标系统在某些时候绘图更方便。坐标变换函数见表8-5。
        常用的坐标变换是平移、旋转和缩放,使用 世界坐标变换矩阵也可以实现这些变换功能,但 是需要单独定义一个 QTransform 类的变量,对于 QPainter 来说,简单的坐标变换使用 QPainter 自 有的坐标变换函数就足够了。

1.1 坐标平移

        坐标平移函数是translate(),其中一种参数形式的函数原型是:
void translate(qreal dx,qreal dy)
        表示将坐标系统水平方向平移个单位,垂直方向平移个单位,在缺省的坐标系统中,单位就是像素。如果是从原始状态平移(dx,dy),那么平移后的坐标原点就移到了(dx,dy)。
        假设一个绘图窗口宽度为300像素,高度为200像素,则其原始坐标系统如图8-10左所示; 若执行平移函数translate(150,100),则坐标系统水平向右平移150像素,向下平移100像素,平 移后的坐标系统如图8-10右所示,坐标原点在窗口的中心,而左上角的坐标变为(-150,-100), 右下角的坐标变为(150,100)。如此将坐标原点变换到窗口中心在绘制某些图形时是非常方便的。

1.2 坐标旋转

        坐标旋转的函数是rotate(),其函数原型为:
void rotate(qreal angle)
        它是将坐标系统绕坐标原点顺时针旋转angle角度,单位是度。当angle为正数时是顺时针旋 转,为负数时是逆时针旋转。
        在图8-10右的基础上,若执行rotate(90),则得到图8-11所示的坐标系统。
        注意:旋转之后并不改变窗口矩形的实际大小,只是改变了坐标轴的方向。      
        在图8-11的新坐标系下,窗口左上角的坐标变成了(-100,150),而右下角的坐标变成了(100,-150)。

1.3 缩放

        缩放函数是scale(),其函数原型为:
void scale(qreal sx,qreal sy)
        其中,sx,sy分别为横向和纵向缩放比例,比例大于1是放大,小于1是缩小。

1.4 状态保存与恢复

        进行坐标变换时,QPainter内部实际上有一个坐标变换矩阵,用save()保存当前坐标状态,用 restore()恢复上次保存的坐标状态,这两个函数 必须配对使用操作的是一个堆栈对象
        resetTransform()函数则是复位所有坐标变换操作,恢复原始的坐标系统。

2 坐标变换绘图实例

2.1 绘制3个五角星的程序

        创建一个基于QWidget的窗口的应用程序samp8_2,窗体上不放置任何组件。在Widget类的 构造函数和paintEvent()事件中编写代码,代码内容如下。
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setPalette(QPalette(Qt::white)); //设置窗口背景色
    setAutoFillBackground(true);
    resize(600,300); //固定初始化窗口大小
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter    painter(this);//创建QPainter对象
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHint(QPainter::TextAntialiasing);

//生成五角星的5个顶点的,假设原点在五角星中心
    qreal   R=100; //半径
    const   qreal Pi=3.14159;
    qreal   deg=Pi*72/180;
    QPoint points[5]={
        QPoint(R,0),
        QPoint(R*std::cos(deg),-R*std::sin(deg)),
        QPoint(R*std::cos(2*deg),-R*std::sin(2*deg)),
        QPoint(R*std::cos(3*deg),-R*std::sin(3*deg)),
        QPoint(R*std::cos(4*deg),-R*std::sin(4*deg)),
    };
//设置字体
    QFont   font;
    font.setPointSize(12);
    font.setBold(true);
    painter.setFont(font);
//设置画笔
    QPen    penLine;
    penLine.setWidth(2); //线宽
    penLine.setColor(Qt::blue); //划线颜色
    penLine.setStyle(Qt::SolidLine);//线的类型,实线、虚线等
    penLine.setCapStyle(Qt::FlatCap);//线端点样式
    penLine.setJoinStyle(Qt::BevelJoin);//线的连接点样式
    painter.setPen(penLine);
//设置画刷
    QBrush  brush;
    brush.setColor(Qt::yellow); //画刷颜色
    brush.setStyle(Qt::SolidPattern); //画刷填充样式
    painter.setBrush(brush);
//设计绘制五角星的PainterPath,以便重复使用
    QPainterPath starPath;
    starPath.moveTo(points[0]);
    starPath.lineTo(points[2]);
    starPath.lineTo(points[4]);
    starPath.lineTo(points[1]);
    starPath.lineTo(points[3]);
    starPath.closeSubpath(); //闭合路径,最后一个点与第一个点相连
    starPath.addText(points[0],font,"0"); //显示端点编号
    starPath.addText(points[1],font,"1");
    starPath.addText(points[2],font,"2");
    starPath.addText(points[3],font,"3");
    starPath.addText(points[4],font,"4");
//绘图
    painter.save(); //保存坐标状态
    painter.translate(100,120);
    painter.drawPath(starPath); //画星星
    painter.drawText(0,0,"S1");
    painter.restore(); //恢复坐标状态

    painter.translate(300,120); //平移
    painter.scale(0.8,0.8); //缩放
    painter.rotate(90); //顺时针旋转
    painter.drawPath(starPath);//画星星
    painter.drawText(0,0,"S2");

    painter.resetTransform(); //复位所有坐标变换
    painter.translate(500,120); //平移
    painter.rotate(-145); //逆时针旋转
    painter.drawPath(starPath);//画星星
    painter.drawText(0,0,"S3");
}
     运行该实例程序,得到如图8·12所示的结果,在窗口上绘制了3个五角星。
        第1个是原始的五角星;第2个是缩小为0.8倍,顺时针旋转90度的五角星;第3个是逆时 针旋转145度的五角星。这个程序中使用到了QPainterPath和QPainter的坐标变换功能。

2.2 绘制五角星的PainterPath的定义

        首先假设一个五角星的中心点是原点,第0个点在x轴上,五角星外接圆半径为100,计算 出5个点的坐标,保存在points数组中。
        然后定义了一个QPainterPath类的变量starPath,用于记录画五角星的过程,就是几个点的连 线过程,并且标注点的编号。使用QPainterPath的优点就是定义一个QPainterPath类型的变量记录
一个复杂图形的绘制过程后,可以重复使用。虽然points数组中的点的坐标是假设五角星的中心 点是原点,在绘制不同的五角星时只需将坐标平移到新的原点位置,就可以绘制不同的五角星。
        绘制第1个五角星的程序是:
    painter.save(); //保存坐标状态
    painter.translate(100,120);
    painter.drawPath(starPath); //画星星
    painter.drawText(0,0,"S1");
    painter.restore(); //恢复坐标状态
        这里,save()函数保存当前坐标状态(也就是坐标的原始状态),然后将坐标原点平移到(100,120), 调用绘制路径的函数drawPath(starPath)绘制五角星,在五角星的中心标注“S1”表示第1个五角星,最后调用restore()函数恢复上次的坐标状态。这样就以(100,120)为中心点绘制了第1个五角星。
        绘制第2个五角星的程序是:
    painter.translate(300,120); //平移
    painter.scale(0.8,0.8); //缩放
    painter.rotate(90); //顺时针旋转
    painter.drawPath(starPath);//画星星
    painter.drawText(0,0,"S2");
        这里首先调用坐标平移函数translate(300,120)。由于上次restore()之后回到坐标初始状态,所 以这次平移后,坐标原点到了物理坐标(300,120)。而如果没有上一个restore(),会在上一次的坐
标基础上平移。
        绘图之前调用了缩放函数scale(0.8,0.8),使得缩小到原来的0.8,再顺时针旋转90°,然后调 用绘制路径函数drawPath(starPath)绘制五角星,就得到了第2个五角星。
        绘制第3个五角星时首先使坐标复位,即:
painter.resetTransform();//复位所有坐标变换
        这样会复位所有坐标变换,又回到了原始坐标。

3 视口和窗口

3.1 视口和窗口的定义与原理

        绘图设备的物理坐标是基本的坐标系,通过QPainter的平移、旋转等变换可以得到更容易操 作的逻辑坐标。
        为了实现更方便的坐标, QPainter还提供了视口(Viewport)和窗口(Window)坐标系,通 过QPainter内部的坐标变换矩阵自动转换为绘图设备的物理坐标。
        视口表示绘图设备的任意一个矩形区域的物理坐标,可以只选取物理坐标的一个矩形区域用 于绘图。默认情况下,视口等于绘图设备的整个矩形区。
        窗口与视口是同一个矩形,只不过是用逻辑坐标定义的坐标系。窗口可以直接定义矩形区的 逻辑坐标范围。图8-13是对视口和窗口的图示说明。
        图8-13左图中的矩形框代表绘图设备的物理大小和坐标范围,假设宽度为300像素,高度为 200像素。现在要取其中间的一个正方形区域作为视口, 灰色的正方形就是视口 ,绘图设备的物 理坐标中,视口的左上角坐标为(50,0),右下角坐标为(250,200)。定义此视口,可以使用QPainter 的setViewport()函数,其函数原型为:
void QPainter::setViewport(int x,int y,int width,int height)
        要定义图8-13左图中的视口,使用下面的语句:
painter.setViewport(50,0,200,200);
        表示从绘图设备物理坐标系统的起点(50,0)开始,取宽度为200、高度为200的一个矩形区域 作为视口。
        对于图8-13左图的视口所表示的正方形区域,定义一个窗口(图8-13右图), 窗口坐标的中心在 正方形中心,并设置正方形的逻辑边长为100。可使用QPainter的setWindow()函数,其函数原型为:
void QPainter::setWindow(int x,int y,int width,int height)
        所以,此处定义窗口的语句是:
painter.setWindow(-50,-50,100,100);
        它表示对应于视口的矩形区域,其窗口左上角的逻辑坐标是(-50,-50),窗口宽度为100,高度 为100。这里设置的窗口还是一个正方形,使得从视口到窗口变换时,长和宽的变化比例是相同 的。实际可以任意指定窗口的逻辑坐标范围,长和宽的变化比例不相同也是可以的。

3.2 视口和窗口的使用实例

        使用窗口坐标的优点是,只需按照窗口坐标定义来绘图,而不用管实际的物理坐标范围的大小。 如在一个固定边长为100的正方形窗口内绘图,当实际绘图设备大小变化时,绘制的图形会自动变化大 小。这样,就可以将绘图功能与绘图设备隔离开来,使得绘图功能适用于不同大小、不同类型的设备。
        实例samp8_3演示了使用视口和窗口的方法,项目创建与samp8_1类似,只在Widegt的 paintEvent()事件里添加绘图代码。
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    int W=width();
    int H=height();
    int side=qMin(W,H);//取长和宽的小值
    QRect rect((W-side)/2, (H-side)/2,side,side); //viewport矩形区
    painter.drawRect(rect); //Viewport大小
//    painter.setViewport((W-side)/2, (H-side)/2,side,side);//设置Viewport,物理坐标范围
    painter.setViewport(rect);//设置Viewport
    painter.setWindow(-100,-100,200,200); // 设置窗口大小,逻辑坐标
    painter.setRenderHint(QPainter::Antialiasing);
//    p.setRenderHint(QPainter::TextAntialiasing);

//设置画笔
    QPen pen;
    pen.setWidth(1); //线宽
    pen.setColor(Qt::red); //划线颜色
    pen.setStyle(Qt::SolidLine);//线的类型,实线、虚线等
    painter.setPen(pen);
    for(int i=0; i<36;i++)
    {
        painter.drawEllipse(QPoint(50,0),50,50);
        painter.rotate(10);
    }
}
        运行实例程序samp8_3,可以得到如图8-14所示的图形效果。当窗口的宽度大于高度时,以 高度作为正方形的边长;当高度大于宽度时,以宽度作为正方形边长,且图形是自动缩放的。
图8-14 实例samp8_3使用窗口坐标的绘图效果
        程序首先定义了一个正方形视口,正方形以绘图设备的长、宽中较小者为边长。 然后定义窗口,定义的窗口是原点在中心,边长为 200 的正方形。
图8-14的图形效果实际上是画了36个圆得到的,循环部分的代码如下:
for(int i=0; i<36;i++)
{
    painter.drawEllipse(QPoint(50,0),50,50);
    painter.rotate(10);
}
        每个圆的圆心在X轴上的(50,0),半径为50。画完一个圆之后坐标系旋转10°,再画相同的 圆,这就巧妙应用了坐标轴的旋转。

4 绘图叠加的效果

        对上面的程序稍作修改,增加渐变填充和叠加效果的设置。
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    int W=width();
    int H=height();
    int side=qMin(W,H);//取长和宽的小值
    QRect rect((W-side)/2, (H-side)/2,side,side); //viewport矩形区
    painter.drawRect(rect); //Viewport大小
//    painter.setViewport((W-side)/2, (H-side)/2,side,side);//设置Viewport,物理坐标范围
    painter.setViewport(rect);//设置Viewport
    painter.setWindow(-100,-100,200,200); // 设置窗口大小,逻辑坐标
    painter.setRenderHint(QPainter::Antialiasing);
//    p.setRenderHint(QPainter::TextAntialiasing);

//设置画笔
    QPen pen;
    pen.setWidth(1); //线宽
    pen.setColor(Qt::red); //划线颜色
    pen.setStyle(Qt::SolidLine);//线的类型,实线、虚线等
    painter.setPen(pen);
//线性渐变
    QLinearGradient  linearGrad(0,0,100,0);//从左到右,
    linearGrad.setColorAt(0,Qt::yellow);//起点颜色
    linearGrad.setColorAt(1,Qt::green);//终点颜色
    linearGrad.setSpread(QGradient::PadSpread);  //展布模式
    painter.setBrush(linearGrad);

    //设置复合模式
    painter.setCompositionMode(QPainter::RasterOp_NotSourceXorDestination);
//    painter.setCompositionMode(QPainter::CompositionMode_Difference);
//    painter.setCompositionMode(QPainter::CompositionMode_Exclusion);
    for(int i=0; i<36;i++)
    {
        painter.drawEllipse(QPoint(50,0),50,50);
        painter.rotate(10);
    }
}

        在上面的程序中,对单个圆使用了线性渐变填充,单个圆从左到右,由黄色渐变为绿色。使用QPainter::setCompositionMode()函数设置组合模式,即后面绘制的图与前面绘制的图的叠加模式。函数参数是一个QPainter::CompositionMode枚举类型值,可以查看Qt帮助,这个枚举

类型有近40种取值,表示了后绘制图形与前面图形的不同叠加运算方式。

        图8-15是其中两种叠加模式下的绘图效果,可以发现采用不同的叠加模式,可以得到不同的绘图效果,甚至是意想不到的绚丽效果。用户可以自己修改程序,设置不同渐变颜色、渐变填充模式、不同叠加模式,也许能绘制出更炫的图形呢。

图8-15 渐变填充和叠加效果,(左)CompositionMode_Difference模式叠加,(右)RasterOp_NotSourceXorDestination模式叠加

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链表与二叉树-数据结构

链表与二叉树-数据结构 创建叶子node节点建立二叉树三元组&#xff1a;只考虑稀疏矩阵中非0的元素&#xff0c;并且存储到一个类&#xff08;三元组&#xff09;的数组中。 创建叶子node节点 class Node{int no;Node next;public Node(int no){this.nono;} } public class Lb…

24.云原生ArgoCD高级之数据加密seale sealed

云原生专栏大纲 文章目录 数据加密之seale sealedBitnami Sealed Secrets介绍Bitnami Sealed Secrets工作流程安装sealed-secrets和kubeseal安装sealed-secrets-controller安装kubeseal通过kubeseal将sealed-secrets公钥拿出来通过kubeseal加密secrets替换kustomize下secret为…

Leetcode 热门百题斩(第二天)

介绍 针对leetcode的热门一百题&#xff0c;解决大多数实习生面试的基本算法题。通过我自己的思路和多种方法&#xff0c;供大家参考。 1.两数之和&#xff08;题号&#xff1a;1) 方法一 最先想到的就是两个for去遍历匹配。 class Solution {public int[] twoSum(int[]…

DPVS 多活部署架构部署

一、目标 利用DPVS部署一个基于OSPF/ECMP的提供HTTP服务的多活高可用的测试环境。 本次部署仅用于验证功能&#xff0c;不提供性能验证。 配置两台DPVS组成集群、两台REAL SERVER提供实际HTTP服务。 注&#xff1a;在虚拟环境里面&#xff0c;通过在一台虚拟服务器上面安装FR…

Linux - iptables 防火墙

一. 安全技术和防火墙 1.安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…

阿里云如何找回域名,进行添加或删除?

权威域名管理介绍说明&#xff0c;包含添加域名、删除域名、找回域名、域名分组等操作介绍。 一、添加域名 非阿里云注册域名或子域名如需使用云解析DNS&#xff0c;需要通过添加域名功能&#xff0c;将主域名或子域名添加到云解析控制台&#xff0c;才可以启用域名解析服务。…

Redis -- hash哈希

马行软地易失蹄&#xff0c;人贪安逸易失志。 目录 关于Redis的hash hash命令 hset hget hexists hdel hkeys hvals hgetall hmget hlen hsetnx hash计数 hincrby hincrbyfloat 小结 关于Redis的hash 几乎所有的主流编程语言都提供了哈希&#xff08;hash&a…

基于极大似然法和最小二乘法系统参数辨识matlab仿真,包含GUI界面

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 1.极大似然法系统参数辨识 2. 最小二乘法系统参数辨识 5.完整程序 1.程序功能描述 分别对比基于极大似然法的系统参数辨识以及基于最小二乘法的系统参数辨识&#xff0c;输出起参数辨识收敛…

计算存储设备(Computational Storage Drive, CSD)

随着云计算、企业级应用以及物联网领域的飞速发展&#xff0c;当前的数据处理需求正以前所未有的规模增长&#xff0c;以满足存储行业不断变化的需求。这种增长导致网络带宽压力增大&#xff0c;并对主机计算资源&#xff08;如内存和CPU&#xff09;造成极大负担&#xff0c;进…

计算机网络实验二

目录 实验二 交换机的基本配置 1、实验目的 2、实验设备 &#xff08;1&#xff09;实验内容&#xff1a; &#xff08;2&#xff09;练习&#xff1a; 1.实验内容一&#xff1a;&#xff08;交换机的配置方式&#xff09; 2.实验内容二&#xff1a;&#xff08;交换机…

碳汇的概念

碳汇的概念 在理解碳汇之前&#xff0c;首先理解“汇”&#xff0c;汇是指存储温室气体、气溶胶或温室气体化合物的库&#xff0c;例如土壤、海洋、森林等都是地球的重要碳库。根据《联合国气候变化框架公约》的定义&#xff0c;“碳汇”指从大气中清除二氧化碳等温室气体的过…

有没有合适写毕业论文的AI工具?

最近挺多同学在忙着写毕业论文&#xff0c;不断在“提交-打回-修改-提交”过程里循环着&#xff0c;好不容易写完了&#xff0c;还得考虑论文查重的问题&#xff01;基哥作为一名曾经的毕业生&#xff0c;当然也体验过这种痛苦了。 但是&#xff0c;大人&#xff0c;时代变了&…

元素的显示与隐藏,精灵图,字体图标,CSSC三角

元素的显示与隐藏 类似网站广告&#xff0c;当我们点击关闭就不见了&#xff0c;但是我们重新刷新页面&#xff0c;会重新出现 本质&#xff1a;让元素在页面中隐藏或者显示出来。 1.display显示隐藏 2.visibility显示隐藏 3.overflow溢出显示隐藏 1.display属性&#xff08;…

使用STM32 DMA实现高效数据传输的设计与优化

使用STM32的DMA功能可以有效地实现高效的数据传输。在下面的解释中&#xff0c;我将介绍如何设计和优化使用STM32 DMA进行高效数据传输的方法。同时&#xff0c;我将提供一些示例代码来帮助您理解和实践。 ✅作者简介&#xff1a;热爱科研的嵌入式开发者&#xff0c;修心和技术…

缓存相关问题记录解决

缓存相关问题 在这里我不得不说明,我写的博客都是我自己用心写的,我自己用心记录的,我写的很详细,所以会有点冗长,所以如果你能看的下去的化,会有所收获,我不想写那种copy的文章,因为对我来说没什么益处,我写的这篇博客,就是为了记录我缓存的相关问题,还有我自己的感悟,所以如果…

机器学习_12_梯度下降法、拉格朗日、KKT

文章目录 1 梯度下降法1.1 导数、梯度1.2 梯度下降法1.3 梯度下降法的优化思想1.4 梯度下降法的调优策略1.5 BGD、SGD、MBGD1.5.1 BGD、SGD、MBGD的区别 2 有约束的最优化问题3 拉格朗日乘子法3.1 拉格朗日乘子法理解3.2 对偶问题 4 KKT条件4.1 KKT条件理解4.2 KKT公式理解4.3 …

【DPI(Direct Programming Interface)_2024.02.01】

DPI接口&#xff1a;实现SV与C的交互 ① DPI_svc test.sv文件&#xff1a; 从C import task/function到SV 从SV export task到C 利用DPI调用C code访问register test.c文件&#xff1a; C调用apb_write驱动 ② dpi_perl test.sv文件&#xff1a; 利用DPI调用c code间接调…

【echarts】动态滚动趋势图,解决坐标轴数据太多遮挡覆盖问题

写在前面 业务场景x轴的文字太多&#xff0c;会出现遮挡问题&#xff0c;想到文字倾斜展示&#xff0c;页面不美观&#xff0c;于是想到使用滚动条优化趋势图。 <template><div id"storeDown" style"height: 400px;width:100%"/> </temp…

独立按键实验

轻触开关是一种电子开关&#xff0c;使用时,轻轻按开关按钮就可使开关接通&#xff0c;当松开手时,开关断开。我们使用的开关如下图&#xff1a; 按键在闭合和断开时&#xff0c;触点会存在抖动现象。 硬件消抖电路&#xff1a; 实现现象&#xff1a;下载程序后按下K1按键可以对…

C++类和对象知识点集合

又多又难的C类和对象知识点看这里了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录 1.先看看类是咋用的2.访问限定符3.类中的声明和定义分离4.类对象模型4.1 空类的大小4.2 成员访问 5.内存对齐6.this指针6.1 this在哪里&#xff1f;6.2 (类对象及thi…