利用QPainter绘制常规图形,可以实现绘制矩形、线段、三角形、圆形、菱形、切换画笔样式等。 我们通过重写paintEvent()这个处理函数,帮助我们在画面上进行想要的绘画效果。如果我们想要实现拖动鼠标来进行绘制就需要再重写mouseReleaseEvent()、mouseMoveEvent()、mousePressEvent(),分别为鼠标释放、鼠标移动、鼠标按下这三个鼠标事件处理函数,这样就可以实现鼠标按下拖动到合适的位置再松开完成画图的功能。
将切换绘制图形的功能绑定在鼠标右键的菜单上,并使用connect来连接我们自己的槽函数实现选中效果的展示和对应功能。利用setContextMenuPolicy(Qt::CustomContextMenu)
switch (drawFlag) {
case 0:
foreach (auto &action, painter_menu->actions()) {
if(action == action1)
action->setChecked(true);
else
action->setChecked(false);
}
break;
case 1:
......
break;
case 2:
......
break;
case 3:
......
break;
case 4:
......
break;
default:
foreach (auto &action, painter_menu->actions()){
action->setChecked(false);
}
break;
}
painter_menu->exec(QCursor::pos());
}
觉得这个颜色、宽度、样式都不符合我们的预期,我们就可以使用画笔工具去进行自定义,设置完成会在页面上实时进行更新。
但有一点需要注意的是,如果我们使用QPainter中的drawLine()函数去绘制线段的话,我们是无法进行实时的绘制的,而在这里我们使用drawPolygon()函数去进行绘制线段,我们把线段定义成一个QPolygon类对象,这样就可以实现在拖动的过程中实时绘制了。而对QPolygon类对象的描述可以看看这篇博客QPolygon-优快云博客,我们绘制的三角形、菱形也是QPolygon类对象。
widget.cpp
#include "widget.h"
#include "ui_widget.h"
QPen pen;
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//用于设置Widget的背景
//this->setAutoFillBackground(true);
//QPalette palette;
//QT5 QPalette::Background;QT6 QPalette::Window
//palette.setColor(QPalette::Background, QColor(0x00,0xff,0x00,0x00));//背景透明,QColor(0x00,0xff,0x00,0x00);黑色:Qt::black
//this->setPalette(palette);
//初始化自定义右键菜单
painter_menu = new QMenu(this);
action1 = new QAction("矩 形");
action2 = new QAction("线 段");
action3 = new QAction("圆 形");
action4 = new QAction("三角形");
action5 = new QAction("棱 形");
action1->setIcon(QIcon(":/img/rect.png"));
action2->setIcon(QIcon(":/img/line.png"));
action3->setIcon(QIcon(":/img/circle.png"));
action4->setIcon(QIcon(":/img/traingle.png"));
action5->setIcon(QIcon(":/img/lengx.png"));
action1->setCheckable(true);
action2->setCheckable(true);
action3->setCheckable(true);
action4->setCheckable(true);
action5->setCheckable(true);
painter_menu->addAction(action1);
painter_menu->addAction(action2);
painter_menu->addAction(action3);
painter_menu->addAction(action4);
painter_menu->addAction(action5);
painter_menu->addSeparator();
clear = new QAction("清空");
clear->setIcon(QIcon(":/img/clear_all.png"));
painter_menu->addAction(clear);
confirm = new QAction("确认区域");
confirm->setIcon(QIcon(":/img/confirm.png"));
painter_menu->addAction(confirm);
painter_menu->addSeparator();
change_Pen = new QAction("画笔设置");
change_Pen->setIcon(QIcon(":/img/pen.png"));
painter_menu->addAction(change_Pen);
painter_menu_click(action2);
this->setContextMenuPolicy(Qt::CustomContextMenu);//设置自定义菜单,否则会右键创建菜单失败
connect(this,&QWidget::customContextMenuRequested,this,&Widget::painter_customContextMenuRequested);
connect(painter_menu,SIGNAL(triggered(QAction*)),this,SLOT(painter_menu_click(QAction*)));
connect(clear,&QAction::triggered,this,[=](){
clear_all();
list_point.clear();
});
connect(confirm,&QAction::triggered,this,[=](){
list_point = confirm_Region();
qDebug() << list_point;
});
connect(change_Pen,&QAction::triggered,this,[=](){
changepen->show();
});
QColor color(0xff,0,0);
pen.setColor(color);
pen.setWidth(2);
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::BevelJoin);
pen.setStyle(Qt::DotLine);
//setDashPattern会与style发生冲突,通常根据调用位置来生效其中一个,特殊情况Style设置为Qt::CustomDashLine
//pen.setDashPattern(QVector<qreal>() << 5 << 15 );//实线与虚线比例5:15,5表示5个像素
changepen = new changePen();
connect(changepen,&changePen::need_update,this,&Widget::goto_update);
}
Widget::~Widget()
{
delete changepen;
delete ui;
}
void Widget::painter_menu_click(QAction *ac){
if(ac == action1){
lastflag = drawFlag = 0;
}else if(ac == action2){
lastflag = drawFlag = 1;
}else if(ac == action3){
lastflag = drawFlag = 2;
}else if(ac == action4){
lastflag = drawFlag = 3;
}else if(ac == action5){
lastflag = drawFlag = 4;
}else if(ac == clear){
drawFlag = 999;
}
if(lastflag == 0)
this->setWindowTitle("正在绘制:" + action1->text());
if(lastflag == 1)
this->setWindowTitle("正在绘制:" + action2->text());
if(lastflag == 2)
this->setWindowTitle("正在绘制:" + action3->text());
if(lastflag == 3)
this->setWindowTitle("正在绘制:" + action4->text());
if(lastflag == 4)
this->setWindowTitle("正在绘制:" + action5->text());
this->update();
}
void Widget::painter_customContextMenuRequested()
{//设置右键菜单样式选中
switch (drawFlag) {
case 0:
foreach (auto &action, painter_menu->actions()) {
if(action == action1)
action->setChecked(true);
else
action->setChecked(false);
}
break;
case 1:
foreach (auto &action, painter_menu->actions()){
if(action == action2)
action->setChecked(true);
else
action->setChecked(false);
}
break;
case 2:
foreach (auto &action, painter_menu->actions()){
if(action == action3)
action->setChecked(true);
else
action->setChecked(false);
}
break;
case 3:
foreach (auto &action, painter_menu->actions()){
if(action == action4)
action->setChecked(true);
else
action->setChecked(false);
}
break;
case 4:
foreach (auto &action, painter_menu->actions()){
if(action == action5)
action->setChecked(true);
else
action->setChecked(false);
}
break;
default:
foreach (auto &action, painter_menu->actions()){
action->setChecked(false);
}
break;
}
painter_menu->exec(QCursor::pos());
}
void Widget::paintEvent(QPaintEvent *event)
{
// 创建画笔对象
QPainter painter(this);
painter.setPen(pen);
// 设置绘制对象抗锯齿
painter.setRenderHint(QPainter::Antialiasing);
// 绘制当前rect对象
switch (drawFlag) {
case 0:
painter.drawRect(m_tmpRect);
break;
case 1:
foreach (const auto &line, m_listLines) {
painter.drawPolygon(line);
}
painter.drawPolygon(m_tmpLine);
break;
case 2:
painter.drawEllipse(m_tmpCircle);
break;
case 3:
painter.drawPolygon(m_tmpTrgl);
break;
case 4:
painter.drawPolygon(m_tmpleng);
break;
case 999:
painter.eraseRect(this->rect());
drawFlag = lastflag;
break;
default:
break;
}
if(event)
return;
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if(!leftbtn_click)
return;
int width = 0,height = 0;
switch (drawFlag) {
case 0:
m_tmpRect = QRect(m_startPos, event->pos());
break;
case 1:
if(m_linePoint.count() >= 6)
return;
m_tmpLine.clear();
if(m_linePoint.count() > 1)
m_tmpLine << m_endPos << event->pos();
else
m_tmpLine << m_startPos << event->pos();
break;
case 2:
m_tmpCircle = QRect(m_startPos, event->pos());
break;
case 3:
m_tmpTrgl.clear();
m_tmpTrgl << m_startPos << QPoint(event->pos().x(),m_startPos.y()) << event->pos();
break;
case 4:
width = abs(m_startPos.x() - event->pos().x());
height = abs(m_startPos.y() - event->pos().y());
m_tmpleng.clear();
if((m_startPos.x() > event->pos().x()) && (m_startPos.y() > event->pos().y())){
m_tmpleng << QPoint(m_startPos.x()-width/2,m_startPos.y()) << QPoint(m_startPos.x(),m_startPos.y()-height/2)
<< QPoint(event->pos().x()+width/2,event->pos().y()) << QPoint(event->pos().x(),event->pos().y()+height/2);
}else if((m_startPos.x() > event->pos().x()) && (m_startPos.y() < event->pos().y())){
m_tmpleng << QPoint(m_startPos.x()-width/2,m_startPos.y()) << QPoint(m_startPos.x(),m_startPos.y()+height/2)
<< QPoint(event->pos().x()+width/2,event->pos().y()) << QPoint(event->pos().x(),event->pos().y()-height/2);
}else if((m_startPos.x() < event->pos().x()) && (m_startPos.y() > event->pos().y())){
m_tmpleng << QPoint(m_startPos.x()+width/2,m_startPos.y()) << QPoint(m_startPos.x(),m_startPos.y()-height/2)
<< QPoint(event->pos().x()-width/2,event->pos().y()) << QPoint(event->pos().x(),event->pos().y()+height/2);
}else if((m_startPos.x() < event->pos().x()) && (m_startPos.y() < event->pos().y())){
m_tmpleng << QPoint(m_startPos.x()+width/2,m_startPos.y()) << QPoint(m_startPos.x(),m_startPos.y()+height/2)
<< QPoint(event->pos().x()-width/2,event->pos().y()) << QPoint(event->pos().x(),event->pos().y()-height/2);
}
break;
default:
break;
}
// 更新界面
this->update(this->rect());
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if(!leftbtn_click)
return;
m_endPos = event->pos();
switch (drawFlag) {
case 0:
transPoint();
break;
case 1:
if(m_linePoint.count() >= 6)
return;
m_linePoint << m_endPos;
m_listLines << m_tmpLine;
m_tmpLine.clear();
if(m_linePoint.count() == 6){
m_tmpLine << m_linePoint[m_linePoint.count()-1] << m_linePoint[0];
m_listLines << m_tmpLine;
m_tmpLine.clear();
//qDebug() << m_linePoint;
//qDebug() << m_listLines;
}
break;
default:
break;
}
// 重绘界面
this->repaint(this->rect());
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if(event->button() != Qt::LeftButton){
leftbtn_click = 0;
return;
}else{
leftbtn_click = 1;
m_startPos = event->pos();
if(drawFlag == 1){
if(m_linePoint.count() >= 6)
return;
m_linePoint << m_startPos;
}
}
}
void Widget::clear_all(){
point_rect.left_down = QPoint();
point_rect.left_up = QPoint();
point_rect.right_down = QPoint();
point_rect.right_up = QPoint();
m_startPos = QPoint();
m_endPos = QPoint();
m_tmpRect = QRect();
m_tmpCircle = QRect();
m_tmpTrgl.clear();
m_tmpleng.clear();
m_tmpLine.clear();
m_listLines.clear();
m_linePoint.clear();
list_point.clear();
}
void Widget::transPoint(){
int x1 = 0,x2 = 0,y1 = 0,y2 = 0;
if(m_startPos == m_endPos)
return;
x1 = m_startPos.x();
y1 = m_startPos.y();
x2 = m_endPos.x();
y2 = m_endPos.y();
//qDebug() << x1 << x2 << y1 << y2;
if(x1 > this->width())
x1 = this->width();
if(x2 > this->width())
x2 = this->width();
if(y1 > this->height())
y1 = this->height();
if(y2 > this->height())
y2 = this->height();
if(x1 < 0)
x1 = 0;
if(x2 < 0)
x2 = 0;
if(y1 < 0)
y1 = 0;
if(y2 < 0)
y2 = 0;
if(x1 > x2){
x2 += x1;
x1 = x2 - x1;
x2 = x2 - x1;
}
if(y1 > y2){
y2 += y1;
y1 = y2 - y1;
y2 = y2 - y1;
}
//qDebug() << x1 << x2 << y1 << y2;
point_rect.left_down = QPoint(x1,y2);
point_rect.left_up = QPoint(x1,y1);
point_rect.right_down = QPoint(x2,y2);
point_rect.right_up = QPoint(x2,y1);
//qDebug()<< point_rect.left_up << point_rect.right_up << point_rect.left_down << point_rect.right_down;
}
void Widget::goto_update(){
this->update();
}
QList<QPoint> Widget::confirm_Region(){
QList<QPoint> polygon;
switch (drawFlag) {
case 0:
polygon << point_rect.left_up << point_rect.right_up << point_rect.left_down << point_rect.right_down;
break;
case 1:
if(m_linePoint.count() < 6){
QMessageBox::critical(this,tr("警告"),tr("所画线段过少\n无法组成一个四边形\n请补充完整再提交。"),QMessageBox::Ok);
}else{
polygon << m_listLines[0].point(0) << m_listLines[1].point(0) << m_listLines[2].point(0) << m_listLines[3].point(0);
}
break;
case 3:
polygon << m_tmpTrgl.point(0) << m_tmpTrgl.point(1) << m_tmpTrgl.point(2);
break;
case 4:
polygon << m_tmpleng.point(0) << m_tmpleng.point(1) << m_tmpleng.point(2) << m_tmpleng.point(3);
break;
default:
break;
}
return polygon;
}