您的位置:首页 > 编程语言 > Qt开发

Qt学习笔记(四)二维图形

2013-10-15 09:30 190 查看
[html] view
plaincopy

本章介绍Qt的二维图形引擎,Qt的二维图形引擎是基于QPainter类的。<span style="color:#ff0000;">QPainter既可以绘制几何图形(点、线、矩形等),也可以绘制像素映射、图像和文字。此外QPainter还支持一些高级特性,例如反走样、像素混合、渐变填充和矢量路径等。QPainter也支持线性变换,例如平移、旋转、错切和缩放。</span>

QPainter可以画在“绘图设备”(QWidget、QPixmap、QImage和QSvgGenerator)上,也可以与QPrinter一起使用来打印文件和创建PDF文档。

重新实现QWidget::PaintEvent()可用于定制窗口部件,随心所欲的控制他们的外观

一项普通的需求是在二维画板上显示大量的、轻量级的、可以用户交互的、任意形状的项。

可使用OpenGL命令来代替QPainter。OPenGL是一个绘制三维图形的标准库。

8.1用QPainter绘图

想要在绘图设备上绘图,只需要创建一个QPainter,再将指针传到该设备中。

void MyWidget::paintEvent(QPaintEvent* event)

{

QPainter painter(this);

........

}

使用QPainter的draw...()函数,可以绘制各种各样的形状。绘制的效果取决于QPainter的设置。三个主要的设置是画笔、画刷、字体:

①画笔用来画线和边缘。QPen,设置属性setPen()

②画刷用来填充几何形状的图案。QBrush,设置属性setBrush()

③字体用来绘制文字。QFont,设置属性setFont()

绘制椭圆:

[cpp] view
plaincopy

<span style="color:#33cc00;BACKGROUND-COLOR: #ffffff">QPainter painter(this);

painter.setRenderHint(QPainter::Antialiasing,true);

painter.setPen(QPen(Qt::black,12,Qt::DashDotLine,Qt::RoundCap));

painter.setBrush(QBrush(Qt::green,Qt::SolidPattern));

painter.drawEllipse(80,80,400,240);</span>

setRenderHint(QPainter::Antialiasing,true)可以启用反走样,可在支持这一特性的平台和设备上得到平滑的边缘。

QPainterPath类可以通过连接基本的图形元素来确定任意的矢量图形:直线、椭圆、多边形等绘制路径。绘制路劲是基本的图元,任何图形或图形组合都可以用绘制路径描述。路径可以确定一个边缘,由边缘锁定的区域可以用画刷来填充。

QPainter painter(this);

painter.setRenderHint(QPainter::Antialiasing, true);

QPainterPath path;

path.moveTo(80, 230);

painter.cubicTo(200, 80, 320, 80, 480, 320);

painter.setPen(Qpen(Qt::black, 8));

painter.drawPath(path);

路径可以确定一个边缘,由边缘锁定的区域可以用画刷来填充。

在现代应用中,渐变填充已成为单色填充的流行替代品。渐变填充利用颜色差值使得两个或更多颜色之间能够平滑的过度。

Qt支持3种类型的渐变:

线性渐变,由两个控制点定义,连接这两点的线上有一系列的颜色断点。

锥形渐变,由一个中心点和一个角度定义。

辐射渐变,由一个中心点、半径、一个焦点,以及颜色断点定义。颜色由焦点向外扩散,焦点可以是中心点或者圆内的其他点。

QPainter还有其他影响图形和文字绘制方式的设置:

(1)当背景模式是Qt::OpaqueMode(默认设置为Qt::TransparentMode)时,背景画刷可以用来填充几何图形的背景(画刷模式下)、文字或位图。

(2)画刷的原点是画刷模式的启动点,通常是窗口部件的左上角

(3)剪切区域是设备的绘图区域,在剪切区域之外绘图不起作用

(4)视图、窗口和矩形矩阵决定了如何将QPainter的逻辑坐标映射到设备的物理绘图坐标。默认情况下,为了使逻辑坐标和物理坐标保持一致,这些是设置好的。

(5)复合模式指定新绘制的像素如何跟绘图设备上已经显示的像素相互作用。默认方式是"source over",这种情况下像素会被alpha混合在存在的像素上。

可以通过调用save()而随时将设备的当前状态存入一个内部堆栈,稍后通过调用restore()恢复。

8.2坐标系统变换

在QPainter的默认坐标系中,点(0,0)位于绘图设备的左上角,x坐标向右增长,y坐标向下增长。默认坐标系的每个像素占1x1大小的区域。

理论上像素的中心取决于半像素坐标,一个像素正好位于四个像素的重合处。如果不需要这种效果,可以通过制定半像素坐标或者通过便宜QPainter(+0.5,+0.5)来避免这种效果的出现。

QPainter的视口,是物理坐标系下制定的任意矩形。

QPainter的窗口也是指同一矩形,只不过是在逻辑坐标系下。这种窗口-视口机制对于编写独立于绘制设备大小和分辨率的绘制代码是有用的。

当绘制图形时,在逻辑坐标下指定的点,这些坐标都是基于当前的窗口---视口设定并以线性代数的方式转换为物理坐标。

默认情况下,窗口和视口都是被设置成设备的矩形。例如,如果设备是320x200的矩形,视口和窗口都是左上角为(0, 0)的320x200的相同矩形。这种情况下,窗口和视口是一致的。

这种窗口---视口机制对于编写独立于绘制设备大小和分辨率的绘制代码是很有用的。例如,如果想让逻辑坐标从(-50, -50)到(+50, +50),并且(0, 0)在中间,可以设置窗口:

painter.setWindow(-50, -50, 100, 100); //(-50, -50)指定了原点,(100, 100)指定了宽和高,这意味着逻辑坐标(-50, -50)对应于物理坐标(0, 0),而逻辑坐标(50, 50)对应于物理坐标(320, 200)。

世界变换:

世界变换是在窗口-视口转换之外使用的变换矩阵。它允许移动、缩放、旋转或者拉伸绘制的项。例如如果想使用点(50, 50)作为中心点,并使用45度角绘制文本,可以使用:

QTransform transform;

transform.translate(+50.0, +50.0);

transform.rotate(+45.0);

painter.setWorldTransform(transform);

painter.drawText(pos, tr("Sales"));

OvenTimer窗口部件代码

这个OvenTimer模仿烤箱的定时器,它是烤箱内置的钟表。用户可以单击刻度来设置持续时间。转轮会自动的逆时针转到0,OvenTimer在这一点发射timeout()信号。

ovenTimer.h

[cpp] view
plaincopy

#ifndef OVENTIMER_H

#define OVENTIMER_H



#include <QDateTime>

#include <QWidget>



class QTimer;



class OvenTimer : public QWidget

{

Q_OBJECT



public:

OvenTimer(QWidget *parent = 0);



void setDuration(int secs);

int duration() const;

void draw(QPainter *painter);



signals:

void timeout();



protected:

void paintEvent(QPaintEvent *event);

void mousePressEvent(QMouseEvent *event);



private:

QDateTime finishTime;

QTimer *updateTimer;

QTimer *finishTimer;

};



#endif

oventimer.cpp

[cpp] view
plaincopy

#include <QtGui>

#include <cmath>



#ifndef M_PI

#define M_PI 3.14159265359

#endif



#include "oventimer.h"



const double DegreesPerMinute = 7.0;

const double DegreesPerSecond = DegreesPerMinute / 60;

const int MaxMinutes = 45;

const int MaxSeconds = MaxMinutes * 60;

const int UpdateInterval = 5;



OvenTimer::OvenTimer(QWidget *parent)

: QWidget(parent)

{

finishTime = QDateTime::currentDateTime(); //获取当前时间



updateTimer = new QTimer(this);

connect(updateTimer, SIGNAL(timeout()), this, SLOT(update()));



finishTimer = new QTimer(this);

finishTimer->setSingleShot(true);

connect(finishTimer, SIGNAL(timeout()), this, SIGNAL(timeout()));

connect(finishTimer, SIGNAL(timeout()), updateTimer, SLOT(stop()));



QFont font;

font.setPointSize(8); //设置窗口字体的大小

setFont(font);

}



void OvenTimer::setDuration(int secs) //设置了烤箱定时器的持续时间为给定的秒数。

{

secs = qBound(0, secs, MaxSeconds); //防止越界0~MaxSeconds



finishTime = QDateTime::currentDateTime().addSecs(secs);



if (secs > 0) {

updateTimer->start(UpdateInterval * 1000);

finishTimer->start(secs * 1000);

} else {

updateTimer->stop();

finishTimer->stop();

}

update();

}



int OvenTimer::duration() const //获取定时器完成前剩余的秒数。

{

int secs = QDateTime::currentDateTime().secsTo(finishTime);

if (secs < 0)

secs = 0;

return secs;

}



void OvenTimer::mousePressEvent(QMouseEvent *event)

{

QPointF point = event->pos() - rect().center();

double theta = std::atan2(-point.x(), -point.y()) * 180.0 / M_PI;

setDuration(duration() + int(theta / DegreesPerSecond));

update();

}



void OvenTimer::paintEvent(QPaintEvent * /* event */)

{

QPainter painter(this);

painter.setRenderHint(QPainter::Antialiasing, true);



int side = qMin(width(), height()); //返回两个参数中较小的一个



painter.setViewport((width() - side) / 2, (height() - side) / 2,

side, side);

painter.setWindow(-50, -50, 100, 100);



draw(&painter);

}



void OvenTimer::draw(QPainter *painter)

{

static const int triangle[3][2] = {

{ -2, -49 }, { +2, -49 }, { 0, -47 }

};

QPen thickPen(palette().foreground(), 1.5); //palette().foreground()得到笔刷

QPen thinPen(palette().foreground(), 0.5);

QColor niceBlue(150, 150, 200);



painter->setPen(thinPen);

painter->setBrush(palette().foreground());

painter->drawPolygon(QPolygon(3, &triangle[0][0]));



QConicalGradient coneGradient(0, 0, -90.0);

coneGradient.setColorAt(0.0, Qt::darkGray);

coneGradient.setColorAt(0.2, niceBlue);

coneGradient.setColorAt(0.5, Qt::white);

coneGradient.setColorAt(1.0, Qt::darkGray);



painter->setBrush(coneGradient);

painter->drawEllipse(-46, -46, 92, 92);



QRadialGradient haloGradient(0, 0, 20, 0, 0);

haloGradient.setColorAt(0.0, Qt::lightGray);

haloGradient.setColorAt(0.8, Qt::darkGray);

haloGradient.setColorAt(0.9, Qt::white);

haloGradient.setColorAt(1.0, Qt::black);



painter->setPen(Qt::NoPen);

painter->setBrush(haloGradient);

painter->drawEllipse(-20, -20, 40, 40);



QLinearGradient knobGradient(-7, -25, 7, -25);

knobGradient.setColorAt(0.0, Qt::black);

knobGradient.setColorAt(0.2, niceBlue);

knobGradient.setColorAt(0.3, Qt::lightGray);

knobGradient.setColorAt(0.8, Qt::white);

knobGradient.setColorAt(1.0, Qt::black);



painter->rotate(duration() * DegreesPerSecond);

painter->setBrush(knobGradient);

painter->setPen(thinPen);

painter->drawRoundRect(-7, -25, 14, 50, 99, 49);



for (int i = 0; i <= MaxMinutes; ++i) {

if (i % 5 == 0) {

painter->setPen(thickPen);

painter->drawLine(0, -41, 0, -44);

painter->drawText(-15, -41, 30, 30,

Qt::AlignHCenter | Qt::AlignTop,

QString::number(i));

} else {

painter->setPen(thinPen);

painter->drawLine(0, -42, 0, -44);

}

painter->rotate(-DegreesPerMinute);

}

}

8.3用QImage高质量绘图

绘图时,我们可能需要面对速度和准确率的折中问题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: