QPainter偏移原点、旋转、缩放功能Demo【3】 原创

QPainter偏移原点、旋转、缩放功能Demo【3】

更多精彩内容
👉个人内容分类汇总 👈

1、概述

  • Qt版本:V5.12.5

  • 本文内容

    1. 使用QPainterPath设置绘制较复杂的图案;
    2. 演示绘图时偏移坐标轴原点、选择、缩放操作。
  • 整体效果

    在这里插入图片描述

2、关键代码

  • renderarea.h

    #ifndef RENDERAREA_H
    #define RENDERAREA_H
    
    #include <QWidget>
    
    // 绘图操作类型
    enum Operation {
        NoOperation,        // 没有进行操作
        Translate,          // 偏移绘图坐标原点
        Rotate,             // 以坐标原点为中心进行旋转
        Scale               // 将绘制图案按比例缩放
    };
    
    class RenderArea : public QWidget
    {
        Q_OBJECT
    public:
        explicit RenderArea(QWidget *parent = nullptr);
    
        void setShape(const QPainterPath& shape);
        void setOperations(const QList<Operation>& operations);
    
    protected:
        void paintEvent(QPaintEvent *event) override;
        void drawOutline(QPainter& painter);
        void transformPainter(QPainter& painter);
        void drawCoordinates(QPainter& painter);
    
    public slots:
    
    private:
        QList<Operation> m_operations;    // 保存所有的缩放、偏移、旋转操作
        QRect m_fontRectX;              // X轴字体矩形范围
        QRect m_fontRectY;              // Y轴字体矩形范围
        QPainterPath m_shape;           // 用于绘制图案的路径
    };
    
    #endif // RENDERAREA_H
    
    
  • renderarea.cpp

    #include "renderarea.h"
    #include <QDebug>
    #include <qpainter.h>
    
    RenderArea::RenderArea(QWidget *parent) : QWidget(parent)
    {
        this->setMinimumSize(200, 200);
    
        // 设置背景填充色
        this->setBackgroundRole(QPalette::Base);
        this->setAutoFillBackground(true);
    
        QFont newFont = this->font();
        newFont.setPixelSize(20);    // 设置字体大小
        this->setFont(newFont);      // 设置当前类使用的字体
    
        QFontMetrics fontMetrics(newFont);  // QFontMetrics 类提供字体度量信息。
        m_fontRectX = fontMetrics.boundingRect("X");    // 返回由 text 指定的字符串中字符的边界矩形
        m_fontRectY = fontMetrics.boundingRect("Y");
    }
    
    /**
     * @brief       设置用于绘制的图案路径
     * @param shape
     */
    void RenderArea::setShape(const QPainterPath &shape)
    {
        this->m_shape = shape;
        this->update();
    }
    
    /**
     * @brief            设置绘制图案的操作
     * @param operations
     */
    void RenderArea::setOperations(const QList<Operation> &operations)
    {
        this->m_operations = operations;
        this->update();
    }
    
    /**
     * @brief        重绘事件函数
     * @param event
     */
    void RenderArea::paintEvent(QPaintEvent *event)
    {
        QWidget::paintEvent(event);
        QPainter painter(this);        // 创建一个画家对象
        painter.setRenderHint(QPainter::Antialiasing);  // 设置抗锯齿
    
        painter.translate(60, 60);         // 坐标原点偏移到(60, 60)位置
        drawOutline(painter);              // 画一个虚线矩形框
    
        painter.save();                    // 保存当前绘制状态,save()后所有QPainter设置在restore()后将失效(比如画笔设置)
        transformPainter(painter);         // 设置偏移、缩放、旋转
        drawCoordinates(painter);          // 绘制坐标系
        painter.fillPath(m_shape, Qt::blue);
        painter.restore();
    }
    
    /**
     * @brief           画一个虚线矩形框
     * @param painter
     */
    void RenderArea::drawOutline(QPainter &painter)
    {
        painter.save();                   // 将QPainter状态保存,后面的设置不会产生影响
    
        painter.setPen(Qt::darkBlue);     // 设置画笔颜色
        painter.setPen(Qt::DashLine);     // 画笔样式为虚线
        painter.drawRect(0, 0, 100,100);  // 画虚线矩形
    
        painter.restore();
    }
    
    /**
     * @brief          在这个函数中设置画家对象的坐标原点偏移、旋转、缩放
     * @param painter
     */
    void RenderArea::transformPainter(QPainter &painter)
    {
        for(auto operation : m_operations)
        {
            switch (operation)
            {
            case Translate:
                painter.translate(50, 50);    // 坐标原点偏移 50 像素
                break;
            case Scale:
                painter.scale(0.75, 0.75);    // 缩小到原来的75%大小
                break;
            case Rotate:
                painter.rotate(60);           // 顺时针旋转60度
                break;
            default:break;
            }
        }
    }
    
    /**
     * @brief          绘制当前画图使用的坐标系
     * @param painter
     */
    void RenderArea::drawCoordinates(QPainter &painter)
    {
        painter.save();
    
        painter.setPen(Qt::red);
        painter.drawLine(0, 0, 50, 0);        // 绘制X轴直线
        painter.drawLine(45, -3, 50, 0);      // 画X轴箭头
        painter.drawLine(45, 3, 50, 0);
        painter.drawText(QRect(50, 0, m_fontRectX.width(), -m_fontRectX.height()), Qt::AlignCenter, "X");
    
        painter.drawLine(0, 0, 0, 50);        // 绘制Y轴
        painter.drawLine(3, 45, 0, 50);       // 画Y轴箭头
        painter.drawLine(-3, 45, 0, 50);
        painter.drawText(QRect(0, 50, -m_fontRectX.width(), m_fontRectX.height()), Qt::AlignCenter, "Y");
    
    
        painter.restore();
    }
    
    
  • widget.h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    
    QT_BEGIN_NAMESPACE
    class QComboBox;
    class RenderArea;
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    private:
        void initUI();
        void setupShapes();
    
    private slots:
        void shapeSelected(int index);
        void operationChanged(int index);
    
    private:
        enum {NumTransformendAreas = 3};    // 定义一个常量,等同于宏常量【枚举常量可设置私有】
    
        QComboBox* m_comShape = nullptr;    // 绘制图案选择
        QComboBox* m_comOperation[NumTransformendAreas];  // 包含多个QComboBox指针的指针数组
        QList<QPainterPath> m_shapes;       // 绘制图案
        RenderArea* m_originalRenderArea = nullptr;       // 原始渲染区域
        RenderArea* m_transformedRenderAreas[NumTransformendAreas];  // 可进行缩放、偏移、旋转的区域
    };
    #endif // WIDGET_H
    
    
  • widget.cpp

    #include "widget.h"
    #include "renderarea.h"
    
    #include <qcombobox.h>
    #include <qdebug.h>
    #include <qgridlayout.h>
    
    Widget::Widget(QWidget *parent): QWidget(parent)
    {
        this->setWindowTitle("QPainter偏移原点、旋转、缩放功能Demo");
        initUI();
        setupShapes();         // 设置形状
    }
    
    Widget::~Widget()
    {
    }
    
    void Widget::initUI()
    {
        m_originalRenderArea = new RenderArea(this);
        m_comShape = new QComboBox(this);
        m_comShape->addItem("卡车图案");
        m_comShape->addItem("时钟图案");
        m_comShape->addItem("房子图案");
        m_comShape->addItem("文字图案");
        // 这里可以用activated也可以用currentIndexChanged,由于这两个信号都有重载,所以需要加上QOverload
        connect(m_comShape, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Widget::shapeSelected);
    
        // 添加布局
        QGridLayout* layout = new QGridLayout(this);
        layout->addWidget(m_originalRenderArea, 0, 0);
        layout->addWidget(m_comShape, 1, 0);
    
        for(int i = 0; i < NumTransformendAreas; i++)
        {
            m_transformedRenderAreas[i] = new RenderArea(this);
            m_comOperation[i] = new QComboBox(this);
            m_comOperation[i]->addItem("默认状态");
            m_comOperation[i]->addItem("坐标原点偏移(50,50)");
            m_comOperation[i]->addItem("旋转60度");
            m_comOperation[i]->addItem("缩小75%");
            connect(m_comOperation[i], QOverload<int>::of(&QComboBox::currentIndexChanged), this, &Widget::operationChanged);
    
            // 添加布局
            layout->addWidget(m_transformedRenderAreas[i], 0, i + 1);
            layout->addWidget(m_comOperation[i], 1, i + 1);
        }
    }
    
    void Widget::setupShapes()
    {
        // 绘制卡车图案的路径
        QPainterPath truck;
    
        truck.setFillRule(Qt::WindingFill);   // 设置填充规则
        truck.moveTo(0.0, 87.0);              // 将绘制起点移动到给定点,隐式启动新的子路径并关闭前一个子路径。
        truck.lineTo(0.0, 60.0);              // 添加从当前位置到给定端点的直线。绘制线后,当前位置将更新为线的端点。
        truck.lineTo(10.0, 60.0);
        truck.lineTo(35.0, 35.0);
        truck.lineTo(100.0, 35.0);
        truck.lineTo(100.0, 87.0);
        truck.lineTo(0.0, 87.0);
    
        truck.moveTo(17.0, 60.0);
        truck.lineTo(55.0, 60.0);
        truck.lineTo(55.0, 40.0);
        truck.lineTo(37.0, 40.0);
        truck.lineTo(17.0, 60.0);
        truck.addEllipse(17.0, 75.0, 25.0, 25.0);  // 添加一个椭圆,椭圆由一条【顺时针曲线】组成,起点和终点为零度(3点钟位置)
        truck.addEllipse(63.0, 75.0, 25.0, 25.0);
    
        // 绘制时钟的路径
        QPainterPath clock;
        clock.addEllipse(-50.0, -50.0, 100.0, 100.0);
        clock.addEllipse(-48.0, -48.0, 96.0, 96.0);
        clock.moveTo(0.0, 0.0);
        clock.lineTo(-2.0, -2.0);
        clock.lineTo(0.0, -42.0);
        clock.lineTo(2.0, -2.0);
        clock.lineTo(0.0, 0.0);
        clock.moveTo(0.0, 0.0);
        clock.lineTo(2.732, -0.732);
        clock.lineTo(24.495, 14.142);
        clock.lineTo(0.732, 2.732);
        clock.lineTo(0.0, 0.0);
    
        // 绘制房子路径
        QPainterPath house;
        house.moveTo(-45.0, -20.0);
        house.lineTo(0.0, -45.0);
        house.lineTo(45.0, -20.0);
        house.lineTo(45.0, 45.0);
        house.lineTo(-45.0, 45.0);
        house.lineTo(-45.0, -20.0);
        house.addRect(15.0, 5.0, 20.0, 35.0);     // 添加一个矩形,矩形被添加为一组顺时针方向的直线。添加矩形后,绘制路径的当前位置位于矩形的【左上角】
        house.addRect(-35.0, -15.0, 25.0, 25.0);
    
        // 绘制文本路径
        QPainterPath text;
        QFont font;
        font.setPixelSize(50);   // 设置文字大小
        QRect fontRect = QFontMetrics(font).boundingRect("1a你好");  // 计算输入字符串在指定的font字体时所占的矩形大小
        text.addText(-QPointF(fontRect.center()), font, "1a你好");   // 添加需要绘制的字体
    
        // 将创建的绘画路径添加进列表
        m_shapes.append(truck);
        m_shapes.append(clock);
        m_shapes.append(house);
        m_shapes.append(text);
    
        shapeSelected(0);        // 显示第一个图案
    }
    
    /**
     * @brief       当下拉框选择图案后通过信号修改显示的图案路径
     * @param index
     */
    void Widget::shapeSelected(int index)
    {
        QPainterPath shape = m_shapes.at(index);
        m_originalRenderArea->setShape(shape);
    
        for(int i = 0; i < NumTransformendAreas; i++)
        {
            m_transformedRenderAreas[i]->setShape(shape);
        }
    }
    
    /**
     * @brief 设置绘制图案的原点偏移、旋转、缩放
     */
    void Widget::operationChanged(int index)
    {
        Q_UNUSED(index)
    
        QList<Operation> operations;
        for(int i = 0; i < NumTransformendAreas; i++)
        {
            operations.append(Operation(m_comOperation[i]->currentIndex()));
            m_transformedRenderAreas[i]->setOperations(operations);   // 第一个绘图operations长度为1,第二个operations为2,第三个operations长度为3
        }
    }
    
    
    

3、源代码

💡💡💡💡💡💡💡💡💡💡💡💡💡💡

posted @ 2022-08-17 15:38  mahuifa  阅读(0)  评论(0编辑  收藏  举报  来源