前言

想要实现在QML中画线,有几种方式:
第一种,用在QML中用Canvas来实现画线功能,经过实践,效率比较低,折线非常严重,特别是在Android手机上运行。
第二种,通过QPainter来绘制,在C++中继承QQuickItem,然后实现paintEvent事件去绘图,然后在QML中显示绘图层,该方式在桌面端应用效果勉强能接受,但是在Android端效果也很差。
第三种方式就是通过Scene graph来画线,这是基于OpenGL来渲染图形,同样是在C++中继承QQuickItem,然后实现updatePaintNode函数,再到QML中显示绘图层。这种方式在桌面和移动端相对来说表现良好。
本文就来介绍第三种绘图方式。
先来看看效果图

正文

这里要实现的效果是,可以进行画线和擦除,这两种模式下的实现方式不太一样,如果用QPainter绘制的话,只需要将线的宽度变大,然后改变绘制颜色和绘制方式,即可实现擦除效果,但是这里不行,因为用scane graph画线的时候线条宽度不能加太粗,所以无法通过QPainter的模式来实现擦除效果。

来看源码

头文件

#include <QPainter>
#include <QVector>
#include <QPointF>
#include <QLineF>
#include <QPen>
#include <QQuickItemGrabResult>
#include <QSharedPointer>
#include <QSGSimpleRectNode>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 ALOpenGLDrawLine : public QQuickItem
{Q_OBJECT
public:explicit ALOpenGLDrawLine(QQuickItem *parent = 0);QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);Q_INVOKABLE void setDrawMode(int mode);//设置画线模式,0为画线,1为擦除Q_INVOKABLE int getDrawMode(){return m_currentMode;}Q_PROPERTY(QColor penColor READ getPenColor WRITE setPenColor)QColor getPenColor() const {return m_penColor;}void setPenColor(const QColor &c){m_penColor = c;update();}Q_INVOKABLE void clearAll();protected:void mousePressEvent(QMouseEvent *event);void mouseMoveEvent(QMouseEvent *event);void mouseReleaseEvent(QMouseEvent *event);signals:void sigMousePress(QPointF curPoint);void sigMouseMove(QPointF curPoint);void sigMouseRelease(QPointF curPoint);private :QPointF m_lastPoint;QVector<ElementGroup*> m_elements;ElementGroup * m_element;bool m_bPressed;QPen m_pen;int lastlenth;QSGNode *nodeP;bool m_bFlag;int m_nPenWidth; //笔宽QColor m_penColor; //笔颜色int m_currentMode;  //0->画线   1->擦除
};

源文件

#include "alopengldrawline.h"
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgflatcolormaterial.h>
#include <QFileInfo>
#include <QDir>
#include "smoothcolormaterial.h"namespace
{
struct Color4ub
{unsigned char r, g, b, a;
};inline Color4ub colorToColor4ub(const QColor &c)
{Color4ub color = { uchar(c.redF() * c.alphaF() * 255),uchar(c.greenF() * c.alphaF() * 255),uchar(c.blueF() * c.alphaF() * 255),uchar(c.alphaF() * 255)};return color;
}struct SmoothVertex
{float x, y;Color4ub color;float dx, dy;void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy){x = nx; y = ny; color = ncolor;dx = ndx; dy = ndy;}
};const QSGGeometry::AttributeSet &smoothAttributeSet()
{static QSGGeometry::Attribute data[] = {QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE, false),QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false)};static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };return attrs;
}
}ALOpenGLDrawLine::ALOpenGLDrawLine(QQuickItem *parent) :QQuickItem(parent),nodeP(NULL),m_bFlag(false),m_nPenWidth(5),m_penColor(QColor(Qt::red))
{setAcceptedMouseButtons(Qt::LeftButton);setFlag(ItemHasContents, true);lastlenth=0;setSmooth(true);setAntialiasing(true);m_currentMode = 0;
}QSGNode *ALOpenGLDrawLine::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{if(!oldNode){nodeP = new QSGNode;}else{nodeP = static_cast<QSGNode *>(oldNode);}QSGGeometryNode *node = 0;QSGGeometry *geometry = 0;if(m_currentMode == 0){node = new QSGGeometryNode;geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);geometry->setLineWidth(m_nPenWidth);geometry->setDrawingMode(GL_LINE_STRIP);node->setGeometry(geometry);node->setFlag(QSGNode::OwnsGeometry);QSGFlatColorMaterial *material = new QSGFlatColorMaterial;material->setColor(m_penColor);node->setMaterial(material);node->setFlag(QSGNode::OwnsMaterial);QSGGeometry::Point2D *vertices ;vertices = geometry->vertexDataAsPoint2D();int size = m_elements.size();ElementGroup *element;for(int k = 0; k < size ; ++k){m_bFlag = true;element = m_elements.at(k);int currentIndex = 0;if(k == size -1){QVector<QLineF>::iterator i;for (i = element->m_lines.begin(); i != element->m_lines.end(); ++i) {currentIndex++;if(element->m_lines.size()>1){if(currentIndex == lastlenth){vertices[0].set(i->p1().rx(),i->p1().ry());}if(currentIndex == element->m_lines.size()){vertices[1].set(i->p1().rx(),i->p1().ry());}}else{vertices[0].set(i->p1().rx(),i->p1().ry());vertices[1].set(i->p2().rx(),i->p2().ry());}}lastlenth = element->m_lines.size();}}node->markDirty(QSGNode::DirtyGeometry);if(m_bFlag){nodeP->appendChildNode(node);m_bFlag = false;}}else{QSGSmoothColorMaterial *material;node = new QSGGeometryNode;geometry = new QSGGeometry(smoothAttributeSet(), 0);geometry->setDrawingMode(GL_TRIANGLE_STRIP);material = new QSGSmoothColorMaterial();node->setGeometry(geometry);node->setFlag(QSGNode::OwnsGeometry);node->setMaterial(material);node->setFlag(QSGNode::OwnsMaterial);int vertexStride = geometry->sizeOfVertex();int vertexCount = 8;geometry->allocate(vertexCount, 0);SmoothVertex *smoothVertices = reinterpret_cast<SmoothVertex *>(geometry->vertexData());memset(smoothVertices, 0, vertexCount * vertexStride);float lineWidth = 100;float tlX,tlY,blX,blY,trX,trY,brX,brY;float delta = lineWidth * 0.8f;Color4ub fillColor = colorToColor4ub(QColor(255,255,255,255));Color4ub transparent = { 0, 0, 0, 0 };int size = m_elements.size();ElementGroup *element;for(int k = 0; k < size ; ++k){element = m_elements.at(k);int currentIndex = 0;if(k == size -1){QVector<QLineF>::iterator i;for (i = element->m_lines.begin(); i != element->m_lines.end(); ++i) {currentIndex++;if(element->m_lines.size()>1){if(currentIndex == lastlenth){tlX = i->p1().rx();tlY = i->p1().ry();}if(currentIndex == element->m_lines.size()){trX = i->p1().rx();trY = i->p1().ry();}}else{tlX = i->p1().rx();tlY = i->p1().ry();trX = i->p2().rx();trY = i->p2().ry();}}lastlenth = element->m_lines.size();}}blX = tlX;blY = tlY + lineWidth;brX = trX;brY = trY + lineWidth;smoothVertices[0].set(trX, trY, transparent, delta, -delta);smoothVertices[1].set(tlX, tlY, transparent, -delta, -delta);smoothVertices[2].set(trX, trY, fillColor, -delta, delta);smoothVertices[3].set(tlX, tlY, fillColor, delta, delta);smoothVertices[4].set(brX, brY, fillColor, -delta, -delta);smoothVertices[5].set(blX, blY, fillColor, delta, -delta);smoothVertices[6].set(brX, brY, transparent, delta, delta);smoothVertices[7].set(blX, blY, transparent, -delta, delta);node->markDirty(QSGNode::DirtyGeometry);nodeP->appendChildNode(node);}return nodeP;
}void ALOpenGLDrawLine::setDrawMode(int mode)
{m_currentMode = mode;if(mode == 0){m_penColor = QColor(Qt::red);m_nPenWidth = 5;}else if(mode == 1){m_penColor = QColor(Qt::white);m_nPenWidth = 100;}update();
}void ALOpenGLDrawLine::clearAll()
{if(nodeP->childCount() > 0){nodeP->removeAllChildNodes();m_elements.clear();update();}
}void ALOpenGLDrawLine::mousePressEvent(QMouseEvent *event)
{m_bPressed = true;m_element = new ElementGroup(m_pen);m_elements.append(m_element);m_lastPoint = event->localPos();event->setAccepted(true);emit sigMousePress(event->localPos());
}void ALOpenGLDrawLine::mouseMoveEvent(QMouseEvent *event)
{if(m_bPressed){m_element->m_lines.append(QLineF(m_lastPoint, event->localPos()));m_lastPoint = event->localPos();update();emit sigMouseMove(event->localPos());}event->setAccepted(true);
}void ALOpenGLDrawLine::mouseReleaseEvent(QMouseEvent *event)
{m_bPressed = false;m_elements.removeFirst();emit sigMouseRelease(event->localPos());event->setAccepted(true);
}

(部分代码摘自网络)
代码中还用到了QSGSmoothColorMaterialShader这个类,这是实现类用于实现阴影效果的,主要针对擦除的模式。
来看看代码
头文件

class QSGSmoothColorMaterial : public QSGMaterial
{
public:QSGSmoothColorMaterial();int compare(const QSGMaterial *other) const;
protected:virtual QSGMaterialType *type() const;virtual QSGMaterialShader *createShader() const;
};//----------------------------------------------------------------------class QSGSmoothColorMaterialShader : public QSGMaterialShader
{
public:QSGSmoothColorMaterialShader();virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);virtual char const *const *attributeNames() const;
private:void initialize();int m_matrixLoc;int m_opacityLoc;int m_pixelSizeLoc;
};

源文件

#include "smoothcolormaterial.h"QSGSmoothColorMaterial::QSGSmoothColorMaterial()
{setFlag(RequiresFullMatrixExceptTranslate, true);setFlag(Blending, true);
}int QSGSmoothColorMaterial::compare(const QSGMaterial *other) const
{Q_UNUSED(other)return 0;
}QSGMaterialType *QSGSmoothColorMaterial::type() const
{static QSGMaterialType type;return &type;
}QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
{return new QSGSmoothColorMaterialShader();
}//----------------------------------------------------------------------QSGSmoothColorMaterialShader::QSGSmoothColorMaterialShader(): QSGMaterialShader()
{setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/shaders/smoothcolor.vert"));setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/shaders/smoothcolor.frag"));
}void QSGSmoothColorMaterialShader::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{Q_UNUSED(newEffect)if (state.isOpacityDirty())program()->setUniformValue(m_opacityLoc, state.opacity());if (state.isMatrixDirty())program()->setUniformValue(m_matrixLoc, state.combinedMatrix());if (oldEffect == 0) {// The viewport is constant, so set the pixel size uniform only once.QRect r = state.viewportRect();program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());}
}const char * const *QSGSmoothColorMaterialShader::attributeNames() const
{static char const *const attributes[] = {"vertex","vertexColor","vertexOffset",0};return attributes;
}void QSGSmoothColorMaterialShader::initialize()
{m_matrixLoc = program()->uniformLocation("matrix");m_opacityLoc = program()->uniformLocation("opacity");m_pixelSizeLoc = program()->uniformLocation("pixelSize");
}

main函数实现

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QSurfaceFormat>
#include "alopengldrawline.h"int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);qmlRegisterType<ALOpenGLDrawLine>("OpenGLDrawLine", 1, 0, "OpenGLDrawLine");QQuickView view;QSurfaceFormat format = view.format();format.setSamples(16);view.setFormat(format);view.setResizeMode(QQuickView::SizeRootObjectToView);view.setSource(QUrl("qrc:/main.qml"));view.show();return app.exec();
}

main.qml实现

import QtQuick 2.6
import QtQuick.Window 2.2
import OpenGLDrawLine 1.0
import QtQuick.Controls 2.1Item {visible: truewidth: 800height: 600OpenGLDrawLine{id:drawLineanchors.fill: parent}Column{anchors.top: parent.topanchors.horizontalCenter: parent.horizontalCenterspacing: 15Button{width: 100height: 50text:"画线"onClicked: {drawLine.setDrawMode(0)}}Button{width: 100height: 50text:"擦除"onClicked: {drawLine.setDrawMode(1)}}}
}

完整工程在这里,点击下载

Qt Scene graph画线相关推荐

  1. Qt 实现钢笔画线效果详细原理

    前言 上一篇文章:Qt 实现画线笔锋效果详细原理,根据这篇介绍的实现笔锋效果的原理,我们很容易实现另外一种笔效:钢笔. 所谓的钢笔笔效,就是真实还原钢笔书写出来的线条效果,其特征就是:根据笔的绘制速度 ...

  2. Qt实现桌面画线、标记,流畅绘制,支持鼠标和多点触控绘制原创

    前言 经常会在网上直播讲课或者点评中看到可以在课件上或者桌面上进行画线标记划重点,其实实现并不难,原理就是在桌面上盖一个透明图层,然后根据鼠标点绘制曲线. 今天分享如何通过Qt的QGraphics体系 ...

  3. Qt实现桌面画线、标记,流畅绘制,支持鼠标和多点触控绘制

    前言 经常会在网上直播讲课或者点评中看到可以在课件上或者桌面上进行画线标记划重点,其实实现并不难,原理就是在桌面上盖一个透明图层,然后根据鼠标点绘制曲线. 今天分享如何通过Qt的QGraphics体系 ...

  4. 【QT 5 学习笔记-学习绘图相关+画线图形等+绘图事件+基础学习(1)】

    [QT 5 学习笔记-学习绘图相关+画线图形等+绘图事件+基础学习(1)] 1.说明 2.实验环境 3.参照学习链接 4.自己的学习与理解 5.学习与实践代码. (1)建立基础工程. (2)加入绘图事 ...

  5. Qt Quick 渲染之 Scene Graph 详解

    作者:billy 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 简介 在 Qt widget 中的渲染方式是传统的命令式绘图系统,使用 QPainter 依次为每个界面 ...

  6. QT 实现Label上画线

    实现功能:Label上有一张图片,用画笔在图像上画线. 一.头文件添加以下函数和变量 QMouseEvent * mouseEvent;void mousePressEvent(QMouseEvent ...

  7. 【巨人的肩膀上制造世界】——10——Unity3D实用插件之Vectrosity,轻松便捷的实现2D/3D画线功能

    [巨人的肩膀上制造世界]--10--Unity3D实用插件之Vectrosity,轻松便捷的实现2D/3D画线功能 目录 1.博客介绍 2.内容 (1)编辑器预制线条 (2)编辑器修改线条 (3)线条 ...

  8. Unity实用案例之——屏幕画线和线框渲染

    游戏里经常会遇到各种画线的需求,今天我们利用GL来实现其中的两个画线功能. 一.屏幕画线 屏幕画线是一种很好的用户交互方式,通过屏幕画线可以控制游戏中的各种元素,Unity提供了UnityEnige. ...

  9. Unity中在Game窗口画线

    在Unity中画线有几种方式: Debug.DrawLine()  函数画线,在Game窗口不开启 Gizmos 时,只在Scene窗口显示,多用于调试. 通过  LineRenderer  来画线, ...

最新文章

  1. 【神经网络】(18) EfficientNetV2 代码复现,网络解析,附Tensorflow完整代码
  2. 用fgets()函数以字符串形式读取磁盘文件并输出到屏幕
  3. 【指标统计】MsgProxy消息代理配置
  4. 第一次上计算机课日记500,第一次上网课作文500字
  5. Linux下安装LoadRunner LoadGenerator
  6. 走进Windows Server 2008服务器核心(Serve Core)
  7. 安卓小课堂之:读写文件(内部存储)
  8. linux挂载硬盘作用,Linux(挂载) mount umount作用
  9. 有一种生活叫向死而生
  10. Python数据可视化——坐标轴基础
  11. 2018年腾讯社交广告大赛复习总贴
  12. 解决手机端页面的鼠标点击出现蓝色背景
  13. 罗永浩以为×××短信给了马化腾一记暴击,实际……
  14. 数字·空间·光艺术|数字空间和光艺术作品
  15. 《嵌入式系统设计师教程》读后感:2.6嵌入式系统电源
  16. Project build error: Non-resolvable parent POM for com.example:demo:0.0.1-SNAPSHOT: Could not transf
  17. 利用python如何抓取微博评论?
  18. java根据url获取pdf流_从URL获取动态创建的PDF
  19. openCV calcHist函数的使用
  20. GIF图像动态生成-python后台生成

热门文章

  1. AutoDim:自动Embedding维度寻优,如何节省70%的存储空间同时还能大幅提效?
  2. 互联网1分钟 | 0327 华为P30系列发布;微信公号直播工具大范围开放内测资格
  3. 抱抱星英语Antony:不要用互联网思维做教育 | MCtalk教育访谈摘录
  4. C# wpf中DataGrid 支持汇总行
  5. Phalcon7 1.2.3 发布,高性能 PHP 7 框架
  6. 第 18 章 Django 入门
  7. android-Activity
  8. oracle 好书( 09 对象管理 )
  9. java 反射 Constructor、Method、Field 基本用法
  10. win cmd rmdir /s递归删除目录