效果:
代码:
#ifndef DISCBUTTONGRAPHICSPIXMAPITEM_H
#define DISCBUTTONGRAPHICSPIXMAPITEM_H
#include <QGraphicsObject>
//遥控器圆形按钮
class DiscButtonGraphicsPixmapItem : public QGraphicsObject
{
Q_OBJECT
public:
DiscButtonGraphicsPixmapItem(QString uuid,QGraphicsItem *parent = nullptr);
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value)override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)override;
virtual QRectF boundingRect()const override;
void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
void hoverMoveEvent(QGraphicsSceneHoverEvent* event)override;
void mouseMoveEvent(QGraphicsSceneMouseEvent* event)override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)override;
private:
bool IsInResizeArea(const QPointF& pos);
QSizeF itemSize;
bool isResizing; //是否正在改变大小的过程中
QString uuid;
QRectF drawRect;//整个大圆的范围
bool isPointInCir(const QPoint &point, const QRect & rect);
QPainterPath fanShaped[4];
QRectF centerCircularRect;//中心圆按钮的范围
QPainterPath gradientArc(double startAngle, double angleLength, double arcHeight);
enum class pressBtnType //按下的是哪个按钮
{
up = 0,
left,
down,
right,
center,
None
};
pressBtnType pressedBtn{pressBtnType::None};
};
#endif // DISCBUTTONGRAPHICSPIXMAPITEM_H
#include "discbuttongraphicspixmapitem.h"
#include <QGraphicsScene>
#include <QDebug>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <math.h>
const qreal g_cResizePos[] = {9, 6, 3};
DiscButtonGraphicsPixmapItem::DiscButtonGraphicsPixmapItem(QString uuid, QGraphicsItem *parent)
:QGraphicsObject(parent)
{
setAcceptHoverEvents(true);
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);//图形项可发送位置变化信号
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsFocusable);
this->uuid = uuid;
this->itemSize = QSizeF(100,100);
isResizing = false;
setZValue(1);
}
QRectF DiscButtonGraphicsPixmapItem::boundingRect() const
{
return QRectF(0, 0, itemSize.width() + 10, itemSize.height() + 10);
}
bool DiscButtonGraphicsPixmapItem::isPointInCir(const QPoint & point,const QRect & rect)//判断点是否在圆范围内
{
const QPoint & centerPoint = rect.center();
int x = point.x() - centerPoint.x();
int y = point.y() - centerPoint.y();
if(sqrt(pow(x,2) + pow(y,2)) <= static_cast<double>(rect.width() / 2))
{
return true;
}
return false;
}
void DiscButtonGraphicsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
if(IsInResizeArea(event->pos()))
isResizing = true;
QGraphicsObject::mousePressEvent(event);
QPoint point = event->pos().toPoint();
if(isPointInCir(point,drawRect.toRect()))
{
if(isPointInCir(point,centerCircularRect.toRect()))
{
pressedBtn = pressBtnType::center;
}
else
{
QPoint centerPoint = drawRect.toRect().center();
double angle = atan2(point.y()-centerPoint.y(),point.x()-centerPoint.x()); //两点之间的角度(弧度)
angle = -angle*(180 / 3.1415926); //0°~180° - -180°~0°
if(angle < 0.0)
{
angle = 360.0 - abs(angle);
}
if(angle < 45.0 || angle > 315.0)
{
pressedBtn = pressBtnType::right;
}
else if(angle >= 45.0 && angle < 135.0)
{
pressedBtn = pressBtnType::up;
}
else if(angle >= 135.0 && angle < 225.0)
{
pressedBtn = pressBtnType::left;
}
else if(angle >= 225.0 && angle < 315.0)
{
pressedBtn = pressBtnType::down;
}
}
update();
}
return;
}
else if(event->button() == Qt::RightButton)
{
return;
}
return QGraphicsObject::mousePressEvent(event);
}
void DiscButtonGraphicsPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
if(pressedBtn != pressBtnType::None)
{
pressedBtn = pressBtnType::None;
update();
}
if (event->button() == Qt::LeftButton && isResizing)
isResizing = false;
else
QGraphicsObject::mouseReleaseEvent(event);
}
QVariant DiscButtonGraphicsPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
if ((change == ItemPositionChange || change == ItemPositionHasChanged) && scene()) // 控件发生移动
{
QPointF newPos = value.toPointF();
QRectF rect(0, 0, scene()->width(), scene()->height());
if (!rect.contains(newPos))//左上角
{
newPos.setX(qMin(rect.width(), qMax(newPos.x(), 0.0)));
newPos.setY(qMin(rect.height(), qMax(newPos.y(), 0.0)));
// newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left())));
// newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top())));
return newPos;
}
QRectF thisRectF = boundingRect();
QPointF nowPos = QPointF(newPos.x() + thisRectF.width(),newPos.y());
if(!rect.contains(nowPos))//右上角
{
newPos.setX(rect.width() - thisRectF.width());
this->setPos(newPos);
return newPos;
}
nowPos = QPointF(newPos.x(),newPos.y() + thisRectF.height());
if(!rect.contains(nowPos))//左下角
{
newPos.setY(rect.height() - thisRectF.height());
this->setPos(newPos);
return newPos;
}
}
return QGraphicsItem::itemChange(change, value);
}
QPainterPath DiscButtonGraphicsPixmapItem::gradientArc(double startAngle, double angleLength, double arcHeight)
{
QPainterPath path;
path.moveTo(drawRect.center());
path.arcTo(drawRect, startAngle, angleLength);
QPainterPath subPath;
subPath.addEllipse(drawRect.adjusted(arcHeight, arcHeight, -arcHeight, -arcHeight));
// path为扇形 subPath为椭圆
path -= subPath;
return path;
}
void DiscButtonGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing,true);
painter->setRenderHint(QPainter::SmoothPixmapTransform,true);
painter->setRenderHint(QPainter::TextAntialiasing,true);
QRectF thisRectF = boundingRect();
if(option->state & QStyle::State_Selected)
{
painter->setPen(QColor("#D8D8D8"));
painter->drawRect(thisRectF);
setZValue(2);
}
else
{
setZValue(1);
painter->fillRect(thisRectF, QBrush(Qt::transparent));
}
QPointF centerPoint = thisRectF.center();
painter->save();
painter->setPen(QColor("#222222"));
painter->setBrush(QColor("#EAEAEA"));
double radius = (std::min(thisRectF.width(),thisRectF.height()) - 10 * 2 ) / 2;
drawRect = QRectF(centerPoint.x()-radius, centerPoint.y()-radius, radius*2, radius*2);
double arcHeight = radius / 2;
fanShaped[0] = gradientArc(45.0, 90.0, arcHeight);//上
fanShaped[1] = gradientArc(135.0, 90.0, arcHeight);//左
fanShaped[2] = gradientArc(225.0, 90.0, arcHeight);//下
fanShaped[3] = gradientArc(315.0, 90.0, arcHeight);//右
for (int i = 0;i < 4;++i)
{
painter->drawPath(fanShaped[i]);
}
painter->restore();
centerCircularRect = QRectF(centerPoint.x() - (radius / 2), centerPoint.y() - (radius / 2), radius, radius).adjusted(2,2,-2,-2);
painter->save();
painter->setPen(Qt::transparent);
painter->setBrush(QColor("#EAEAEA"));
painter->drawEllipse(centerCircularRect);
painter->restore();
//绘制文字
//左
painter->save();
QRectF textRect = QRectF(drawRect.x(),centerCircularRect.y(),radius / 2,radius);
QPen p(Qt::SolidLine);
p.setColor("#000000");
p.setWidth(2);
painter->setPen(p);
QFont font = painter->font();
font.setPixelSize(24);
painter->setFont(font);
painter->drawText(textRect, Qt::AlignCenter, "〈");//在此区域的中间位置绘制文字 〉
//上
textRect = QRectF(centerCircularRect.x(),drawRect.y(),radius,radius / 2);
painter->drawText(textRect, Qt::AlignCenter, "︿");
//右
textRect = QRectF(centerCircularRect.topRight().x(),centerCircularRect.topRight().y(),radius / 2,radius);
painter->drawText(textRect, Qt::AlignCenter, "〉");
//下
textRect = QRectF(centerCircularRect.bottomLeft().x(),centerCircularRect.bottomLeft().y(),radius,radius / 2);
painter->drawText(textRect, Qt::AlignCenter, "﹀");
painter->drawText(centerCircularRect, Qt::AlignCenter, "OK");
painter->restore();
if(pressedBtn != pressBtnType::None)
{
painter->save();
QColor slightlyOpaqueBlack(0, 0, 0, 63);
painter->setBrush(slightlyOpaqueBlack);
painter->setPen(Qt::transparent);
if(pressedBtn == pressBtnType::center)
{
painter->drawEllipse(centerCircularRect);
}
else
{
int index = -1;
index = static_cast<int>(pressedBtn);
if(index >= 0)
{
painter->drawPath(fanShaped[index]);
}
}
painter->restore();
}
if(option->state & QStyle::State_Selected)
{
qreal w = thisRectF.width();
qreal h = thisRectF.height();
painter->setPen(Qt::red);
for (int i = 0; i < 3; ++i)//三角形
painter->drawLine(static_cast<int>(w - g_cResizePos[i]) , static_cast<int>(h), static_cast<int>(w), static_cast<int>(h - g_cResizePos[i]));
}
painter->restore();
}
void DiscButtonGraphicsPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
if (isResizing || (IsInResizeArea(event->pos()) && isSelected()))
setCursor(Qt::SizeFDiagCursor);
else
setCursor(Qt::ArrowCursor);
QGraphicsObject::hoverMoveEvent(event);
}
void DiscButtonGraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
if (isResizing)
{
qreal w = event->pos().x();
qreal h = event->pos().y();
if (w > 100)
itemSize.setWidth(w);
if (h > 100)
itemSize.setHeight(h);
prepareGeometryChange();
}
else
{
QGraphicsObject::mouseMoveEvent(event);
}
}
bool DiscButtonGraphicsPixmapItem::IsInResizeArea(const QPointF& pos)
{
return (pos.x() - itemSize.width() + g_cResizePos[0]) > (itemSize.height() - pos.y());
}
设计图:
大圆的半径是中间小圆的2倍。
绘制文字的位置:
鼠标按下时根据按下位置与中心点的坐标确定这两点的水平夹角,再根据夹角的大小确定按的是哪个按钮:
绘制扇形:
QPainterPath DiscButtonGraphicsPixmapItem::gradientArc(int startAngle, int angleLength, int arcHeight)
{
QPainterPath path;
path.moveTo(drawRect.center());
path.arcTo(drawRect, startAngle, angleLength);
QPainterPath subPath;
subPath.addEllipse(drawRect.adjusted(arcHeight, arcHeight, -arcHeight, -arcHeight));
// path为扇形 subPath为椭圆
path -= subPath;
return path;
}
其中:
QPainterPath path;
path.moveTo(drawRect.center());
path.arcTo(drawRect, startAngle, angleLength);
QPainterPath subPath;
subPath.addEllipse(drawRect.adjusted(arcHeight, arcHeight, -arcHeight, -arcHeight));
path -= subPath;
QWidget版本的:QT绘图:遥控器圆形按钮控件