本文主要讲解根据多点,绘制平滑曲线。且xy坐标轴根据点的大小,自适应xy轴的刻度。
比如点是1~8.5范围内的,坐标轴范围可以为0~9;如果是0-35.8范围内,坐标轴可以为0~40。
即根据数据范围,确定坐标轴范围。
一、基础知识
好了下面进入正题,要搞懂几个东西。就是我们数学中的点(后面简称【数点】)与QT中的点(后面简称【QT点】)的对应关系。中间还涉及到XY轴【画图区域】坐标(后面简称【图点】)和QT坐标的关系。
要画好图,就是要知道怎么把【数点】转为【QT点】。这样你就知道点画在哪里。
1、【图点】与【QT点】转换关系
如图:我们自定义QWidget,QWidget的大小就是height()*width()。我们预留画图区域与QWidget内边距分别是左右xpadding,上下ypadding。这个边距可以自己定义,也可以上下不一样宽。留这个边距是为了画坐标轴的数值相关。
【QT点】:是以左上角为原点(Qt(0,0)),以水平向右为x递增,以竖直向下为y递增。所以最右下角就是QPointF(width(),height())。右上角就是QPointF(widht(),0);以此类推。
【画图区域】:就是我们选定一个区域作为画图区域,来画x轴,y轴。
【图点】:就是画图区域的点位置。
比如图点(x,y)是相对于画图区域的原点o(0,0)的相对位置。这样是不是和坐标轴有点像了。
那图点转为QT点怎么转呢,从图中不难看出这样的关系。
QT点的x坐标=xpadding+x;
QT点的y坐标=height()-ypadding-y;
const QPointF GraphWidget::screenPointMaptoQTPoint(const QPointF &scPoint)
{
return QPointF(scPoint.x() + xpadding, height() - ypadding - scPoint.y());
}
2、【数点】与【图点】转换关系
其实就是比例尺的问题。就是根据所有【数点】的xy大小,来选择一个合适的xy轴的范围。然后这个xy轴上面多长对应【画图区】的多少像素。这样【数点】就能等比缩放到【画图区域】。
举个例子。【画图区域】尺寸是300px(宽)*200px(高)。而所有【数点】的范围,x范围为0-33.5,y范围为0-8.5。那么我们可以取x轴的范围0~40(即minX=0,maxX=40),y轴范围0~9(minY=0;maxY=9)。这样所有的点都能在这个X轴y轴的范围内。所以我们的数据等比缩放就是。
x轴范围大小maxX-minX=40;对应【画图区域】宽(300px);所以缩放比例为kx=300/40;
y轴范围大小maxY-minY=9;对应【画图区域】高(200px);所以缩放比例为ky=200/9;
所以【数点】(xm,ym)转为【图点】是如下关系。
const QPointF GraphWidget::mathMaptoScreenPoint(const double &xm, const double &ym)
{
return QPointF(qRound64((xm - minX) * kx), qRound64((ym - minY) * ky));
}
3、【数点】与【QT点】转换关系
从1与2可以很容易知道他们之间的转换关系了。
const QPointF GraphWidget::mathMaptoQTPoint(const double &xm, const double &ym)
{
return screenPointMaptoQTPoint(mathMaptoScreenPoint(xm,ym));
}
二、绘图
原理搞明白了,画图就简单了。定义GraphWidget。直接上代码。
1、画坐标系
2、画曲线
3、画极点(可选)、坐标轴说明。
#ifndef GRAPHWIDGET_H
#define GRAPHWIDGET_H
#pragma once
#include <QPaintEvent>
#include <QPainter>
#include <QWidget>
// 坐标
struct CoordinateXY {
CoordinateXY(const double &_x = -1, const double &_y = -1) : x(_x), y(_y) { }
double x;
double y;
};
struct GraphDataInfo {
GraphDataInfo() : min(0), max(0), space(5), k(1), unit("") { }
inline double r