Qt Scene graph画线
前言
想要實(shí)現(xiàn)在QML中畫線,有幾種方式:
第一種,用在QML中用Canvas來實(shí)現(xiàn)畫線功能,經(jīng)過實(shí)踐,效率比較低,折線非常嚴(yán)重,特別是在Android手機(jī)上運(yùn)行。
第二種,通過QPainter來繪制,在C++中繼承QQuickItem,然后實(shí)現(xiàn)paintEvent事件去繪圖,然后在QML中顯示繪圖層,該方式在桌面端應(yīng)用效果勉強(qiáng)能接受,但是在Android端效果也很差。
第三種方式就是通過Scene graph來畫線,這是基于OpenGL來渲染圖形,同樣是在C++中繼承QQuickItem,然后實(shí)現(xiàn)updatePaintNode函數(shù),再到QML中顯示繪圖層。這種方式在桌面和移動(dòng)端相對(duì)來說表現(xiàn)良好。
本文就來介紹第三種繪圖方式。
先來看看效果圖
正文
這里要實(shí)現(xiàn)的效果是,可以進(jìn)行畫線和擦除,這兩種模式下的實(shí)現(xiàn)方式不太一樣,如果用QPainter繪制的話,只需要將線的寬度變大,然后改變繪制顏色和繪制方式,即可實(shí)現(xiàn)擦除效果,但是這里不行,因?yàn)橛胹cane graph畫線的時(shí)候線條寬度不能加太粗,所以無法通過QPainter的模式來實(shí)現(xiàn)擦除效果。
來看源碼
頭文件
#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);//設(shè)置畫線模式,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); }(部分代碼摘自網(wǎng)絡(luò))
代碼中還用到了QSGSmoothColorMaterialShader這個(gè)類,這是實(shí)現(xiàn)類用于實(shí)現(xiàn)陰影效果的,主要針對(duì)擦除的模式。
來看看代碼
頭文件
源文件
#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函數(shù)實(shí)現(xiàn)
#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實(shí)現(xiàn)
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)}}} }完整工程在這里,點(diǎn)擊下載
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Qt Scene graph画线的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Qt 判断网络连接
- 下一篇: Qt 遍历目录并删除目录下所有文件