Qt 提供了内置的绘图系统以及独立的QtOpenGL模块提供对OpenGL的支持。Qt提供了基于状态机的QPainter系统和面向对象的Graphics View系统。

QPainter

基于状态机的绘图系统主要包含QPainter、QPaintEngine、QPaintDevice 三个类。

QPainter有三个主要参数分别用于设置画笔(QPen)、画刷(QBrush)、字体(font),分别由setPen、setBrush、setFont系列方法设定。

widget.h:

#ifndef WIDGET_H
#define WIDGET_H #include <QWidget>
#include <QPainter> namespace Ui {
class Widget;
} class Widget : public QWidget
{
Q_OBJECT public:
explicit Widget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event = 0);
~Widget(); private:
Ui::Widget *ui;
}; #endif // WIDGET_H

widget.cpp:

#include "widget.h"
#include "ui_widget.h" Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
} Widget::~Widget()
{
delete ui;
} void Widget::paintEvent(QPaintEvent *parent) {
// QPainter *painter = new QPainter(this);
QPainter painter(this);
painter.setPen(Qt::blue);
painter. setFont(QFont("Arial", 30));
painter. drawLine(0,0,100,100);
}

main.cpp:

#include "widget.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
w.paintEvent();
return a.exec();
}

使用QPainter进行绘画必须重写QWidget::paintEvent(QPaintEvent *)事件,并在事件中进行绘图。如Widget.cpp中的void Widget::paintEvent(QPaintEvent *parent)

调用可视化组件的update(),repaint()实例方法或者直接调用paintEvent()方法可以对可视化组件进行重绘。

update()允许Qt进行优化从而得到比调用repaint()更快的速度和更少的闪烁。

几次调用update()的结果通常仅仅是一次paintEvent()调用,repaint()则是立即调用paintEvent()

Qt通常在paintEvent()调用之前擦除这个窗口部件的区域,除非设置了WRepaintNoErase窗口部件标记。

画笔QPen主要用于线条的绘制,画笔的样式可以在创建QPen对象时指定也可由setStyle()指定。

画笔主要支持cap、join (结合点)和line三种风格,可以通过setStyle()、setCapStyle()、setJoinStyle()、setlineStyle()等方法进行设置。

画刷QBrush用于二维封闭图形的填充,与QPen类似同样通过style设定填充风格,还可以使用渐变填充。

反走样

反走样技术通过对像素的细微调整避免锯齿状边缘出现,但是反走样算法需要较高计算量且会修改原有图形,所以与大多数2D绘图工具一样QPainter默认不打开反走样算法。

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

使用上述语句将QPainter::Antialiasing设置为true之后painter就会打开反走样算法,直到将QPainter::Antialiasing显式地设为false反走样算法才会关闭。

渐变填充

Qt 的渐变是一个独立的类,包含QLinearGradient(线性渐变),QRadialGrafient(辐射渐变),QConcialGradient(角度渐变)。

示例:

void Widget::paintEvent(QPaintEvent *parent) {
QPainter painter(this);
QLinearGradient gradient(0,0,100,100);
//初始化渐变对象,左上坐标和宽高设定位置
gradient.setColorAt(0.2,Qt::blue);
//设定渐变色彩,第一个参数为参照点的位置比例,第二个参数为参照点的颜色
gradient.setColorAt(0.5,Qt::red);
gradient.setColorAt(0.8,Qt::yellow);
painter. setBrush(QBrush(gradient));
//QBrush接受渐变对象做参数,并将其作为QPainter的绘制工具
painter. drawEllipse(0,0,100,100);
//画图
}

坐标变换

QPainter的坐标位于左上角,x轴正方向向右,y轴正方向向下;每个像素占据

1×1的坐标空间,但像素中心为与方格中心(x+0.5,y+0.5),实际上是一个半像素坐标系。

QPainter使用了viewport和window机制,viewport使用物理坐标而window使用逻辑坐标、QPainter传递逻辑坐标。两者的坐标系通常是相同的,但是QPainter还提供了setViewport()和setWindow()函数用于重置两个坐标系的位置大小,使得可以在不改变物理实现的情况下移动画布的范围。

QPainter还提供了世界坐标用于坐标的变换:

void Widget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QFont font("Courier",12);//初始化字体对象
painter.setFont(font);//为QPainter设置字体 QTransform transform;//定义坐标变换
transform.rotate(+45);//设置旋转
painter.setWorldTransform(transform);//为QPainter设置坐标变换 painter.drawText(150,0,"Hello World!");//绘制
}

QTransform

QTransform是对坐标变换矩阵的封装,通常使用:旋转rotate,放缩scale,裁剪shear,平移translate等方法设定相关变换。一个QTransform对象可以依次设置多个坐标变换操作。

Qtransform也可以使用setmatrix()等方法直接操作变换矩阵。

绘图设备

绘图设备是指QPaintDevice的派生类,包括QPixmap、QBitmap、QImage和QPicture。QPainter提供了drawPixmap()方法可以将图像画到不同设备上。

QPixmap为图片在屏幕显示做了优化,QPixmap与绘图设备底层相关,不提供像素级支持,不同设备上图像有所不同。

可以直接使用QPainter在QPixmap上绘制也可以接受一个图片文件在屏幕上显示。

QBitmap是QPixmap的派生类,只能绘制黑白图像(色深为1)。

QImage是与硬件无关的绘图设备,提供了像素级操作支持。

QPicture用于记录QPainter的操作,并保存到一个序列化的平台独立的二进制文件中。

示例:

void Widget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QPicture picture;
painter.begin(&picture); //开始记录
painter.drawText(100,100,"Hello World!");//绘制
painter.end();//终止记录
picture.save("Hello.pic");//保存到文件 picture.load("Hello.pic");//加载文件
painter.drawPicture(0,0,picture);//重画
}

Qt Graphics View

Graphics View是一种采用MV架构,面向对象的绘图系统。与QPainter状态机使用绘图语句绘制的模式不同,Gaphics View中每个图形元素都是一个对象。

Graphics View的架构中Model负责存储对象结构View则提供观察窗口,MV架构可以很容易的实现转换视角,摄像机,碰撞检测等功能。

Grphics View框架采用BSP树管理item,可以对大量items做出快速响应。

示例:

QGraphicsScene scene;
scene.addText("Hello, world!");
QGraphicsView view(&scene);
view.show();

QGraphicsScene

QGraphicsScene类作为容器(MV中的模型Model),用于存储所有的图形元素;QGraphicsView则是观察窗口,可以显示场景的一部分或者全局;所有的图形元素均继承自QGraphicsItem。

QGraphicsScene的addItem(QGraphicsItem * item);方法可以向容器中添加图形元素。

addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(), addRect(), or addText()可以方便地添加图形元素并返回指向图形元素的指针。QGraphicsScene::removeItem(QGraphicsItem * item)用于移除相应的Item。

QGraphicsScene使用下标来高效的管理item的位置,默认的使用BSP树,适用于一个大型的场景,其中的item都是静止不变的,可以选择调用setItemIndexMethod().来禁用下标,可以查看itemIndexMethod来获取更多的信息。

items()函数可以在数微秒内找到item的位置,item()有一些重载函数。itemAt()函数可以根据提供的位置返回所在位置处的最上层的item指针。

场景的边界可以使用setSceneRect()来设置,item可以放置在场景中任何位置,场景默认的大小是不受限制的。如果场景边界矩形没有设置,QgrapbhicsScene将会使用所有item图元的边界,使用函数itemsBoundingRect()返回。

通过继承QGraphicsScene并重写事件响应函数可以对Scene中的Item的点击、拖动等事件进行响应。在场景变换时,QGraphicsScene将会发射changed()信号,通知相应槽函数进行处理。

QGraphicsView提供了用于显示的widget,用于显示Scene,可以使用构造函数或void setScene(QGraphicsScene * scene)将多个View联系到同一个scene,给相同的数据提供多个View,视口支持openGL,甚至可以将QGLWidget作为视口,只要调用一下QGraphicsView::setViewport()

QGraphicsView::mapToScene(), QGraphicsView::mapFromScene()等多个函数可以在view和scene之间转换坐标系。

QGraphicsView

QGraphicsView是一个观察窗口,它将QGraphicsScene中item显示出来,并将用户的鼠标和键盘事件映射到QGraphicsScene事件,且将坐标转换为QGraphicsScene的坐标。

  • setScence

QGraphicsView的构造函数可以接受一个QGraphicsScene的指针作为参数或者使用void QGraphicsView::setScene ( QGraphicsScene * scene )

  • transform()

QGraphicsView::setTransform()使用转换矩阵来改变视图坐标系,达到旋转或缩放的目的。并通过QGraphicsView::transform()访问QTransform。

示例:

QTransform transform;//定义坐标变换
transform.rotate(+45);//设置旋转
view.setTransform(transform);
view.show()

当然可以用void QGraphicsView::rotate(qreal angle)或者void QGraphicsView::scale(qreal sx, qreal sy)

示例:

QGraphicsScene scene;
scene.addText("GraphicsView rotated clockwise"); QGraphicsView view(&scene);
view.rotate(90); // the text is rendered with a 90 degree clockwise rotation
view.show();

QGraphicsView使用ViewportAnchor属性来决定当转换矩阵修改和坐标系统修改时候如何摆放场景的在viewport中的位置。

默认的是 AnchorViewCenter,这样使场景点在变换时候保持在view中心点不变。例如当旋转时候,场景将会围绕着view中心点来旋转。

只有场景中的一部分可见时候这个属性才显而易见的。例如:当view中有滚动条时候,否则整个场景都在view中,场景将会使用QGraphicsView::aligenment来摆放它的位置。

void QGraphicsView::render ( QPainter * painter)将QGraphicsView通过QPainter映射到QPaintDevice上显示。

来自官方文档的示例:

QGraphicsScene scene;
scene.addItem(...
... QGraphicsView view(&scene);
view.show();
... QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPrinter::A4);
QPainter painter(&printer); // print, fitting the viewport contents into a full page
view.render(&painter); // print the upper half of the viewport into the lower.
// half of the page.
QRect viewport = view.viewport()->rect();
view.render(&painter,
QRectF(0, printer.height() / 2,
printer.width(), printer.height() / 2),
viewport.adjusted(0, 0, 0, -viewport.height() / 2));

QGraphicsItem

QGraphicsItem是所有item的基类,要自定义item需要继承QGraphicsItem并实现两个纯虚函数:

  • QRectF boundingRect() const

返回一个代表形状QRectF对象。

  • void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,

    QWidget *widget)

控制绘图过程。

QGraphicsScene认为所有item的boundingRect函数与shape函数都是不发生改变的,除非用户进行通知。如果想改变一个item,必需先调用prepareGeometryChange以允许QGraphicsScene进行更新。

Graphics View框架使用shape()collidesWithPath()实现碰撞检测。

  • QPainterPath QGraphicsItem::shape() const

函数返回碰撞箱形状,默认实现为调用boundingRect()并返回简单的矩形。

重写该函数,定义更复杂的碰撞箱形状,但复杂的形状会使计算量增大。

默认实现:

QPainterPath RoundItem::shape() const
{
QPainterPath path;
path.addEllipse(boundingRect());
return path;
}
  • bool QGraphicsItem::collidesWithPath(const QPainterPath & path)

当item与参数指定的PainterPath发生碰撞时返回true,否则为false。

碰撞箱形状决定于shape(),可以使用shape()函数进行两个item的碰撞检测:

item1.collidesWithPath(item2.shape());

Qt提供了默认实现,可以重写该方法实现更复杂的碰撞检测。

Qt Animation

QPropertyAnimation

示例:

QPushButton button("Animated Button");  

button.show();  

QPropertyAnimation animation(&button, "geometry");
//初始化Animation对象,指定要动画的属性 animation.setDuration(10000);
//设定持续时间,单位为毫秒 animation.setStartValue(QRect(0, 0, 0, 0));
//设定初始值 animation1->setKeyValueAt(0.4, QRect(20, 250, 20, 30));
//设定中间关键值,第一个参数为时间百分比,第二个参数为关键值 animation.setEndValue(QRect(250, 250, 100, 30));
//设定结束值 animation.start();
//启动动画

QPropertyAnimation是Qt动画框架提供的一个实现类,它按照设置对某个Qt属性进行线性插值,并在时间轴的控制下动态调节该属性达到动画的效果。

QPropertyAnimation动画的属性必须是Qt属性,Qt属性由Q_PROPERTY宏来声明,并且类必须继承QObject。标准的Qt Widgets组件均使用Q_PROPERTY属性,可以直接进行动画,若自定义组件使用动画效果则需要使用Q_PROPERTY进行声明。

Q_PROPERTY宏的原型为:

Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
  • 属性类型必须有一个read函数。它用来读取属性值。因此用Const限定。它的返回值类型必须为属性类型或者属性类型的引用或者指针。不能是其他类型例如:QWidget::hasFocus()。

  • 有一个可选的write函数。它用来设置属性值,它的返回值必须为void型,而起必须要含有一个参数。例如:QWidget::setEnabled()。

  • 一个reset函数能够把property设置成其默认状态,它也是可选的。复位功能必须返回void,并且不带参数。

  • 一个可选的NOTIFY信号, 它提供了一个在值发生改变时会自动被触发信号。

  • 如果定义了"STODE"属性表明这是一直存在的。

  • 一个"DESIGNABLE"属性表明该property能在GUI builder(一般为Qt Designer)可见。

  • USER 属性 表面是否可以被用户所编辑

  • CONST设定属性是不可修改的 所以不能跟WRITE或者NOTIFY同时出现

  • FINAL表明该属性不会被派生类中重写

示例:

class Test : public QObject {

Q_OBJECT

Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)

public:

Test(QObject *parent = 0) : QObject(parent) {}

virtual ~Test(){}

void setEnabled(bool e) { enabled = e; }

bool isEnabled() const { return enabled; }

private:

bool enabled;

};

EasyCurving

QPropertyAnimation默认进行线性插值,也可以使用宽松曲线(EasyCurvig)设置属性变化规律,如产生组件弹跳进入的效果等。

QPushButton button("Animated Button");  

button.show();  

QPropertyAnimation animation(&button, "geometry");
//初始化Animation对象,指定要动画的属性 animation.setDuration(10000);
//设定持续时间,单位为毫秒 animation.setStartValue(QRect(0, 0, 0, 0));
//设定初始值 animation1->setKeyValueAt(0.4, QRect(20, 250, 20, 30));
//设定中间关键值,第一个参数为时间百分比,第二个参数为关键值 animation.setEndValue(QRect(250, 250, 100, 30));
//设定结束值 animation1->setEasingCurve(QEasingCurve::OutBounce);
//使用弹跳曲线 animation.start();
//启动动画

QEasyCurving::Type枚举类型中定义了各种曲线,可以从中选择或者自行实现然后注册到EasyCurving。

QTimeLine

Qt的 动画效果离不开时间轴的控制,Animation框架依赖QTimeLine提供时间轴。我们可以使用QTimeLine制作自己的动画。

QTimeLine接受一个以毫秒为单位的参数,代表动画运行的总时间。

例:timeline = new QTimeLine(1000);

在计时结束后将会发出finished()信号,或者调用stop()方法手动使QTimeLine进入NotRunning状态。

调用start方法,QTimeLine开始计时:timeLine->start();

QTimeLine进入Running状态后,默认每隔40ms发送一个frameChanged()信号,间隔规律可以手动设置:setUpdateInterval(int interval);

默认情况下QTimeLine均匀的发送信号,也可以手动设置间隔规律:timeline->setCurveShape(QTimeLine::LinearCurve);

目前,Qt支持的模式有:

  • QTimeLine::EaseInCurve = 0

The value starts growing slowly, then increases in speed.先慢后快

  • QTimeLine::EaseOutCurve = 1

The value starts growing steadily, then ends slowly.先匀加速,后减速

  • QTimeLine::EaseInOutCurve = 2

The value starts growing slowly, then runs steadily, then grows slowly again.

先慢,中间稳定,最后慢

  • QTimeLine::LinearCurve = 3

The value grows linearly (e.g., if the duration is 1000 ms, the value at time 500 ms is 0.5).匀速的

  • QTimeLine::SineCurve = 4

The value grows sinusoidally.正选曲线式

  • QTimeLine::CosineCurve = 5

The value grows cosinusoidally.余弦曲线式

Qt 绘图与动画系统的更多相关文章

  1. Qt Creator中的3D绘图及动画教程(参照NeHe)

    Qt Creator中的3D绘图及动画教程(参照NeHe) http://blog.csdn.net/cly116/article/details/47184729 刚刚学习了Qt Creator,发 ...

  2. WPF学习之绘图和动画

    如今的软件市场,竞争已经进入白热化阶段,功能强.运算快.界面友好.Bug少.价格低都已经成为了必备条件.这还不算完,随着计算机的多媒体功能越来越强,软件的界面是否色彩亮丽.是否能通过动画.3D等效果是 ...

  3. WPF学习之绘图和动画--DarrenF

    Blend作为专门的设计工具让WPF如虎添翼,即能够帮助不了解编程的设计师快速上手,又能够帮助资深开发者快速建立图形或者动画的原型. 1.1   WPF绘图 与传统的.net开发使用GDI+进行绘图不 ...

  4. 用C#绘图实现动画出现卡屏(运行慢)问题的解决办法

    原文:用C#绘图实现动画出现卡屏(运行慢)问题的解决办法 正在用C#做一个小游戏,需要用到动画,上次解决的问题是闪烁问题,用双缓冲技术.以为不会有什么问题了.后来当把图片全部绘制上去的时候依然出现了卡 ...

  5. Unity Animation System(动画系统)

    动画系统: 支持:动画融合,混合,叠加动画,行走循环的时间同步,动画层,控制动画的各个方面(时间,速度,融合权重)   带有每顶点1.2或4骨骼的蒙皮网格,以及支持基于物理的布娃娃系统和程序动画.   ...

  6. Android动画学习(一)——Android动画系统框架简介

    2015-11-09补充:Drawable Animation极有可能是Frame Animation 这几天在找工作,面试的时候被问到了Android动画,之前完全没接触过这部分,直接给懵了,当然其 ...

  7. unity3D5旧动画系统注意事项

    最近在写人物控制时因为习惯用旧动画系统所以也没想那么多,就直接在新系统下按照老样子写了,突然发现animation.play不能用了,后来重新声明了变量: public Animation anim; ...

  8. unity5.0新功能-布料、动画系统

    原作者:只待苍霞 这一章讲一下布料系统, 这次的布料系统有很大的改良.Unity4中, 需要对SkinnedMeshRenderer使用SkinnedCloth, 或者对Cloth Renderer使 ...

  9. 【腾讯GAD暑期训练营游戏程序开发】游戏中的动画系统作业

    游戏中的动画系统作业说明文档   一.实现一个动画状态机:至少包含3组大的状态节点

随机推荐

  1. python——回文函数(reversed)

    回文数:正向排列与反向排列所得结果是相等的(即从左到右和从右到左的结果是相等的),例如:“123321”,“0000”等. reversed函数:反转一个序列对象,将其元素从后向前颠倒构建成一个新的迭 ...

  2. SqlServer数据库同时备份到两台服务器上(并自动删除过期文件)

    数据库同时备份到两台服务器上(并自动删除过期文件) 举例 :(本地)服务器A: IP :192.168.1.1 (远程)服务器B: IP :192.168.1.2 数据库版本:SqlServer200 ...

  3. c#字典怎么获取第一个键值 List<对象>获取重复项,转成Dictionary<key,List<对象>>

    c#字典怎么获取第一个键值 Dictionary<string, int> dictionary = new Dictionary<string, int>(); dictio ...

  4. webapi token、参数签名是如何生成的(转载)

    API接口保障安全性原则:1.有调用者身份2.请求的唯一性3.请求的参数不能被篡改4.请求的有效时间 在刚接触接口开发时,可能脑子里压根就没有这个接口调用安全性的原则,但常识性的经验告诉我们,每一个请 ...

  5. Python os.path.join() 进行路径拼接

    在python 项目开发过程中,经常需要将获取到的路径进行拼接, # os.path.join(path1,path2) 将两个路径拼接起来 os.path.join("/usr" ...

  6. JavaScript基础函数和词法分析以及常用的内置对象和使用方法(4)

    day52 参考:https://www.cnblogs.com/liwenzhou/p/8004649.html 函数 函数定义 JavaScript中的函数和Python中的非常类似,只是定义方式 ...

  7. 80端口被系统进程PID-4占用解决办法

    今天因为工程需要就把tomcat服务器的端口改成了80了,可是一启动就出现问题了 发现报错信息是端口占用了,于是我马上就在了命令行敲入了netstat -ano查看端口占用情况 终于发现是PID为4的 ...

  8. Java并发工具类之并发数控制神器Semaphore

    Semaphore(信号量)使用来控制通知访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源. 我们可以这么理解Semaphore,比如一个厕所只有6个坑,同时只能满足6个人上厕所( ...

  9. Git 使用流程

    # 下载远程仓库到本地 git clone 仓库地址cd 本地仓库文件夹 # 创建本地开发分支并与远程开发分支关联 git checkout -b develop origin/developgit ...

  10. day67 crm(4) stark组件的增删改 以及 model_from使用和from组件回顾

        前情提要:Django  stark 组件开发的 增删改,  model_form组件的使用 form组件的回顾 一:list_display_link  创建 功能描述:   使包含的字段能 ...