欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt绘制云盘系列1

程序员文章站 2022-05-29 16:23:03
...

先抛出一个问题:QImage: out of memory, returning null image,下方错误示范效果图报出此错误,希望各位大佬们帮忙解决一下,在此先谢谢了!
工程链接-百度云链接:https://pan.baidu.com/s/1nccUlTPgZQc5QeP2bzczcw
提取码:akvt
错误示范效果图:
Qt绘制云盘系列1
实际上应该是下方这个效果,但是由于QImage: out of memory, returning null image错误,无法绘制出来!
Qt绘制云盘系列1
头文件

#ifndef QWHARCDISC_H
#define QWHARCDISC_H

/*
 * 云盘系列控件1
 * 该控件支持两种圆弧种类:半圆弧、整圆弧
 * 该控件支持两种按钮样式:矩形、圆形
 * 该控件支持鼠标三态效果:悬浮、按下、离开
 * 其余颜色等效果设置与获取请自行添加函数即可
 */

#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QEnterEvent>

class QWHArcDisc : public QWidget
{
    Q_OBJECT
public:
    //圆弧种类
    enum ArcDiscType
    {
        SemiCircle, //半圆,圆弧180度
        Circle,     //正圆,圆弧360度
    };
    //按钮样式
    enum BtnStyle
    {
        BtnStyle_Rect,      //矩形
        BtnStyle_Circle,    //圆形
    };

    //鼠标状态
    enum mouseState
    {
        mouseState_Hover,   //鼠标悬浮
        mouseState_Pressed, //鼠标按下
        mouseState_Leave,   //鼠标离开
    };
    //数据结构
    struct ArcDiscData
    {
        QPixmap pixmap;     //存储图片
        QRect rect;         //存储图片区域
        mouseState state;   //鼠标状态
    };

    explicit QWHArcDisc(QWidget *parent = nullptr);
    ~QWHArcDisc();

public:
    //设置图片
    void setPixmaps(QVector<QPixmap> pixmaps);
    //设置圆弧种类
    void setArcDiscType(ArcDiscType type);
    //设置按钮样式
    void setBtnStyle(BtnStyle style);

protected:
    void paintEvent(QPaintEvent *);
    //绘制半圆弧背景
    void drawSemiArc(QPainter *painter);
    //绘制半圆弧图片
    void drawSemiPixmaps(QPainter *painter);
    //绘制圆弧背景
    void drawArc(QPainter *painter);
    //绘制圆弧图片
    void drawPixmaps(QPainter *painter);

    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void resizeEvent(QResizeEvent *event);
    void leaveEvent(QEvent *);

private:
    //设置图片区域
    void setRects();
    //绘制图片
    void drawPixmap(QPainter *painter, const QPixmap &pixmap, int index, QRect rect);

signals:
    //发送按钮对应的序号:半圆弧形-序号0从0度(西)开始,整圆弧形-序号0从90度(北)开始
    void clicked(int index);

public slots:

private:
    QColor m_bgArcColor;                //圆弧背景颜色
    QColor m_borderColor;               //圆弧边框颜色
    QColor m_picBorderColor;            //图片边框颜色

    ArcDiscType m_arcDiscType;          //圆弧种类
    BtnStyle m_btnStyle;                //按钮样式
    QVector<ArcDiscData> m_arcDiscData; //存储信息
    QVector<QPixmap> m_resizePixmaps;   //保存当前窗口大小下的图片尺寸,避免在paintEvent中pixmap.scale()函数频繁缩放图片大小造成的时间浪费
    int m_curPressedIndex;              //保存当前按下的按钮索引
};

#endif // QWHARCDISC_H

cpp文件

#include "qwharcdisc.h"
#include <QtMath>
#include <QDebug>

const float offsetAngle = 10;       //两边角度偏移量
const int sideLength = 20;          //图标正方形边长
const int selectSideLength = 40;    //选中图标正方形边长
const int radius = 80;              //图片中心距离圆弧中心点距离

QWHArcDisc::QWHArcDisc(QWidget *parent) : QWidget(parent)
{
    m_bgArcColor = QColor(30, 30, 30);  //圆弧背景颜色
    m_borderColor = QColor(Qt::blue);   //圆弧边框颜色
    m_picBorderColor = Qt::white;       //图片边框颜色
    m_arcDiscType = SemiCircle;         //圆弧种类
    m_btnStyle = BtnStyle_Circle;       //按钮样式
    m_curPressedIndex = -1;             //保存当前按下的按钮索引
    this->setMouseTracking(true);
}

QWHArcDisc::~QWHArcDisc()
{

}

void QWHArcDisc::setPixmaps(QVector<QPixmap> pixmaps)
{
    m_arcDiscData.clear();
    m_curPressedIndex = -1;
    for (int i = 0; i < pixmaps.count(); i++)
    {
        ArcDiscData data;
        data.pixmap = pixmaps[i];
        data.state = mouseState_Leave;
        m_arcDiscData.append(data);
    }
    //重新设置pixmap大小
    m_resizePixmaps.clear();
    QPixmap pixmap;
    for (int i = 0; i < m_arcDiscData.count(); i++)
    {
        if (m_arcDiscType == SemiCircle)
            pixmap = m_arcDiscData[i].pixmap.scaled(sideLength * 200, sideLength * 100, Qt::IgnoreAspectRatio);
        else
            pixmap = m_arcDiscData[i].pixmap.scaled(sideLength * 200, sideLength * 200, Qt::IgnoreAspectRatio);
        m_resizePixmaps.append(pixmap);
    }
    update();
}

void QWHArcDisc::setArcDiscType(QWHArcDisc::ArcDiscType type)
{
    m_arcDiscType = type;
    update();
}

void QWHArcDisc::setBtnStyle(QWHArcDisc::BtnStyle style)
{
   m_btnStyle = style;
   update();
}

void QWHArcDisc::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    int width = this->width();
    int height = this->height();
    if (m_arcDiscType == SemiCircle)
    {
        painter.translate(width / 2.0, height);
        painter.scale(width / 200.0, height / 100.0);
        drawSemiArc(&painter);
        drawSemiPixmaps(&painter);
    }
    else if (m_arcDiscType == Circle)
    {
        painter.translate(width / 2, height / 2.0);
        painter.scale(width / 200.0, height / 200.0);
        drawArc(&painter);
        drawPixmaps(&painter);
    }
}

void QWHArcDisc::drawSemiArc(QPainter *painter)
{
    painter->save();

    QRect outRect = QRect(-99, -99, 198, 198);
    QRect inRect = QRect(-60, -60, 120, 120);
    QPainterPath path;
    path.moveTo(60, 0);
    path.lineTo(100, 0);
    path.arcTo(outRect, 0, 180);
    path.lineTo(-100, 0);
    path.lineTo(-60, 0);
    path.arcTo(inRect, 180, -180);

    painter->setPen(QPen(m_borderColor, 1));
    painter->setBrush(m_bgArcColor);
    painter->drawPath(path);

    painter->restore();
}

void QWHArcDisc::drawSemiPixmaps(QPainter *painter)
{
    painter->save();

    painter->setPen(QPen(m_picBorderColor, 2));
    int count = m_arcDiscData.count();
    if (count % 2 == 0)
    {
        float perAngle = (180 - 2 * offsetAngle) / (count - 1);
        for (int i = 0; i < count; i++)
        {
            int x = radius * qCos(qDegreesToRadians(i * perAngle + offsetAngle));
            int y = -radius * qSin(qDegreesToRadians(i * perAngle + offsetAngle));
            QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
            const QPixmap &pixmap = m_resizePixmaps[i];
            drawPixmap(painter, pixmap, i, rect);
        }
    }
    else
    {
        //绘制中间图片
        int midIndex = count / 2;
        const QPixmap &pixmap = m_resizePixmaps[midIndex];
        QRect rect = QRect(-sideLength / 2, -radius - sideLength / 2, sideLength, sideLength);
        drawPixmap(painter, pixmap, midIndex, rect);
        //绘制offsetAngle-90度之间的图片
        float perAngle = (90 - offsetAngle) / midIndex;
        for (int i = midIndex - 1; i >= 0; i--)
        {
            int x = radius * qCos(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
            int y = -radius * qSin(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
            QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
            const QPixmap &pixmap = m_resizePixmaps[i];
            drawPixmap(painter, pixmap, i, rect);
        }
        //绘制90度-(180-offsetAngle)之间的图片
        for (int i = midIndex + 1; i < count; i++)
        {
            int x = radius * qCos(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
            int y = -radius * qSin(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
            QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
            const QPixmap &pixmap = m_resizePixmaps[i];
            drawPixmap(painter, pixmap, i, rect);
        }
    }
    //绘制选中图片
    if (m_curPressedIndex != -1)
    {
        QRect rect = QRect(-selectSideLength / 2, -selectSideLength, selectSideLength, selectSideLength);
        QPainterPath path;
        path.addEllipse(rect);
        painter->setClipPath(path);
        QPixmap pixmap = m_resizePixmaps[m_curPressedIndex];
        pixmap = pixmap.scaled(pixmap.width() * 2, pixmap.height() * 2);
        painter->drawPixmap(rect, pixmap);
    }

    painter->restore();
}

void QWHArcDisc::drawArc(QPainter *painter)
{
    painter->save();

    QPainterPath path1, path2, path;
    path1.addEllipse(QRect(-99, -99, 198, 198));
    path2.addEllipse(QRect(-60, -60, 120, 120));
    path = path1 - path2;

    painter->setPen(QPen(m_borderColor, 1));
    painter->setBrush(m_bgArcColor);
    painter->drawPath(path);

    painter->restore();
}

void QWHArcDisc::drawPixmaps(QPainter *painter)
{
    painter->save();

    painter->setPen(QPen(m_picBorderColor, 2));
    int count = m_arcDiscData.count();
    float perAngle = 360 / count;
    for (int i = 0; i < count; i++)
    {
        //+90度是为了使得第一个图标绘制在正上方(奇数个对齐)
        int x = radius * qCos(qDegreesToRadians(i * perAngle + 90));
        int y = -radius * qSin(qDegreesToRadians(i * perAngle + 90));
        QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
        QPixmap pixmap = m_resizePixmaps[i];
        drawPixmap(painter, pixmap, i, rect);
    }
    //绘制选中图片
    if (m_curPressedIndex != -1)
    {
        QRect rect = QRect(-selectSideLength * 0.75, -selectSideLength * 0.75, selectSideLength * 1.5, selectSideLength * 1.5);
        QPainterPath path;
        path.addEllipse(rect);
        painter->setClipPath(path);
        QPixmap pixmap = m_resizePixmaps[m_curPressedIndex];
        pixmap = pixmap.scaled(pixmap.width() * 2, pixmap.height() * 2);
        painter->drawPixmap(rect, pixmap);
    }

    painter->restore();
}

void QWHArcDisc::mouseMoveEvent(QMouseEvent *event)
{
    for (int i = 0; i < m_arcDiscData.count(); i++)
    {
        if (i == m_curPressedIndex)
            continue;
        else if (m_arcDiscData[i].rect.contains(event->pos().x(), event->pos().y(), true))
            m_arcDiscData[i].state = mouseState_Hover;
        else
            m_arcDiscData[i].state = mouseState_Leave;
    }
    update();
}

void QWHArcDisc::mousePressEvent(QMouseEvent *event)
{
    bool mouseInRect = false;
    for (int i = 0; i < m_arcDiscData.count(); i++)
    {
        if (m_arcDiscData[i].rect.contains(event->pos().x(), event->pos().y(), true))
        {
            m_arcDiscData[i].state = mouseState_Pressed;
            m_curPressedIndex = i;
            mouseInRect = true;
        }
        else
            m_arcDiscData[i].state = mouseState_Leave;
    }
    if (!mouseInRect && m_curPressedIndex != -1)
        m_arcDiscData[m_curPressedIndex].state = mouseState_Pressed;
    if (mouseInRect)
        emit clicked(m_curPressedIndex);

    update();
}

void QWHArcDisc::resizeEvent(QResizeEvent *event)
{
    setRects();
    //重新设置pixmap大小
    m_resizePixmaps.clear();
    QPixmap pixmap;
    for (int i = 0; i < m_arcDiscData.count(); i++)
    {
        if (m_arcDiscType == SemiCircle)
            pixmap = m_arcDiscData[i].pixmap.scaled(sideLength * 200, sideLength * 100, Qt::IgnoreAspectRatio);
        else
            pixmap = m_arcDiscData[i].pixmap.scaled(sideLength * 200, sideLength * 200, Qt::IgnoreAspectRatio);
        m_resizePixmaps.append(pixmap);
    }
}

void QWHArcDisc::leaveEvent(QEvent *)
{
    for (int i = 0; i < m_arcDiscData.count(); i++)
    {
        if (m_arcDiscData[i].state == mouseState_Hover)
            m_arcDiscData[i].state = mouseState_Leave;
    }
    update();
}

void QWHArcDisc::setRects()
{
    int width = this->width();
    int height = this->height();
    int translateX = width / 2;
    int translateY = height;
    float scaleX = width / 200.0;
    float scaleY = height / 100.0;

    int count = m_arcDiscData.count();
    int midIndex = count / 2;
    if (m_arcDiscType == SemiCircle)
    {
        if (count % 2 == 0)
        {
            float perAngle = (180 - 2 * offsetAngle) / (count - 1);
            for (int i = 0; i < count; i++)
            {
                int x = radius * qCos(qDegreesToRadians(i * perAngle + offsetAngle));
                int y = -radius * qSin(qDegreesToRadians(i * perAngle + offsetAngle));
                QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
                //将paintEvent函数中的图片区域转换为实际区域
                rect = QRect(rect.x() * scaleX + translateX, rect.y() * scaleY + translateY, sideLength * scaleX, sideLength * scaleY);
                m_arcDiscData[i].rect = rect;
            }
        }
        else
        {
            //存储中间图片区域
            QRect rect = QRect(-sideLength / 2, -radius - sideLength / 2, sideLength, sideLength);
            rect = QRect(rect.x() * scaleX + translateX, rect.y() * scaleY + translateY, sideLength * scaleX, sideLength * scaleY);
            m_arcDiscData[midIndex].rect = rect;
            //存储offsetAngle-90度之间的图片区域
            int perAngle = (90 - offsetAngle) / midIndex;
            for (int i = midIndex - 1; i >= 0; i--)
            {
                int x = radius * qCos(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
                int y = -radius * qSin(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
                QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
                //将paintEvent函数中的图片区域转换为实际区域
                rect = QRect(rect.x() * scaleX + translateX, rect.y() * scaleY + translateY, sideLength * scaleX, sideLength * scaleY);
                m_arcDiscData[i].rect = rect;
            }
            //存储90度-(180 - offsetAngle)之间的图片区域
            for (int i = midIndex + 1; i < count; i++)
            {
                int x = radius * qCos(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
                int y = -radius * qSin(qDegreesToRadians(90.0 - perAngle * (midIndex - i)));
                QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
                //将paintEvent函数中的图片区域转换为实际区域
                rect = QRect(rect.x() * scaleX + translateX, rect.y() * scaleY + translateY, sideLength * scaleX, sideLength * scaleY);
                m_arcDiscData[i].rect = rect;
            }
        }
    }
    else if (m_arcDiscType == Circle)
    {
        float perAngle = 360.0 / count;
        scaleY = scaleY / 2.0;
        translateY = translateY / 2.0;
        for (int i = 0; i < count; i++)
        {
            int x = radius * qCos(qDegreesToRadians(i * perAngle + 90));
            int y = -radius * qSin(qDegreesToRadians(i * perAngle + 90));
            QRect rect = QRect(x - sideLength / 2, y - sideLength / 2, sideLength, sideLength);
            //将paintEvent函数中的图片区域转换为实际区域
            rect = QRect(rect.x() * scaleX + translateX, rect.y() * scaleY + translateY, sideLength * scaleX, sideLength * scaleY);
            m_arcDiscData[i].rect = rect;
        }
    }
}

void QWHArcDisc::drawPixmap(QPainter *painter, const QPixmap &pixmap, int index, QRect rect)
{
    if (m_btnStyle == BtnStyle_Rect)
    {
        painter->drawPixmap(rect, pixmap);
        if (m_arcDiscData[index].state == mouseState_Pressed || m_arcDiscData[index].state == mouseState_Hover)
            painter->drawRect(rect);
    }
    else if (m_btnStyle == BtnStyle_Circle)
    {
        QPainterPath path;
        path.addEllipse(rect);
        painter->setClipPath(path);
        painter->drawPixmap(rect, pixmap);
        if (m_arcDiscData[index].state == mouseState_Pressed || m_arcDiscData[index].state == mouseState_Hover)
            painter->drawEllipse(rect);
    }
}

测试代码

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

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //半圆弧形奇数个
    QVector<QPixmap> pixmaps1;
    for (int i = 0; i < 5; i++)
    {
        QPixmap pixmap(QString(":/RES/%1.png").arg(i + 1));
        pixmaps1.append(pixmap);
    }
    ui->widget->setPixmaps(pixmaps1);
    ui->widget->setArcDiscType(QWHArcDisc::SemiCircle);
    ui->widget->setBtnStyle(QWHArcDisc::BtnStyle_Rect);
    //半圆弧形偶数个
    QVector<QPixmap> pixmaps2;
    for (int i = 0; i < 4; i++)
    {
        QPixmap pixmap(QString(":/RES/%1.png").arg(i + 1));
        pixmaps2.append(pixmap);
    }
    ui->widget_2->setPixmaps(pixmaps2);
    ui->widget_2->setArcDiscType(QWHArcDisc::SemiCircle);
    ui->widget_2->setBtnStyle(QWHArcDisc::BtnStyle_Circle);
    //正圆弧形奇数个
    QVector<QPixmap> pixmaps3;
    for (int i = 0; i < 3; i++)
    {
        QPixmap pixmap(QString(":/RES/%1.png").arg(i + 1));
        pixmaps3.append(pixmap);
    }
    ui->widget_3->setPixmaps(pixmaps3);
    ui->widget_3->setArcDiscType(QWHArcDisc::Circle);
    ui->widget_3->setBtnStyle(QWHArcDisc::BtnStyle_Rect);
    //正圆弧形偶数个
    QVector<QPixmap> pixmaps4;
    for (int i = 0; i < 5; i++)
    {
        qDebug() << QString(":/RES/%1.png").arg(i + 1);
        QPixmap pixmap(QString(":/RES/%1.png").arg(i + 1));
        pixmaps4.append(pixmap);
    }
    ui->widget_4->setPixmaps(pixmaps4);
    ui->widget_4->setArcDiscType(QWHArcDisc::Circle);
    ui->widget_4->setBtnStyle(QWHArcDisc::BtnStyle_Circle);

    connect(ui->widget, &QWHArcDisc::clicked, this, [&](int index){ui->lblValue1->setText(QString::number(index));});
    connect(ui->widget_2, &QWHArcDisc::clicked, this, [&](int index){ui->lblValue2->setText(QString::number(index));});
    connect(ui->widget_3, &QWHArcDisc::clicked, this, [&](int index){ui->lblValue3->setText(QString::number(index));});
    connect(ui->widget_4, &QWHArcDisc::clicked, this, [&](int index){ui->lblValue4->setText(QString::number(index));});
}

Widget::~Widget()
{
    delete ui;
}
相关标签: 自定义控件