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