QGraphicsView通过鼠标操作来绘制图元

在Visio和MindManager等类似的图元操作软件中,很多时候我们会通过鼠标操作来添加自定义的图元。这里就介绍一下如何在QT的图形视图框架中通过鼠标绘制来添加图元。
在实现的时候我们先添加一个自定义图元用来响应鼠标操作,自定义图元的实现如下所示.

绘制辅助图元

辅助图元类似于标尺线,用来标记用户鼠标框选的范围和位置。实现如下:

//canvasitembase.h
#ifndef _CANVASE_ITEM_BASE_
#define _CANVASE_ITEM_BASE_

#include 
#include 
#include 
#include 

class CanvaseItemBase : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    CanvaseItemBase(QGraphicsItem* parentItem = nullptr);

public:
    //修改图元尺寸
    void set_item_size(int width,int height);
    void get_item_size(int& width,int& height);

protected:
    //边界矩形
    QRectF boundingRect() const override;
    //绘制事件
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) final;
    //获取形状
    QPainterPath shape() const override;

    //鼠标事件
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;

    QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;

    //自定义元素绘制
    virtual void customPaint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    //获取自定义绘制所需要的矩形
    QRectF getCustomRect(void) const;


private:
    //图元的宽和高
    int m_rect_width = 0;
    int m_rect_height = 0;

    // 画笔设置
    QColor m_cPenColor;
    int m_nPenWidth = 1;
    // 画刷设置
    QColor m_cBrushColor;
};
#endif

//canvasitembase.cpp
#include "canvasitembase.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PI 3.14159265358979

CanvaseItemBase::CanvaseItemBase(QGraphicsItem* parentItem)
    :QGraphicsItem(parentItem)
    ,m_cPenColor(255, 0, 0)
    ,m_cBrushColor(200, 100, 100)
{
    //设置图元可以选中
    this->setFlag(QGraphicsItem::ItemIsSelectable, false);
    m_rect_width = 0;
    m_rect_height = 0;
}


void CanvaseItemBase::set_item_size(int width, int height)
{
    m_rect_width = width;
    m_rect_height = height;
}

void CanvaseItemBase::get_item_size(int &width, int &height)
{
    width = m_rect_width;
    height = m_rect_height;
}

QRectF CanvaseItemBase::boundingRect() const
{
    QRectF rectF = getCustomRect();
    return rectF;
}

void CanvaseItemBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setRenderHint(QPainter::Antialiasing, true);
    painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter->setRenderHint(QPainter::TextAntialiasing, true);

    // 自定义绘制
    customPaint(painter, option, widget);
}

QPainterPath CanvaseItemBase::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());

    return path;
}

void CanvaseItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    return QGraphicsItem::mousePressEvent(event);
}

void CanvaseItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    return QGraphicsItem::mouseMoveEvent(event);
}

void CanvaseItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    return QGraphicsItem::mouseReleaseEvent(event);
}

QVariant CanvaseItemBase::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    if (change == QGraphicsItem::ItemSelectedChange)
        prepareGeometryChange();

    return QGraphicsItem::itemChange(change, value);
}


void CanvaseItemBase::customPaint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPen pen;
    pen.setWidth(m_nPenWidth);
    pen.setColor(m_cPenColor);
    pen.setStyle(Qt::DashLine);
    pen.setWidth(2);
    painter->setPen(pen);

    QRectF itemRect = this->getCustomRect();
    QRectF outLintRect = itemRect;
    if((m_rect_width == 0) ||(m_rect_width == 0))
    {
        painter->setCompositionMode(QPainter::CompositionMode_Clear);
        painter->eraseRect(outLintRect);
        return;
    }
    else
    {
        painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
        painter->drawRect(outLintRect);
    }
}

QRectF CanvaseItemBase::getCustomRect(void) const
{
   return QRectF(0,0,m_rect_width,m_rect_height);
}

在视图中当鼠标框选范围发生变化的时候,自定义图元会通过绘制红色的虚线框来标记鼠标的框选范围。

自定义视图

通过重写QGraphicsView,我们来响应对应的鼠标键盘消息,从而实现对应的绘制操作,自定义视图类的实现如下所示:

//mygraphicsview.h
#ifndef _MY_GRAPHICS_VIEW_H_
#define _MY_GRAPHICS_VIEW_H_

#include 
#include "canvasitembase.h"
#include 
#include 
#include 


class MyGraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    explicit MyGraphicsView(QWidget *parent = nullptr);
    ~MyGraphicsView();

protected:
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void mouseReleaseEvent(QMouseEvent* event);
    void resizeEvent(QResizeEvent* event);

private:
    //绘制辅助图元
    CanvaseItemBase* m_custom_item;

    //主场景
    QGraphicsScene m_main_scene;
    //鼠标起始点和结束点
    QPoint m_start_point;
    QPoint m_end_point;
    //鼠标左键是否按下
    bool m_is_leftbtn_pressed = false;
};

#endif
//mygraphicsview.cpp
#include "mygraphicsview.h"

MyGraphicsView::MyGraphicsView(QWidget *parent) :QGraphicsView(parent)
{

    //屏蔽显示滚动条
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    //添加自定义
    m_custom_item  = new CanvaseItemBase();
    m_main_scene.addItem(m_custom_item);
    this->setScene(&m_main_scene);

    //跟踪鼠标移动
    this->setMouseTracking(true);

    int view_width = this->width();
    int view_height = this->height();
    this->setSceneRect(-0.5*view_width,-0.5*view_height,view_width,view_height);
}

MyGraphicsView::~MyGraphicsView()
{

}

void MyGraphicsView::mousePressEvent(QMouseEvent *event)
{
    QGraphicsView::mousePressEvent(event);
    if(event->button() == Qt::LeftButton)
    {
        m_start_point = m_end_point = event->pos();
        QList<QGraphicsItem*> items = this->items(m_end_point);
        //依据鼠标点击的控件位置设置元素的位置
        if(items.empty())
        {
            m_is_leftbtn_pressed = true;
            //将控件坐标映射到场景坐标
            QPointF view_point = this->mapToScene(m_start_point);
            m_custom_item->setPos(view_point);
            m_custom_item->set_item_size(0,0);
            m_custom_item->update();
            this->viewport()->update();
        }
    }
    //右键添加图元
    else if(event->button() == Qt::RightButton)
    {
        int width,height;
        m_custom_item->get_item_size(width,height);
        QPointF scene_pos = m_custom_item->scenePos();
        QGraphicsRectItem* rect_item = new QGraphicsRectItem(scene_pos.x(),scene_pos.y(),width,height);
        m_main_scene.addItem(rect_item);
    }
}

void MyGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    //鼠标移动过程中计算元素的宽高并更新显示
    if(m_is_leftbtn_pressed)
    {
        m_end_point = event->pos();
        int width = m_end_point.x() - m_start_point.x();
        int height = m_end_point.y() - m_start_point.y();
        m_custom_item->set_item_size(width,height);
        m_custom_item->update();
        this->viewport()->update();
    }
    this->update();
    QGraphicsView::mouseMoveEvent(event);
}

void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        if(m_start_point != m_end_point)
        {
            m_custom_item->update();
        }
        m_is_leftbtn_pressed = false;
    }
    QGraphicsView::mouseReleaseEvent(event);
}

void MyGraphicsView::resizeEvent(QResizeEvent *event)
{
    //将场景和控件完全重合,这样坐标就一一对应了
    int view_width = this->width();
    int view_height = this->height();
    this->setSceneRect(-0.5*view_width,-0.5*view_height,view_width,view_height);
}

添加了自定义视图之后,我们就可以通过鼠标操作来添加自定义图元了。鼠标在视图上左键框选一个范围,然后松开鼠标左键点击鼠标右键便可以添加一个对应范围内的图元元素了。

示例效果图

使用效果如下图所示:
QGraphicsView通过鼠标操作来绘制图元_第1张图片

你可能感兴趣的:(QT,qt,开发语言,c++,图形视图框架)