Qt5入门学习——图形视图框架

简介: Qt5入门学习——图形视图框架

文章目录

图形视图体系结构

Graphics View的特点

Graphics View的三元素

QGraphicsView的坐标系统

图形视图

飞舞的蝴蝶【示例】

地图浏览器【示例】

图元创建【示例】

图元的旋转、缩放、切变和位移【示例】


Graphics View(图形视图)框架结构取代了之前Qt版本中的QCanvas模块,它提供了基于图元的模型/视图编程,类似于QtInterView的模型/视图结果,只是这里的数据是图形。


图形视图体系结构

Graphics View框架结构的特点主要包含元素及坐标系统。


Graphics View的特点

Graphics View框架结构的主要特点如下:


  • Graphics View框架结构中,系统可以利用Qt绘图系统的反锯齿、OpenGL工具来改善绘图性能。
  • Graphics View支持事件传播体系,可以使图元在场景(scene)中的交互能力提高1倍,图元能够处理键盘事件和鼠标事件。其中,鼠标事件包含鼠标按下、移动、释放和双击,还可以跟踪鼠标的移动。
  • 在Graphics View框架中,通过二元空间划分树(Binary Space Partitioning, BSP)提供快速的图元查找,这样能够实时显示包含上百万个图元的大场景。

Graphics View的三元素

Graphics View框架结构主要包含三个类:场景类(QGraphicsScene)、视图类(QGraphicsView)和图元类(QGraphicsItem),统称为“三元素”。其中,场景类提供了一个用于管理位于其中的众多图元容器,视图类用于显示场景中的图元,一个场景可以通过多个视图表现,一个场景包括多个几何图形。它们三者这间关系如下:

20200315092208918.png


  • 场景类:QGraphicsScene类

这是一个用于放置图元的容器,本身是不可见的,必须通过与之相连的视图类来显示及与外界进行互操作。通过 QGraphicsScene::addItem()可以添加一个图元到场景中。图元可以通过多个函数进行检索。QGraphicsScene::items()和一些重载函数可以返回和点、矩形、多边形或向量路径相交的所有图元。QGraphicsScene::itemAt()返回指定点的顶层图元。

场景类主要完成的工作包括提供对它包含的图元的操作接口和传递事件、管理各个图元的状态(如选择和焦点处理)、提供无变换的绘制功能(如打印)等。

事件传播体系结构将场景事件发送给图元,同时管理图元之间的事件传递。如果场景接收到了在某一点的鼠标单击事件,场景会将事件传给这一点的图元。

管理各个图元的状态(如选择和焦点处理)。可以通过QGraphicsScene::setSelectionArea()函数旋转图元,选择区域可以是任意的形状,使用QPainterPath表示。若要得到当前选择的图元列表,则可以使用函数QGraphicsScene::selectedItems()。可以通过QGraphicsScene::setFocusItem()函数或QGraphicsScene::setFocus()函数来设置图元的焦点,获得当前具有焦点的图元使用函数QGraphicsScene::focusItem()。

如果需要将场景内容绘制到特定的绘图设备,则可以使用QGraphicsScene::render()函数在绘图设备上绘制场景。


  • 视图类:QGraphicsView类

它提供一个可视的窗口,用于显示场景的图元。在同一个场景中可以有多个视图,也可以为相同的数据提供几种不同的视图。

QGraphicsView是可滚动的窗口部件,可以提供滚动条来浏览大的场景。如果需要使用OpenGL,则可以使用QGraphicsView::setViewport()将视图设置成为QGLWideget。

视图接收键盘和鼠标的输入事件,并将它们翻译为场景事件(将坐标转换为场景的坐标)。使用变换矩阵函数QGraphicsView::matrix()可以变换场景的坐标,实现场景的缩放和旋转。QGraphicsView提供QGraphicsView::mapToScene()和QGraphicsView::mapFromScene()用于与场景的坐标进行转换。


  • 图元类:QGraphicsItem类

它是场景中各个图元的基类,在它的基础上可以继承出各种图元类,Qt已经预置的包括直线(QGraphicsLineItem)、椭圆(QGraphicsEllipseItem)、文本图元(QGraphicsTextItem)、矩形(QGraphicsRectItem)等。当然,也可以在QGraphicsItem类的基础上实现自定义的图元类。用户可以继承QGraphicsItem实现符合自己需要的图元。

图元有自己的坐标系,也提供场景和图元。图元还可以通过QGraphicsItem::matrix()来进行自身的变换,可以包含子图元。

QGraphicsItem主要有以下功能:


  • 处理鼠标按下、移动、释放、双击、悬停、滚轮和右键菜单事件。
  • 处理键盘输入事件。
  • 处理拖拽事件。
  • 分组。
  • 碰撞检测。

QGraphicsView的坐标系统

QGraphicsView坐标基于笛卡尔坐标系,一个图元的场景具有x坐标和y坐标。当使用没有变换的视图观察场景时,场景中的一个单元对应屏幕上的一个像素。

三个Graphics View基本类有各自不同的坐标系,场景坐标、视图坐标和图元坐标。Graphics View提供了三个坐标系统之间的转换函数。在绘制图形时,Graphics View的场景坐标对应QPainter的逻辑坐标、视图坐标和设备坐标。


  • 场景坐标

场景坐标是所有图元的基础坐标系统。场景坐标系统描述了顶层的图元,每个图元都有场景坐标和相应的包容框。场景坐标的原点在场景中心,坐标原点是x轴正方形向右,y轴正方向向下。

QGraphicsScene类的坐标系以中心为原点(0,0)。

20200315165854991.png

  • 视图坐标

视图坐标是窗口部件的坐标。视图坐标的单位是像素。QGraphicsView视图的左上角(0,0),x轴正方向向右,y轴正方向向下。所有的鼠标事件最开始都是使用视图坐标。

QGraphicsView类继承自QWidget类,因此它与其他的QWidget类一样,以窗口的左上角作为自己坐标系的原点。

20200315165809739.png

  • 图元坐标

图元使用自己的本地坐标,这个坐标系统通常以图元中心为原点,这也是所有变换的原点。图元坐标方向是x轴正方向向右,y轴正方向向下。创建图元后,只需要注意图元坐标即可,QGraphicsScene和QGraphicsView会完成所有的变换。

QGraphicsItem类的坐标系,若在调用QGraphicsView类的paint()函数重绘图元时,则以此坐标系为基准,如下:

20200315170253865.png

根据需要,Qt提供了这三个坐标系之间的互相转换函数,以及图元与图元之间的转换函数,若需要从QGraphicsItem坐标系中的某一点坐标转换到场景中的坐标,则可调用QGraphicsItem的mapToScene()函数进行映射。而QGraphicsItem的mapToParent()函数则可将QGraphicsItem坐标系中的某点坐标映射到它的上一级坐标系中,有可能是场景坐标,也可能是另一个QGraphicsItem坐标。

Graphics View框架提供了多种坐标变换函数。如下:


映射函数 转换类型

QGraphicsView::mapToScene 视图到场景

QGraphicsView::mapFromScene 场景到视图

QGraphicsItem::mapFromScene 场景到图元

QGraphicsItem::mapToScene 图元到场景

QGraphicsItem::mapToParent 子图元到父图元

QGraphicsItem::mapRectFromParent 父图元到子图元

QGraphicsItem::mapToItem 本图元到其他图元

QGraphicsItem::mapRectFromItem 其他图元到本图元

图形视图

同示例介绍如何进行自定义QGraphicsItem,以及如何通过利用定时器来实现QGraphicsItem动画效果。


飞舞的蝴蝶【示例】

设计界面,一个蝴蝶在屏幕上不停地上下飞舞。


  • 新建一个Qt Widgets Application项目,项目名称为“Butterfly”,基类选择“QMainWindow”,类名命名为“mainwindow”,取消“创建界面”复选框。
  • 添加新建类“Butterfly”,基类旋转“QObject”。
  • butterfly.h
#ifndef BUTTERFLY_H
#define BUTTERFLY_H
#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QGraphicsScene>
#include <QGraphicsView>
class Butterfly : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Butterfly(QObject *parent = nullptr);
    void timerEvent(QTimerEvent *event);//定时器实现动画的原理是在定时器的timerEvent()中对QGraphicsItem进行重绘
    QRectF boundingRect()const;//为图元限定区域范围,所有继承自QGraphicsItem的自定义图元都必须实现此函数
signals:
public slots:
protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr);//重绘函数
private:
    bool up;
    QPixmap pix_up;
    QPixmap pix_down;
    qreal angle;
};
#endif // BUTTERFLY_H


  • butterfly.cpp
#include "butterfly.h"
#include <math.h>
const static double PI = 3.1416;
Butterfly::Butterfly(QObject *parent) : QObject(parent)
{
    up = true;//标志蝴蝶翅膀位置的变量赋初值
    pix_up.load("up.png");//加载图片
    pix_down.load("down.png");
    startTimer(100);
}
QRectF Butterfly::boundingRect() const
{
    qreal adjust = 2;
    return QRectF(-pix_up.width()/2 - adjust, -pix_up.height()/2-adjust, pix_up.width() + adjust*2, pix_up.height() + adjust*2);
}
void Butterfly::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    if(up)
    {
        painter->drawPixmap(boundingRect().topLeft(), pix_up);
        up = !up;
    }
    else{
        painter->drawPixmap(boundingRect().topLeft(), pix_down);
        up = !up;
    }
}
void Butterfly::timerEvent(QTimerEvent *event)
{
    //边界控制
    qreal edgex = scene()->sceneRect().right() + boundingRect().width() / 2;//限定蝴蝶飞舞的右边界
    qreal edgetop = scene()->sceneRect().top() + boundingRect().height() / 2;//限定蝴蝶飞舞的上边界
    qreal edgebottom = scene()->sceneRect().bottom() + boundingRect().height() / 2;//限定蝴蝶飞舞的下边界
    if(pos().x() >= edgex)//超过右边界,水平回到最左边
        setPos(scene()->sceneRect().left(), pos().y());
    if(pos().y() <= edgetop)//超过上边界,回到底边
        setPos(pos().x(), scene()->sceneRect().bottom());
    if(pos().y() >= edgebottom)//超过底边,回到上边界
        setPos(pos().x(), scene()->sceneRect().top());
    angle += (qrand() % 10) / 20.0;
    qreal dx = fabs(sin(angle * PI) * 10.0);
    qreal dy = fabs(qrand()%20) - 10.0;
    setPos(mapToParent(dx, dy));//完成飞行路径。
}


  • main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QGraphicsScene>
#include "butterfly.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QGraphicsScene *scene = new QGraphicsScene;
    scene->setSceneRect(QRectF(-200, -200, 400, 400));
    Butterfly *butterfly = new Butterfly;
    butterfly->setPos(-100, 0);
    scene->addItem(butterfly);
    QGraphicsView *view = new QGraphicsView;
    view->setScene(scene);
    view->resize(400, 400);
    view->show();
//    MainWindow w;
//    w.show();
    return a.exec();
}


  • 执行结果

20200315175750515.gif

地图浏览器【示例】

实现一个地图浏览器的基本功能,来介绍使用Graphics View框架。


  • 新建Qt Widgets Application,项目名称“MapWidget”,基类选择“QMainWindow”,类名命名为“MainWindow”,取消“创建界面”复选框。
  • 新建类“MapWidget”,继承基类“QGraphicsView”。
  • mapwidget.h
#ifndef MAPWIDGET_H
#define MAPWIDGET_H
#include <QObject>
#include <QGraphicsView>
#include <QMouseEvent>
#include <QLabel>
class MapWidget : public QGraphicsView
{
    Q_OBJECT
public:
    MapWidget();
    void readMap();//读取地图信息
    QPointF mapToMap(QPointF);//用于实现场景坐标系与地图坐标之间的映射,以获得某点的经纬度值。
public slots:
    void slotZoom(int);
protected:
    void drawBackground(QPainter *painter, const QRectF &rect);//完成地图显示功能
    void mouseMoveEvent(QMouseEvent *event);
private:
    QPixmap map;
    qreal zoom;
    QLabel *viewCoord;
    QLabel *sceneCoord;
    QLabel *mapCoord;
    double x1, y1;
    double x2, y2;
};
#endif // MAPWIDGET_H


  • mapwidget.cpp
#include "mapwidget.h"
#include <QSlider>
#include <QGridLayout>
#include <QFile>
#include <QTextStream>
#include <QGraphicsScene>
#include <math.h>
MapWidget::MapWidget()
{
    //读取地图信息
    readMap();//用于读取描述地图信息的文件
    zoom = 50;
    int width = map.width();
    int height = map.height();
    QGraphicsScene *scene = new QGraphicsScene(this);//新建一个对象为主窗口连接一个场景
    //限定场景的显示区域为地图的大小
    scene->setSceneRect(-width/2, -height/2, width, height);
    setScene(scene);
    setCacheMode(CacheBackground);
    //用于地图缩放的滑动条
    QSlider *slider = new QSlider;
    slider->setOrientation(Qt::Vertical);
    slider->setRange(1, 100);
    slider->setTickInterval(10);
    slider->setValue(50);
    connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slotZoom(int)));
    QLabel *zoominLabel = new QLabel;
    zoominLabel->setScaledContents(true);
    zoominLabel->setPixmap(QPixmap("zoomin.png"));
    QLabel *zoomoutLabel = new QLabel;
    zoomoutLabel->setScaledContents(true);
    zoomoutLabel->setPixmap(QPixmap("zoomout.png"));
    //坐标值显示区
    QLabel *label1 = new QLabel(tr("GraphicsView:"));
    viewCoord = new QLabel;
    QLabel *label2 = new QLabel(tr("GraphicsScene:"));
    sceneCoord = new QLabel;
    QLabel *label3 = new QLabel(tr("map:"));
    mapCoord = new QLabel;
    //坐标显示区布局
    QGridLayout *gridLayout = new QGridLayout;
    gridLayout->addWidget(label1, 0, 0);
    gridLayout->addWidget(viewCoord, 0, 1);
    gridLayout->addWidget(label2, 1, 0);
    gridLayout->addWidget(sceneCoord, 1, 1);
    gridLayout->addWidget(label3, 2, 0);
    gridLayout->addWidget(mapCoord, 2, 1);
    gridLayout->setSizeConstraint(QLayout::SetFixedSize);
    QFrame *coordFrame = new QFrame;
    coordFrame->setLayout(gridLayout);
    //缩放控制子布局
    QVBoxLayout *zoomLayout = new QVBoxLayout;
    zoomLayout->addWidget(zoominLabel);
    zoomLayout->addWidget(slider);
    zoomLayout->addWidget(zoomoutLabel);
    //坐标显示区域布局
    QVBoxLayout *coordLayout = new QVBoxLayout;
    coordLayout->addWidget(coordFrame);
    coordLayout->addStretch();
    //主布局
    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->addLayout(zoomLayout);
    mainLayout->addLayout(coordLayout);
    mainLayout->addStretch();
    mainLayout->setMargin(30);
    mainLayout->setSpacing(10);
    setLayout(mainLayout);
    setWindowTitle(tr("Map Widget"));
    setMinimumSize(600, 400);
}
void MapWidget::readMap()
{
    QString mapName;
    QFile mapFile("maps.text");//新建一个QFile对象, 描述地图信息文本
    int ok = mapFile.open(QIODevice::ReadOnly);//以只读方式打开文件
    if(ok)
    {
        QTextStream ts(&mapFile);
        if(!ts.atEnd())
        {
            ts >> mapName;
            ts >> x1 >> y1 >> x2 >> y2;
        }
    }
    map.load(mapName);//将地图读取到私有变量map中。
}
void MapWidget::slotZoom(int value)
{
    qreal s;
    if(value > zoom)
    {//放大
        s = pow(1.01, (value - zoom));
    }
    else
    {//缩小
        s = pow(1/1.01, (zoom - value));
    }
    scale(s, s);
    zoom = value;
}
void MapWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
    painter->drawPixmap(int(sceneRect().left()), int(sceneRect().top()), map);
}
void MapWidget::mouseMoveEvent(QMouseEvent *event)
{
    //QGraphicsView坐标
    QPoint viewPoint = event->pos();
    viewCoord->setText(QString::number(viewPoint.x()) + "," + QString::number(viewPoint.y()));
    //QGraphicsScene坐标
    QPointF scenePoint = mapToScene(viewPoint);
    sceneCoord->setText(QString::number(scenePoint.x()) + "," + QString::number(scenePoint.y()));
    //地图坐标
    QPointF latLon = mapToMap(scenePoint);
    mapCoord->setText(QString::number(latLon.x()) + "," + QString::number(latLon.y()));
}
QPointF MapWidget::mapToMap(QPointF p)
{
    QPointF latLon;
    qreal w = sceneRect().width();
    qreal h = sceneRect().height();
    qreal lon = y1 - ((h/2 + p.y()) * abs(y1 - y2)/h);
    qreal lat = x1 + ((w/2 + p.x()) + abs(x1 - x2)/w);
    latLon.setX(lat);
    latLon.setY(lon);
    return latLon;
}


  • main.cpp
#include "mainwindow.h"
#include <QApplication>
#include "mapwidget.h"
#include <QFont>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QFont font("ARPL KaitiM GB", 12);
    font.setBold(true);
    a.setFont(font);
    MapWidget mapWidget;
    mapWidget.show();
//    MainWindow w;
//    w.show();
    return a.exec();
}


  • 执行结果

20200315184139828.png

图元创建【示例】

通过一个示例来演示各个图元的创建。代码结构如下:

20200318101535917.png

具体代码如下:


  • main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}


  • mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QMenuBar>
#include <QGraphicsEllipseItem>
#include "flashitem.h"
#include "startitem.h"
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    //初始化场景
    void initScene();
    //创建主窗体的所有动作
    void createActions();
    //创建主窗体的菜单栏
    void createMenus();
public slots:
    void slotNew();//新建显示窗体
    void slotClear();//清除场景中所有的图元
    void slotAddEllipseItem();//添加一个椭圆形图元
    void slotAddPolygonItem();//添加一个多边形图元
    void slotAddTextItem();           //在场景中加入一个文字图元
    void slotAddRectItem();           //在场景中加入一个长方形图元
    void slotAddAlphaItem();          //在场景中加入一个透明蝴蝶图片
    void slotAddFlashItem();//添加flash图元
    void slotAddAnimationItem();//添加动画图元
private:
    QGraphicsScene *scene;
    QAction *newAct;
    QAction *clearAct;
    QAction *exitAct;
    QAction *addEllipseItemAct;
    QAction *addPolygonItemAct;
    QAction *addTextItemAct;
    QAction *addRectItemAct;
    QAction *addAlphaItemAct;
    QAction *addFlashItemAct;
    QAction *addAnimItemAct;
};
#endif // MAINWINDOW_H


  • mainwindow.cpp
#include "mainwindow.h"
#include <QGraphicsItemAnimation>
#include <QTimeLine>
#include <QList>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //创建主窗体的所有动作
    createActions();
    //创建主窗体的菜单栏
    createMenus();
    scene = new QGraphicsScene;
    scene->setSceneRect(-200, -200, 400, 400);
    //初始化场景
    initScene();
    QGraphicsView *view = new QGraphicsView;
    view->setScene(scene);
    view->setMinimumSize(400, 400);
    view->show();
    setCentralWidget(view);
    resize(550, 450);
    setWindowTitle(tr("Graphics Items"));
}
MainWindow::~MainWindow()
{
}
void MainWindow::createActions()
{
    //创建主窗体的所有动作
    newAct = new QAction(tr("新建"), this);
    clearAct = new QAction(tr("清除"), this);
    exitAct = new QAction(tr("退出"), this);
    addEllipseItemAct = new QAction(tr("加入 椭圆"),this);
    addPolygonItemAct = new QAction(tr("加入 多边形"),this);
    addTextItemAct = new QAction(tr("加入 文字"),this);
    addRectItemAct = new QAction(tr("加入 长方形"),this);
    addAlphaItemAct = new QAction(tr("加入 图片"),this);
    connect(newAct,SIGNAL(triggered()),this,SLOT(slotNew()));
    connect(clearAct,SIGNAL(triggered()),this,SLOT(slotClear()));
    connect(exitAct,SIGNAL(triggered()),this,SLOT(close()));
    connect(addEllipseItemAct,SIGNAL(triggered()),this,SLOT (slotAddEllipseItem()));
    connect(addPolygonItemAct,SIGNAL(triggered()),this,SLOT (slotAddPolygonItem()));
    connect(addTextItemAct,SIGNAL(triggered()),this,SLOT (slotAddTextItem()));
    connect(addRectItemAct,SIGNAL(triggered()),this,SLOT (slotAddRectItem()));
    connect(addAlphaItemAct,SIGNAL(triggered()),this,SLOT (slotAddAlphaItem()));
    addFlashItemAct = new QAction(tr("加入闪烁圆"),this);
    connect(addFlashItemAct,SIGNAL(triggered()),this,SLOT(slotAddFlashItem()));
    addAnimItemAct = new QAction(tr("加入 移动星星"),this);
    connect(addAnimItemAct,SIGNAL(triggered()),this,SLOT(slotAddAnimationItem()));
}
void MainWindow::createMenus()
{
    QMenu *fileMenu = menuBar()->addMenu(tr("文件"));
    fileMenu->addAction(newAct);
    fileMenu->addAction(clearAct);
    fileMenu->addSeparator();
    fileMenu->addAction(exitAct);
    QMenu *itemMenu = menuBar()->addMenu(tr("元素"));
    itemMenu->addAction(addEllipseItemAct);
    itemMenu->addAction(addPolygonItemAct);
    itemMenu->addAction(addTextItemAct);
    itemMenu->addAction(addRectItemAct);
    itemMenu->addAction(addAlphaItemAct);
    itemMenu->addAction(addFlashItemAct);
    itemMenu->addAction(addAnimItemAct);
}
void MainWindow::initScene()
{
    //初始化场景
    for(int i = 0; i < 3; i++)
        slotAddEllipseItem();
    for(int i = 0; i < 3; i++)
        slotAddPolygonItem();
    for(int i = 0; i < 3; i++)
        slotAddTextItem();
    for(int i = 0; i < 3; i++)
        slotAddRectItem();
    for(int i = 0; i < 3; i++)
        slotAddAlphaItem();
    for(int i = 0; i < 3; i++)
        slotAddFlashItem();
    for(int i = 0; i < 3; i++)
        slotAddAnimationItem();
}
void MainWindow::slotNew()
{
    //新建一个显示窗体
    slotClear();
    initScene();
    MainWindow *newWin = new MainWindow;
    newWin->show();
}
void MainWindow::slotClear()
{
    //清除场景中的所有图元
    QList<QGraphicsItem*> listItem = scene->items();
    while(!listItem.empty())
    {
        scene->removeItem(listItem.at(0));
        listItem.removeAt(0);
    }
}
void MainWindow::slotAddEllipseItem()
{
    //在场景中加入椭圆形图元
    QGraphicsEllipseItem *item = new QGraphicsEllipseItem(QRectF(0, 0, 80, 60));
    item->setPen(Qt::NoPen);
    item->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
    item->setFlag(QGraphicsItem::ItemIsMovable);
    scene->addItem(item);
    item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddPolygonItem()
{
    QVector<QPoint> v;
    v << QPoint(30, -15) << QPoint(0, -30) << QPoint(-30, -15) << QPoint(-30, 15) << QPoint(0, 30) << QPoint(30, 15);
    QGraphicsPolygonItem *item = new QGraphicsPolygonItem(QPolygonF(v));
    item->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
    item->setFlag(QGraphicsItem::ItemIsMovable);
    scene->addItem(item);
    item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddTextItem()
{
    //在场景中添加一个文字图元
    QFont font("Times", 16);
    QGraphicsTextItem *item = new QGraphicsTextItem("Hello Qt!");
    item->setFont(font);
    item->setFlag(QGraphicsItem::ItemIsMovable);
    item->setDefaultTextColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
    scene->addItem(item);
    item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddRectItem()
{
    //添加长方形图元
    QGraphicsRectItem *item = new QGraphicsRectItem(QRectF(0, 0, 60, 60));
    QPen pen;
    pen.setColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
    item->setPen(pen);
    item->setBrush(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
    item->setFlag(QGraphicsItem::ItemIsMovable);
    scene->addItem(item);
    item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddAlphaItem()
{
    //添加一个透明蝴蝶图片
    QGraphicsPixmapItem *item = scene->addPixmap(QPixmap("image.png"));
    item->setFlag(QGraphicsItem::ItemIsMovable);
    item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddFlashItem()
{
    //添加闪烁图元
    FlashItem *item = new FlashItem;
    scene->addItem(item);
    item->setPos((qrand() % int(scene->sceneRect().width())) - 200, (qrand() % int(scene->sceneRect().height())) - 200);
}
void MainWindow::slotAddAnimationItem()
{
    //添加动画星星
    StartItem *item = new StartItem;
    QGraphicsItemAnimation *anim = new QGraphicsItemAnimation;
    anim->setItem(item);
    QTimeLine *timeLine = new QTimeLine(4000);
    timeLine->setCurveShape(QTimeLine::SineCurve);
    timeLine->setLoopCount(0);
    anim->setTimeLine(timeLine);
    int y = (qrand() % 400) - 200;
    for(int i = 0; i < 400; i++)
    {
        anim->setPosAt(i/400.0, QPointF(i-200, y));
    }
    timeLine->start();
    scene->addItem(item);
}


  • startitem.h
#ifndef STARTITEM_H
#define STARTITEM_H
#include <QGraphicsItem>
#include <QPainter>
class StartItem : public QGraphicsItem
{
public:
    StartItem();
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr);
private:
    QPixmap pix;
};
#endif // STARTITEM_H


  • startitem.cpp
#include "startitem.h"
StartItem::StartItem()
{
    pix.load("start.png");
}
QRectF StartItem::boundingRect() const
{
    return QRectF(-pix.width()/2, -pix.height()/2, pix.width(), pix.height());
}
void StartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->drawPixmap(boundingRect().topLeft(), pix);
}


  • flashitem.h
#ifndef FLASHITEM_H
#define FLASHITEM_H
#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QTimer>
class FlashItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit FlashItem(QObject *parent = 0);
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr);
    void timerEvent(QTimerEvent *event);
private:
    bool flash;
    QTimer *timer;
};
#endif // FLASHITEM_H


  • flashitem.cpp
#include "flashitem.h"
FlashItem::FlashItem(QObject *parent)
{
    flash = true;//为颜色切换标识。
    setFlag(ItemIsMovable);
    startTimer(1000);//启动定时器
}
QRectF FlashItem::boundingRect() const
{
    qreal adjust = 2;
    return QRectF(-10-adjust, -10-adjust, 43 + adjust, 43 + adjust);
}
void FlashItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::NoPen);
    painter->setBrush(Qt::darkGray);//深灰色
    painter->drawEllipse(-7, -7, 40, 40);
    painter->setPen(QPen(Qt::black, 0));
    //闪烁区边线为黑色
    painter->setBrush(flash ? (Qt::red) : (Qt::yellow));
    painter->drawEllipse(-10, -10, 40, 40);
}
void FlashItem::timerEvent(QTimerEvent *event)
{
    flash = !flash;
    update();
}


  • 执行效果:

20200318101739661.gif

图元的旋转、缩放、切变和位移【示例】

通过实现一个示例演示将图元进行旋转、缩放、切变和位移


  • main.cpp
#include "mainwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;
    w.show();
    return a.exec();
}


  • mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QFrame>
#include "pixitem.h"
class MainWidget : public QWidget
{
    Q_OBJECT
public:
    MainWidget(QWidget *parent = 0);
    ~MainWidget();
    void createControlFrame();
private:
    int angle;
    qreal scaleValue;
    qreal shearValue;
    qreal translateValue;
    QGraphicsView *view;
    QFrame *ctrlFrame;
    PixItem *pixItem;
public slots:
    void slotRotate(int);
    void slotScale(int);
    void slotShear(int);
    void slotTranslate(int);
};
#endif // MAINWIDGET_H


  • mainwidget.cpp
#include "mainwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSlider>
#include <QGroupBox>
#include <math.h>
MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    angle = 0;
    scaleValue = 5;
    shearValue = 5;
    translateValue = 50;
    QGraphicsScene *scene = new QGraphicsScene;
    //限定新建QGraphicsScene对象的显示区域
    scene->setSceneRect(-200,-200,400,400);
    QPixmap *pixmap = new  QPixmap("image.png");
    pixItem = new PixItem(pixmap);
    scene->addItem(pixItem);
    pixItem->setPos(0,0);
    view = new QGraphicsView;   //新建一个视图对象
    view->setScene(scene);    //将视图对象与场景相连
    view->setMinimumSize(400,400);  //设置视图的最小尺寸为(400,400)
    ctrlFrame = new QFrame;
    createControlFrame();    //新建主窗体右侧的控制面板区
    //主窗口布局
    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->setMargin(10);
    mainLayout->setSpacing(20);
    mainLayout->addWidget(view);
    mainLayout->addWidget(ctrlFrame);
    setLayout(mainLayout);
    setWindowTitle(tr("Graphics Item Transformation"));//设置主窗体的标题
}
void MainWidget::createControlFrame()
{
    //旋转控制
    QSlider *rotateSlider = new QSlider;
    rotateSlider->setOrientation(Qt::Horizontal);
    rotateSlider->setRange(0,360);
    QHBoxLayout *rotateLayout = new QHBoxLayout;
    rotateLayout->addWidget(rotateSlider);
    QGroupBox *rotateGroup = new QGroupBox(tr("Rotate"));
    rotateGroup->setLayout(rotateLayout);
    //缩放控制
    QSlider *scaleSlider = new QSlider;
    scaleSlider->setOrientation(Qt::Horizontal);
    scaleSlider->setRange(0,2*scaleValue);
    scaleSlider->setValue(scaleValue);
    QHBoxLayout *scaleLayout = new QHBoxLayout;
    scaleLayout->addWidget(scaleSlider);
    QGroupBox *scaleGroup = new QGroupBox(tr("Scale"));
    scaleGroup->setLayout(scaleLayout);
    //切变控制
    QSlider *shearSlider = new QSlider;
    shearSlider->setOrientation(Qt::Horizontal);
    shearSlider->setRange(0,2*shearValue);
    shearSlider->setValue(shearValue);
    QHBoxLayout *shearLayout = new QHBoxLayout;
    shearLayout->addWidget(shearSlider);
    QGroupBox *shearGroup = new QGroupBox(tr("Shear"));
    shearGroup->setLayout(shearLayout);
    //位移控制
    QSlider *translateSlider = new QSlider;
    translateSlider->setOrientation(Qt::Horizontal);
    translateSlider->setRange(0,2*translateValue);
    translateSlider->setValue(translateValue);
    QHBoxLayout *translateLayout = new QHBoxLayout;
    translateLayout->addWidget(translateSlider);
    QGroupBox *translateGroup = new QGroupBox(tr("Translate"));
    translateGroup->setLayout(translateLayout);
    connect(rotateSlider,SIGNAL(valueChanged(int)),this,SLOT(slotRotate(int)));
    connect(scaleSlider,SIGNAL(valueChanged(int)),this,SLOT(slotScale(int)));
    connect(shearSlider,SIGNAL(valueChanged(int)),this,SLOT(slotShear(int)));
    connect(translateSlider,SIGNAL(valueChanged(int)),this,SLOT(slotTranslate(int)));
    //控制面板布局
    QVBoxLayout *frameLayout = new QVBoxLayout;
    frameLayout->setMargin(10);
    frameLayout->setSpacing(20);
    frameLayout->addWidget(rotateGroup);
    frameLayout->addWidget(scaleGroup);
    frameLayout->addWidget(shearGroup);
    frameLayout->addWidget(translateGroup);
    ctrlFrame->setLayout(frameLayout);
}
void MainWidget::slotRotate(int value)
{
    view->rotate(value-angle);
    angle = value;
}
void MainWidget::slotScale(int value)
{
    qreal s;
    if(value>scaleValue)
        s=pow(1.1,(value-scaleValue));
    else
        s=pow(1/1.1,(scaleValue-value));
    view->scale(s,s);
    scaleValue=value;
}
void MainWidget::slotShear(int value)
{
    view->shear((value-shearValue)/10.0,0);
    shearValue=value;
}
void MainWidget::slotTranslate(int value)
{
    view->translate(value-translateValue,value-translateValue);
    translateValue=value;
}
MainWidget::~MainWidget()
{
}


  • 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


  • 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);
}


20200318231257192.gif

执行结果

20200318231257192.gif

相关文章
|
14天前
|
算法 计算机视觉
基于qt的opencv实时图像处理框架FastCvLearn实战
本文介绍了一个基于Qt的OpenCV实时图像处理框架FastCvLearn,通过手撕代码的方式详细讲解了如何实现实时人脸马赛克等功能,并提供了结果展示和基础知识回顾。
基于qt的opencv实时图像处理框架FastCvLearn实战
|
14天前
|
文字识别 计算机视觉 开发者
基于QT的OCR和opencv融合框架FastOCRLearn实战
本文介绍了在Qt环境下结合OpenCV库构建OCR识别系统的实战方法,通过FastOCRLearn项目,读者可以学习Tesseract OCR的编译配置和在Windows平台下的实践步骤,文章提供了技术资源链接,帮助开发者理解并实现OCR技术。
基于QT的OCR和opencv融合框架FastOCRLearn实战
|
14天前
|
计算机视觉
基于QT的opencv插件框架qtCvFrameLearn实战
这篇文章详细介绍了如何基于Qt框架开发一个名为qtCvFrameLearn的OpenCV插件,包括项目配置、插件加载、Qt与OpenCV图像转换,以及通过各个插件学习OpenCV函数的使用,如仿射变换、卡通效果、腐蚀、旋转和锐化等。
23 10
|
14天前
|
定位技术 Go 开发工具
dynamic-situational-awareness-qt学习记录
本文是作者yantuguiguziPGJ关于dynamic-situational-awareness-qt学习记录的分享,介绍了在Qt学习过程中发现的qml资源丰富的代码仓库,并提供了资源路径和相关的安装、配置步骤,涉及的内容有数字地球、GIS纹理等,同时提供了相关链接和git命令来克隆代码仓库和ArcGIS Runtime SDK for Qt的安装说明。
|
14天前
|
C语言 C++ Windows
QT多插件通信框架CTK编译记录
本文记录了编译QT多插件通信框架CTK的过程,包括编译结果截图、部署配置、Log4Qt编译配置、参考链接和拓展资料。文中提供了详细的编译步骤和配置文件示例,以及相关的资源链接。
QT多插件通信框架CTK编译记录
|
2月前
|
XML Linux C++
【Qt】图形化和纯代码实现Hello world的比较
【Qt】图形化和纯代码实现Hello world的比较
|
3月前
|
C++
Qt中的信号与槽如何学习?(包括自定义信号)这篇文章告诉你
以现实中的事件来举例的话,例如有两把不同颜色的信号枪,分别是红色,绿色,打响不通颜色的信号枪会触发不同的槽发生,比如说打响红色这个人就跑步,绿色就走步,但是还有一个很重要的机制,那就是连接,我们需要把信号枪去跟这个人的动作连接起来。 如果上面理解没问题的话我们可以把信号和槽看成两个工具,我们最重要的是如何去把这两个工具连接起来。 它的作用可以让我们更加灵活的去使用不同窗口间的切换以及某些事件的连接。
|
3月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
153 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
2月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
55 0
|
14天前
|
开发工具 C++
qt开发技巧与三个问题点
本文介绍了三个Qt开发中的常见问题及其解决方法,并提供了一些实用的开发技巧。

热门文章

最新文章