本文章示例要实现的功能是qml工程中实现圆形、矩形、多边形和直尺的绘制,并且可以通过控制点来改变路径,效果如图:

qml工程中进行绘制除了使用Canvas,还有一种方法就是继承QQuickPaintedItem了,然后重写其虚函数paint(),调用update()来触发绘制。对于该类更详细的描述我就不介绍了,大家可以自己查阅。
定义Annotation类,然后继承自QQuickPaintedItem
#pragma once
#include <QPainterPath>
#include <QQuickPaintedItem>
#include <vector>
#include <QPoint>
#include <memory>
#include "basedraw.h"
struct moveDraw
{
std::shared_ptr<BaseDraw> pDraw;
int ptIndex;
void reset()
{
pDraw.reset();
ptIndex = -1;
}
bool isEmpty()
{
return pDraw.get() == nullptr;
}
};
class Annotation : public QQuickPaintedItem
{
Q_OBJECT
Q_ENUMS(EM_DRAW_TYPE)
Q_PROPERTY(EM_DRAW_TYPE drawType READ drawType WRITE setDrawType)
public:
enum EM_DRAW_TYPE{
ELLIPSE,//椭圆
RECTANGLE,//矩形
POLYGON,//多边形(直线型)
SPLINE,//多边形(曲线型)
MEASUREMENT,//直尺
MAX
};
Annotation(QQuickPaintedItem *parent = nullptr);
protected:
void paint(QPainter *painter);
public slots:
void slotCompare();
public:
Q_INVOKABLE void mousePress(qreal x, qreal y);
Q_INVOKABLE void mouseMove(qreal x, qreal y);
Q_INVOKABLE void mouseRelease(qreal x, qreal y);
Q_INVOKABLE void undo();
EM_DRAW_TYPE drawType();
void setDrawType(const EM_DRAW_TYPE& type);
private:
EM_DRAW_TYPE _drawType = MAX;//当前绘制类型
std::vector<std::shared_ptr<BaseDraw>> _draws;//保存的图形
std::shared_ptr<BaseDraw> _pCurDraw = nullptr;
moveDraw _moveDraw;
};
然后重写其paint()虚函数,实现绘制功能
void Annotation::paint(QPainter *painter)
{
painter->setRenderHint(QPainter::Antialiasing);
painter->save();
for (auto pDraw : _draws)
{
pDraw->paint(painter);
}
if (_pCurDraw)
_pCurDraw->paint(painter);
painter->restore();
}
其中BaseDraw是绘图的基类,ellipseDraw、measurementDraw、polygonDraw、rectangeDraw、splineDraw继承于BaseDraw,分别实现绘制不同的功能。关系如图:

BaseDraw基类代码:
#ifndef BASEDRAW_H
#define BASEDRAW_H
#include <QObject>
#include <QPainterPath>
#include <QPainter>
#include <vector>
#include <QPoint>
class BaseDraw : public QObject
{
Q_OBJECT
public:
explicit BaseDraw(QObject *parent = 0);
signals:
void sigCompare();
public slots:
public:
virtual void mousePress(qreal x, qreal y);
virtual void mouseMove(qreal x, qreal y);
virtual void mouseRelease(qreal x, qreal y);
protected:
virtual void pointsChanged(){}
virtual QPainterPath getCurrentPath(const std::vector<QPoint>& coords){ return QPainterPath();}
public:
void reset();
virtual void paint(QPainter *painter)=0;
std::vector<QPoint> getPoints(){return _pts;}
void setPoints(const std::vector<QPoint>&);
void setPoint(int idx, int x, int y);
bool closed() { return _finished; }
protected:
std::vector<QPoint> _pts;//当前点
QPainterPath _currentPath;
QPolygonF _polys;
QPoint _startPt,_lastPt,falsePt;
bool _pressed =false;//是否按下
bool _finished = true;//是否闭合
bool _startPtLight = false;//起始点是否高亮
QString _type = "spline";//多边形类型
};
#endif // BASEDRAW_H
BaseDraw基类中有个paint()纯虚函数,继承于它的几个子类都必须重写该虚函数用于实现不同的绘制工作,且该paint()都是在Annotation类的paint()处被调用。
其它类的代码和详细的实现我就不全部展示出来了,我只是提供我的实现思路,我会给出代码链接,需要代码的朋友可以去下载。
在cpp中,我们对Annotation类进行注册
qmlRegisterType<Annotation>("df.annotation", 1, 0, "Annotation");
import QtQuick 2.7
import QtQuick.Window 2.2
import an.utility 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item{
anchors.fill: parent
Rectangle{
id:id_rect_btn
width: 60
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left:parent.left
color: "#d9d9d9"
Column{
property var ck_name: ""
id:id_com
spacing: 10
CButton{
img_source:"qrc:/resource/ellipse.png"
isChecked:id_com.ck_name === "ellipse"
onClicked: {
id_com.ck_name = "ellipse"
id_annotation.drawType = Annotation.ELLIPSE
}
}
CButton{
img_source:"qrc:/resource/rectangle.png"
isChecked:id_com.ck_name === "rectangle"
onClicked: {
id_com.ck_name = "rectangle"
id_annotation.drawType = Annotation.RECTANGLE
}
}
CButton{
img_source:"qrc:/resource/poly.png"
isChecked:id_com.ck_name === "poly"
onClicked: {
id_com.ck_name = "poly"
id_annotation.drawType = Annotation.POLYGON
}
}
CButton{
img_source:"qrc:/resource/spline.png"
isChecked:id_com.ck_name === "spline"
onClicked: {
id_com.ck_name = "spline"
id_annotation.drawType = Annotation.SPLINE
}
}
CButton{
img_source:"qrc:/resource/measure.png"
isChecked:id_com.ck_name === "measure"
onClicked: {
id_com.ck_name = "measure"
id_annotation.drawType = Annotation.MEASUREMENT
}
}
}
}
Annotation
{
id: id_annotation
anchors.left: id_rect_btn.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
MouseArea
{
anchors.fill: parent
hoverEnabled: true
onPressed: id_annotation.mousePress(mouse.x, mouse.y)
onPositionChanged:id_annotation.mouseMove(mouse.x, mouse.y)
onReleased: id_annotation.mouseRelease(mouse.x, mouse.y)
focus: true
Keys.enabled: true
Keys.onPressed: {
if (event.matches(StandardKey.Undo))
{
id_annotation.undo()
}
}
}
}
}
}
CButton是我自定义的按钮控件,大家可以无视。。。