一、QT坐标系简介
Qt中每一个窗口都有一个坐标系,默认窗口左上角为坐标原点,然后水平向右依次增大,水平向左依次减小,垂直向下依次增大,垂直向上依次减小。原点即为(0,0)点,以像素为单位增减。
1、物理坐标系
物理坐标系即设备坐标系,原点在左上角的位置,单位为像素,X坐标向右增长,Y坐标向下增长。
2、逻辑坐标系
抽象坐标系,单位由具体问题决定,坐标轴的增长方向由具体问题决定。
QPainter使用逻辑坐标系绘制图形,逻辑坐标系中图形的大小和位置会转换为物理坐标系后绘制在设备上。默认情况下,逻辑坐标系与物理坐标系相同。
3、视口
视口(view port)是物理坐标系中任意指定的矩形区域。
4、窗口
窗口是逻辑坐标系中对应到物理坐标系中的相同矩形区域。
视口与窗口是不同坐标系下的同一个矩形,视口与窗口中的坐标点存在一一映射的关系,视口和窗口通过坐标变换可以相互转换。
二、坐标系变换
QPainter逻辑坐标与QPaintDevice物理坐标的映射,由QPainter的变换矩阵(transformation matrix)、视口(viewport)和窗口(window)完成。默认情况下,物理坐标与逻辑坐标系统是重合的,QPainter也支持坐标转换, 例如:旋转、缩放。
坐标系变换是利用变换矩阵来进行的, 通常利用QTransform类来设置变换矩阵。QPainter类提供了对坐标系的平移,缩放,旋转,扭曲等变换函数。
void translate(const QPointF & offset)
void translate(const QPoint & offset)
void translate(qreal dx, qreal dy)
void scale(qreal sx, qreal sy)
void shear(qreal sh, qreal sv)
void rotate(qreal angle)
void save()
void restore()
1、平移变换
QT利用translate()函数进行平移变换。
平移变换代码:
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
//将当前坐标系下的点(100,100)设为原点
painter.translate(100,100);
painter.setBrush(Qt::red);
painter.drawRect(0,0,50,50);
//将当前坐标系下的点(-100,-100)设为原点
painter.translate(-100,-100);
painter.drawLine(0,0,20,20);
2、比例变换
利用scale()函数进行比例变换,实现缩放效果。
比例变换代码:
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,100,100);
//将坐标系在x,y方向放大2倍
painter.scale(2,2);
painter.setBrush(Qt::red);
painter.drawRect(50,50,50,50);
3、扭曲变换
利用shear()函数就行扭曲变换。
扭曲变换代码:
QPainter painter(this);
painter.setBrush(Qt::yellow);
painter.drawRect(0,0,50,50);
//对当前坐标系的y轴进行扭曲
painter.shear(0,1);
painter.setBrush(Qt::red);
painter.drawRect(50,0,50,50);
4、翻转变换
利用rotate()函数进行翻转变换。
翻转变换代码:
QPainter painter(this);
//以原点为中心,顺时针旋转30度
painter.rotate(30);
painter.drawLine(0,0,100,0) ;
//以原点为中心,顺时针再旋转30度
painter.rotate(30);
painter.drawLine(0,0,100,0);
5、坐标系状态的保护
绘图过程中需要快速的进行坐标系切换,绘制出不同的图形,因此需要对坐标系状态进行保护,可以先利用save()函数来保存坐标系现在的状态,然后进行变换操作,操作完之后,再用restore()函数将以前的坐标系状态恢复,本质是对坐标系进行入栈和出栈的操作。
坐标系状态保护代码:
QPainter painter(this);
//保存当前坐标系状态
painter.save();
//平移坐标系原点到(100, 100)
painter.translate(100,100);
painter.drawLine(0,0,50,50);
//将当前原点位于点(100, 100)的坐标系恢复到保存的坐标系状态
painter.restore();
painter.drawLine(0,0,50,50);
三、绘图实例
1、正弦波绘制
SineWave.h文件:
#ifndef SINEWAVE_H
#define SINEWAVE_H
#include <QWidget>
#include <QPainter>
class SineWave : public QWidget
{
Q_OBJECT
private:
void drawBackGround(QPainter* painter);
void drawCave(QPainter* painter);
void paintEvent(QPaintEvent *event);
public:
SineWave(QWidget *parent = 0);
~SineWave();
};
#endif // SINEWAVE_H
SineWave.cpp文件:
#include "SineWave.h"
#include <QPen>
#include <QPointF>
#include <qmath.h>
SineWave::SineWave(QWidget *parent):QWidget(parent)
{
}
SineWave::~SineWave()
{
}
void SineWave::drawBackGround(QPainter* painter)
{
QPen pen;
const double delta = 1;
pen.setStyle(Qt::SolidLine);
painter->setViewport(50, 50, width()-100, height()-100);
painter->setWindow(-10, 2, 20, -4); // (-10, 2) (10, -2)
painter->fillRect(-10, 2, 20, -4, Qt::black);
pen.setColor(Qt::white);
pen.setWidthF(painter->window().width()/painter->viewport().width() * delta);
painter->setPen(pen);
painter->drawLine(QPointF(-10, 1.5), QPointF(10, 1.5));
painter->drawLine(QPointF(-10, 1), QPointF(10, 1));
painter->drawLine(QPointF(-10, 0.5), QPointF(10, 0.5));
painter->drawLine(QPointF(-10, -0.5), QPointF(10, -0.5));
painter->drawLine(QPointF(-10, -1), QPointF(10, -1));
painter->drawLine(QPointF(-10, -1.5), QPointF(10, -1.5));
pen.setWidthF(painter->window().height()/painter->viewport().height() * delta);
painter->setPen(pen);
painter->drawLine(QPointF(-8, 2), QPointF(-8, -2));
painter->drawLine(QPointF(-6, 2), QPointF(-6, -2));
painter->drawLine(QPointF(-4, 2), QPointF(-4, -2));
painter->drawLine(QPointF(-2, 2), QPointF(-2, -2));
painter->drawLine(QPointF(2, 2), QPointF(2, -2));
painter->drawLine(QPointF(4, 2), QPointF(4, -2));
painter->drawLine(QPointF(6, 2), QPointF(6, -2));
painter->drawLine(QPointF(8, 2), QPointF(8, -2));
pen.setWidthF(painter->window().width()/painter->viewport().width() * delta*10);
pen.setColor(Qt::green);
painter->setPen(pen);
painter->drawLine(QPointF(-10, 0), QPointF(10, 0)); // x
pen.setWidthF(painter->window().height()/painter->viewport().height() * delta*10);
painter->setPen(pen);
painter->drawLine(QPointF(0, 2), QPointF(0, -2)); // y
}
void SineWave::SineWave::drawCave(QPainter* painter)
{
for(double x=-10; x<10; x+=0.01)
{
double y = qSin(x);
painter->drawPoint(QPointF(x, y));
}
}
void SineWave::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
drawBackGround(&painter);
drawCave(&painter);
}
2、绘图板
功能需求:可以自由绘制图形,直线,矩形,椭圆,并可以选择绘制线条颜色。
A、自由图形绘制
自由绘图必须记录鼠标移动时经过的所有点的坐标,绘图参数必须有能力保存所有的坐标。
绘制图形方案如下:
a、鼠标按下开始绘图,记录开始坐标
b、记录鼠标移动时经过的点坐标
c、鼠标释放时结束绘图,记录结束坐标
d、按照记录顺序在相邻两点间绘制直线
B、基础图形绘制
绘制基础图形时,图形的开始点坐标在鼠标按下时已经确定,鼠标移动时按实时坐标绘图,当鼠标释放时确定最终的结束坐标,图形绘制完成。因此,基础图形绘制只需要记录两个坐标。
绘制图形方案如下:
a、鼠标按下开始绘图,记录开始坐标
b、将鼠标移动时经过的每个坐标作为临时结束坐标
c、鼠标释放结束绘图,记录最终结束坐标
d、在开始和结束坐标间绘制图形
Widget.h文件:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QRadioButton>
#include <QComboBox>
#include <QGroupBox>
#include <QList>
#include <QPoint>
class Widget : public QWidget
{
Q_OBJECT
enum DrawType
{
NONE,
FREE,
LINE,
RECT,
ELLIPSE
};
struct DrawParam
{
DrawType type;
Qt::GlobalColor color;
QList<QPoint> points;
};
QGroupBox m_group;
QRadioButton m_free;
QRadioButton m_line;
QRadioButton m_rect;
QRadioButton m_ellipse;
QComboBox m_color;
QList<DrawParam> m_drawList;
DrawParam m_current;
DrawType drawType();
Qt::GlobalColor drawColor();
void draw(QPainter& painter, DrawParam& param);
void append(QPoint p);
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
public:
Widget(QWidget* parent = 0);
~Widget();
};
#endif // WIDGET_H
Widget.cpp文件:
#include "Widget.h"
#include <QMouseEvent>
#include <QPainter>
#include <QPen>
#include <QBrush>
Widget::Widget(QWidget *parent):QWidget(parent)
{
m_group.setParent(this);
m_group.setTitle("Setting");
m_group.resize(600, 65);
m_group.move(20, 20);
m_free.setParent(&m_group);
m_free.setText("Free");
m_free.resize(70, 30);
m_free.move(35, 20);
m_free.setChecked(true);
m_line.setParent(&m_group);
m_line.setText("Line");
m_line.resize(70, 30);
m_line.move(140, 20);
m_rect.setParent(&m_group);
m_rect.setText("Rect");
m_rect.resize(70, 30);
m_rect.move(245, 20);
m_ellipse.setParent(&m_group);
m_ellipse.setText("Ellipse");
m_ellipse.resize(70, 30);
m_ellipse.move(350, 20);
m_color.setParent(&m_group);
m_color.resize(80, 25);
m_color.move(480, 23);
m_color.addItem("Black");
m_color.addItem("Blue");
m_color.addItem("Green");
m_color.addItem("Red");
m_color.addItem("Yellow");
setFixedSize(width(), 600);
m_current.type = NONE;
m_current.color = Qt::white;
m_current.points.clear();
}
Widget::DrawType Widget::drawType()
{
DrawType ret = NONE;
if( m_free.isChecked() ) ret = FREE;
if( m_line.isChecked() ) ret = LINE;
if( m_rect.isChecked() ) ret = RECT;
if( m_ellipse.isChecked() ) ret = ELLIPSE;
return ret;
}
Qt::GlobalColor Widget::drawColor()
{
Qt::GlobalColor ret = Qt::black;
if( m_color.currentText() == "Black") ret = Qt::black;
if( m_color.currentText() == "Blue") ret = Qt::blue;
if( m_color.currentText() == "Green") ret = Qt::green;
if( m_color.currentText() == "Red") ret = Qt::red;
if( m_color.currentText() == "Yellow") ret = Qt::yellow;
return ret;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
m_current.type = drawType();
m_current.color = drawColor();
m_current.points.append(event->pos());
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
append(event->pos());
update();
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
append(event->pos());
m_drawList.append(m_current);
m_current.type = NONE;
m_current.color = Qt::white;
m_current.points.clear();
update();
}
void Widget::append(QPoint p)
{
if( m_current.type != NONE )
{
if( m_current.type == FREE )
{
m_current.points.append(p);
}
else
{
if( m_current.points.count() == 2 )
{
m_current.points.removeLast();
}
m_current.points.append(p);
}
}
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
for(int i=0; i<m_drawList.count(); i++)
{
draw(painter, m_drawList[i]);
}
draw(painter, m_current);
}
void Widget::draw(QPainter& painter, DrawParam& param)
{
if( (param.type != NONE) && (param.points.count() >= 2) )
{
int x = (param.points[0].x() < param.points[1].x()) ?
param.points[0].x() : param.points[1].x();
int y = (param.points[0].y() < param.points[1].y()) ?
param.points[0].y() : param.points[1].y();
int w = qAbs(param.points[0].x() - param.points[1].x()) + 1;
int h = qAbs(param.points[0].y() - param.points[1].y()) + 1;
painter.setPen(QPen(param.color));
painter.setBrush(QBrush(param.color));
switch(param.type)
{
case FREE:
for(int i=0; i<param.points.count()-1; i++)
{
painter.drawLine(param.points[i], param.points[i+1]);
}
break;
case LINE:
painter.drawLine(param.points[0], param.points[1]);
break;
case RECT:
painter.drawRect(x, y, w, h);
break;
case ELLIPSE:
painter.drawEllipse(x, y, w, h);
break;
default:
break;
}
}
}
Widget::~Widget()
{
}