当前位置: 首页 > article >正文

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();
}


http://www.kler.cn/news/148026.html

相关文章:

  • ORACLE 多实例监听的配置
  • 接收网络包的过程——从硬件网卡解析到IP层
  • jar包不挂断地运行命令
  • 【电路笔记】-电源电压
  • 时间复杂度和运算
  • lack——主页前后端开发优化(精华:java多线程实现数据插入)
  • 【docker】docker的基础命令
  • 消失的数字,旋转数组(leetcode 一题多解)
  • 力扣 hot100 最小覆盖子串 滑动窗口 字符计数
  • 【沁恒蓝牙mesh】CH58x 将RTC时钟切换为LSE外部低速时钟
  • 中年人怎么发展?持续发展?
  • 牛客 算法 HJ103 Redraiment的走法 golang语言实现
  • 【brpc学习实践九】mbvar及bvar可观测
  • Web语言基础课程期末代做
  • 【git】pip install git+https://github.com/xxx/xxx替换成本地下载编译安装解决网络超时问题
  • 【开源】基于Vue和SpringBoot的企业项目合同信息系统
  • 【实验】配置用户通过IPv6方式上网
  • uni-app图片上传
  • idea打开.class文件没有反编译
  • “SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展应用
  • Redis 面试题——持久化
  • Leetcode 2939. Maximum Xor Product
  • 问答知识库快速构建技术解析及行业实践
  • springsecurity6配置三
  • [Java][单列集合+数组遍历方法]通过Lambda表达式简化匿名内部类遍历数组学习体会
  • Python常见基础数据结构
  • 门店管理系统小程序作用是什么
  • 【git】工作中常用的命令
  • linux复习笔记05(小滴课堂)
  • FPGA模块——AD高速转换模块(并行输出转换的数据)