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