QGraphicsRectItem美觀實現縮放,旋轉,平移

在使用QGraphicsRectItem時,涉及到需要縮放控件,於是在網上查找相關代碼。很幸運的很快就找到了一個python寫的旋轉方法,效果也正是我想要的,於是我將代碼改爲C++的,測試成功。後來增加旋轉需求,很幸運,這部分代碼同樣支持旋轉,即旋轉後仍然可以進行縮放。至於界面效果,我大概完整地實現了同PS上操作矩形的方式。
python參考地址:http://www.chinaoc.com.cn/p/1177045.html

效果demo展示

在這裏插入圖片描述

接口文件及說明

實現思路

放縮部分:我只是對python原文進行了python到C++轉譯而已,思路參見http://www.chinaoc.com.cn/p/1177045.html。

旋轉部分思路:在view的mouseMoveEvent中調用getRotateCursor,獲取指針形狀。針對item設置c_rotate_tolerance 旋轉容忍度,只有接近item周圍纔會支持旋轉。mousePressEvent中設置setRotateStart,表示鼠標已按下,開始旋轉。同時爲了實現動態旋轉,在mouseMoveEvent重複調用setRotateEnd,設置鼠標實時位置。

注1:使用時,應將mapCursors中的地址換成正確有效的地址。
注2:由於上述的放縮是基於item內部座標完成的放縮,所以旋轉並不會影響其效果。所以,你大可以重新實現更好的旋轉效果,如果可以的話,也可以分享給我你的想法。

#include <QGraphicsRectItem>
#include <QStyleOptionGraphicsItem>
#include <QStyleOption>
#include <QCursor>

const Qt::CursorShape handleCursors[] = {
	Qt::SizeFDiagCursor,
	Qt::SizeVerCursor,
	Qt::SizeBDiagCursor,
	Qt::SizeHorCursor,
	Qt::SizeFDiagCursor,
	Qt::SizeVerCursor,
	Qt::SizeBDiagCursor,
	Qt::SizeHorCursor,
};

const QString mapCursors[] = {
	"",
	":/QtGuiApplication4/Resources/rotate_top_left.png",
	":/QtGuiApplication4/Resources/rotate_top_middle.png",
	":/QtGuiApplication4/Resources/rotate_top_right.png",
	":/QtGuiApplication4/Resources/rotate_middle_right.png",
	":/QtGuiApplication4/Resources/rotate_bottom_right.png",
	":/QtGuiApplication4/Resources/rotate_bottom_middle.png",
	":/QtGuiApplication4/Resources/rotate_bottom_left.png",
	":/QtGuiApplication4/Resources/rotate_middle_left.png"
};

class CustomRectItem : public QGraphicsRectItem
{
	enum MOUSEHANDLE {
		handleNone = 0,
		handleTopLeft = 1,
		handleTopMiddle = 2,
		handleTopRight = 3,
		handleMiddleRight = 4,
		handleBottomRight = 5,
		handleBottomMiddle = 6,
		handleBottomLeft = 7,
		handleMiddleLeft = 8,
	};

	enum MOUSEROTATEHANDLE {
		handleRotateNone = 0,
		handleRotateTopLeft = 1,
		handleRotateTopMiddle = 2,
		handleRotateTopRight = 3,
		handleRotateMiddleRight = 4,
		handleRotateBottomRight = 5,
		handleRotateBottomMiddle = 6,
		handleRotateBottomLeft = 7,
		handleRotateMiddleLeft = 8,
	};

	const float c_handle_size = 8.0;
	const float c_handle_space = -4.0;

	const float c_rotate_tolerance = 20.0;
	const int c_handle_cursors_size = 8;	// handleCursors[] size
	const int c_rotate_cursors_size = 9;	// MOUSEROTATEHANDLE size
	const QSize c_rotate_cursor_size = QSize(20, 20);

public:
	CustomRectItem(QGraphicsItem *parent = Q_NULLPTR);
	~CustomRectItem();

	// Returns the shape of this item as a QPainterPath in local coordinates.
	QPainterPath shape() const override;

	// Returns the bounding rect of the shape (including the resize handles).
	QRectF boundingRect() const override;

	void updateHandlesPos();

	bool isHover();

	// point is scene coordinate
	QCursor getRotateCursor(const QPointF& point);
	// set point for start rorate
	// @note point is scene coordinate
	void setRotateStart(const QPointF& point);
	// set point for end rorate
	// @note point is scene coordinate
	void setRotateEnd(const QPointF& point);

protected:
	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE;

	void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
	
	//  Executed when the mouse leaves the shape (NOT PRESSED).
	void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);

	// Executed when the mouse moves over the shape (NOT PRESSED).
	void hoverMoveEvent(QGraphicsSceneHoverEvent *event);

	//  Executed when the mouse is being moved over the item while being pressed.
	void mouseMoveEvent(QGraphicsSceneMouseEvent *event);

	// Executed when the mouse is pressed on the item.
	void mousePressEvent(QGraphicsSceneMouseEvent *event);

	// Executed when the mouse is released from the item.
	void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

	// Executed when the key is pressed on the item.
	void keyPressEvent(QKeyEvent *event);

private:
	//  Returns the resize handle below the given point.
	MOUSEHANDLE handleAt(const QPointF& point);
	//  Perform shape interactive resize.
	void interactiveResize(const QPointF& mousePos);

	// the length2 with point1 and point2
	float getLength2(const QPointF& point1, const QPointF& point2);

private:
	std::map<MOUSEROTATEHANDLE, QPointF> m_points;
	std::map<MOUSEROTATEHANDLE, QCursor> m_cursorRotate;
	std::map<MOUSEHANDLE, QRectF> m_handles;
	MOUSEHANDLE m_handle;
	QCursor m_cursor;
	QPointF m_mousePressPos;
	QRectF m_mousePressRect;

	QPointF m_mouseRotateStart;
	float m_fLastAngle;

	MOUSEHANDLE m_bhandleSelected;
	
	bool m_isHover;
};

源文件代碼參考

分步介紹我就不加頭文件咯!另外,我是摘出來的這段代碼,有小錯誤的話,自己糾正一下就行咯!

重載paint函數,繪製了選中時的,邊角矩形和去掉了選中狀態(那種難看的虛線框)。

void CustomRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	QStyleOptionGraphicsItem op;

	if (widget == nullptr)
		op = *option;
	else
		op.initFrom(widget);

	if (option->state & QStyle::State_Selected)
		op.state = QStyle::State_None;

	QGraphicsRectItem::paint(painter, &op, widget);

	if (isSelected() == true)
	{
		painter->setRenderHint(QPainter::Antialiasing);
		painter->setBrush(QBrush(QColor(255, 255, 255, 255)));
		painter->setPen(QPen(QColor(0x293a56ff), 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
		for (auto it : m_handles)
		{
			if (m_bhandleSelected == MOUSEHANDLE::handleNone || it.first == m_bhandleSelected)
				painter->drawRect(it.second);
		}
	}
}

初始化部分,不做介紹。

CustomRectItem::CustomRectItem(QGraphicsItem * parent)
	: QGraphicsRectItem(parent)
	, m_points()
	, m_cursorRotate()
	, m_handles()
	, m_handle(MOUSEHANDLE::handleNone)
	, m_cursor(Qt::ArrowCursor)
	, m_mousePressPos()
	, m_mousePressRect()
	, m_mouseRotateStart()
	, m_fLastAngle(0.0)
	, m_bhandleSelected(MOUSEHANDLE::handleNone)
	, m_isHover(false)
{
	setAcceptHoverEvents(true);
	setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable 
		| QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges);

	m_handles.insert(std::make_pair(MOUSEHANDLE::handleTopLeft, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleTopMiddle, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleTopRight, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleMiddleLeft, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleMiddleRight, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleBottomLeft, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleBottomMiddle, QRectF()));
	m_handles.insert(std::make_pair(MOUSEHANDLE::handleBottomRight, QRectF()));

	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateTopLeft, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateTopMiddle, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateTopRight, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateMiddleRight, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateBottomRight, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateBottomMiddle, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateBottomLeft, QPointF()));
	m_points.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateMiddleLeft, QPointF()));

	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateTopLeft, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateTopLeft]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateTopMiddle, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateTopMiddle]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateTopRight, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateTopRight]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateMiddleRight, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateMiddleRight]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateBottomRight, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateBottomRight]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateBottomMiddle, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateBottomMiddle]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateBottomLeft, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateBottomLeft]).scaled(c_rotate_cursor_size))));
	m_cursorRotate.insert(std::make_pair(MOUSEROTATEHANDLE::handleRotateMiddleLeft, QCursor(QPixmap(mapCursors[MOUSEROTATEHANDLE::handleRotateMiddleLeft]).scaled(c_rotate_cursor_size))));

	updateHandlesPos();
}
CustomRectItem::~CustomRectItem()
{
}

這部分我也不清楚什麼意思。

QPainterPath CustomRectItem::shape() const
{
	QPainterPath path = QPainterPath();
	path.addRect(this->rect());
	if (this->isSelected())
	{
		for (auto shape : m_handles)
			path.addEllipse(shape.second);
	}
			
	return path;
}

調整了下有效邊界,這影響着hoverEnterEvent,hoverLeaveEvent生效的範圍。c_handle_size 是調整大小的方框的大小,c_handle_space表示方框嵌入item的寬度,是個負數,正常爲c_handle_size 的一半。

QRectF CustomRectItem::boundingRect() const
{
	auto o = c_handle_size + c_handle_space;
	return this->rect().adjusted(-o, -o, o, o);
}

m_isHover 表示鼠標是否在item上。當進行放縮時,鼠標可能會出界,導致view捕捉到,並且修改指針形狀,通過判斷該參數可糾正。

void CustomRectItem::hoverEnterEvent(QGraphicsSceneHoverEvent * event)
{
	QGraphicsRectItem::hoverEnterEvent(event);
	m_isHover = true;
}
void CustomRectItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * event)
{
	setCursor(Qt::ArrowCursor);
	QGraphicsRectItem::hoverLeaveEvent(event);
	m_isHover = false;
}

這裏基於python的原文,進行了適當修改。當item進行了旋轉後,鼠標指針的方向也應該跟着改變。而且這裏有個22.5度的逆向偏移。如果不理解可以運行demo,去掉試試。比如,旋轉角度爲44度,46度時,會有一個看着彆扭。

void CustomRectItem::hoverMoveEvent(QGraphicsSceneHoverEvent * event)
{
	if (isSelected())
	{
		m_handle = handleAt(event->pos());

		if (MOUSEHANDLE::handleNone == m_handle)
			m_cursor = Qt::SizeAllCursor;
		else
		{
			float angle = this->rotation() + 22.5;
			while (angle >= 360.0)
				angle -= 360;
			// choose the right cursor
			m_cursor = handleCursors[((int)m_handle + (int)(angle / 45) - 1) % c_handle_cursors_size];
		}
		setCursor(m_cursor);
	}	
	QGraphicsRectItem::hoverMoveEvent(event);
}

判斷鼠標指針的狀態,進行調整大小。

void CustomRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
	if (MOUSEHANDLE::handleNone != m_bhandleSelected)
		interactiveResize(event->pos());
	else
		QGraphicsRectItem::mouseMoveEvent(event);
}

void CustomRectItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
	m_bhandleSelected = handleAt(event->pos());
	if (MOUSEHANDLE::handleNone != m_bhandleSelected)
	{
		m_mousePressPos = event->pos();
		m_mousePressRect = boundingRect();
	}
	QGraphicsRectItem::mousePressEvent(event);
}

void CustomRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
	m_bhandleSelected = MOUSEHANDLE::handleNone;
	m_mousePressPos = QPointF();
	m_mousePressRect = QRectF();
	this->update();
	QGraphicsRectItem::mouseReleaseEvent(event);
}

鍵盤控制平移的簡單實現。

void CustomRectItem::keyPressEvent(QKeyEvent * event)
{
	switch (event->key())
	{
	case Qt::Key_Up:
		this->moveBy(0, -1);
		event->setAccepted(true);
		break;
	case Qt::Key_Down:
		this->moveBy(0, 1);
		event->setAccepted(true);
		break;
	case Qt::Key_Left:
		this->moveBy(-1, 0);
		event->setAccepted(true);
		break;
	case Qt::Key_Right:
		this->moveBy(1, 0);
		event->setAccepted(true);
		break;
	default:
		event->setAccepted(false);
		break;
	}
	//QGraphicsRectItem::keyPressEvent(event);
}

調整四周小矩形的位置和大小。

void CustomRectItem::updateHandlesPos()
{
	auto s = c_handle_size;
	auto b = boundingRect();

	m_handles[MOUSEHANDLE::handleTopLeft] = QRectF(b.left(), b.top(), s, s);
	m_handles[MOUSEHANDLE::handleTopMiddle] = QRectF(b.center().x() - s / 2, b.top(), s, s);
	m_handles[MOUSEHANDLE::handleTopRight] = QRectF(b.right() - s, b.top(), s, s);
	m_handles[MOUSEHANDLE::handleMiddleLeft] = QRectF(b.left(), b.center().y() - s / 2, s, s);
	m_handles[MOUSEHANDLE::handleMiddleRight] = QRectF(b.right() - s, b.center().y() - s / 2, s, s);
	m_handles[MOUSEHANDLE::handleBottomLeft] = QRectF(b.left(), b.bottom() - s, s, s);
	m_handles[MOUSEHANDLE::handleBottomMiddle] = QRectF(b.center().x() - s / 2, b.bottom() - s, s, s);
	m_handles[MOUSEHANDLE::handleBottomRight] = QRectF(b.right() - s, b.bottom() - s, s, s);
}

bool CustomRectItem::isHover()
{
	return m_isHover;
}

判斷是否在可旋轉範圍內,是則返回對應的QCursor。計算應當返回什麼QCursor的思路是,先在item座標下,計算出距離最近的是哪個小方塊,然後再加上旋轉角度的偏移量,就得到了應該在視覺上看到的正確的鼠標形狀。或許有更方便的實現方法。

QCursor CustomRectItem::getRotateCursor(const QPointF & point)
{
	if (m_isHover == true || !isSelected())
		return QCursor();

	if (boundingRect().contains(mapFromScene(point)))
		return QCursor();

	auto srcRect = rect();
	auto frameRect = srcRect.adjusted(-c_rotate_tolerance, -c_rotate_tolerance, c_rotate_tolerance, c_rotate_tolerance);

	QPointF innerPoint = mapFromScene(point);

	if (!frameRect.contains(innerPoint))
		return QCursor();

	m_points[MOUSEROTATEHANDLE::handleRotateTopLeft] = srcRect.topLeft();
	m_points[MOUSEROTATEHANDLE::handleRotateTopMiddle] = QPointF(srcRect.center().x(), srcRect.top());
	m_points[MOUSEROTATEHANDLE::handleRotateTopRight] = srcRect.topRight();
	m_points[MOUSEROTATEHANDLE::handleRotateMiddleRight] = QPointF(srcRect.right(), srcRect.center().y());
	m_points[MOUSEROTATEHANDLE::handleRotateBottomRight] = srcRect.bottomRight();
	m_points[MOUSEROTATEHANDLE::handleRotateBottomMiddle] = QPointF(srcRect.center().x(), srcRect.bottom());
	m_points[MOUSEROTATEHANDLE::handleRotateBottomLeft] = srcRect.bottomLeft();
	m_points[MOUSEROTATEHANDLE::handleRotateMiddleLeft] = QPointF(srcRect.left(), srcRect.center().y());

	auto ret = MOUSEROTATEHANDLE::handleRotateNone;
	float l = 3.4028235E38;
	for (auto& iter : m_points)
	{
		auto length = getLength2(iter.second, innerPoint);
		if (length < l)
		{
			l = length;
			ret = iter.first;
		}
	}

	if (ret == MOUSEROTATEHANDLE::handleRotateNone)
		return QCursor();
	float angle = this->rotation() + 22.5;
	while (angle >= 360.0)
		angle -= 360;
	ret = MOUSEROTATEHANDLE(((int)ret + (int)( angle/ 45)) % c_rotate_cursors_size);
	if (ret == MOUSEROTATEHANDLE::handleRotateNone)
		ret = MOUSEROTATEHANDLE::handleRotateTopLeft;

	return m_cursorRotate[ret];
}

設置setRotateStart,表示開始旋轉,記錄此時的鼠標落點和角度。
設置setRotateEnd,基於上次設置的Start,進行旋轉。並且始終保持rotation在[0,360)之間。
注:point是場景座標。

void CustomRectItem::setRotateStart(const QPointF & point)
{
	m_mouseRotateStart = point;
	m_fLastAngle = rotation();
}
void CustomRectItem::setRotateEnd(const QPointF & point)
{
	QPointF ori = mapToScene(transformOriginPoint());
	QPointF v1 = m_mouseRotateStart - ori;
	QPointF v2 = point - ori;

	float angle = std::atan2f(v2.y(), v2.x()) - std::atan2f(v1.y(), v1.x());
	
	angle = m_fLastAngle + angle * 180 / 3.1415926;

	// angle = [0,360)
	while (angle < 0.0)
		angle += 360;
	while (angle >= 360.0)
		angle -= 360;

	setRotation(angle);
}

判斷鼠標位置,應爲的鼠標狀態。

CustomRectItem::MOUSEHANDLE CustomRectItem::handleAt(const QPointF& point)
{
	for (auto it : m_handles)
	{
		if (it.second.contains(point))
			return it.first;
	}
	return MOUSEHANDLE::handleNone;
}

調整大小

void CustomRectItem::interactiveResize(const QPointF & mousePos)
{
	auto offset = c_handle_size + c_handle_space;
	auto bRect = boundingRect();
	auto rr = this->rect();
	auto diff = QPointF(0, 0);

	prepareGeometryChange();

	if (m_bhandleSelected == MOUSEHANDLE::handleTopLeft)
	{
		auto fromX = m_mousePressRect.left();
		auto fromY = m_mousePressRect.top();
		auto toX = fromX + mousePos.x() - m_mousePressRect.x();
		auto toY = fromY + mousePos.y() - m_mousePressRect.y();

		if (!(toX - fromX >= rr.width() || toY - fromY >= rr.height()))
		{
			diff.setX(toX - fromX);
			diff.setY(toY - fromY);
			bRect.setLeft(toX);
			bRect.setTop(toY);
			rr.setLeft(bRect.left() + offset);
			rr.setTop(bRect.top() + offset);
			this->setRect(rr);
		}		
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleTopMiddle)
	{
		auto fromY = m_mousePressRect.top();
		auto toY = fromY + mousePos.y() - m_mousePressPos.y();

		if (!(toY - fromY >= rr.height()))
		{
			diff.setY(toY - fromY);
			bRect.setTop(toY);
			rr.setTop(bRect.top() + offset);
			this->setRect(rr);
		}	
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleTopRight)
	{
		auto fromX = m_mousePressRect.right();
		auto fromY = m_mousePressRect.top();
		auto toX = fromX + mousePos.x() - m_mousePressPos.x();
		auto toY = fromY + mousePos.y() - m_mousePressPos.y();

		if (!(fromX - toX >= rr.width() || toY - fromY >= rr.height()))
		{
			diff.setX(toX - fromX);
			diff.setY(toY - fromY);
			bRect.setRight(toX);
			bRect.setTop(toY);
			rr.setRight(bRect.right() - offset);
			rr.setTop(bRect.top() + offset);
			this->setRect(rr);
		}
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleMiddleLeft)
	{
		auto fromX = m_mousePressRect.left();
		auto toX = fromX + mousePos.x() - m_mousePressPos.x();

		if (!(toX - fromX >= rr.width()))
		{
			diff.setX(toX - fromX);
			bRect.setLeft(toX);
			rr.setLeft(bRect.left() + offset);
			this->setRect(rr);
		}		
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleMiddleRight)
	{
		auto fromX = m_mousePressRect.right();
		auto toX = fromX + mousePos.x() - m_mousePressPos.x();

		if (!(fromX - toX >= rr.width()))
		{
			diff.setX(toX - fromX);
			bRect.setRight(toX);
			rr.setRight(bRect.right() - offset);
			this->setRect(rr);
		}
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleBottomLeft)
	{
		auto fromX = m_mousePressRect.left();
		auto fromY = m_mousePressRect.bottom();
		auto toX = fromX + mousePos.x() - m_mousePressPos.x();
		auto toY = fromY + mousePos.y() - m_mousePressPos.y();

		if (!(toX - fromX >= rr.width() || fromY - toY >= rr.height()))
		{
			diff.setX(toX - fromX);
			diff.setY(toY - fromY);
			bRect.setLeft(toX);
			bRect.setBottom(toY);
			rr.setLeft(bRect.left() + offset);
			rr.setBottom(bRect.bottom() - offset);
			this->setRect(rr);
		}	
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleBottomMiddle)
	{
		auto fromY = m_mousePressRect.bottom();
		auto toY = fromY + mousePos.y() - m_mousePressPos.y();

		if (!(fromY - toY >= rr.height()))
		{
			diff.setY(toY - fromY);
			bRect.setBottom(toY);
			rr.setBottom(bRect.bottom() - offset);
			this->setRect(rr);
		}
	}
	else if (m_bhandleSelected == MOUSEHANDLE::handleBottomRight)
	{
		auto fromX = m_mousePressRect.right();
		auto fromY = m_mousePressRect.bottom();
		auto toX = fromX + mousePos.x() - m_mousePressPos.x();
		auto toY = fromY + mousePos.y() - m_mousePressPos.y();

		if (!(fromX - toX >= rr.width() || fromY - toY >= rr.height()))
		{
			diff.setX(toX - fromX);
			diff.setY(toY - fromY);
			bRect.setRight(toX);
			bRect.setBottom(toY);
			rr.setRight(bRect.right() - offset);
			rr.setBottom(bRect.bottom() - offset);
			this->setRect(rr);
		}
	}
	updateHandlesPos();
}

簡單的求兩點距離平方的函數。因爲這不屬於這個類的附屬功能,所以沒有開放。當然,聲明爲static private會更好。

float CustomRectItem::getLength2(const QPointF & point1, const QPointF & point2)
{
	return (point1.x() - point2.x()) * (point1.x() - point2.x()) + (point1.y() - point2.y()) * (point1.y() - point2.y());
}

使用實例

#pragma once

#include <QtWidgets/QWidget>
#include <QGraphicsView>
#include "CustomRectItem.h"
#include "ui_QtGuiApplication4.h"

class QtGuiApplication4 : public QGraphicsView
{
	Q_OBJECT

public:
	QtGuiApplication4(QWidget *parent = Q_NULLPTR);

	void mouseMoveEvent(QMouseEvent* event) override;
	void mousePressEvent(QMouseEvent* event) override;
	void mouseReleaseEvent(QMouseEvent* event) override;

private:
	// the length2 with point1 and point2
	float getLength2(const QPointF& point1, const QPointF& point2);

private:
	Ui::QtGuiApplication4Class ui;
	QGraphicsScene* m_scene;
	CustomRectItem* m_rect;

	volatile bool m_bTurn;
};
#include "QtGuiApplication4.h"
#include <QDebug>
#include <QMouseEvent>
#include <QTimer>
#include <QBitmap>

QtGuiApplication4::QtGuiApplication4(QWidget *parent)
	: QGraphicsView(parent)
	, m_rect(nullptr)
	, m_scene(nullptr)
	, m_bTurn(false)
{
	ui.setupUi(this);

	m_scene = new QGraphicsScene();
	setScene(m_scene);
	resize(400, 400);

	QTimer::singleShot(1000, [this]() {
		m_rect = new CustomRectItem();
		m_rect->setPos(50, 50);
		m_scene->addItem(m_rect);

		m_rect->setRect(0, 0, 50, 50);
		m_rect->setTransformOriginPoint(25, 25);
		m_rect->setRotation(45);
	});
}

void QtGuiApplication4::mouseMoveEvent(QMouseEvent * event)
{
	if (m_bTurn != true)
	{
		QCursor cursor = m_rect->getRotateCursor(mapToScene(event->pos()));
		if (!cursor.pixmap().isNull())
		{
			viewport()->setCursor(cursor);
		}
		else if (!m_rect->isHover())
		{
			viewport()->setCursor(Qt::ArrowCursor);
		}
	}
	else
	{
		m_rect->setRotateEnd(mapToScene(event->pos()));
	}

	QGraphicsView::mouseMoveEvent(event);
}

void QtGuiApplication4::mousePressEvent(QMouseEvent * event)
{
	if (!m_rect->getRotateCursor(mapToScene(event->pos())).pixmap().isNull())
	{
		m_rect->setRotateStart(mapToScene(event->pos()));
		m_bTurn = true;
	}

	QGraphicsView::mousePressEvent(event);
}

void QtGuiApplication4::mouseReleaseEvent(QMouseEvent * event)
{
	if (true == m_bTurn)
	{
		m_bTurn = false;
	}
	QGraphicsView::mouseReleaseEvent(event);
}

float QtGuiApplication4::getLength2(const QPointF & point1, const QPointF & point2)
{
	return (point1.x() - point2.x()) * (point1.x() - point2.x()) + (point1.y() - point2.y()) * (point1.y() - point2.y());
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章