找回密码
 立即注册
收起左侧

Qt学习之路第24篇 Qt绘制系统简介

13
回复
28014
查看
[复制链接]
累计签到:3 天
连续签到:1 天
来源: 2013-9-9 14:26:44 显示全部楼层 |阅读模式
版权声明

该文章原创于Qter开源社区(www.qter.org),作者devbean,博客www.devbean.net,转载请注明出处!




Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于 QPainter,QPainterDevice 和 QPaintEngine 三个类。



QPainter 用来执行绘制的操作;QPaintDevice 是一个二维空间的抽象,这个二维空间允许 QPainter 在其上面进行绘制,也就是 QPainter 工作的空间;QPaintEngine 提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口。QPaintEngine 类应用于 QPainter 和 QPaintDevice 之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心 QPaintEngine 这个类的。我们可以把 QPainter 理解成画笔;把 QPaintDevice 理解成使用画笔的地方,比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了 QPaintEngine 类,这个类让不同的纸张、屏幕都能使用一种画笔。



下图给出了这三个类之间的层次结构(出自 Qt API 文档):





上面的示意图告诉我们,Qt 的绘图系统实际上是,使用 QPainter 在 QPainterDevice 上进行绘制,它们之间使用 QPaintEngine 进行通讯(也就是翻译 QPainter 的指令)。



下面我们通过一个实例来介绍 QPainter 的使用:

  1. //!!! Qt4/Qt5

  2. class PaintedWidget : public QWidget
  3. {
  4.     Q_OBJECT
  5. public:
  6.     PaintedWidget(QWidget *parent = 0);
  7. protected:
  8.     void paintEvent(QPaintEvent *);
  9. };
复制代码

注意我们重写了 QWidget 的 paintEvent() 函数。这或许是我们在理解了 Qt 事件系统之后首次实际应用。接下来就是 PaintedWidget 的源代码:

  1. //!!! Qt4/Qt5

  2. PaintedWidget::PaintedWidget(QWidget *parent) :
  3.     QWidget(parent)
  4. {
  5.     resize(800, 600);
  6.     setWindowTitle(tr("Paint Demo"));
  7. }

  8. void PaintedWidget::paintEvent(QPaintEvent *)
  9. {
  10.     QPainter painter(this);
  11.     painter.drawLine(80, 100, 650, 500);
  12.     painter.setPen(Qt::red);
  13.     painter.drawRect(10, 10, 100, 400);
  14.     painter.setPen(QPen(Qt::green, 5));
  15.     painter.setBrush(Qt::blue);
  16.     painter.drawEllipse(50, 150, 400, 200);
  17. }
复制代码

在构造函数中,我们仅仅设置了窗口的大小和标题。而 paintEvent() 函数则是绘制的代码。首先,我们在栈上创建了一个 QPainter 对象,也就是说,每次运行 paintEvent() 函数的时候,都会重建这个 QPainter 对象。注意,这一点可能会引发某些细节问题:由于我们每次重建 QPainter,因此第一次运行时所设置的画笔颜色、状态等,第二次再进入这个函数时就会全部丢失。有时候我们希望保存画笔状态,就必须自己保存数据,否则的话则需要将 QPainter 作为类的成员变量。



QPainter 接收一个 QPaintDevice 指针作为参数。QPaintDevice 有很多子类,比如 QImage,以及 QWidget。注意回忆一下,QPaintDevice 可以理解成要在哪里去绘制,而现在我们希望画在这个组件,因此传入的是 this 指针。



QPainter 有很多以 draw 开头的函数,用于各种图形的绘制,比如这里的 drawLine(),drawRect() 以及 drawEllipse() 等。当绘制轮廓线时,使用 QPainter 的 pen() 属性。比如,我们调用了 painter.setPen(Qt::red) 将 pen 设置为红色,则下面绘制的矩形具有红色的轮廓线。接下来,我们将 pen 修改为绿色,5 像素宽(painter.setPen(QPen(Qt::green, 5))),又设置了画刷为蓝色。这时候再调用 draw 函数,则是具有绿色 5 像素宽轮廓线、蓝色填充的椭圆。



运行一下我们的程序,可以看到最终效果:


我们会在后面的章节详细介绍画笔 QPen 和画刷 QBrush 的属性。



另外要说明一点,请注意我们的绘制顺序,首先是直线,然后是矩形,最后是椭圆。按照这样的绘制顺序,可以看到直线是第一个绘制,位于最下一层;矩形是第二个绘制,在中间一层;椭圆是最后绘制,在最上层。



如果了解 OpenGL,肯定听说过这么一句话:OpenGL 是一个状态机。所谓状态机,就是说,OpenGL 保存的只是各种状态。比如,将画笔颜色设置成红色,那么,除非你重新设置另外的颜色,它的颜色会一直是红色。QPainter 也是这样,它的状态不会自己恢复,除非你使用了各种设置函数。因此,如果在上面的代码中,我们在椭圆绘制之后再画一个矩形,它的样式还会是绿色 5 像素的轮廓线以及蓝色的填充,除非你显式地调用了设置函数进行状态的更新。这是大多数绘图系统的实现方式,包括 OpenGL、QPainter 以及 Java2D。正因为 QPainter 是一个状态机,才会引出我们前面曾经介绍过的一个细节问题:由于 paintEvent() 是需要重复进入的,因此,需要注意第二次进入时,QPainter 的状态是不是和第一次一致,否则的话可能会造成闪烁的现象。这个闪烁并不是由于双缓冲的问题,而是由于绘制状态的快速切换。






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
参与人数 1人气 +1 收起 理由
恒星shephered + 1 必须支持!

查看全部评分总评分 : 人气 +1

回复

使用道具 举报

尚未签到

2017-1-10 09:28:44 显示全部楼层
主函数中怎么用啊,不会写主函数中的使用方法……好菜啊我
回复 支持 1 反对 1

使用道具 举报

尚未签到

2018-1-18 23:45:42 显示全部楼层
完整程序,自己学着写的,写的不好,但是能出运行。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 1 反对 0

使用道具 举报

累计签到:2 天
连续签到:1 天
2014-6-3 14:56:32 显示全部楼层
我想在其他函数中调用painter来绘制图形应该怎么做?
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2015-3-18 09:57:31 显示全部楼层
资料都不错,就是找起来相当麻烦呀
回复 支持 反对

使用道具 举报

尚未签到

2017-1-10 09:30:01 显示全部楼层
QWidget::paintEngine: Should no longer be called
回复 支持 反对

使用道具 举报

累计签到:3 天
连续签到:1 天
2017-6-20 17:43:27 显示全部楼层
paintevent函数的参数,是怎么个情况,只有参数类型,没有参数名
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2017-8-21 00:09:54 显示全部楼层
楼主设置了相关帖子,还是配合相关帖子看着学更舒畅。感谢豆子大大!
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2017-8-21 00:10:32 显示全部楼层
如果把程序的具体源码放一下就太好了!已经很感谢了!
回复 支持 反对

使用道具 举报

尚未签到

2018-4-9 09:38:57 显示全部楼层
建议每篇后面附上完整的源码
回复 支持 反对

使用道具 举报

累计签到:13 天
连续签到:1 天
2018-4-19 18:25:31 显示全部楼层
感谢楼主的福利!感谢楼主的福利!感谢楼主的福利!
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2018-7-7 14:15:10 显示全部楼层
不错真的真的........................................................
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2019-8-22 14:48:39 显示全部楼层
本帖最后由 123zhang 于 2019-8-22 14:53 编辑

int main(int argc,char *argv[]){
  QApplication a(argc,argv);
  PaintedWidget  paint;
  paint.show();
  return  a.exec();
}
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了