只有继承布局项QGraphicsLayoutItem的对象才能放入布局。继承QGraphicsLayoutItem后需要重写setGeometry()、sizeHint()这两个函数。
以自定义滑动条图形项为例,继承布局项。代码如下:
#ifndef SLIDERGRAPHICSPIXMAPITEM_H
#define SLIDERGRAPHICSPIXMAPITEM_H
#include <QGraphicsObject>
#include <QGraphicsLayoutItem>
class sliderGraphicsPixmapItem : public QGraphicsObject , public QGraphicsLayoutItem
{
Q_OBJECT
public:
sliderGraphicsPixmapItem(bool isInLayout,QString uuid,Qt::Orientation orientation);
void setSliderOrientation(Qt::Orientation orientation)
{
this->sliderOrientation = orientation;
update();
}
void setRange(const int maxValue,const int minValue)
{
this->maxValue = maxValue;
this->minValue = minValue;
update();
}
virtual QRectF boundingRect()const override;
virtual void setGeometry(const QRectF &rect)override;
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value)override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)override;
virtual QPainterPath shape()const override;
void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
void hoverMoveEvent(QGraphicsSceneHoverEvent* event)override;
void mouseMoveEvent(QGraphicsSceneMouseEvent* event)override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)override;
virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF())const override;
private:
bool IsInResizeArea(const QPointF& pos);
QSizeF itemSize;
bool isResizing; //是否正在改变大小的过程中
QString uuid;
Qt::Orientation sliderOrientation;
int maxValue{100};
int minValue;
int nowValue{50};
QRect thisSliderRect;//滑块区域
bool pressThisSlider;//是否按下滑块
bool inThisSliderRect;//鼠标在滑块上
QRect sliderRect;
bool isInLayout{false};//是否要添加到布局里
};
#endif // SLIDERGRAPHICSPIXMAPITEM_H
#include "slidergraphicspixmapitem.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QDebug>
const qreal g_cResizePos[] = {9, 6, 3};
sliderGraphicsPixmapItem::sliderGraphicsPixmapItem(bool isInLayout, QString uuid, Qt::Orientation orientation)
:QGraphicsObject(),
QGraphicsLayoutItem(nullptr,false)
{
setAcceptHoverEvents(true);
setFlag(QGraphicsItem::ItemIsMovable,!isInLayout);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);//图形项可发送位置变化信号
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsFocusable);
this->itemSize = QSizeF(250,80);
isResizing = false;
this->uuid = uuid;
setZValue(1);
this->sliderOrientation = orientation;
maxValue = 40;
nowValue = 25;
minValue = 15;
this->isInLayout = isInLayout;
setGraphicsItem(this);//不加这个图形项不会显示出来
}
QRectF sliderGraphicsPixmapItem::boundingRect() const
{
return QRectF(0, 0, itemSize.width() + 10, itemSize.height() + 10);
}
QPainterPath sliderGraphicsPixmapItem::shape() const
{
QPainterPath path;
path.addRect(QRectF(0, 0, itemSize.width(), itemSize.height() + 10));
return path;
}
void sliderGraphicsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
QPointF point = event->pos();
if(IsInResizeArea(point))
{
isResizing = true;
}
if(thisSliderRect.contains(point.x(),point.y()))
{
pressThisSlider = true;
if(!isInLayout)
{
setFlag(QGraphicsItem::ItemIsMovable,false);
}
}
else
{
pressThisSlider = false;
if(!isInLayout)
{
setFlag(QGraphicsItem::ItemIsMovable,true);
}
}
QGraphicsObject::mousePressEvent(event);
return;
}
else if(event->button() == Qt::RightButton)
{
return;
}
return QGraphicsObject::mousePressEvent(event);
}
void sliderGraphicsPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
if(pressThisSlider)
pressThisSlider = false;
if (event->button() == Qt::LeftButton && isResizing)
isResizing = false;
else
QGraphicsObject::mouseReleaseEvent(event);
}
QVariant sliderGraphicsPixmapItem::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)));
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);
}
void sliderGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QRectF thisRectF = boundingRect();
if(!isInLayout)
{
if(option->state & QStyle::State_Selected)
{
painter->setPen(QColor("#D8D8D8"));
painter->drawRect(thisRectF);
setZValue(2);
painter->fillRect(thisRectF, QBrush(Qt::transparent));
}
else
{
setZValue(1);
painter->fillRect(thisRectF, QBrush(Qt::transparent));
}
}
QRect rect = thisRectF.toRect();
painter->save();
painter->setPen(Qt::transparent);
if(sliderOrientation == Qt::Horizontal)
{
sliderRect = QRect(5,(rect.height() - 12) / 2,rect.width() - 10,12);
painter->save();
QColor slightlyOpaqueBlack(0, 0, 0, 63);
painter->setBrush(slightlyOpaqueBlack);
painter->drawRect(sliderRect);
painter->restore();
painter->save();
painter->setBrush(QColor("#00beac"));
int nowValueWidth = static_cast<int>(static_cast<float>(sliderRect.width() * (nowValue - minValue)) / static_cast<float>(maxValue - minValue));
sliderRect.setWidth(nowValueWidth);
painter->drawRoundRect(sliderRect);
int thisSliderRectX = nowValueWidth + 5;
if(thisSliderRectX < 5)
thisSliderRectX = 5;
if(thisSliderRectX > (5 + rect.width() - 20))
thisSliderRectX = 5 + rect.width() - 20 - 10;
thisSliderRect = QRect(thisSliderRectX,sliderRect.y() - 4,20,20);
if(pressThisSlider || inThisSliderRect)
painter->setBrush(QColor("#01968c"));
painter->drawEllipse(thisSliderRect);
painter->restore();
}
else
{
sliderRect = QRect((rect.width() - 12) / 2,5,12,rect.height() - 10);
painter->save();
QColor slightlyOpaqueBlack(0, 0, 0, 63);
painter->setBrush(slightlyOpaqueBlack);
painter->drawRect(sliderRect);
painter->restore();
painter->save();
painter->setBrush(QColor("#00beac"));
int nowValueHeight = static_cast<int>(static_cast<float>(sliderRect.height() * (nowValue - minValue)) / static_cast<float>(maxValue - minValue));
sliderRect.setHeight(nowValueHeight);
painter->drawRoundRect(sliderRect);
int thisSliderRectY = nowValueHeight + 5;
if(thisSliderRectY < 5)
thisSliderRectY = 5;
if(thisSliderRectY > (5 + rect.height() - 20))
thisSliderRectY = 5 + rect.height() - 20 - 10;
thisSliderRect = QRect(sliderRect.x() - 4,thisSliderRectY,20,20);
if(pressThisSlider || inThisSliderRect)
painter->setBrush(QColor("#01968c"));
painter->drawEllipse(thisSliderRect);
painter->restore();
}
painter->restore();
if(!isInLayout)
{
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 sliderGraphicsPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
QPointF point = event->pos();
if (isResizing || (IsInResizeArea(point) && isSelected()))
setCursor(Qt::SizeFDiagCursor);
else
setCursor(Qt::ArrowCursor);
if(thisSliderRect.contains(point.x(),point.y()))
{
inThisSliderRect = true;
}
else
{
inThisSliderRect = false;
}
update();
QGraphicsObject::hoverMoveEvent(event);
}
void sliderGraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
if(pressThisSlider)
{
int temp = 0;
if(sliderOrientation == Qt::Horizontal)
{
temp = static_cast<int>(event->pos().x() - 5) * (maxValue - minValue) / static_cast<float>(boundingRect().width() - 10);
}
else
{
temp = static_cast<int>(event->pos().y() - 5) * (maxValue - minValue) / static_cast<float>(boundingRect().height() - 10);
}
if(temp < 0)
temp = 0;
if(temp > (maxValue - minValue))
temp = (maxValue - minValue);
nowValue = temp + minValue;
update();
}
if (isResizing & !isInLayout)
{
qreal w = event->pos().x();
qreal h = event->pos().y();
if (w > 0)
itemSize.setWidth(w);
if (h > 0)
itemSize.setHeight(h);
prepareGeometryChange();
}
else
{
QGraphicsObject::mouseMoveEvent(event);
}
}
bool sliderGraphicsPixmapItem::IsInResizeArea(const QPointF& pos)
{
return (pos.x() - itemSize.width() + g_cResizePos[0]) > (itemSize.height() - pos.y());
}
//小部件设置布局的时候调用,缓存大小。
QSizeF sliderGraphicsPixmapItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint)const
{
auto p = parentItem()->toGraphicsObject();
qDebug()<<p->metaObject()->className()<<which;
switch ( which )
{
case Qt::MinimumSize:
return QSizeF(10,10);
case Qt::PreferredSize:
return this->boundingRect().size();
case Qt::MaximumSize:
return QSizeF(10000,10000);
default:
return this->boundingRect().size();
}
return constraint;
}
void sliderGraphicsPixmapItem::setGeometry(const QRectF &rect)
{
prepareGeometryChange();
QGraphicsLayoutItem::setGeometry(rect);
setPos(rect.topLeft());
QSizeF effectiveSize = rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize))
.boundedTo(effectiveSizeHint(Qt::MaximumSize));
itemSize = QRectF(rect.topLeft(), effectiveSize).size();
}
然后是小部件代理。也做了一些修改。代码如下:
#ifndef GRAPHICSPROXYWIDGET_H
#define GRAPHICSPROXYWIDGET_H
#include <QGraphicsProxyWidget>
class GraphicsProxyWidget : public QGraphicsProxyWidget
{
Q_OBJECT
public:
GraphicsProxyWidget(QGraphicsItem *parent = nullptr);
void setCenterWidget(QWidget * w);
void setCenterLayout(QGraphicsLayout * layout);
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;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)override;
private:
QSizeF itemSize;
QSizeF itemMinSize;
bool IsInResizeArea(const QPointF& pos);
bool isResizing;
QPointF dragPosition;
bool grabbedByWidget;
};
#endif // GRAPHICSPROXYWIDGET_H
#include "graphicsproxywidget.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsLayout>
const qreal g_cResizePos[] = {9, 6, 3};
GraphicsProxyWidget::GraphicsProxyWidget(QGraphicsItem *parent)
:QGraphicsProxyWidget(parent)
{
setAcceptHoverEvents(true);
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);//图形项可发送位置变化信号
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsFocusable);
this->itemSize = QSizeF(100,100);
}
bool GraphicsProxyWidget::IsInResizeArea(const QPointF& pos)
{
return (pos.x() - itemSize.width() + g_cResizePos[0]) > (itemSize.height() - pos.y());
}
QRectF GraphicsProxyWidget::boundingRect() const
{
return QRectF(0, 0, itemSize.width() + 5, itemSize.height() + 5);
}
void GraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
QPointF pos = event->pos();
if(IsInResizeArea(pos))
{
isResizing = true;
}
else
{
if(widget())
{
if (auto alienWidget = widget()->childAt(pos.toPoint()))
{
QGraphicsProxyWidget::mousePressEvent(event);
grabbedByWidget = true;
}
else
{
QGraphicsItem::mousePressEvent(event);
grabbedByWidget = false;
}
}
else
{
QGraphicsItem::mousePressEvent(event);
grabbedByWidget = false;
}
}
}
}
void GraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
if(widget())
{
if (auto alienWidget = widget()->childAt(event->pos().toPoint()))
{
QGraphicsProxyWidget::mouseDoubleClickEvent(event);
grabbedByWidget = true;
}
else
{
QGraphicsItem::mouseDoubleClickEvent(event);
grabbedByWidget = false;
}
}
else
{
QGraphicsItem::mouseDoubleClickEvent(event);
grabbedByWidget = false;
}
}
void GraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton && isResizing)
isResizing = false;
else
{
if (grabbedByWidget)
QGraphicsProxyWidget::mouseReleaseEvent(event);
else
QGraphicsItem::mouseReleaseEvent(event);
}
grabbedByWidget = false;
}
QVariant GraphicsProxyWidget::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)));
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);
}
void GraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing,true);
painter->setRenderHint(QPainter::SmoothPixmapTransform,true);
painter->setRenderHint(QPainter::TextAntialiasing,true);
QRectF thisRectF = boundingRect();
painter->fillRect(option->rect,QBrush(QColor("#e0e0e0")));
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();
QGraphicsProxyWidget::paint(painter,option,widget);
}
void GraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
{
if (isResizing || (IsInResizeArea(event->pos()) && isSelected()))
{
setCursor(Qt::SizeFDiagCursor);
QGraphicsObject::hoverMoveEvent(event);
}
else
{
if(widget())
{
if (auto alienWidget = widget()->childAt(event->pos().toPoint()))
{
setCursor(alienWidget->cursor());
}
else
setCursor(Qt::ArrowCursor);
}
else
setCursor(Qt::ArrowCursor);
QGraphicsProxyWidget::hoverMoveEvent(event);
}
}
void GraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
if (isResizing)
{
qreal w = event->pos().x();
qreal h = event->pos().y();
if (w > itemMinSize.width())
itemSize.setWidth(w);
if (h > itemMinSize.height())
itemSize.setHeight(h);
resize(w,h);
if(layout())
{
layout()->invalidate();
}
prepareGeometryChange();
}
else
{
if (grabbedByWidget)
{
QGraphicsProxyWidget::mouseMoveEvent(event);
}
else
QGraphicsItem::mouseMoveEvent(event);
}
}
void GraphicsProxyWidget::setCenterWidget(QWidget * w)
{
setWidget(w);
itemSize = w->sizeHint();
itemMinSize = w->minimumSize();
update();
}
void GraphicsProxyWidget::setCenterLayout(QGraphicsLayout * layout)
{
setLayout(layout);
setContentsMargins(10,10,10,10);
adjustSize();
itemSize = this->sizeHint(Qt::PreferredSize);
itemMinSize = this->sizeHint(Qt::MinimumDescent);
}
效果:
QGraphicsGridLayout *layout = new QGraphicsGridLayout;
layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 0, 0);
layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 0, 1);
layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 1, 0);
layout->addItem(new sliderGraphicsPixmapItem(true,"",Qt::Horizontal), 1, 1);
GraphicsProxyWidget *form = new GraphicsProxyWidget;
form->setCenterLayout(layout);
gphs->addItem(form);
效果勉强还可以\(^o^)/。