Qt实现绘制自定义形状

先创建一个继承自QWidget的控件:

class MyPainterWidget:public QWidget

重写各种鼠标方法:

protected:
	void paintEvent(QPaintEvent *) override;
	void mousePressEvent(QMouseEvent *e) override;       //按下
	void mouseMoveEvent(QMouseEvent *e) override;        //移动
	void mouseReleaseEvent(QMouseEvent *e) override;     //松开
	void mouseDoubleClickEvent(QMouseEvent *event) override;

在构造函数中声明跟踪鼠标:

#include <QMouseEvent>
setMouseTracking(true);

定义两个标志位作为开始绘制和鼠标移动的标志:

bool m_bStartDraw = false;    //是否已经开始左键点击,同时标识是否开始进行绘制
    bool bMove = false;           //是否处于绘制时的鼠标移动状态

创建一个点集记录每次按下的坐标点:

QVector<QPointF> pointList;

在程序一开始的时候先清除点集:

MyPainterWidget::MyPainterWidget(QWidget *parent) : QWidget(parent)
{
	setMouseTracking(true);
	pointList.clear();
}

按下鼠标时将开始绘制标志位设为true:

// 按下
void MyPainterWidget::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        if(!m_bStartDraw)
        {
            pointList.clear();
            m_bStartDraw = true;
        }
    }   
}

创建一个点用于获取鼠标移动时的当前位置:

QPointF movePoint;

重写移动事件:

// 移动
void MyPainterWidget::mouseMoveEvent(QMouseEvent *e)
{
    if(m_bStartDraw)
    {
        movePoint = e->pos();
        
        this->update();//重新进入painEvent事件进行绘制

        // 先刷新再设为true, 防止第一点和(0,0)连在一块
        bMove = true;
    }
}

重写释放事件:

// 松开
void MyPainterWidget::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        if (m_bStartDraw)
        {
            // 鼠标松开后的点需要添加到路径中
            pointList.push_back(QPointF(e->x(), e->y()));
            bMove = false;
            this->update();//刷新绘制
        }
    }
}

写一个表示结束绘制的功能函数:

void MyPainterWidget::endDraw()
{
    m_bStartDraw = false;

    //需要把第一个点连起来
    pointList.push_back(pointList[0]);
    this->update();
}

实现清除所有已绘的功能函数:

void MyPainterWidget::clearPath()
{
    pointList.clear();
    this->update();
}

主窗口:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMenu>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

protected:
    // 右键菜单需要重写的类
    void contextMenuEvent(QContextMenuEvent* event) override;

private:
    Ui::MainWindow *ui;

    QMenu* m_pMenu;
};
#endif // MAINWINDOW_H
/*

使用方式

左键点击,移动鼠标开始绘制,双击左键结束绘制,或者右键点击结束绘制

*/

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenu>
#include <QAction>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

	m_pMenu = new QMenu(this);

	QAction* pAc1 = new QAction(QString::fromLocal8Bit("结束绘制"), this);
	pAc1->setShortcut(QKeySequence("Ctrl+E"));
	QAction* pAc2 = new QAction(QString::fromLocal8Bit("清除"), this);
	pAc2->setShortcut(QKeySequence("Ctrl+D"));

	// 这是个假动作,为了让菜单消失,且不影响绘制路径
	QAction* pAc3 = new QAction(QString::fromLocal8Bit("退出菜单"), this);

	m_pMenu->addAction(pAc1);
	m_pMenu->addAction(pAc2);
	m_pMenu->addAction(pAc3);

	m_pMenu->setStyleSheet("QMenu{font:18px;}");

	connect(pAc1, &QAction::triggered, [=] {
		ui->graphicsPainter->endDraw();
		});

	connect(pAc2, &QAction::triggered, [=] {
		ui->graphicsPainter->clearPath();
		});
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 右键菜单
void MainWindow::contextMenuEvent(QContextMenuEvent* event)
{
	m_pMenu->move(cursor().pos());
	m_pMenu->show();
}

完整代码:

#ifndef GRAPHICSPAINTER_H
#define GRAPHICSPAINTER_H

#include <QWidget>

class MyPainterWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyPainterWidget(QWidget *parent = nullptr);

    void endDraw();
    void clearPath();

protected:
    void paintEvent(QPaintEvent *) override;
    void mousePressEvent(QMouseEvent *e) override;       //按下
    void mouseMoveEvent(QMouseEvent *e) override;        //移动
    void mouseReleaseEvent(QMouseEvent *e) override;     //松开
    void mouseDoubleClickEvent(QMouseEvent *event) override;     //双击

    bool m_bStartDraw = false;    //是否已经开始左键点击,同时标识是否开始进行绘制
    bool bMove = false;           //是否处于绘制时的鼠标移动状态

    QVector<QPointF> pointList;
    QPointF movePoint;
};

#endif // GRAPHICSPAINTER_H
#include "MyPainterWidget.h"
#include <QPainter>
#include <QMouseEvent>

MyPainterWidget::MyPainterWidget(QWidget *parent) : QWidget(parent)
{
    setMouseTracking(true);

    pointList.clear();
}

void MyPainterWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setPen(QColor(255,0,0));

    QVector<QLineF> lines;

    for(int i = 0; i < pointList.size()-1; i++)
    {
        QLineF line(QPointF(pointList[i].x(), pointList[i].y()), 
            QPointF(pointList[i+1].x(), pointList[i+1].y()));

        lines.push_back(line);
    }

    if (m_bStartDraw)
    {
        int size = pointList.size();

        if (bMove && size > 0)
        {
            QLineF line(QPointF(pointList[pointList.size() - 1].x(), pointList[pointList.size() - 1].y()),
                movePoint);

            lines.push_back(line);
        }
    }

    painter.drawLines(lines);
}

// 按下
void MyPainterWidget::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        if(!m_bStartDraw)
        {
            pointList.clear();
            m_bStartDraw = true;
        }
    }   
}

// 移动
void MyPainterWidget::mouseMoveEvent(QMouseEvent *e)
{
    if(m_bStartDraw)
    {
        movePoint = e->pos();
        
        this->update();//重新进入painEvent事件进行绘制

        // 先刷新再设为true, 防止第一点和(0,0)连在一块
        bMove = true;
    }
}

// 松开
void MyPainterWidget::mouseReleaseEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        if (m_bStartDraw)
        {
            // 鼠标松开后的点需要添加到路径中
            pointList.push_back(QPointF(e->x(), e->y()));
            bMove = false;
            this->update();//刷新绘制
        }
    }
}

// 双击结束绘制
void MyPainterWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    endDraw();
}

void MyPainterWidget::endDraw()
{
    m_bStartDraw = false;

    //需要把第一个点连起来
    pointList.push_back(pointList[0]);
    this->update();
}

void MyPainterWidget::clearPath()
{
    pointList.clear();
    this->update();
}

  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值