简单的记录下学习自定义QGraphicsItem 移动、旋转、缩放、组合。
1. QGraphicsView缩放
通过鼠标滚轮缩放整个视图
重写GraphicsView的wheelEvent(QWheelEvent *event)
事件
//
//QttGraphicsView::QttGraphicsView(QGraphicsScene* scene, QWidget* parent) : QGraphicsView(scene, parent){}
void QttGraphicsView::wheelEvent(QWheelEvent *event)
{
static float scale = 1.1;
auto angle = event->angleDelta();
if(angle.y() > 0)
{
this->scale(scale, scale);
}
else
{
this->scale(1/scale, 1/scale);
}
}
2.拖动、旋转、缩放
继承重写QGraphicsItem
#ifndef QTTGRAPHICSITEMA_H
#define QTTGRAPHICSITEMA_H
#include <QObject>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPixmap>
#include <QRectF>
#include <QVector3D>
//enum MyCursorTypes{
// Normal = 0,
// Move = 1,
// Rotate = 2,
// LeftTop = 3,
// LeftBottom = 4,
// RightTop = 5,
// RightBottom = 6,
//};
class QttGraphicsItemCursor;
class QttGraphicsItemA : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
public:
QttGraphicsItemA(double x, double y, double width, double height);
QRectF getRect() const;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
virtual QRectF boundingRect() const override;
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
private:
void rotate(QGraphicsSceneMouseEvent * event);
private:
// 主框大小,即四边形虚线范围
QRectF m_rect;
qreal m_width, m_height;
qreal m_left, m_top, m_right, m_bottom;
qreal m_scale;
QSize minSize;
QRectF m_leftTopRect, m_rightTopRect, m_rightBottomRect, m_leftBottomRect;
qreal m_line_rotate = 20;
QPixmap m_leftTopPix;
QPixmap m_rightTopPix;
QPixmap right_BottomPix;
QPixmap m_leftBottomPix;
QPixmap m_rotatePix;
QttGraphicsItemCursor* item_rotate;
QttGraphicsItemCursor* item_leftTop;
QttGraphicsItemCursor* item_rightTop;
QttGraphicsItemCursor* item_rightBottom;
QttGraphicsItemCursor* item_leftBottom;
QPointF pPress;
void init_painter_button();
//!
void paint_button(qreal radius);
};
//! 缩放、旋转图标item
//! 用于判断鼠标是否放再特定位置
class QttGraphicsItemCursor : public QObject, public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
public:
QttGraphicsItemCursor(double x, double y, double width, double height, Qt::CursorShape type = Qt::ArrowCursor);
void setItemRect(QRectF rect);
void setCursorType(Qt::CursorShape type);
Qt::CursorShape getCursorShape(){return m_cursor_shape;}
bool isPress();
protected:
virtual QRectF boundingRect() const override;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
private:
QRectF m_rect;
Qt::CursorShape m_cursor_shape;
};
#endif // QTTGRAPHICSITEMA_H
#include "qttgraphicsitema.h"
#include <QPainter>
#include <QDebug>
#include <QStyleOptionGraphicsItem>
#include <QDebug>
#include <QImage>
#include <QPolygonF>
#include <QVector2D>
#include <QVector3D>
#include <QtMath>
#include <cmath>
QRectF ImageRect2(QRectF rect)
{
//图片占主题轮廓的比例0.8
double x = rect.x();
double y = rect.y();
double width = rect.width();
double height = rect.height();
double new_x = x+width*0.1;
double new_y = y+height*0.1;
double new_w = width*0.8;
double new_h = height*0.8;
return QRectF(new_x, new_y, new_w, new_h);
}
double getMargin(QRectF rect)
{
return rect.width()*0.1 <= rect.height()*0.1 ? rect.width()*0.1 : rect.height()*0.1;
}
QttGraphicsItemA::QttGraphicsItemA(double x, double y, double width, double height) : m_rect(x, y, width, height)
, m_width(width)
, m_height(height)
, m_left(x)
, m_top(y)
, m_right(x+width)
, m_bottom(y+height)
, m_scale(width/height)
, minSize(40, 40*m_scale)
{
this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
this->setAcceptHoverEvents(true);
m_leftTopPix = QPixmap(":/circle_grey.png");
m_rightTopPix = QPixmap(":/circle_grey.png");
right_BottomPix = QPixmap(":/circle_grey.png");
m_leftBottomPix = QPixmap(":/circle_grey.png");
m_rotatePix = QPixmap(":/rotate_hover.png");
item_rotate = nullptr;
item_leftTop = nullptr;
item_rightTop = nullptr;
item_rightBottom = nullptr;
item_leftBottom = nullptr;
init_painter_button();
}
void QttGraphicsItemA::init_painter_button()
{
double radius = getMargin(m_rect)/2;
if(item_leftBottom == nullptr)
{
item_leftBottom = new QttGraphicsItemCursor(m_rect.x()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2, Qt::SizeBDiagCursor);
item_leftBottom->setCursorType(Qt::CursorShape::SizeBDiagCursor);
item_leftBottom->setParentItem(this);
item_leftBottom->setVisible(false);
}
if(item_leftTop == nullptr)
{
item_leftTop = new QttGraphicsItemCursor(m_rect.x()-radius, m_rect.y()-radius, radius*2, radius*2, Qt::SizeFDiagCursor);
item_leftTop->setCursorType(Qt::CursorShape::SizeFDiagCursor);
item_leftTop->setParentItem(this);
item_leftTop->setVisible(false);
}
if(item_rightBottom == nullptr)
{
item_rightBottom = new QttGraphicsItemCursor(m_rect.x()+m_rect.width()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2, Qt::CursorShape::SizeFDiagCursor);
item_rightBottom->setCursorType(Qt::CursorShape::SizeFDiagCursor);
item_rightBottom->setParentItem(this);
item_rightBottom->setVisible(false);
}
if(item_rightTop == nullptr)
{
item_rightTop = new QttGraphicsItemCursor(m_rect.x()+m_rect.width()-radius, m_rect.y()-radius, radius*2, radius*2, Qt::SizeBDiagCursor);
item_rightTop->setCursorType(Qt::CursorShape::SizeBDiagCursor);
item_rightTop->setParentItem(this);
item_rightTop->setVisible(false);
}
if(item_rotate == nullptr)
{
item_rotate = new QttGraphicsItemCursor(m_rect.x()+m_rect.width()/2-radius, m_rect.y()-m_line_rotate-radius*2, radius*2, radius*2, Qt::SizeAllCursor);
item_rotate->setCursorType(Qt::CursorShape::SizeAllCursor);
item_rotate->setParentItem(this);
item_rotate->setVisible(false);
}
}
void QttGraphicsItemA::paint_button(qreal radius)
{
if(item_leftBottom)
{
item_leftBottom->setItemRect(QRectF(m_rect.x()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2));
item_leftBottom->setVisible(true);
}
if(item_leftTop)
{
item_leftTop->setItemRect(QRectF(m_rect.x()-radius, m_rect.y()-radius, radius*2, radius*2));
item_leftTop->setVisible(true);
}
if(item_rightTop)
{
item_rightBottom->setItemRect(QRectF(m_rect.x()+m_rect.width()-radius, m_rect.y()+m_rect.height()-radius, radius*2, radius*2));
item_rightBottom->setVisible(true);
}
if(item_leftBottom)
{
item_rightTop->setItemRect(QRectF(m_rect.x()+m_rect.width()-radius, m_rect.y()-radius, radius*2, radius*2));
item_rightTop->setVisible(true);
}
if(item_rotate)
{
item_rotate->setItemRect(QRectF(m_rect.x()+m_rect.width()/2-radius, m_rect.y()-m_line_rotate-radius*2, radius*2, radius*2));
item_rotate->setVisible(true);
}
}
void QttGraphicsItemA::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
{
QPixmap pix(":/timg.jpg");
painter->drawPixmap(ImageRect2(m_rect), pix, pix.rect());
if(isSelected())
{
// 图像轮廓
QPen pen;
pen.setStyle(Qt::DashLine);
pen.setColor(QColor(0,0,255));
painter->setPen(pen);
painter->drawRect(m_rect);
// 5个顶点item
double radius = getMargin(m_rect)/2;
paint_button(radius);
//line
m_line_rotate = radius*2;
QPointF start(m_rect.x()+m_rect.width()/2, m_rect.y());
QPointF end(m_rect.x()+m_rect.width()/2, m_rect.y()-m_line_rotate);
painter->drawLine(QLineF(start, end));
}
else
{
if(item_rotate)item_rotate->setVisible(false);
if(item_leftTop)item_leftTop->setVisible(false);
if(item_rightTop)item_rightTop->setVisible(false);
if(item_leftBottom)item_leftBottom->setVisible(false);
if(item_rightBottom)item_rightBottom->setVisible(false);
}
}
void QttGraphicsItemA::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(flags() & ItemIsMovable)
{
if ((event->buttons() & Qt::LeftButton))
{
QPointF diff = event->pos()-event->lastPos();
qreal dx = diff.x();
qreal dy = diff.y();
QRectF rect = m_rect;
if(item_rotate->isPress())
{
rotate(event);
}
else if(item_leftBottom->isPress())
{
dx = (rect.width()-dx*2 < minSize.width()) ? (rect.width()-minSize.width())/2 : dx;
dy = (rect.height()+dy*2 < minSize.height()) ? (minSize.height()- rect.height())/2 : dy;
rect.adjust(dx, -dy, -dx, dy);
}
else if(item_leftTop->isPress())
{
dx = (rect.width()-dx*2 < minSize.width()) ? (rect.width()-minSize.width())/2 : dx;
dy = (rect.height()-dy*2 < minSize.height()) ? (rect.height()-minSize.height())/2 : dy;
rect.adjust(dx, dy, -dx, -dy);
}
else if(item_rightBottom->isPress())
{
dx = (rect.width()+dx*2 < minSize.width()) ? (minSize.width()-rect.width())/2 : dx;
dy = (rect.height()+dy*2 < minSize.height()) ? (minSize.height()- rect.height())/2 : dy;
rect.adjust(-dx, -dy, dx, dy);
}
else if(item_rightTop->isPress())
{
dx = (rect.width()+dx*2 < minSize.width()) ? (minSize.width()-rect.width())/2 : dx;
dy = (rect.height()-dy*2 < minSize.height()) ? (rect.height()-minSize.height())/2 : dy;
rect.adjust(-dx, dy, dx, -dy);
}
else if(getRect().contains(event->pos()))
{
//! 此方法在旋转item后移动会抖动,具体原因不了解
//QPointF diff = event->scenePos() - this->pPress;
//this->setPos(diff);
//! 通过计算鼠标在scene中前后两次位置差,移动item
QPointF diff = event->scenePos() - event->lastScenePos();
// 不用鼠标在item中坐标是因为当item旋转后计算所得的值不可直接使用
//QPointF diff2 = event->pos() - event->lastPos();
this->moveBy(diff.x(),diff.y());
}
if(m_rect != rect)
{
prepareGeometryChange();
m_rect = rect;
}
this->update();
this->scene()->update();
}
}
//此处不调用, 影响功能
//QGraphicsItem::mouseMoveEvent(event);
}
void QttGraphicsItemA::rotate(QGraphicsSceneMouseEvent *event)
{
QPointF originPos = this->getRect().center();
// 从原点延伸出去两条线,鼠标按下时的点和当前鼠标位置所在点的连线
QLineF p1 = QLineF(originPos, pPress);
QLineF p2 = QLineF(originPos, event->pos());
// 旋转角度
qreal dRotateAngle = p2.angleTo(p1);
// 设置旋转中心
this->setTransformOriginPoint(originPos);
// 计算当前旋转的角度
qreal dCurAngle = this->rotation() + dRotateAngle;
while (dCurAngle > 360.0) {
dCurAngle -= 360.0;
}
prepareGeometryChange();
// 设置旋转角度
this->setRotation(dCurAngle);
}
void QttGraphicsItemA::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
pPress = event->pos();
QGraphicsItem::mousePressEvent(event);
}
void QttGraphicsItemA::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseReleaseEvent(event);
}
QRectF QttGraphicsItemA::getRect() const
{
QRectF rect = m_rect;
if(isSelected() || !isSelected())
{
qreal radius = getMargin(rect)/2;
rect.adjust(-radius, -radius, radius, radius);
rect.adjust(0, -m_line_rotate-radius, 0, 0);
}
return rect;
}
QRectF QttGraphicsItemA::boundingRect() const
{
QRectF rect = getRect();
return rect;
}
void QttGraphicsItemA::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
if(m_rect.contains(event->pos())) setCursor(Qt::OpenHandCursor);
QGraphicsItem::hoverMoveEvent(event);
}
//!####################################################################################################################################
//!
//!
//!
//!####################################################################################################################################
//! QttGraphicsItemCursor
//!
QttGraphicsItemCursor::QttGraphicsItemCursor(double x, double y, double width, double height, Qt::CursorShape type) : m_rect(x, y, width, height)
, m_cursor_shape(type)
{
this->setAcceptHoverEvents(true);
}
void QttGraphicsItemCursor::setItemRect(QRectF rect)
{
if(rect != m_rect)
{
prepareGeometryChange();
m_rect = rect;
}
}
void QttGraphicsItemCursor::setCursorType(Qt::CursorShape type)
{
m_cursor_shape = type;
}
QRectF QttGraphicsItemCursor::boundingRect() const
{
return m_rect;
}
void QttGraphicsItemCursor::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
switch (m_cursor_shape) {
case Qt::CursorShape::ArrowCursor:
{
QPixmap pix(":/circle_grey.png");
painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());
}break;
case Qt::CursorShape::OpenHandCursor:
{
QPixmap pix(":/circle_grey.png");
painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());
}break;
case Qt::CursorShape::SizeAllCursor:
{
QPixmap pix(":/rotate_hover.png");
painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());
}break;
case Qt::CursorShape::SizeFDiagCursor:
{
QPixmap pix(":/circle_grey.png");
painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());
}break;
case Qt::CursorShape::SizeBDiagCursor:
{
QPixmap pix(":/circle_grey.png");
painter->drawPixmap(/*ImageRect2(m_rect)*/m_rect, pix, pix.rect());
}break;
default:
break;
}
}
void QttGraphicsItemCursor::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
QPointF pos = event->pos();
if(!m_rect.contains(pos))
{
setCursor(Qt::ArrowCursor);
}
else
{
setCursor(m_cursor_shape);
}
QGraphicsItem::hoverMoveEvent(event);
}
void QttGraphicsItemCursor::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseMoveEvent(event);
}
void QttGraphicsItemCursor::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mousePressEvent(event);
}
void QttGraphicsItemCursor::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseReleaseEvent(event);
}
void QttGraphicsItemCursor::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
setCursor(Qt::ArrowCursor);
QGraphicsItem::hoverEnterEvent(event);
}
void QttGraphicsItemCursor::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
setCursor(Qt::ArrowCursor);
QGraphicsItem::hoverLeaveEvent(event);
}
//! 判断设定的鼠标图标样式是否与实际样式一致,是则返回true,否则返回false
bool QttGraphicsItemCursor::isPress()
{
return this->cursor() == m_cursor_shape;
}
3. 使用
m_scene = new QttGraphicsScene();
// ui->graphicsView 是在ui界面添加的QGraphicsView,然后提升为QttGraphicsView
ui->graphicsView->setScene(scene);
QttGraphicsItemA* item = new QttGraphicsItemA(0,0,200,200);
m_scene->addItem(item);
4.组合
//组合
void Widget::on_pushButton_clicked()
{
auto items = m_scene->selectedItems();
if(items.length() <= 0) return;
QGraphicsItemGroup *itemGroup = new QGraphicsItemGroup;
itemGroup->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
scene->addItem(itemGroup);
foreach (auto item, items) {
itemGroup->addToGroup(item);
}
}
//取消组合
void Widget::on_pushButton_2_clicked()
{
auto items = m_scene->selectedItems();
if(items.length() != 1) return;
QGraphicsItemGroup *itemGroup = reinterpret_cast<QGraphicsItemGroup *>(items.at(0));
itemGroup->removeFromGroup(itemGroup->childItems().first());
m_scene->destroyItemGroup(itemGroup);
}