Qt学习_QGraphics进阶学习笔记

QGraphics进阶学习

1.保存图片函数

QPixmap QWidget::grab(const QRect &rectangle = QRect(QPoint(0, 0), QSize(-1, -1)))

Renders the widget into a pixmap restricted by the given rectangle. If the widget has any children, then they are also painted in the appropriate positions.
If a rectangle with an invalid size is specified (the default), the entire widget is painted.

2.场景里面添加QWidget部件

 QGraphicsProxyWidget* item = _scene->addWidget(new QLineEdit("hello"));
    item->resize(50,50);
    item->setPos(_scene->sceneRect().center());
    item->setFlags(QGraphicsItem::ItemIsMovable|
                   QGraphicsItem::ItemIsSelectable|
                   QGraphicsItem::ItemIsFocusable);

3.对QWidget部件的移动,对scene重写

#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H

#include 

/** 可以移动QGraphicsProxyWidget的scene
 * @brief The GraphicsScene class
 */
class GraphicsScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit GraphicsScene(QObject *parent = nullptr);
    void setMoveMode(bool move){m_isMoveMode=move;}
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);


private:
    QGraphicsItem   *m_pItemSelected{Q_NULLPTR};                   // 鼠标单选item
    QPoint          m_shiftOrg;                         // 鼠标点选item中位置相对于item原点的偏移量
    bool            m_isMoveMode{false};
};

#endif // GRAPHICSSCENE_H

#include "GraphicsScene.h"
#include 
#include 
#include 
#include 
#include 

GraphicsScene::GraphicsScene(QObject *parent) : QGraphicsScene(parent)
{
}

// 鼠标按下获取当前单选中的QGraphicsProxyWidget图元对象
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{

//    qDebug() << "scene mouse press";
    QGraphicsScene::mousePressEvent(event);
    //左键则弹出menu框
    if (event->button() == Qt::RightButton){
        m_pItemSelected = nullptr;
        if(items(event->scenePos()).isEmpty())
        {
            QMenu *menu = new QMenu();
            menu->setAttribute(Qt::WA_DeleteOnClose);
            menu->addAction("editMode",this,[this](){
                setMoveMode(false);
            });
            menu->addAction("moveMode",this,[this](){
                setMoveMode(true);
            });
            menu->popup(event->screenPos());
        }

    }

    //进入edit模式则直接退出
    if(m_isMoveMode==false){
        m_pItemSelected = nullptr;
        return;
    }

    //进入move模式则不可编辑
    if (event->button() == Qt::LeftButton)
    {
        // 检测光标下是否有 item
        m_pItemSelected = nullptr;
        foreach (QGraphicsItem *item, items(event->scenePos()))
        {
            if (item->type() == QGraphicsProxyWidget::Type)   // 代理Widget
            {
                QGraphicsProxyWidget *proxyWidget = qgraphicsitem_cast<QGraphicsProxyWidget *>(item);
                QPointF point = proxyWidget->mapToScene(QPointF(0.0, 0.0));
                m_shiftOrg.setX(event->scenePos().x() - point.x());
                m_shiftOrg.setY(event->scenePos().y() - point.y());
                m_pItemSelected = item;
                break;
            }
        }
    }


}
// 鼠标移动过程中跟随位置改变
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
//    qDebug() << "scene mouse move";
    QGraphicsScene::mouseMoveEvent(event);
    if(m_pItemSelected != nullptr)
    {
        m_pItemSelected->setPos(event->scenePos().x() - m_shiftOrg.x(), event->scenePos().y() - m_shiftOrg.y());
    }
}
// 鼠标释放后作为最后的位置
void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
//    qDebug() << "scene mouse release";
    QGraphicsScene::mouseReleaseEvent(event);

    if(m_pItemSelected != nullptr)
    {
        m_pItemSelected->setPos(event->scenePos().x() - m_shiftOrg.x(), event->scenePos().y() - m_shiftOrg.y());
        m_pItemSelected = nullptr;
    }
}

4.自定义QGraphicsLineItem 创建动态连线

需要重写 QRectF boundingRect()void paint() 函数

需要添加头尾两个item指针

头文件

#ifndef CONNECTLINE_H
#define CONNECTLINE_H

#include 

class ConnectLine : public QGraphicsLineItem
{
public:
    ConnectLine();
    ConnectLine(QGraphicsItem* startitem, QGraphicsItem* enditem);
    ~ConnectLine();
    double getlength(){return _length;}
private:
    QGraphicsItem* _startItem;
    QGraphicsItem* _endItem;
    double _length=0;

    // QGraphicsItem interface
public:
    virtual QRectF boundingRect() const;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

#endif // CONNECTLINE_H

源文件

#include "ConnectLine.h"
#include 
#include 

ConnectLine::ConnectLine()
{

}

ConnectLine::ConnectLine(QGraphicsItem *startitem, QGraphicsItem *enditem)
{
    _startItem = startitem;
    _endItem = enditem;
    this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
}

ConnectLine::~ConnectLine()
{
}

QRectF ConnectLine::boundingRect() const
{
    return QRectF(_startItem->pos()-QPointF(10,10),_endItem->pos()+QPointF(10,10));
}

void ConnectLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
//    painter->drawLine(_startItem->pos(),_endItem->pos());

    QPainterPath path;
    path.moveTo(_startItem->pos());
    path.cubicTo(_startItem->pos()+QPointF(50,0),_endItem->pos()-QPointF(50,0),_endItem->pos());
    _length = path.length();
    painter->drawPath(path);

}

5.自定义 textitem 双击可以编辑

知识点:

flags Qt::TextInteractionFlags

QGraphicsTextItem::setTextInteractionFlags(Qt::TextInteractionFlag::TextEditorInteraction);

头文件

#ifndef GRAPHICSEDITABLETEXTITEM_H
#define GRAPHICSEDITABLETEXTITEM_H

#include 

class GraphicsEditableTextItem : public QGraphicsTextItem
{
    Q_OBJECT

public:
    GraphicsEditableTextItem();
    GraphicsEditableTextItem(const QString& str);

    // QGraphicsItem interface
protected:
    void focusOutEvent(QFocusEvent *event) override;
    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
};

#endif // GRAPHICSEDITABLETEXTITEM_H

源文件

#include "GraphicsEditableTextItem.h"

GraphicsEditableTextItem::GraphicsEditableTextItem()
{
    this->setFlags(QGraphicsItem::ItemIsMovable |
                   QGraphicsItem::ItemIsFocusable|
                   QGraphicsItem::ItemIsSelectable);
}

GraphicsEditableTextItem::GraphicsEditableTextItem(const QString &str)
{
    this->setPlainText(str);
    this->setFlags(QGraphicsItem::ItemIsMovable |
                   QGraphicsItem::ItemIsFocusable|
                   QGraphicsItem::ItemIsSelectable);
}

void GraphicsEditableTextItem::focusOutEvent(QFocusEvent *event)
{
    this->setSelected(false);
    if(this->textInteractionFlags()==Qt::TextInteractionFlag::TextEditorInteraction){
        this->setTextInteractionFlags(Qt::TextInteractionFlag::NoTextInteraction);
    }
    QGraphicsTextItem::focusOutEvent(event);
}

void GraphicsEditableTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    if(this->textInteractionFlags()==Qt::TextInteractionFlag::NoTextInteraction){
        this->setTextInteractionFlags(Qt::TextInteractionFlag::TextEditorInteraction);
    }
    QGraphicsTextItem::mouseDoubleClickEvent(event);
}

Qt学习_QGraphics进阶学习笔记_第1张图片

6.绘制外轮廓

// 绘制轮廓
void ConnectLine::drawOutline(QPainter *painter, QPainterPath path)
{
    // 生成可填充的轮廓
    QPainterPathStroker stroker;
    stroker.setCapStyle(Qt::RoundCap);  // 端点风格
    stroker.setJoinStyle(Qt::RoundJoin);  // 连接样式
    stroker.setDashPattern(Qt::DashLine);  // 虚线图案
    stroker.setWidth(10);  // 宽度

    // 生成一个新路径(可填充区域),表示原始路径 path 的轮廓
    QPainterPath outlinePath = stroker.createStroke(path);

    // 绘制轮廓时所用的画笔(轮廓外边框灰色部分)
    QPen pen = painter->pen();
    pen.setColor(QColor(0, 160, 230));
    pen.setWidth(10);

    // 用指定的画笔 pen 绘制 outlinePath
    // painter->strokePath(outlinePath, pen);
    painter->setPen(pen);
    painter->drawPath(outlinePath);

    // 用指定的画刷 brush 填充路径 outlinePath
    painter->fillPath(outlinePath, QBrush(Qt::yellow));

}

7.MyMind思维导图

Qt学习_QGraphics进阶学习笔记_第2张图片

void MainWindow::on_pushButton_clicked()
{
    QGraphicsItem* previtem=0;
    qDebug()<<_scene->selectedItems();
    if(!_scene->selectedItems().isEmpty()) {
        previtem = _scene->selectedItems().at(0);
        qDebug()<<"selected previtem";
    }

    GraphicsEditableTextItem* textitem = new GraphicsEditableTextItem("hello");
    _scene->addItem(textitem);

    if(previtem){
        qDebug()<<"draw line";
        textitem->setPos(previtem->pos()+QPointF(100,0));
        ConnectLine* line = new ConnectLine(previtem,textitem);
        _scene->addItem(line);
    }
}

8.保存为Json格式并读取

保存:

void MainWindow::on_actsave_triggered()
{
    //保存为图片
    QPixmap pic = ui->View->grab(scene->sceneRect().toRect());
    pic.save("D:\\lesliex\\test.jpg");
    qDebug()<<scene->items(scene->itemsBoundingRect());

    //保存图元的数据
    QJsonArray array;
    for(auto item:scene->items(scene->itemsBoundingRect())){
        QJsonObject obj;
        obj.insert("x",item->pos().x());
        obj.insert("y",item->pos().y());
        obj.insert("type",item->type());
        obj.insert("zValue",item->zValue());
        array.append(obj);
    }
    QJsonDocument Doc(array);
    QFile file("D:\\lesliex\\test.txt");
    file.open(QIODevice::ReadWrite);
    file.write(Doc.toJson());
    file.close();
}

Json数据

[
    {
        "type": 4,//图元类型
        "x": 325,//x坐标
        "y": 277,//y坐标
        "zValue": 0//z坐标
    },
    {
        "type": 4,
        "x": 297,
        "y": 277,
        "zValue": 0
    }
]

读取:

void MainWindow::on_actload_triggered()
{
    //读取图元的数据
    QFile file("D:\\lesliex\\test.txt");
    file.open(QIODevice::ReadWrite);
    QByteArray data = file.readAll();
    file.close();

    QJsonDocument Doc(QJsonDocument::fromJson(data));
    QJsonArray array(Doc.array());
    for(auto i:array){
        QJsonObject obj = i.toObject();
        drawItem(obj["type"].toInt(),obj["x"].toDouble(),\
                obj["y"].toDouble(),obj["zValue"].toInt());
    }
}

还可以不使用json,使用QTextStream

内容:

Rectangle Rectangle #ff0000 198 401 207 50
Circle Circle #ff0000 117 160 307 128

解析

bool Document::load(QTextStream &stream)
{
    m_shapeList.clear();

    while (!stream.atEnd()) {//逐行操作
        QString shapeType, shapeName, colorName;
        int left, top, width, height;
        //此处会以空白符分割一行,将字符串输入变量中
        stream >> shapeType >> shapeName >> colorName >> left >> top >> width >> height;
        if (stream.status() != QTextStream::Ok)
            return false;

9.给QgraphicsTextItem加上背景色

Qt学习_QGraphics进阶学习笔记_第3张图片

void GraphicsEditableTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setBrush(Qt::yellow);
    painter->drawRect(this->boundingRect());
    QGraphicsTextItem::paint(painter,option,widget);
}

10.利用boundingrect获取起始点和终点

void ConnectLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPointF startpos = mapFromItem(_startItem, _startItem->boundingRect().center());
    QPointF endpos =mapFromItem( _endItem,_endItem->boundingRect().center());

    painter->setPen(this->pen());

    QPainterPath path;
    path.moveTo(startpos);

    if(startpos.x()>endpos.x())
        path.cubicTo(startpos-QPointF(100,0),endpos+QPointF(100,0),endpos);
    else
        path.cubicTo(startpos+QPointF(100,0),endpos-QPointF(100,0),endpos);

    painter->drawPath(path);
}

11.向下移动

if(previtem){
        qDebug()<<"draw line";
        textitem->setPos(previtem->pos()+QPointF(100,0));
        while(!textitem->collidingItems().isEmpty()){
            textitem->setPos(textitem->collidingItems().first()->pos()+QPointF(0,100));
        }
        ConnectLine* line = new ConnectLine(previtem,textitem);
        _scene->addItem(line);

12.关于碰撞

item是根据 boundingRect 和 shape 来检测碰撞的,简单的方形图使用boundingRect 检测,复杂的如三角等使用 shape 来检测

13.关于场景大小

scene->itemsBoundingRect() 为所有items的集合的总的大小

  connect(_scene,&QGraphicsScene::changed,this,[this](const QList<QRectF>& area){
        _scene->update();//使连线重画
        if(_scene->sceneRect().width() < _scene->itemsBoundingRect().width() ||
            _scene->sceneRect().height() < _scene->itemsBoundingRect().height()    )
        _scene->setSceneRect(_scene->itemsBoundingRect());
    });

14.点击连线按钮后在场景画线

头文件

#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H

#include 

QT_BEGIN_NAMESPACE
class QGraphicsLineItem;
QT_END_NAMESPACE

class MyGraphicsScene : public QGraphicsScene
{
public:
    enum ITEMMODE{
        LINEITEM,
        LAST
    };
    explicit MyGraphicsScene(QObject *parent = nullptr);
    void setmode(int mode){_mode = mode;}

private:
    int _mode=LAST;
    QGraphicsLineItem* _line{Q_NULLPTR};

    // QGraphicsScene interface
protected:
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);


};

#endif // MYGRAPHICSSCENE_H

源文件

#include "MyGraphicsScene.h"
#include 
#include 

MyGraphicsScene::MyGraphicsScene(QObject *parent) : QGraphicsScene(parent)
{

}

void MyGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button()!=Qt::LeftButton)return;

    if(_mode==LINEITEM){
        _line = new QGraphicsLineItem(QLineF(event->scenePos(),event->scenePos()));//在点击处添加线段
        this->addItem(_line);//在场景中加入线段
    }else{
        QGraphicsScene::mousePressEvent(event);
    }
}

void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(_line!=0 && _mode==LINEITEM){
        _line->setLine(QLineF(_line->line().p1(),event->scenePos()));
    }else{
        QGraphicsScene::mouseMoveEvent(event);
    }
}

void MyGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if(_line!=0 && _mode==LINEITEM){
        QLineF newline = _line->line();
        QGraphicsLineItem* newlineitem = new QGraphicsLineItem(newline);
        this->addItem(newlineitem);
        delete _line;
        _line=0;
        _mode = LAST;
    }else{
        QGraphicsScene::mouseReleaseEvent(event);
    }
}

使用

//action可以设置为checkable
void MainWindow::on_actdrawline_triggered(bool checked)
{
    if(checked) scene->setmode(MyGraphicsScene::LINEITEM);
    else scene->setmode(MyGraphicsScene::LAST);
}

15.在重写scene中插入item

根据type插入item

item定位前随鼠标移动

头文件

#ifndef MYGRAPHICSSCENE_H
#define MYGRAPHICSSCENE_H

#include 

QT_BEGIN_NAMESPACE
class QGraphicsLineItem;
QT_END_NAMESPACE

class MyGraphicsScene : public QGraphicsScene
{
public:
    enum ITEMMODE{
        COMMONITEM,
        LINEITEM,
        BEZIERITEM,
        SPECIALITEM,
        LAST
    };
    enum SHAPE{
        RECT,
        ELLIPSE,
        CIRCLE,
        TRIANGLE,
        POLYGON,
        TEXT,
        LINE,
        BezierCur,
        CONNECTLINE,
        SHAPE_LAST
    };
    explicit MyGraphicsScene(QObject *parent = nullptr);
    void setMode(int mode){_mode = mode;}
    void setType(int type);

private:
    int _mode=LAST;
    //添加普通图元,获取图元类型
    int _type=0;
    QGraphicsItem* _curItem{Q_NULLPTR};//为实时追踪鼠标的图元
    //画线
    QGraphicsLineItem* _line{Q_NULLPTR};
    //画贝塞尔曲线

    // QGraphicsScene interface
protected:
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

#endif // MYGRAPHICSSCENE_H

源文件

void MyGraphicsScene::setType(int type)
{//如果只使用已有形状判断的话,无法判断椭圆和圆的区别,还是自己写一个类型枚举的好
    _type = type;
    QGraphicsItem* item{Q_NULLPTR};
    switch (_type) {
    case SHAPE::RECT :
        item = new QGraphicsRectItem(0,0,100,100);
        break;
    case SHAPE::CIRCLE :
        item = new QGraphicsEllipseItem(0,0,100,100);
        break;
    case SHAPE::TRIANGLE :
        item = new QGraphicsPolygonItem(QPolygonF(QVector<QPointF>{{0,0},{120,0},{0,-160}}));
        break;
    default:break;
    }
    this->addItem(item);
    _curItem=item;
}

void MyGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button()!=Qt::LeftButton){//右键取消摆放
        if(_curItem){
            _mode = LAST;
            delete _curItem;
            _curItem=0;
        }
        return;
    }

    switch (_mode) {
    case LINEITEM:
    {
        _line = new QGraphicsLineItem(QLineF(event->scenePos(),event->scenePos()));//在点击处添加线段
        this->addItem(_line);//在场景中加入线段
    }break;
    case COMMONITEM:
    {
       //        _mode = LAST;
        _curItem->setFlags(QGraphicsItem::ItemIsMovable|
                           QGraphicsItem::ItemIsSelectable|
                           QGraphicsItem::ItemIsFocusable);
        setType(_type);//使他连续创建,直到右键取消
    }break;
    default:
    {
        QGraphicsScene::mousePressEvent(event);
    }break;
    }
}

void MyGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(_line!=0 && _mode==LINEITEM){
        _line->setLine(QLineF(_line->line().p1(),event->scenePos()));
    }else if(_mode == COMMONITEM){
        //使鼠标在图形的中心点
        _curItem->setPos(event->scenePos()-_curItem->boundingRect().center());
    }

    else{
        QGraphicsScene::mouseMoveEvent(event);
    }
}

16.创建控制点

给直线图元加两个控制点

Qt学习_QGraphics进阶学习笔记_第4张图片

重写控制点的图元:

#include 
#include 

class ContrlPoint : public QGraphicsItem
{
public:
    ContrlPoint(QGraphicsItem* parent,QPointF pos);

    // QGraphicsItem interface
public:
    virtual QRectF boundingRect() const;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private:
    QPointF _pos;
};

ContrlPoint::ContrlPoint(QGraphicsItem *parent, QPointF pos)
    :QGraphicsItem(parent),_pos(pos)
{
    this->setPos(_pos);
    this->setFlags(QGraphicsItem::ItemIsMovable);
}

QRectF ContrlPoint::boundingRect() const{
    return QRectF(0, 0, 10, 10);
}

void ContrlPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
    //    if(!this->parentItem()->isSelected())return;
    painter->setPen(QColor(255,0,255));
    painter->setBrush(QBrush(Qt::blue));
    painter->drawRect(QRectF(0, 0, 10, 10));
}

重写线的图元:主要是添加一个控制点链表,然后根据图元与控制点的关系重写paint函数

#include 
#include "ContrlPoint.h"
#include 

class ContrlableLine : public QGraphicsLineItem
{
public:
    ContrlableLine(const QLineF &line, QGraphicsItem *parent = nullptr);
    // QGraphicsItem interface
public:
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

private:
    QList<ContrlPoint*>  _ctlPoints;
};

ContrlableLine::ContrlableLine(const QLineF &line, QGraphicsItem *parent)
{
    QGraphicsLineItem(line, parent);
	//添加控制点
    _ctlPoints.append(new ContrlPoint(this,line.p1()));
    _ctlPoints.append(new ContrlPoint(this,line.p2()));
}

void ContrlableLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    //线随着控制点变化
    //painter->drawLine(_ctlPoints[0]->scenePos(),_ctlPoints[1]->scenePos());
    //若要在中心点
    painter->drawLine(mapFromItem(_ctlPoints[0],_ctlPoints[0]->boundingRect().center()),
                      mapFromItem(_ctlPoints[1],_ctlPoints[1]->boundingRect().center()));
}

在连线中加入控制点成为四阶贝塞尔曲线

Qt学习_QGraphics进阶学习笔记_第5张图片

static void createNBezierCurve(const QVector<QPointF> &src, QVector<QPointF> &dest, qreal precision)
{
///
}

ConnectLine::ConnectLine(QGraphicsItem *startitem, QGraphicsItem *enditem)
{
    _startItem = startitem;
    _endItem = enditem;
    this->setFlags(QGraphicsLineItem::ItemIsSelectable | QGraphicsLineItem::ItemIsFocusable);

    //添加控制点
    _ctlPoints.append(new ContrlPoint(this,_startItem->pos()+QPointF(50,0)));
    _ctlPoints.append(new ContrlPoint(this,QPointF((_startItem->pos().x()+_endItem->pos().x())/2,
                                                   (_startItem->pos().y()+_endItem->pos().y())/2)));
    _ctlPoints.append(new ContrlPoint(this,_endItem->pos()-QPointF(50,0)));
}

QRectF ConnectLine::boundingRect() const
{
    return QRectF(_startItem->pos()-QPointF(10,10),_endItem->pos()+QPointF(10,10));
}

void ConnectLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
//   根据控制点绘制贝塞尔曲线

    QVector<QPointF> src;
    src.append(_startItem->pos());
    for(auto i:_ctlPoints)
    src.append(i->pos());
    src.append(_endItem->pos());

    QVector<QPointF> dest;
    createNBezierCurve(src,dest,0.01);
    dest.insert(0,_startItem->pos());
    dest.append(_endItem->pos());

    QPainterPath path;
    path.moveTo(_startItem->pos());
    for(auto i:dest)path.lineTo(i);
    QPen pen(QBrush(Qt::green),5);
    painter->setPen(pen);
    painter->drawPath(path);

    QPainterPath pathAssist;
    pathAssist.moveTo(_startItem->pos());
    for(auto i:src)pathAssist.lineTo(i);
    QPen penAssist(QBrush(Qt::yellow),3,Qt::DashLine);
    painter->setPen(penAssist);
    painter->drawPath(pathAssist);
}

17.给两个图元连线

需要判断线的端点处是否存在 item,首先需要在端点处清除线图元自身,然后再判断

Qt学习_QGraphics进阶学习笔记_第6张图片

void MyGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if(_line!=0 && _mode==LINEITEM){
        QLineF newline = _line->line();
        QList<QGraphicsItem *> startitems = items(_line->line().p1());
        if (startitems.count() && startitems.first() == _line)
            startitems.removeFirst();
        QList<QGraphicsItem *> enditems = items(_line->line().p2());
        if (enditems.count() && enditems.first() == _line)
            enditems.removeFirst();
        this->removeItem(_line);
        delete _line;

        if(startitems.count() > 0 && enditems.count() > 0 && startitems.first() != enditems.first()){
            ConnectLine* item = new ConnectLine(startitems.at(0),enditems.at(0));
            this->addItem(item);
        }else{
            QGraphicsLineItem* newlineitem = new QGraphicsLineItem(newline);
            this->addItem(newlineitem);
        }
        _line=0;
    }else{
        QGraphicsScene::mouseReleaseEvent(event);
    }
}

18.移动控制点设置旋转

Qt学习_QGraphics进阶学习笔记_第7张图片

头文件

#ifndef CONTRLPOINT_H
#define CONTRLPOINT_H

#include 

class ContrlPoint : public QGraphicsItem
{
public:
    //不同类型的控制点在移动时有不同的动作,对于不同的图元还需要判断类型执行相应操作
    enum PointTYPE{
        NORMAL,
        RESIZE,
        ROTATE,
        SPECIAL,
        LAST
    };

    ContrlPoint(QGraphicsItem* parent,QPointF pos,int type);

    // QGraphicsItem interface
public:
    virtual QRectF boundingRect() const;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);


private:
    QPointF _pos;
    int _type = LAST;

    // QGraphicsItem interface
protected:
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
};

#endif // CONTRLPOINT_H

源文件

#include "ContrlPoint.h"
#include 
#include 
#include 

ContrlPoint::ContrlPoint(QGraphicsItem *parent, QPointF pos,int type)
    :QGraphicsItem(parent),_pos(pos)
{
    this->setPos(_pos);
    this->setFlags(QGraphicsItem::ItemIsMovable|
                   QGraphicsItem::ItemIsSelectable|
                   QGraphicsItem::ItemIsFocusable);
    _type = type;
}

QRectF ContrlPoint::boundingRect() const{
    return QRectF(0, 0, 10, 10);
}

void ContrlPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
//    if(!this->parentItem()->isSelected())return;
    painter->setPen(QColor(255,0,255));
    painter->setBrush(QBrush(Qt::blue));
    painter->drawRect(QRectF(0, 0, 10, 10));
}

void ContrlPoint::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    switch (_type) {
    case NORMAL:{
        QGraphicsItem::mouseMoveEvent(event);
    }break;
    case ROTATE:{
        QGraphicsItem* item = this->parentItem();
        qreal nx = event->scenePos().x();
        qreal ny = event->scenePos().y();
        qreal ddx = nx - item->sceneBoundingRect().center().x();
        qreal ddy = ny - item->sceneBoundingRect().center().y();
        qreal angle = qAtan2(-ddy,-ddx)*180/3.14; //qAtan2()输出为reg圆周率数,不是度数
        item->setRotation(angle);
        //这里不添加QGraphicsItem::mouseMoveEvent(event);控制点就不会移动
    }break;
    default:{
       QGraphicsItem::mouseMoveEvent(event);
       break;
    }
    }

}

19.磁吸线

思路:画一个水平区域,检测出区域内即水平线上的各图元,将当前图元的“中点”和检测到的图元“中点”进行对比,若在范围内则将当前图元的y坐标重置,再将两个中点连线。垂直同上。注意对画的线要有合理的释放。
Qt学习_QGraphics进阶学习笔记_第8张图片
Qt学习_QGraphics进阶学习笔记_第9张图片
水平加垂直:

//使鼠标在图形的中心点
        _curItem->setPos(event->scenePos() - _curItem->boundingRect().center());
        if(_magLine) {delete _magLine; _magLine = 0;}
        if(_magVLine) {delete _magVLine; _magVLine = 0;}

        {//水平磁吸线
            //磁吸线判断区域
            //判断区域越小越好,最好所有在区域内的图元都在同一水平线上,这样相近的两个图元不会互相干扰
            QRectF magarea(QPointF(this->sceneRect().left(),event->scenePos().y()-0.5),QSizeF(this->width(),1));
            //判断区域内有无图元
            QList<QGraphicsItem*> areaitems = items(magarea);
            //去除掉自己,可增加去除掉控制点类型图元
            //  if(areaitems.contains(_curItem))areaitems.removeOne(_curItem);
            areaitems.removeFirst();//需要去除初始产生的 0,0 位置图元,有bug
            for(auto i:areaitems) if(i->type()==QGraphicsItem::UserType+11)areaitems.removeOne(i);

            //判断有无图元
            if(!areaitems.isEmpty()){
                //因为判断的图元基本都在一条水平线,所以只要对准第一个图元即可
                //            QGraphicsItem* i = areaitems.first();
                for(auto i:areaitems){

                    //根据鼠标y坐标和另一个图元判断,水平对齐
                    float y = event->scenePos().y();
                    float ny =  i->sceneBoundingRect().center().y();
                    if(_magLine) {delete _magLine; _magLine = 0;}
                    //判断在范围内则将图元吸附到水平线上,范围要足够大最好是整个图元大小,这样保证判断区域内的图元都在同一水平线
                    if( y > ny-25 && y < ny+25){
                        _curItem->setPos(QPointF(_curItem->sceneBoundingRect().center().x(),ny) - _curItem->boundingRect().center());
                        //画线
                        _magLine = new QGraphicsLineItem(QLineF(_curItem->sceneBoundingRect().center(),
                                                                i->sceneBoundingRect().center()));
                        _magLine->setPen(QPen(Qt::DashLine));
                        this->addItem(_magLine);
                    }
                }
            }
        }
        {//垂直磁吸线
            QRectF magarea(QPointF(event->scenePos().x()-0.5,this->sceneRect().top()),QSizeF(1,this->height()));
            QList<QGraphicsItem*> areaitems = items(magarea);
            areaitems.removeFirst();
            for(auto i:areaitems) if(i->type()==QGraphicsItem::UserType+11)areaitems.removeOne(i);
            if(!areaitems.isEmpty()){
                for(auto i:areaitems){
                    float x = event->scenePos().x();
                    float nx =  i->sceneBoundingRect().center().x();
                    if(_magVLine) {delete _magVLine; _magVLine = 0;}
                    if( x > nx-25 && x < nx+25){
                        _curItem->setPos(QPointF(nx,_curItem->sceneBoundingRect().center().y()) - _curItem->boundingRect().center());
                        _magVLine = new QGraphicsLineItem(QLineF(_curItem->sceneBoundingRect().center(),
                                                                i->sceneBoundingRect().center()));
                        _magVLine->setPen(QPen(Qt::DashLine));
                        this->addItem(_magVLine);
                    }
                }
            }
        }

20.保存scene

void Widget::saveToImage(const QString& imagePath)
{
    int width = scene->width();
    int height = scene->height();

    QPixmap pixSaveImage(width, height);
    pixSaveImage.fill(QColor(0, 0, 0, 0));//用透明色填充
    QPainter painterTanns(&pixSaveImage);
    painterTanns.setRenderHint(QPainter::Antialiasing, true);
    painterTanns.setRenderHint(QPainter::TextAntialiasing, true);
    painterTanns.setRenderHint(QPainter::SmoothPixmapTransform, true);
    scene->clearSelection();
    scene->render(&painterTanns);
    pixSaveImage.save(imagePath);
    return;
}

你可能感兴趣的:(QT学习,qt,c++)