Qt Quick实现的涂鸦程序

这篇博客介绍了如何使用Qt Quick的QQuickPaintedItem结合C++实现一个简单的涂鸦程序,包括设置线条宽度、颜色、背景色和undo功能。文章详细讨论了QQuickPaintedItem的绘图机制,C++实现QML图元,以及自定义图元处理鼠标事件的方法,并提供了源码分析和QML文档示例。
摘要由CSDN通过智能技术生成

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

    之前一直以为 Qt Quick 里 Canvas 才可以自绘,后来发觉不是,原来还有好几种方式都可以绘图!可以使用原始的 OpenGL(Qt Quick 使用 OpenGL 渲染),可以构造QSGNode 来绘图,还可以使用 QPainter !哇, QPainter 我很熟悉啊。于是,我用 QPainter 结合 QML 实现了一个简单的涂鸦程序: PaintedItem 。它有下列功能:

  • 设置线条宽度
  • 设置线条颜色
  • 设置背景颜色
  • 清除涂鸦
  • 无限级undo

    程序很简陋,效果如下:


                               图1 PaintedItem效果图

    程序虽然简单,但也还是有一些新内容之前没有提到:

  • QQuickPaintedItem
  • C++实现QML可视图元(Item)
  • 自定义图元如何处理鼠标事件

    下面咱们一个一个来说一下。

    版权所有 foruok ,转载请注明出处:http://blog.csdn.net/foruok

QQuickPaintedItem

    Qt Quick 的核心是 Scene Graph ,可以在 Qt 帮助的索引模式下以 “Scene Graph” 为关键字来检索学习。 Scene Graph 的设计思想和 QGraphicsView/QGraphicsScene 框架类似,一个场景,很多图元往场景里放。不同之处是 Item 的绘制, QGraphicsView 框架里是通过 View 的绘图事件来驱动 Item 的绘制,QGraphicsItem 有一个 paint() 虚函数,只要你从 QGraphicsItem 继承来的 Item 实现这个 paint() 函数,就可以往 QPaintDevice 上绘制了,逻辑直接;而 Qt Quick 的绘制,其实另有一个渲染线程, Scene 里的 Item 没有 paint() 这种直观的绘图函数,只有一个 updatePaintNode() 方法让你来构造你的 Item 的几何表示,当程序轮转到渲染循环时,渲染循环把所有 Item 的 QSGNode 树取出来绘制。

    updatePaintNode() 这种绘制的方式很不直观,它来自 OpenGL 或者 Direct 3D 的绘图模式:你构造图元的几何表示,别人会在某一个时刻根据你提供的材料帮你绘制,就像你扔一袋垃圾到门口,过一阵子有人会来帮你收走这种感觉。用惯 Qt Widgets 和 QPainter 的开发者可能会不适应这种方式,所以 Qt Quick 提供了一种兼容老习惯的方式:引入 QQuickPaintedItem ,使用 QPainter 绘制。

    一般地,你可以这样理解: QQuickPaintedItem 使用 Qt Widgets 里惯常的 2D 绘图方式,将你想要的线条、图片、文字等绘制到一个内存中的 QImage 上,然后把这个 QImage 作为一个 QSGNode 放在那里等着 Qt Quick 的渲染线程来取走它,把它绘制到实际的场景中。按照这种理解, QQuickPaintedItem 会多个绘图步骤,有性能上的损失!不过为了开发方便,有时候这一点点性能损失是可以承受的——只要你的应用仍然可以流畅运行。

    QQuickPaintedItem 是一切想使用 QPainter 来绘图的 Qt Quick Item 的基类,它有一个纯虚函数—— paint(QPainter * painter)  ,你自定义的 Item 只要实现 paint() 虚函数就可以了。

    QQuickPaintedItem 是 QQuickItem 的派生类, QQuickItem 的 boundingRect() 方法返回一个 Item 的矩形,你可以根据它来绘制你的 Item 。fillColor() 返回 Item 的填充颜色(默认是透明的), Qt Quick 会使用这个颜色在 paint() 方法调用前绘制你的 Item 的背景。 setFillColor()  可以改变填充颜色。

    Qt Quick 提供了一个“Scene Graph - Painted Item”示例来演示 QQuickPaintedItem 的用法,你可以参考。

C++实现QML可视图元

    Qt Quick 提供的相当一部分图形元素都是在 C++ 中实现后导出到 QML 环境中的,比如 Text 。那我们也可以这么做,只要你从 QQuickItem(对应 QML 中的 Item 元素) 继承来实现你的 C++ 类即可。

    我们的示例要使用 QPainter 绘图,所以从 QQuickPaintedItem 继承,重写 paint() 方法。

    完成了 C++ 类,导出到 QML 环境中,就可以像使用 QML 内建元素一样来使用我们导出的类。如何导出又如何在 QML 中使用,请参看《Qt Quick 之 QML 与 C++ 混合编程详解》。

自定义图元如何处理鼠标事件

    在 QML 中我们一直使用 MouseArea 来处理鼠标事件。 MouseArea 对应 C++ 中的 QQuickMouseArea 类,其实也是 QQuickItem 的派生类。 其实 QQuickItem 定义了一系列处理鼠标事件的虚函数,比如 mousePressEvent 、 mouseMoveEvent 、 mouseMoveEvent 等,它本身就可以处理鼠标事件,只不过 QQuickItem 没有导出这些函数,我们在 QML 中无法使用。而之所以引入 QQuickMouseArea (QML 中的 MouseArea ),是为了方便鼠标事件的处理,你不需要为每个 Item 像 QWidget 那样来重写很多方法,那样真的很烦的, QML 的这种方式虽然多用了一个对象,但是更方便一些。可是我们的 PaintedItem 类,如果绕回到 QML 中使用 MouseArea 来处理鼠标事件,那我们跟踪鼠标轨迹来绘制线条时,就需要不断地将鼠标事件中携带的像素点信息再回传到 C++ 中来,非常麻烦,性能也不好,所以我们直接重写 QQuickItem 的相关虚函数来处理鼠标事件。

    我们知道 MouseArea 有一个 acceptedButtons 属性,可以设置 Item 处理哪个鼠标按键,而实际上,“要处理的鼠标按键”这个信息,是保存在 QQuickItem 中的,通过 setAcceptedMouseButtons() 方法来设置。默认情况下, QQuickItem 不处理任何鼠标按键,所以我们要处理鼠标按键,必须在我们的 PaintedItem 中来设置一下,就像 MouseArea 那样。我们的示例中,在 PaintedItem 的构造函数中做了这件事:

PaintedItem::PaintedItem(QQuickItem *parent)    : QQuickPaintedItem(parent)    , m_element(0)    , m_bEnabled(true)    , m_bPressed(false)    , m_bMoved(false)    , m_pen(Qt::black){    setAcceptedMouseButtons(Qt::LeftButton);}

    如代码所示,我们只处理鼠标左键。如果你不设置这个,你收不到任何鼠标事件。

PaintedItem 源码分析

    因为我们实现的功能简单,源码也不复杂。

自定义 Item

    先看 PaintedItem.h :

#ifndef PAINTEDITEM_H#define PAINTEDITEM_H#include <QQuickPaintedItem>#include <QVector>#include <QPointF>#include <QLineF>#include <QPen>class ElementGroup{
    public:    ElementGroup()    {    }    ElementGroup(const QPen &pen)        : m_pen(pen)    {    }    ElementGroup(const ElementGroup &e)    {        m_lines = e.m_lines;        m_pen = e.m_pen;    }    ElementGroup & operator=(const ElementGroup &e)    {        if(this != &e)        {            m_lines = e.m_lines;            m_pen = e.m_pen;        }        return *this;    }    ~ElementGroup()    {    }    QVector<QLineF> m_lines;    QPen m_pen;};class PaintedItem : public QQuickPaintedItem{    Q_OBJECT    Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)    Q_PROPERTY(int penWidth READ penWidth WRITE setPenWidth)    Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor)public:    PaintedItem(QQuickItem *parent = 0);    ~PaintedItem();    bool isEnabled() const{ return m_bEnabled; }    void setEnabled(bool enabled){ m_bEnabled = enabled; }    int penWidth() const { return m_pen.width(); }    void setPenWidth(int width) { m_pen.setWidth(width); }    QColor penColor() const { return m_pen.color(); }    void setPenColor(QColor color) { m_pen.setColor(color); }    Q_INVOKABLE void clear();    Q_INVOKABLE void undo();    void paint(QPainter *painter);protected:    void mousePressEvent(QMouseEvent *event);    void mouseMoveEvent(QMouseEvent *event);    void mouseReleaseEvent(QMouseEvent *event);    void purgePaintElements();protected:    QPointF m_lastPoint;    QVector<ElementGroup*> m_elements;    ElementGroup * m_element; // the Current ElementGroup    bool m_bEnabled;    bool m_bPressed;    bool m_bMoved;    QPen m_pen; // the Current Pen};#endif 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值