Qt 通过重写QWidget的paintEvent方法绘制一个象棋棋盘

头文件

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPainter>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void paintEvent(QPaintEvent *e)override;
private:
    Ui::Widget *ui;
    int m_cellLength{90};//单元格大小
    int m_W{840};
    int m_H{920};
    QPoint m_topLeft{60,55};//棋盘起始位置
};
#endif // WIDGET_H

源文件

#include "widget.h"
#include "ui_widget.h"
#include <QPainterPath>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setMinimumHeight(m_H);
    this->setMaximumHeight(m_H);
    this->setMinimumWidth(m_W);
    this->setMaximumWidth(m_W);
}

Widget::~Widget()
{
    delete ui;
}
//重写绘图事件,绘制棋盘
void Widget::paintEvent(QPaintEvent *event){
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::LosslessImageRendering);

    //设置画笔
    QPen pen;
    pen.setWidth(4);//线条宽度
    pen.setColor(Qt::black);//画笔颜色
    pen.setStyle(Qt::SolidLine);//线条样式
    pen.setCapStyle(Qt::FlatCap);//端点样式
    pen.setJoinStyle(Qt::BevelJoin);//连接样式
    painter.setPen(pen);

    //设置画刷
    QBrush boardBrush;
    boardBrush.setStyle(Qt::NoBrush);//画刷填充样式
    painter.setBrush(boardBrush);//设置画刷
    QRect boardRect(m_topLeft.x(),m_topLeft.y(),m_cellLength*8,m_cellLength*9);//{60,55,720,810}
    painter.drawRect(boardRect);//绘制最外边的矩形
    //绘制矩形内的8条横线
    for(int i=1;i<9;i++){
        QLine line(m_topLeft.x(),m_topLeft.y()+i*m_cellLength,m_topLeft.x()+m_cellLength*8,m_topLeft.y()+i*m_cellLength);
        painter.drawLine(line);
    }
    //绘制上半部分的7条竖线
    for(int i=1;i<8;i++){
        QLine line(m_topLeft.x()+i*m_cellLength,m_topLeft.y(),m_topLeft.x()+i*m_cellLength,m_topLeft.y()+4*m_cellLength);
        painter.drawLine(line);
    }
    //绘制下半部分的7条竖线
    for(int i=1;i<8;i++){
        QLine line(m_topLeft.x()+i*m_cellLength,m_topLeft.y()+5*m_cellLength,m_topLeft.x()+i*m_cellLength,m_topLeft.y()+9*m_cellLength);
        painter.drawLine(line);
    }
    //绘制4条斜线
    QLine line(m_topLeft.x()+3*m_cellLength,m_topLeft.y(),m_topLeft.x()+5*m_cellLength,m_topLeft.y()+2*m_cellLength);
    painter.drawLine(line);
    line.setLine(m_topLeft.x()+3*m_cellLength,m_topLeft.y()+7*m_cellLength,m_topLeft.x()+5*m_cellLength,m_topLeft.y()+9*m_cellLength);
    painter.drawLine(line);
    line.setLine(m_topLeft.x()+3*m_cellLength,m_topLeft.y()+2*m_cellLength,m_topLeft.x()+5*m_cellLength,m_topLeft.y());
    painter.drawLine(line);
    line.setLine(m_topLeft.x()+3*m_cellLength,m_topLeft.y()+9*m_cellLength,m_topLeft.x()+5*m_cellLength,m_topLeft.y()+7*m_cellLength);
    painter.drawLine(line);
    //楚河汉界
    QFont font;
    font.setPointSize(50);//设置字体大小
    painter.setFont(font);
    painter.drawText(QRect(m_topLeft.x(),m_topLeft.y()+4*m_cellLength,m_topLeft.x()+8*m_cellLength,m_cellLength),
                     tr("\t\t\t\t\t\t\t楚\t河\t\t\t\t\t\t\t汉\t界"));//第一个参数为矩形,第二个参数为矩形内的文字
    //绘制炮跟卒位置的角
    int angleLength=15;//边长
    int angleSpace=5;//间隔
    //初始位置,与左上角重合
    QPoint points[]{QPoint(m_topLeft.x()+angleLength,m_topLeft.y()),
                    QPoint(m_topLeft.x(),m_topLeft.y()),
                    QPoint(m_topLeft.x(),m_topLeft.y()+angleLength)};
    QPainterPath anglePath;
    anglePath.moveTo(points[0]);
    anglePath.lineTo(points[1]);
    anglePath.lineTo(points[2]);

    //右下三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.translate(angleSpace,3*m_cellLength+angleSpace);
    painter.save();//保存位置
    for(int i=0;i<4;i++){
        painter.drawPath(anglePath);//绘制
        painter.translate(0,3*m_cellLength);//向下移动3个单元格
        painter.drawPath(anglePath);//绘制
        painter.translate(0,-3*m_cellLength);
        painter.translate(2*m_cellLength,0);//向右移动2个单元格
    }
    //右上三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.restore();//恢复位置
    painter.translate(angleSpace,m_cellLength+angleLength);
    for(int i=0;i<4;i++){
        painter.rotate(-90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(90);//恢复原来的角度
        painter.translate(0,3*m_cellLength);//向下移动3个单元格
        painter.rotate(-90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(90);//恢复原来的角度
        painter.translate(0,-3*m_cellLength);
        painter.translate(2*m_cellLength,0);//向右移动2个单元格
    }
    //左上三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.translate(-5*m_cellLength+3*angleSpace,-angleLength+2*angleSpace);
    for(int i=0;i<4;i++){
        painter.rotate(-180);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(180);//恢复原来的角度
        painter.translate(0,3*m_cellLength);//向下移动3个单元格
        painter.rotate(-180);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(180);//恢复原来的角度
        painter.translate(0,-3*m_cellLength);
        painter.translate(2*m_cellLength,0);//向右移动2个单元格
    }
    //左下三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.translate(-8*m_cellLength-angleLength+2*angleSpace,-m_cellLength-3*angleSpace);
    for(int i=0;i<4;i++){
        painter.rotate(90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(-90);//恢复原来的角度
        painter.translate(0,3*m_cellLength);//向下移动3个单元格
        painter.rotate(90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(-90);//恢复原来的角度
        painter.translate(0,-3*m_cellLength);
        painter.translate(2*m_cellLength,0);//向右移动2个单元格
    }
    //炮右下三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.translate(-10*m_cellLength-angleLength,-m_cellLength+angleSpace);
    painter.save();//保存位置
    for(int i=0;i<2;i++){
        painter.drawPath(anglePath);//绘制
        painter.translate(0,5*m_cellLength);//向下移动5个单元格
        painter.drawPath(anglePath);//绘制
        painter.translate(0,-5*m_cellLength);
        painter.translate(6*m_cellLength,0);//向右移动6个单元格
    }
    //炮右上三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.restore();//恢复位置
    painter.translate(angleSpace,m_cellLength+angleLength);
    for(int i=0;i<2;i++){
        painter.rotate(-90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(90);//恢复原来的角度
        painter.translate(0,5*m_cellLength);//向下移动5个单元格
        painter.rotate(-90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(90);//恢复原来的角度
        painter.translate(0,-5*m_cellLength);
        painter.translate(6*m_cellLength,0);//向右移动6个单元格
    }
    //炮左上三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.translate(-11*m_cellLength+3*angleSpace,-angleLength+2*angleSpace);
    for(int i=0;i<2;i++){
        painter.rotate(-180);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(180);//恢复原来的角度
        painter.translate(0,5*m_cellLength);//向下移动5个单元格
        painter.rotate(-180);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(180);//恢复原来的角度
        painter.translate(0,-5*m_cellLength);
        painter.translate(6*m_cellLength,0);//向右移动6个单元格
    }
    //炮左下三角,先绘制上半棋盘的部分,再绘制下半棋盘的部分
    painter.translate(-12*m_cellLength-angleLength+2*angleSpace,-m_cellLength-3*angleSpace);
    for(int i=0;i<2;i++){
        painter.rotate(90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(-90);//恢复原来的角度
        painter.translate(0,5*m_cellLength);//向下移动5个单元格
        painter.rotate(90);//逆时针旋转90度
        painter.drawPath(anglePath);//绘制
        painter.rotate(-90);//恢复原来的角度
        painter.translate(0,-5*m_cellLength);
        painter.translate(6*m_cellLength,0);//向右移动6个单元格
    }
}

效果图

在这里插入图片描述

### 重写 `paintEvent` 方法绘制曲线 在 Qt 中,为了实现在窗口部件上绘制自定义图形(如曲线),通常的做法是在继承自 `QWidget` 的类中重写 `paintEvent` 函数。此函数会在窗口需要更新显示内容时由框架自动调用[^2]。 下面是一个简单的例子展示如何利用贝塞尔曲线来创建平滑路径并将其渲染到屏幕上: ```cpp #include <QPainter> #include <QWidget> class CurveWidget : public QWidget { protected: void paintEvent(QPaintEvent *) override; }; void CurveWidget::paintEvent(QPaintEvent *) { QPainter painter(this); // 设置抗锯齿选项使线条更光滑 painter.setRenderHint(QPainter::Antialiasing); QPen pen(Qt::blue, 3); painter.setPen(pen); // 创建控制点列表用于构建二次或三次贝塞尔曲线 QVector<QPointF> points = { QPointF(50, 100), QPointF(150, 50), QPointF(250, 180), QPointF(350, 90) }; // 使用 QPainterPath 来描述复杂的形状 QPainterPath path(points.first()); for (int i=1; i<points.size()-2;i+=2){ qreal dx1=(points[i].x()-points[i-1].x())*0.4f; qreal dy1=(points[i].y()-points[i-1].y())*0.4f; qreal dx2=(points[i+1].x()-points[i].x())*0.4f; qreal dy2=(points[i+1].y()-points[i].y())*0.4f; path.cubicTo( points[i-1]+QPointF(dx1,dy1), points[i]-QPointF(-dx2,-dy2), points[i]); if(i==points.size()-3){ path.quadTo((points.last()+points.at(i))/2, points.last()); } } painter.drawPath(path); } ``` 上述代码片段展示了如何使用 `QPainter`, `QVector<QPointF>` 和 `QPainterPath` 结合起来生成一条经过给定点集的连续曲线。这里采用的是分段三阶贝塞尔曲线连接相邻两点的方式,在最后一个区间则应用二阶贝塞尔曲线以确保终点处不会出现突兀的变化[^1]。 #### 关键技术要点说明: - **抗锯齿**:通过设置 `setRenderHint(QPainter::Antialiasing)` 可让布上的几何对象边缘更加柔和自然。 - **颜色与宽度定制化**:借助于 `QPen` 对象可以方便地调整所绘线条的颜色以及粗细程度。 - **复杂路径表示法**:运用 `QPainterPath` 能够轻松表达任意形态的闭合或多边形轮廓,并支持多种类型的弧度转换操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平面海螺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值