天天看點

QGraphicsView移動圖元/場景以及坐标轉換

使用Qt來畫圖的時候,需要了解一下QGraphicsView(視圖)、QGraphicsScene(場景)、QGraphicsItem(圖元),已經他們之間的關系。

通過把各種 圖元(基類都是:QGraphicsItem)組合在一起搭建成場景(QGraphicsScene),把搭建好的場景通過視圖展示出來(QGraphicsView)。

通過一個示例來展示,選中圖元的時候可以移動圖元,選中空白的時候可以移動整個場景:

QGraphicsView移動圖元/場景以及坐标轉換

1.坐标轉換

這三者之間的存在不同的坐标系,他們之間需要坐标轉換,而且隻用QGraphicsItem和QGraphicsView有坐标轉換的功能,也就是說QGraphicsItem和QGraphicsView都可以QGraphicsScene直接進行坐标轉換,但是QGraphicsItem和QGraphicsView之間的坐标轉換需要通過QGraphicsScene進行轉換。

所有的圖元QGraphicsItem都是放到QGraphicsScene中,是以QGraphicsScene是所有的圖元的父圖元。

常用的坐标轉換函數:

QGraphicsView::mapToScene() - 視圖 -> 場景

QGraphicsView::mapFromScene() -  場景 -> 視圖

QGraphicsItem::mapFromScene() -  場景 -> 圖元

QGraphicsItem::mapToScene() - 圖元 -> 場景

QGraphicsItem::mapToParent() - 子圖元 -> 父圖元

QGraphicsItem::mapFromParent() - 父圖元 -> 子圖元

QGraphicsItem::mapToItem() - 本圖元 -> 其他圖元

QGraphicsItem::mapFromItem() - 其他圖元 -> 本圖元
           

2.圖元/場景移動

如果是移動單個圖元的話,

setFlag(QGraphicsItem::ItemIsMovable, true);//可以拖動
	setFlag(QGraphicsItem::ItemIsSelectable, true);//可以選中
           

貼上代碼:

自定義圖元:

#pragma once

#include <QGraphicsEllipseItem>

class IGraphicsCoord;

class AAAEllipseItem : public QGraphicsEllipseItem {

public:
	AAAEllipseItem(QGraphicsItem *parent);
	~AAAEllipseItem();

	inline void setCoord(IGraphicsCoord* coord){
		_graphicsCoord = coord;
	}
private:
	virtual void mousePressEvent(QGraphicsSceneMouseEvent *event)Q_DECL_OVERRIDE;
	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event)Q_DECL_OVERRIDE;
	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)Q_DECL_OVERRIDE;

private:
	IGraphicsCoord* _graphicsCoord = nullptr;
	QPointF _mousePress;
};

           
#include "AAAEllipseItem.h"
#include <QDebug>
#include <QPen>
#include <QGraphicsSceneMouseEvent>
#include "IGraphicsCoord.h"


AAAEllipseItem::AAAEllipseItem(QGraphicsItem *parent)
	: QGraphicsEllipseItem(parent) {
	setFlag(QGraphicsItem::ItemIsMovable, true);
	setFlag(QGraphicsItem::ItemIsSelectable, true);

	QPen pen;
	pen.setColor(QColor(247, 99, 0));
	pen.setWidth(3);

	setPen(pen);
}

AAAEllipseItem::~AAAEllipseItem() {
}


void AAAEllipseItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
	QGraphicsEllipseItem::mousePressEvent(event);
	qDebug() << "AAAEllipseItem:" << event->pos().x() << event->pos().y();
	QPointF ptScene = mapToScene(event->pos());
	qDebug() << "AAAEllipseItem mapToScene:" << ptScene.x() << ptScene.y();
	if (_graphicsCoord != nullptr){
		_graphicsCoord->itemToSence(ptScene);
	}
}

void AAAEllipseItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
	QGraphicsEllipseItem::mouseMoveEvent(event);

}

void AAAEllipseItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
	QGraphicsEllipseItem::mouseReleaseEvent(event);

} 
           

自定義場景:

#pragma once

#include <QGraphicsScene>

class AAAGraphicsScene : public QGraphicsScene {
	Q_OBJECT

public:
	AAAGraphicsScene(QObject *parent);
	~AAAGraphicsScene();
private:
	virtual void mousePressEvent(QGraphicsSceneMouseEvent *event)Q_DECL_OVERRIDE;
	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event)Q_DECL_OVERRIDE;
	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)Q_DECL_OVERRIDE;
};

           
#include "AAAGraphicsScene.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>

AAAGraphicsScene::AAAGraphicsScene(QObject *parent)
	: QGraphicsScene(parent) {
}

AAAGraphicsScene::~AAAGraphicsScene() {
}

void AAAGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
	QGraphicsScene::mousePressEvent(event);
	qDebug() << "AAAGraphicsScene:" << event->pos().x() << event->pos().y();
}

void AAAGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
	QGraphicsScene::mouseMoveEvent(event);
	
}

void AAAGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
	QGraphicsScene::mouseReleaseEvent(event);
}

           

自定義視圖:

#pragma once

#include <QGraphicsView>
#include "IGraphicsCoord.h"

class IGraphicsCoord2;

class AAAGraphicsView : public QGraphicsView,public IGraphicsCoord {
	Q_OBJECT

public:
	AAAGraphicsView(QWidget *parent);
	~AAAGraphicsView();

	inline void setItemCoord(IGraphicsCoord2* coord){
		_graphicsCoord2 = coord;
	}

protected:
	void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
	void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
	void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

private:
	virtual void itemToSence(QPointF pt)override;
private:
	IGraphicsCoord2* _graphicsCoord2 = nullptr;
	QPoint _mouseLBtnDown;
	bool _isLBtnDown = false;
};

           
#include "AAAGraphicsView.h"
#include <QDebug>
#include <QMouseEvent>

AAAGraphicsView::AAAGraphicsView(QWidget *parent)
	: QGraphicsView(parent) {

	//隐藏水準/豎直滾動條
	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

	//設定場景範圍
	setSceneRect(INT_MIN / 2, INT_MIN / 2, INT_MAX, INT_MAX);
	//反鋸齒
	setRenderHints(QPainter::Antialiasing);
}

AAAGraphicsView::~AAAGraphicsView() {
}

void AAAGraphicsView::mousePressEvent(QMouseEvent *event) {
	QGraphicsView::mousePressEvent(event);
	qDebug() << "AAAGraphicsView:" << event->pos().x() << event->pos().y();

	if (event->button() == Qt::LeftButton){
		if (scene()->itemAt(mapToScene(event->pos()), transform()) == nullptr) {//沒有選中任何圖元
			qDebug() << QStringLiteral("沒有選中圖元");
			_mouseLBtnDown = event->pos();
			_isLBtnDown = true;
		}
	}
}

void AAAGraphicsView::mouseMoveEvent(QMouseEvent *event) {
	QGraphicsView::mouseMoveEvent(event);
	if (_isLBtnDown){
		QPointF ptNow = mapToScene(event->pos());
		QPointF movePt = ptNow - mapToScene(_mouseLBtnDown);

		//根據滑鼠目前的點作為定位點
		setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
		QPoint nowCenter(-movePt.x(),  -movePt.y());
		//qDebug() << "nowCenter:" << nowCenter.x()<< "  "<< nowCenter.y();
		centerOn((nowCenter));
		setTransformationAnchor(QGraphicsView::AnchorViewCenter);
	}
}

void AAAGraphicsView::mouseReleaseEvent(QMouseEvent *event) {
	QGraphicsView::mouseReleaseEvent(event);
	if (event->button() == Qt::LeftButton) {
		_isLBtnDown = false;
	}
}

void AAAGraphicsView::itemToSence(QPointF pt) {
	QPointF ptFromScene = mapFromScene(pt);
	qDebug() << "AAAGraphicsView mapFromSence:" << ptFromScene.x() << ptFromScene.y();
}

           

定義接口:

#pragma once
#include <QPointF>

class IGraphicsCoord {
public:
	IGraphicsCoord();
	~IGraphicsCoord();

	virtual void itemToSence(QPointF pt);
}; 
           
#include "IGraphicsCoord.h"

IGraphicsCoord::IGraphicsCoord() {
}

IGraphicsCoord::~IGraphicsCoord() {
}

void IGraphicsCoord::itemToSence(QPointF pt) {

}
           

建立場景:

void QtGuiOPenGL::init3() {
	AAAGraphicsScene* scene = new AAAGraphicsScene(this);
	AAAEllipseItem* e1 = new AAAEllipseItem(nullptr);
	e1->setRect(10, 10, 100, 100);
	scene->addItem(e1);

	AAAEllipseItem* e2 = new AAAEllipseItem(nullptr);
	e2->setRect(20, 20, 200, 100);
	scene->addItem(e2);

	scene->addLine(0.0, 0.0, 100.0, 0.0);
	scene->addLine(0.0, 0.0, 0.0, 100.0);

	AAAGraphicsView* view = new AAAGraphicsView(this);
	e1->setCoord(view);

	//view->setAlignment(Qt::AlignLeft | Qt::AlignTop);
	view->setScene(scene);

	ui.verticalLayout->addWidget(view)                                          ;
}

           

運作之後,當點選一個圖元的時候,輸出的資訊:

AAAEllipseItem: 54 43
AAAEllipseItem mapToScene: 54 43
AAAGraphicsScene: 54 43
AAAGraphicsView: 357 304
           

這也說明了,滑鼠事件傳遞的順序:

圖元-> 場景->視圖

QGraphicsItem -> QGraphicsScene -> QGraphicsView

視圖和圖元的互動是通過場景來裝換的。

aaa

繼續閱讀