二十五、图形视图框架
我们将要用到三个类,QGraphicsView(视图类)、QGraphicsScene(场景类)、QGraphicsItem(图元类)。
QGraphicsView(视图类)
继承QWidget类,与其他类一样,以窗口的左上角作为自己坐标系的原点,主要用于渲染显示场景中的图元,支持OpenGL渲染工作。
QGraphicsScene(场景类)
坐标系以中心作为自己的原点
QGraphicsItem(图元类)
如果调用paint()函数重绘图元时,则以此坐标系为基准
接下来我们实现以下的案例
该项目具有旋转、缩放、倾斜功能
首先我们创建一个QWidget工程
然后继续创建一个C++Class类,该类继承QGraphicsItem类,取名为“PixItem”
首先我们看pixitem.h文件
#ifndef PIXITEM_H
#define PIXITEM_H#include<QGraphicsItem>
#include<QPixmap>
#include<QPainter>class PixItem : public QGraphicsItem
{public:PixItem(QPixmap *pixmap);QRectF boundingRect() const;void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);private:QPixmap pix;};#endif // PIXITEM_H
QRectF QGraphicsItem::boundingRect() const:
这个纯虚函数将项目的外部边界定义为矩形;所有的绘制必须限制在一个项目的边界范围内,QGraphicsView使用这个来确定项目是否需要重画。
尽管项目的形状可以是任意的,但边界矩形始终是矩形,并且不受项目转换的影响。
如果你想改变项目的边界矩形,你必须首先调用prepareGeometryChange()。这会通知场景即将发生的更改,以便它可以更新其项目几何索引;否则,场景将不知道项目的新几何形状,并且结果是未定义的(通常,渲染工件留在视图中)。
重新实现这个函数,让QGraphicsView确定小部件的哪些部分(如果有的话)需要重新绘制。
注意:对于绘制轮廓/笔画的形状,在边界矩形中包含笔宽度的一半是很重要的,但是没有必要补偿抗锯齿。
void QGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = ...):
这个函数通常由QGraphicsView调用,它用局部坐标绘制项的内容 。
在QGraphicsItem子类中重新实现此函数,以使用painter提供该项的绘制实现。option参数为项目提供样式选项,例如其状态、暴露区域和详细级别提示。widget参数是可选的。如果提供,则指向正在绘制的小部件;否则,它是0。对于缓存的绘画,widget总是0。
pixitem.cpp
#include "pixitem.h"PixItem::PixItem(QPixmap *pixmap)
{pix=*pixmap;
}QRectF PixItem::boundingRect() const
{return QRectF(-2-pix.width()/2,-2-pix.height()/2,pix.width()+4,pix.height()+4);
}void PixItem::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{painter->drawPixmap(-pix.width()/2,-pix.height()/2,pix);
}
boundingRect()函数:该函数返回一个QRectF类型的矩形,用于描述PixItem的边界矩形。该矩形的左上角坐标为(-2-pix.width()/2,-2-pix.height()/2),宽度为pix.width()+4,高度为pix.height()+4。
paint()函数:该函数用于绘制PixItem。在该函数中,首先调用painter->drawPixmap()函数绘制pix图像,该函数的参数为(-pix.width()/2,-pix.height()/2)表示图像的左上角坐标。
widget.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QWidget>#include"pixitem.h"
#include<math.h>
#include<QGraphicsView>
#include<QGraphicsScene>
#include<QFrame>
#include<QVBoxLayout>
#include<QHBoxLayout>
#include<QGroupBox>
#include<QSlider>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QWidget
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void createControlFrameFunc(); //创建控件框架private:Ui::MainWindow *ui;int angle; //角度qreal scaleValue; //缩放qreal leanValue; //倾斜QGraphicsView *view;QFrame *controlFrame; //控制边框样式PixItem *pixitem;private slots:void rotateFunc(int); //旋转void scaleFunc(int);void leanFunc(int);
};
#endif // MAINWINDOW_H
typedef qreal:
类型定义为double,除非Qt配置了-qreal float选项。
widget.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QWidget(parent), ui(new Ui::MainWindow)
{//ui->setupUi(this);setWindowTitle("图形测试");angle=3;scaleValue=3;leanValue=3;//场景QGraphicsScene *scene=new QGraphicsScene;scene->setSceneRect(-200,-200,380,380);QPixmap *pixmap=new QPixmap("E:/blog/source/img/wallhaven-gp7mq3.jpg");//图元pixitem=new PixItem(pixmap);scene->addItem(pixitem);pixitem->setPos(0,0);//视图view=new QGraphicsView();view->setScene(scene);view->setMinimumSize(800,600);controlFrame=new QFrame;createControlFrameFunc();//主窗体布局设计QHBoxLayout *hbl=new QHBoxLayout(this);hbl->addWidget(view);hbl->addWidget(controlFrame);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::createControlFrameFunc()
{//旋转QSlider *rotateSlider=new QSlider;rotateSlider->setOrientation(Qt::Horizontal);rotateSlider->setRange(0,360);QHBoxLayout *rotateLayout=new QHBoxLayout;rotateLayout->addWidget(rotateSlider);QGroupBox *rotateGroup=new QGroupBox("图形旋转");rotateGroup->setLayout(rotateLayout);//缩放QSlider *scalesilder=new QSlider;scalesilder->setOrientation(Qt::Horizontal);scalesilder->setRange(0,2*scaleValue);scalesilder->setValue(scaleValue);QHBoxLayout *scalelayout=new QHBoxLayout;scalelayout->addWidget(scalesilder);QGroupBox *scalegroup=new QGroupBox("图形缩放");scalegroup->setLayout(scalelayout);//倾斜QSlider *leansilder = new QSlider;leansilder->setOrientation(Qt::Horizontal);leansilder->setRange(0,2*leanValue);leansilder->setValue(leanValue);QHBoxLayout *leanlayout = new QHBoxLayout;leanlayout->addWidget(leansilder);QGroupBox *leangroup = new QGroupBox(tr("图形倾斜"));leangroup->setLayout(leanlayout);connect(rotateSlider,SIGNAL(valueChanged(int)),this,SLOT(rotateFunc(int)));connect(scalesilder,SIGNAL(valueChanged(int)),this,SLOT(scaleFunc(int)));connect(leansilder,SIGNAL(valueChanged(int)),this,SLOT(leanFunc(int)));// 控制面板设计布局QVBoxLayout *vlayoutframe=new QVBoxLayout;vlayoutframe->addWidget(rotateGroup);vlayoutframe->addWidget(scalegroup);vlayoutframe->addWidget(leangroup);controlFrame->setLayout(vlayoutframe);}void MainWindow::rotateFunc(int val)
{view->rotate(val-angle);angle=val;
}void MainWindow::scaleFunc(int val)
{qreal qr;if(val>scaleValue){qr=pow(1.1,(val-scaleValue));}else{qr=pow(1/1.1,(scaleValue-val));}view->scale(qr,qr);scaleValue=val;
}void MainWindow::leanFunc(int val)
{view->shear((val-leanValue)/2.0,0);leanValue=val;
}
代码实现了一个图形界面,包含一个可以旋转、缩放、倾斜的图片。具体实现过程如下:
- 创建一个QGraphicsScene对象,设置其大小为(-200,-200,380,380)。
- 创建一个QPixmap对象,加载一张图片。
- 创建一个PixItem对象,将QPixmap对象作为参数传入,然后将PixItem对象添加到QGraphicsScene对象中。
- 创建一个QGraphicsView对象,将QGraphicsScene对象作为参数传入,然后设置QGraphicsView对象的最小大小为(800,600)。
- 创建一个QFrame对象,调用createControlFrameFunc()函数创建一个控制面板。
- 创建一个水平布局QHBoxLayout对象,将QGraphicsView对象和QFrame对象添加到该布局中。
- 将QHBoxLayout对象设置为主窗口的布局。
- 在createControlFrameFunc()函数中,创建三个QSlider对象,分别用于控制旋转、缩放、倾斜。
- 创建三个QHBoxLayout对象,将QSlider对象添加到对应的布局中。
- 创建三个QGroupBox对象,将对应的布局添加到QGroupBox对象中。
- 将三个QGroupBox对象添加到一个垂直布局QVBoxLayout对象中。
- 将QVBoxLayout对象设置为QFrame对象的布局。
- 通过connect()函数将QSlider对象的valueChanged()信号与对应的槽函数rotateFunc()、scaleFunc()、leanFunc()连接起来。
- 在rotateFunc()函数中,调用QGraphicsView对象的rotate()函数实现旋转。
- 在scaleFunc()函数中,根据QSlider对象的值计算缩放比例,然后调用QGraphicsView对象的scale()函数实现缩放。
- 在leanFunc()函数中,调用QGraphicsView对象的shear()函数实现倾斜。
void setSceneRect(qreal x, qreal y, qreal w, qreal h):
这个属性保存了场景矩形;场景的边界矩形
场景矩形定义了场景的范围。它主要由QGraphicsView用于确定视图的默认可滚动区域,并由QGraphicsScene用于管理项目索引。
如果未设置,或者设置为空QRectF, scen直立()将返回自场景创建以来场景中所有项目的最大边界矩形(即,当项目添加到场景中或在场景中移动时,矩形会增长,但不会缩小)。
void QGraphicsItem::setPos(qreal x, qreal y):
这是一个重载函数。
这个方便的函数相当于调用setPos(QPointF(x, y))。
void QGraphicsItem::setPos(const QPointF &pos):
将项目的位置设置为pos,它位于父坐标中。对于没有父项的项目,pos在场景坐标中。
项目的位置在父坐标中描述了它的原点(本地坐标(0,0))。
void QGraphicsView::setScene(QGraphicsScene *scene):
将当前场景设置为场景。如果已经在查看场景,则此函数不执行任何操作。
当在视图上设置场景时,QGraphicsScene::changed()信号自动连接到该视图的updateScene()插槽,并且视图的滚动条被调整以适应场景的大小。
视图不占有场景的所有权。
void setOrientation(Qt::Orientation):
这个属性保存滑块的方向
方向必须是Qt::Vertical(默认)或Qt::Horizontal。
void QAbstractSlider::setRange(int min, int max):
设置滑块的最小值为min,最大值为max。
如果max小于min,则min成为唯一合法的值。
void QGraphicsView::rotate(qreal angle):
顺时针旋转当前视图变换角度度。
void QGraphicsView::scale(qreal sx, qreal sy):
按(sx, sy)缩放当前视图转换。
void QGraphicsView::shear(qreal sh, qreal sv) :
通过(sh, sv)剪切当前视图转换。
pow函数为求次幂函数.