采用QPainter绘图时需要在绘图设备的paintEvent()事件里编写绘图的程序,实现整个绘图过程。这种方法如同使用windos的画图软件在绘图,绘制的图形是位图,这种方法适合于绘制复杂性不高的固定图形,不能实现图件的选择,编辑,拖放,修改等功能。
Qt为绘制复杂的可交互图形提供了Graphics View绘图架构,是一种基于图形项(Graphics Item)的模型/视图模式,使用Graphics View架构可以绘制复杂的有几万个基本图形元件的图形,并且每个图形元件是可选择,可拖放和修改的,类似于矢量绘图软件的绘图功能。
Graphics View架构主要由三个部分组成,即场景和图形项;
1.场景
QGraphicsScene类提供绘图场景(scene)。场景是不可见的,是一个抽象的管理图形项的容器,可以向场景添加图形项,获取场景中的某个图形项等。场景主要具有如下一些功能:
1:提供管理大量图形项的快速接口;
2:将事件传播给每个图形项;
3:管理每个图形项的状态。例如选择状态,焦点状态等;
4:管理未经变换的渲染功能,主要用于打印
除了图形项之外,场景还有背景层和前景层,通常由QBrush指定,也可以通过重新实现drawBackground()和drawForeground()函数来实现自定义的背景和前景,实现一些特殊效果
2:视图
QGraphicsView提供绘图的视图(view)组件,用于显示场景中的内容。可以为一个场景设置几个视图,用于对同一个数据集提供不同的视口。当视图比场景大时,会显示场景中的所有内容,当视图比场景小时则只能显示场景的一部分内容,但是会自动提供卷滚条在整个场景内移动。
视图接收键盘和鼠标输入并转换为场景事件,并进行坐标转换后传送给可视场景。
3:图形项
图形项(Graphics Item)就是一些基本的图形元件,图形项的基类是QGraphicsItem。Qt提供了一些基本的图形项,如绘制椭圆的QGraphicsEllipseItem,绘制矩形的QGraphicsRectItem,绘制文字的QGraphicsTextItem等。
QGraphicsItem支持如下的一些操作:
1:支持鼠标事件响应,包括鼠标按下,移动,释放,双击,还包括鼠标停留,滚轮,快捷菜单等事件
2:支持键盘输入,按键事件;
3:支持拖放操作
4:支持组合,可以是父子项关系组合,也可以通过QGraphicsItemCroup类进行组合。
Graphics View的坐标系统
GRaphics View有三个有效的坐标系,图形坐标,场景坐标,视图坐标。场景坐标等价于QPainter的逻辑坐标,一般以场景的中心为原点;视图坐标与设备坐标相同,是物理坐标,缺省以左上角为原点;图形项坐标是局部逻辑坐标,一般以图件的中心为原点。
1:图形项使用自己的局部坐标(Item Coordinates),通常以其中心为(0,0),也是各种坐标变换的中心。图形项的鼠标事件的坐标是用局部坐标表示的,创建自定义图形项,绘制图形项时只需要考虑其局部坐标,QGaphicsScene和QGraphicsView会自动进行坐标转换。
一个图形项的位置是其中心点在父坐标系统中的坐标,对于没有父图形项的图形项,其父对象就是场景,图形项的位置就是在场景中的坐标。如果一个图形项的位置还是其他图形项的父项,父项进行坐标变化时,子项也做同样的坐标变换。
2:视图坐标(view Coordinates)就是窗口界面的物理坐标,单位是像素。视图坐标只与widget或视口有关,而与观察的场景无关。QGraphicsView视口的左上角坐标总是(0,0),
所有的塑标事件,拖放事件的坐标是由视图坐标定义的,然后用户需要讲这些坐标映射为场景坐标,以便和图形交互。
3:场景坐标
场景是所有图形项的基础坐标,场景坐标描述了每个顶层图形项的位置创建场景时可以定义场景矩形区的坐标范围,例如: scenenew QGraphicsScene(-400,-300,800,600);这样定义的scene是左上角坐标为(-400,-300),宽度为800,高度为600的矩形区域,单位是像素。每个图形项在场景里都有一个位置坐标,由函数QGraphicsItem::scenePos()给出;还有一个图形项边界矩形,由QGraphicsItem::sceneBoundingRect()函数给出。边界矩形可以使QGraphicsScene::changed()信号,参数是一个场景的矩形列表,表示发生变化的矩形区。
4:坐标映射
在场景中操作图形项时,进行场景到图形项,图形项到图形项,或视图到场景之间的坐标变换是比较有用的,即坐标映射(Coordinate Mapping).例如,在QGraphicsView的视口上单击鼠标时,通过函数QGraphicsView::mapToScene()可以将视图坐标映射为场景坐标,然后用QGraphicsScene::item()函数可以获取场景中鼠标光标处的图形项。
Graphics View(视图)相关的类
GraphicsView是用于观察一个场景的物理窗口,当场景小于视图时,整个场精在视图中可见;当场景大于视图时,视图自动提供卷滚条。
QGraphicsView的视口坐标等于显示设备的物理坐标,但是也可以对QGraphicsView的坐标进行平移,旋转,缩放等变换;
QGraphicsView主要函数功能说明:
分组 函数 功能描述
场景 void setScene() 设置关联显示的场景
void setSceneRect() 设置场景在视图中可视的部分的矩形区域
外观 void setAlignment() 设置场景在视图中的对齐方式,缺省是上下都居中
void setBackgroundBrush() 设置场景的背景画刷
void setForegroundBrush() 设置场景的前景画刷
void setRendHints() 设置视图的绘图选项
交互 void setInteractive() 是否允许场景可交互,如果禁止交互,则任何键盘或鼠标操 作都被忽略返回选择矩形框
RRect rubberBandRect() 返回选择矩形
void setRubberBandRect() 返回选择矩形
void setRubberBandSelectionMode() 选择模式,参数为枚举类型Qt::ItemSelectionMode
QGraphicsItem* itemAt() 获取视图坐标系中某个位置处的图形项的列表
坐标映射 QPoint mapFromScene() 将场景中的一个坐标转换为视图的坐标
QPoint mapToScene() 将视图中的一个坐标转换为场景的坐标
QGraphicsScene类的主要接口函数
QGraphicsScene是用于管理图形项的场景,是图形项的容器,有添加,删除图形项的函数,管理图形项的各种函数。
3:图形项
QGraphicsItem是所有图形项的基类,同时我们也可以继承定义自己的图形项。Qt定义了一些常见的图形项,这些常见的图形项的类的继承关系如下图:
QGraphicsItem类提供了图形项的基本操作,常见的函数如下:
setFlags()函数可以设置一个图形项的操作标志,包括可选择,可移动,可获取焦点等,如:
item->setFlags(QGraphicsItem::itemIsMoveable | QGraphicsItem::ItemSelectable | QGraphicsItem::ItemFocusable);
setPos()函数设置图形项在父项中的坐标,如果没有父项,则就是在场景中的坐标;
setZValue()控制图像系那个的叠放顺序,当有多个图形项重叠时,ZValue越大的,越显示在前面
图形项还可以通过setrRotation()进行旋转,通过setScale()进行缩放。
#include
#include
#include
#include
#include
class OWGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit OWGraphicsView(QWidget *parent = nullptr);
protected:
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
signals:
void mouseClicked(QPoint point);
void mouseMovePoint(QPoint point);
};
#include "owgraphicsview.h"
//OWGraphicsView::QWidget(QWidget *parent) : QWidget(parent)
//{
//
//}
OWGraphicsView::OWGraphicsView(QWidget *parent):QGraphicsView(parent)
{
}
void OWGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
//鼠标移动事件
if(event->button()==Qt::LeftButton)
{
QPoint point=event->pos();
//发射信号
emit mouseClicked(point);
}
QGraphicsView::mousePressEvent(event);
}
void OWGraphicsView::mousePressEvent(QMouseEvent *event)
{
//鼠标按下事件
if(event->button()==Qt::LeftButton)
{
QPoint point=event->pos();//QGraphicsView的坐标
emit mouseClicked(point);//发射信号
}
QGraphicsView::mousePressEvent(event);
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
private:
QGraphicsScene *scene;
QLabel *labViewCord;
QLabel *labSceneCord;
QLabel *labItemCord;
private:
void iniGraphicsSystem();//创建Graphics View的各项
protected:
void resizeEvent(QResizeEvent *event);
private slots:
void on_mouseMovePoint(QPoint point);
void on_mouseClicked(QPoint point);
};
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
labViewCord=new QLabel("View坐标");
labViewCord->setMinimumWidth(150);
ui->statusbar->addWidget(labViewCord);
labSceneCord=new QLabel("Scene坐标");
labSceneCord->setMinimumWidth(150);
ui->statusbar->addWidget(labSceneCord);
labItemCord=new QLabel("Item坐标");
labItemCord->setMinimumWidth(150);
ui->statusbar->addWidget(labItemCord);
ui->View->setCursor(Qt::CrossCursor);
ui->View->setMouseTracking(true);
QObject::connect(ui->View,SIGNAL(mouseMovePoint(QPoint)),
this,SLOT(on_mouseMovePoint(QPoint)));
QObject::connect(ui->View,SIGNAL(mouseClicked(QPoint)),
this,SLOT(on_mouseClicked(QPoint)));
iniGraphicsSystem();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::iniGraphicsSystem()
{
QRectF rect(-200,-100,400,200);
//scene逻辑坐标定义
scene=new QGraphicsScene(rect);
ui->View->setScene(scene);
QGraphicsRectItem *item=new QGraphicsRectItem(rect);
item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
QPen pen;
pen.setWidth(2);
item->setPen(pen);
scene->addItem(item);
//画一个位于scene中心的椭圆,测试局部坐标
QGraphicsEllipseItem *item2=new QGraphicsEllipseItem(-100,-50,200,100);
item2->setPos(0,0);
item2->setBrush(QBrush(Qt::blue));
item2->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
scene->addItem(item2);
//画一个圆,中心位于scene的边缘
QGraphicsEllipseItem *item3=new QGraphicsEllipseItem(-50,-50,100,100);
item3->setPos(rect.right(),rect.bottom());
item3->setBrush(QBrush(Qt::red));
item3->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
scene->addItem(item3);
scene->clearSelection();
}
void MainWindow::resizeEvent(QResizeEvent *event)
{
//窗口变化大小时的事件;
QString str="Graphics View坐标,左上角总是(0,0),";
ui->labViewSize->setText(QString::asprintf("Graphics View坐标,左上角总是(0,0),"
"宽度=%d,高度=%d",ui->View->width(),
ui->View->height()));
QRectF rectF=ui->View->sceneRect();
ui->labSceneRect->setText(QString::asprintf("QGraphicsView::sceneRect="
"(Left,Top,Width,Height)=%.0f,%.0f,%.0f,%.0f"
,rectF.left(),rectF.top(),
rectF.width(),rectF.height()));
}
void MainWindow::on_mouseMovePoint(QPoint point)
{
//鼠标移动事件,point是GRaphicsView的坐标,物理坐标
labViewCord->setText(QString::asprintf("View坐标:%d,&d",point.x(),point.y()));
QPointF pointScene=ui->View->mapToScene(point);//转换到Scene坐标
labSceneCord->setText(QString::asprintf("Scene坐标:&.0f,%.0f",
pointScene.x(),pointScene.y()));
}
void MainWindow::on_mouseClicked(QPoint point)
{
//鼠标单击事件
QPointF pointScene=ui->View->mapToScene(point);//转换到Scene坐标
QGraphicsItem *item=NULL;
item=scene->itemAt(pointScene,ui->View->transform());//获取光标下的图形项
if(item!=NULL)//有图形项
{
QPointF pointItem=item->mapFromScene(pointScene);//图形项局部坐标
labItemCord->setText(QString::asprintf("Item坐标:%.0f,%.0f",
pointItem.x(),pointItem.y()));
}
}