Qt5开发及实例V2.0-第七章-Qt图形视图框架

news2024/11/25 3:24:25

Qt5开发及实例V2.0-第七章-Qt图形视图框架

  • 第7章 Qt 5图形视图框架
    • 7.1 图形视图体系结构
      • 7.1.1 Graphics View的特点
      • 7.1.2 Graphics View的三元素
      • 7.1.3 GraphicsView的坐标系统
    • 7.2 【实例】:图形视图
      • 7.2.1 飞舞的蝴蝶
      • 7.2.2 地图浏览器
      • 7.2.3 图元创建
      • 7.2.4 图元的旋转、缩放、切变和位移
  • 本章相关例程源码下载
    • 1.Qt5开发及实例_CH701.rar 下载
    • 2.Qt5开发及实例_CH702.rar 下载
    • 3.Qt5开发及实例_CH703.rar 下载
    • 4.Qt5开发及实例_CH704.rar 下载

第7章 Qt 5图形视图框架

7.1 图形视图体系结构

7.1.1 Graphics View的特点

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

7.1.2 Graphics View的三元素

它们三者之间的关系如图7.1所示。
在这里插入图片描述

1.场景类:QGraphicsScene类
场景类主要完成的工作包括提供对它包含的图元的操作接口和传递事件、管理各个图元的状态(如选择和焦点处理)、提供无变换的绘制功能(如打印)等。
事件传播体系结构将场景事件发送给图元,同时也管理图元之间的事件传播。如果场景接收到了在某一点的鼠标单击事件,场景会将事件传给这一点的图元。
管理各个图元的状态(如选择和焦点处理)。可以通过QGraphicsScene:: setSelectionArea()函数选择图元,选择区域可以是任意的形状,使用QPainterPath表示。若要得到当前选择的图元列表,则可以使用函数QGraphicsScene:: selectedItems()。可以通过QGraphicsScene:: setFocusItem()函数或QGraphicsScene:: setFocus()函数来设置图元的焦点,获得当前具有焦点的图元使用函数QGraphicsScene::focusItem()。

2.视图类:QGraphicsView类
QGraphicsView是可滚动的窗口部件,可以提供滚动条来浏览大的场景。如果需要使用OpenGL,则可以使用QGraphicsView::setViewport()将视图设置为QGLWidget。
视图接收键盘和鼠标的输入事件,并将它们翻译为场景事件(将坐标转换为场景的坐标)。使用变换矩阵函数QGraphicsView::matrix()可以变换场景的坐标,实现场景缩放和旋转。QGraphicsView提供QGraphicsView::mapToScene()和QGraphicsView:: mapFromScene()用于与场景的坐标进行转换。

3.图元类:QGraphicsItem类
它是场景中各个图元的基类,在它的基础上可以继承出各种图元类,Qt已经预置的包括直线(QGraphicsLineItem)、椭圆(QGraphicsEllipseItem)、文本图元(QGraphicsTextItem)、矩形(QGraphicsRectItem)等。
QGraphicsItem主要有以下功能。
 处理鼠标按下、移动、释放、双击、悬停、滚轮和右键菜单事件。
 处理键盘输入事件。
 处理拖曳事件。
 分组。
 碰撞检测。

7.1.3 GraphicsView的坐标系统

1.场景坐标
场景坐标是所有图元的基础坐标系统。场景坐标系统描述了顶层的图元,每个图元都有场景坐标和相应的包容框。场景坐标的原点在场景中心,坐标原点是x轴正方向向右,y轴正方向向下。
QGraphicsScene类的坐标系以中心为原点(0,0),如图7.2所示。
在这里插入图片描述

2.视图坐标
视图坐标是窗口部件的坐标。视图坐标的单位是像素。QGraphicsView视图的左上角是(0,0),x轴正方向向右,y轴正方向向下。所有的鼠标事件最开始都是使用视图坐标。
QGraphicsView类继承自QWidget类,因此它与其他的QWidget类一样,以窗口的左上角作为自己坐标系的原点,如图7.3所示。
在这里插入图片描述

3.图元坐标
图元使用自己的本地坐标,这个坐标系统通常以图元中心为原点,这也是所有变换的原点。图元坐标方向是x轴正方向向右,y轴正方向向下。创建图元后,只需注意图元坐标就可以了,QGraphicsScene和QGraphicsView会完成所有的变换。
QGraphicsItem类的坐标系,若在调用QGraphicsItem类的paint()函数重绘图元时,则以此坐标系为基准,如图7.4所示。
在这里插入图片描述
Graphics View框架提供了多种坐标变换函数,见表7.1。
在这里插入图片描述

7.2 【实例】:图形视图

7.2.1 飞舞的蝴蝶

【例】(难度中等)(CH701)设计界面,一个蝴蝶在屏幕上不停地上下飞舞。
操作步骤如下。
(1)新建Qt Widgets Application(详见1.3.1节),项目名为“Butterfly”,基类选择“QMainWindow”,类名命名默认为“MainWindow”,取消“创建界面”复选框的选中状态。单击“下一步”按钮,最后单击“完成”按钮,完成该项目工程的建立。
(2)在“Butterfly”项目名上单击鼠标右键,在弹出的快捷菜单中选择“添加新文件…”菜单项,在弹出的对话框中选择“C++ Class”选项。单击“Choose…”按钮,弹出对话框,在“Base class”后面的下拉列表框中选择基类名“QObject”,在“Class name”后面的文本框中输入类的名称“Butterfly”。
(3)单击“下一步”按钮,单击“完成”按钮,添加文件“butterfly.h”和“butterfly. cpp”。

(4)Butterfly类继承自QObject类、QGraphicsItem类,在头文件“butterfly.h”中完成的代码具体内容。
(5)在源文件“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");		//调用QPixmap的load()函数加载所用到的图片
    pix_down.load("down.png");
    startTimer(100);		//启动定时器,并设置时间间隔为100毫秒
}

boundingRect()函数为图元限定区域范围。此范围是以图元自身的坐标系为基础设定的。具体实现代码内容如下:

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

在重画函数paint()中,首先判断当前已显示的图片是pix_up还是pix_down。实现蝴蝶翅膀上下飞舞效果时,若当前显示的是pix_up图片,则重绘pix_down图片,反之亦然。具体实现代码内容如下:

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

定时器的timerEvent()函数实现蝴蝶的飞舞,具体实现代码内容如下:

void Butterfly::timerEvent(QTimerEvent *)
{
    //边界控制
    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=(qrand()%20)-10.0;
    setPos(mapToParent(dx,dy));		//(a)
}

(6)完成了蝴蝶图元的实现后,在源文件“main.cpp”中将它加载到场景中,并关联一个视图,具体实现代码内容如下:

#include <QApplication>
#include "butterfly.h"
#include <QGraphicsScene>
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();
    return a.exec();
}

(7)运行程序,将程序中用到的图片保存到该工程的D:\Qt\CH7\CH701\build-Butterfly-Desktop_Qt_5_8_0_MinGW_32bit-Debug文件夹中,运行结果如图7.5所示。
在这里插入图片描述

7.2.2 地图浏览器

【例】(难度中等)(CH702)设计一个地图浏览器,包括地图的浏览、放大、缩小,以及显示各点的坐标等,如图7.6所示。
在这里插入图片描述

操作步骤如下。
(1)新建Qt Widgets Application (详见1.3.1节),项目名称为“MapWidget”,基类选择“QMainWindow”,类名命名默认为“MainWindow”,取消“创建界面”复选框的选中状态。单击“下一步”按钮,最后单击“完成”按钮,完成该项目工程的建立。
(2)在“MapWidget”项目名上单击鼠标右键,在弹出的快捷菜单中选择“添加新文件…”菜单项,在弹出的对话框中选择“C++ Class”选项。单击“Choose…”按钮,弹出对话框,在“Base class”后面的文本框中输入基类名“QGraphicsView”(手工添加),在“Class name”后面的文本框中输入类的名称“MapWidget”。
(3)单击“下一步”按钮,单击“完成”按钮,添加文件“mapwidget.h”和文件“mapwidget.cpp”。
(4)MapWidget类继承自QGraphicsView类,作为地图浏览器的主窗体。在头文件“mapwidget.h”中完成的代码。
(5)在源文件“mapwidget.cpp”中完成的代码。
(6)新建一个文本文件“maps.txt”,利用该文本文件描述与地图相关的信息,将该文件保存在该工程下的D:\Qt\CH7\CH702\ build-MapWidget-Desktop_Qt_5_ 4_0_ MinGW_32bit-Debug文件中,文件内容为:
China.jpg 114.4665527 35.96022297 119.9597168 31.3911575
(7)打开“mapwidget.cpp”文件,添加读取地图信息readMap()函数的具体实现代码如下:

void MapWidget::readMap()            			//读取地图信息
{
    QString mapName;
    QFile mapFile("maps.txt");			//(a)
    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中
}

根据缩放滑动条的当前值,确定缩放的比例,调用scale()函数实现地图缩放。完成地图缩放功能的slotZoom()函数的具体实现代码内容如下:

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

QGraphicsView类的drawBackground()函数中以地图图片重绘场景的背景来实现地图显示。具体实现代码如下:

void MapWidget::drawBackground(QPainter *painter, const QRectF &rect)
{
    painter->drawPixmap(int(sceneRect().left()),int(sceneRect(). top()), map);
}

响应鼠标移动事件mouseMoveEvent()函数,完成某点在各层坐标中的映射及显示。具体实现代码如下:

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

完成从场景坐标至地图坐标的转换mapToMap()函数。具体实现代码如下:

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

(8)下面是文件“main.cpp”的具体代码:

#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();
    return a.exec();
}

(9)将程序用到的图片保存到该工程的D:\Qt\CH7\CH702\build-MapWidget-Desktop_Qt_5_8_0_MinGW_32bit-Debug文件夹中,运行结果如图7.6所示。

7.2.3 图元创建

【例】(难度中等)(CH703)设计窗体,显示各种类型QGraphicsItem(包括不停闪烁的圆及来回移动的星星等),如图7.7所示。
在这里插入图片描述

实现步骤如下。
(1)新建Qt Widgets Application(详见1.3.1节),项目名称为“GraphicsItem”,基类选择“QMainWindow”,类名命名默认为“MainWindow”,取消“创建界面”复选框的选中状态。单击“下一步”按钮,最后单击“完成”按钮,完成该项目工程的建立。
(2)MainWindow类继承自QMainWindow作为主窗体,包含一个加入图元的各种操作的菜单栏,以及一个显示各种类型图元的QGraphicsView作为主窗体的centralWidget。“mainwindow.h”文件的具体代码实现内容。
(3)“mainwindow.cpp”文件中的代码。
(4)将程序中所用图片保存到该工程的D:\Qt\CH7\CH703\build-GraphicsItem-Desktop_Qt_5_8_0_MinGW_32bit-Debug文件夹下,此时运行效果如图7.8所示。
在这里插入图片描述
以上完成了主窗体的显示工作,下面介绍如何实现圆的闪烁功能。
(1)在“GraphicsItem”项目名上单击鼠标右键,在弹出的快捷菜单中选择“添加新文件…”菜单项,在弹出的对话框中选择“C++ Class”选项。单击“Choose…”按钮,弹出对话框,在“Base class”后面的下拉列表框中选择基类名“QObject”,在“Class name”后面的文本框中输入类的名称“FlashItem”。
(2)单击“下一步”按钮,单击“完成”按钮,添加文件“flashitem.h”和文件“flashitem.cpp”。

(3)“flashitem.h”文件的具体代码如下:

#include <QGraphicsItem>
#include <QPainter>
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);
    void timerEvent(QTimerEvent *);
private:
    bool flash;
    QTimer *timer;
signals:
public slots:
};

(4)“flashitem.cpp”文件的具体代码如下:

#include "flashitem.h"
FlashItem::FlashItem(QObject *parent) :  QObject(parent)
{
    flash=true;			//为颜色切换标识赋初值
    setFlag(ItemIsMovable);	//(a)
    startTimer(1000);		//启动一个定时器,以1000毫秒为时间间隔
}

定义图元边界的函数boundingRect(),完成以图元坐标系为基础,增加两个像素点的冗余工作。具体实现代码如下:

QRectF FlashItem::boundingRect() const
{
    qreal adjust = 2;
    return QRectF(-10-adjust,-10-adjust,43+adjust,43+adjust);
}

为自定义图元重绘的函数paint()具体实现代码如下:

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));	
				//闪烁区的椭圆边线颜色为黑色、线宽为0
    painter->setBrush(flash?(Qt::red):(Qt::yellow)); //(a)
    painter->drawEllipse(-10,-10,40,40);	      //(b)
}

定时器响应函数timerEvent()完成颜色切换标识的反置,并在每次反置后调用update()函数重绘图元以实现闪烁的效果。具体实现代码如下:

void FlashItem::timerEvent(QTimerEvent *)
{
    flash=!flash;
    update();
}

(5)在“mainwindow.h”文件中添加代码如下:

public slots:    
    void slotAddFlashItem();
private:
    QAction *addFlashItemAct;

(6)在“mainwindow.cpp”文件中添加代码如下:

#include "flashitem.h"

其中,在createActions()函数中添加代码如下:

addFlashItemAct = new QAction(tr("加入闪烁圆"),this);
connect(addFlashItemAct,SIGNAL(triggered()),this,SLOT(slotAddFlashItem()));

在createMenus()函数中添加代码如下:

itemsMenu->addAction(addFlashItemAct);

在initScene()函数中添加代码如下:

for(i=0;i<3;i++)      
        slotAddFlashItem();

函数slotAddFlashItem()具体实现代码如下:

void MainWindow::slotAddFlashItem()  	//在场景中加入一个闪烁图元
{
    FlashItem *item = new FlashItem;
    scene->addItem(item);
    item->setPos((qrand()%int(scene->sceneRect().width()))-200,
                 (qrand()%int(scene->sceneRect().height()))-200);
}

(7)运行效果如图7.9所示。
在这里插入图片描述

下面将接着完成实现星星移动的功能。
(1)向项目中添加一个新的C++类,类名命名为“StartItem”,操作步骤同前。StartItem类继承自QGraphicsItem类,实际上是一个图片图元。
“startitem.h”文件的具体代码如下:

#include <QGraphicsItem>
#include <QPainter>
class StartItem : public QGraphicsItem
{
public:
    StartItem();
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
    QPixmap pix;
};

(2)StartItem构造函数中仅完成读取图片信息的工作。
“startitem.cpp”文件中的具体代码如下:

#include "startitem.h"
StartItem::StartItem()
{
    pix.load("star.png");
}

定义图元的边界函数boundingRect(),它是所有自定义图元均必须实现的函数,代码如下:

QRectF StartItem::boundingRect() const
{
    return QRectF(-pix.width()/2,-pix.height()/2,pix.width(),pix. height());
}

自定义图元重绘函数paint(),代码如下:

void StartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget)
{
    painter->drawPixmap(boundingRect().topLeft(),pix);
}

(3)在“mainwindow.h”文件中添加代码如下:

public slots:    
    void slotAddAnimationItem();
private:
    QAction *addAnimItemAct;

(4)在“mainwindow.cpp”文件中添加代码如下:

#include "startitem.h"
#include <QGraphicsItemAnimation>
#include <QTimeLine>

其中,在createActions()函数中添加代码如下:

addAnimItemAct = new QAction(tr("加入 星星"),this);
connect(addAnimItemAct,SIGNAL(triggered()),this,SLOT(slotAddAnimationItem()));

在createMenus()函数中添加代码如下:
itemsMenu->addAction(addAnimItemAct);
在initScene()函数中添加代码如下:

for(i=0;i<3;i++)      
    slotAddAnimationItem();

实现函数slotAddAnimationItem()的具体代码如下:

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

(5)最终运行结果如图7.7所示,图中的小星星会不停地左右移动。

7.2.4 图元的旋转、缩放、切变和位移

【例】(难度中等)(CH704)设计界面,实现蝴蝶各种变形。如图7.10所示。
在这里插入图片描述
(1)新建Qt Widgets Application(详见1.3.1节),项目名称为“ItemWidget”,基类选择“QWidget”,类名命名为“MainWidget”,取消“创建界面”复选框的选中状态。单击“下一步”按钮,最后单击“完成”按钮,完成该项目工程的建立。
(2)MainWidget类继承自QWidget,作为主窗体类,用于对图元的显示,包含一个控制面板区及一个显示区。

“mainwidget.h”文件中的代码如下:

#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QFrame>
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;
};

(3)“mainwidget.cpp”文件中的具体代码。
右侧的控制面板区分为旋转控制区、缩放控制区、切变控制区及位移控制区,每个区均由包含一个QSlider对象的QGroupBox对象实现,具体实现代码。
(4)运行效果如图7.11所示。
在这里插入图片描述
上面完成的是主窗体的功能,下面介绍用于变形显示的图元的制作。
(1)在“ItemWidget”项目名上单击鼠标右键,在弹出的快捷菜单中选择“添加新文件…”菜单项,在弹出的对话框中选择“C++ Class”选项。单击“Choose…”按钮,弹出对话框,在“Base class”后面的文本框中输入基类名“QGraphicsItem”(手工添加),在“Class name”后面的文本框中输入类的名称“PixItem”。
(2)单击“下一步”按钮,单击“完成”按钮,添加文件“pixitem.h”和文件“pixitem.cpp”。
(3)自定义PixItem类继承自QGraphicsItem类。

“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;     		//作为图元显示的图片
};

(4)PixItem的构造函数只是初始化了变量pix。“pixitem.cpp”文件中的具体内容如下:
#include “pixitem.h”

PixItem::PixItem(QPixmap *pixmap)
{
    pix = *pixmap;
}

定义图元边界的函数boundingRect(),完成以图元坐标系为基础增加两个像素点的冗余的工作。具体实现代码如下:

QRectF PixItem::boundingRect() const
{
    return QRectF(-2-pix.width()/2,-2-pix.height()/2,pix.width()+4, pix. height()+4);
}

重画函数只需QPainter的drawPixmap()函数将图元图片绘出即可。具体代码如下:

void PixItem::paint(QPainter *painter, const QStyleOptionGraphicsItem  *option,QWidget *widget)
{
    painter->drawPixmap(-pix.width()/2,-pix.height()/2,pix);
}

(5)在“mainwidget.h”文件中添加代码如下:

#include "pixitem.h"
private:
PixItem *pixItem;

(6)打开“mainwidget.cpp”文件,在语句scene->setSceneRect(-200,-200,400,400)与view = new QGraphicsView之间添加如下代码:

QPixmap *pixmap = new  QPixmap("image.png");
pixItem = new PixItem(pixmap);
scene->addItem(pixItem);
pixItem->setPos(0,0);

(7)运行效果如图7.12所示。
在这里插入图片描述
上述内容只是完成了图元图片的加载显示。下面介绍实现图元的各种变形的实际功能。
(1)在“mainwidget.h”文件中添加槽函数声明如下:

public slots:
    void slotRotate(int);
    void slotScale(int);
    void slotShear(int);
    void slotTranslate(int);

(2)在“mainwidget.cpp”文件中添加头文件:

#include <math.h>

其中,在createControlFrame()函数中的QVBoxLayout *frameLayout=new QVBoxLayout语句之前添加以下代码:

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

实现图元的旋转功能函数slotRotate(),主要是调用QGraphicsView类的rotate()函数实现的,它的参数为旋转角度的度数值,具体实现代码如下:

void MainWidget::slotRotate(int value)
{
    view->rotate(value-angle);
    angle = value;
}

实现图元的缩放功能函数slotScale(),主要是调用QGraphicsView类的scale()函数实现的,它的参数为缩放的比例,具体实现代码如下:

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

实现图元的切变功能函数slotShear(),主要是调用QGraphicsView类的shear()函数实现的,它的参数为切变的比例,具体实现代码如下:

void MainWidget::slotShear(int value)
{
    view->shear((value-shearValue)/10.0,0);
    shearValue=value;
}

实现图元的位移功能函数slotTranslate(),主要是调用QGraphicsView类的translate()函数实现的,它的参数为位移的大小,具体实现代码如下:

void MainWidget::slotTranslate(int value)
{
    view->translate(value-translateValue,value-translateValue);
    translateValue=value;
}

(3)最终运行结果如图7.10所示,读者可以试着拖曳滑块观看图形的各种变换效果。



本章相关例程源码下载

1.Qt5开发及实例_CH701.rar 下载

Qt5开发及实例_CH701.rar

2.Qt5开发及实例_CH702.rar 下载

Qt5开发及实例_CH702.rar

3.Qt5开发及实例_CH703.rar 下载

Qt5开发及实例_CH703.rar

4.Qt5开发及实例_CH704.rar 下载

Qt5开发及实例_CH704.rar

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1024231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据-kafka学习笔记

Kafka Kafka 是一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。 Kafka可以用作Flink应用程序的数据源。Flink可以轻松地从一个或多个Kafka主题中消费数据流。这意味着您可以使用Kafka来捕获和传输…

Python 图形化界面基础篇:创建顶部菜单

Python 图形化界面基础篇&#xff1a;创建顶部菜单 引言 Tkinter 库简介步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口步骤3&#xff1a;创建顶部菜单栏步骤4&#xff1a;处理菜单项的点击事件步骤5&#xff1a;启动 Tkinter 主事件循环 完整示例代码…

Python 如何把 String 转换为 Json 对象

在我们对 JSON 进行处理的时候&#xff0c;大概率我们会需要把字符串转换为 JSON 对象后才能进行处理。 Python 贴心的使用 json.loads(employee_string)就可以了。 首先需要做的就是导入 JSON 库。 #include json library import json 对现代程序员来说&#xff0c;JSON …

CNC 3D浮雕 Aspire 11.55 Crack

Aspire 提供了功能强大且直观的软件解决方案&#xff0c;用于在 CNC 铣床上创建和切割零件。有用于 2D 设计和计算 2D 刀具路径的工具&#xff0c;例如仿形、型腔加工和钻孔以及 2.5D 刀具路径&#xff0c;包括&#xff1a;V 形雕刻、棱镜雕刻、成型刀具路径、凹槽、 倒角刀具路…

抖音seo矩阵系统开源代码定制部署

抖音SEO底层开发逻辑主要包括以下几个方面&#xff1a; 1. 关键词优化&#xff1a;抖音SEO需要优化关键词&#xff0c;将关键词嵌入短视频标题、描述、标签等地方&#xff0c;提升抖音短视频在搜索引擎中的排名。 2. 标题优化&#xff1a;抖音短视频的标题应简明扼要&#xff…

C/C++满足条件的数的累加 2023年5月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C满足条件的数的累加 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C满足条件的数的累加 2023年5月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 现有n个整数&#x…

【前端面试题】浏览器面试题

文章目录 前言一、浏览器面试问题1.cookie sessionStorage localStorage 区别2.如何写一个会过期的localStorage&#xff0c;说说想法2.如何定时删除localstorage数据2.localStorage 能跨域吗2.memory cache 如何开启2.localstorage的限制2.浏览器输入URL发生了什么2.浏览器如何…

IIC协议详解

目录 1.IIC协议概述 2.IIC总线传输 3.IIC-51单片机应用 1.起始信号 2.终止信号 3.应答信号 4.数据发送 4.IIC-32单片机应用 用到的库函数&#xff1a; 1.IIC协议概述 IIC全称Inter-Integrated Circuit (集成电路总线)是由PHILIPS公司在80年代开发的两线式串行总线&…

进程组.会话.终端

一.进程组.会话.终端概念 1.1进程组 在Linux操作系统中&#xff0c;进程组&#xff08;Process Group&#xff09;是一组进程的集合。进程组内的每个进程都有一个相同的进程组ID&#xff08;PGID&#xff09;。进程组可以用于进行作业控制、信号传递和进程状态管理等操作。 …

大模型+检索增强(RAG、Atlas 和 REPLUG)

https://zhuanlan.zhihu.com/p/651380539 https://github.com/ninehills/blog/issues/97 1. 检索增强生成 RAG 在问答和对话的场景下&#xff0c;通常可以通过检索和生成两种方式得到一个回复。检索式回复是在外部知识库中检索出满意的回复&#xff0c;较为可靠和可控&#…

如何使用 MATLAB 数学编程软件调用 Python 脚本详细教程(每周更新中)

MATLAB 读写操作 在 MATLAB 中&#xff0c;可以使用各种函数来读取和写入文件。其中&#xff0c;filename.txt 是要读取或写入的文件名&#xff0c;r 表示读取模式&#xff0c;w 表示写入模式。fscanf 和 fprintf 函数用于读取和写入文件内容&#xff0c;%c 和 %s 是格式说明符…

Python 通过 stomp 发送消息到 ActiveMQ 的代码

只需要下面简单的几行代码&#xff0c;我们就可以把我们本地数据发送到 ActiveMQ 上面去。 def send_mq(data):hosts [(AMQHOST, AMQPORT)]conn stomp.Connection(host_and_portshosts, auto_content_lengthFalse)conn.connect(usernameAMQUSER, passcodeAMQPASS, waitTrue)…

基于Spring Boot的医院预约挂号系统设计与实现

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

功夫再高也怕菜刀。多年经验,会独立开发的机器视觉工程师,技术太强,但是找工作能力差劲

功夫再高也怕菜刀&#xff0c;专业的事情交给专业的人去做。 今年7月份中旬的时候&#xff0c;遇到一位老朋友&#xff0c;向我咨询某公司的信息&#xff0c;其实我根本不了解这家公司的情况与实力&#xff0c;向他说了&#xff0c;抱歉&#xff0c;我查下&#xff0c;等我晚上…

怎么把利用paddlepaddle导出的json文件转化为yolo或者voc文件

这两天想偷懒&#xff0c;想让模型先在数据上标一遍&#xff0c;然后我再做修正&#xff0c;主要是图个省事。由于我们的业务主要是利用paddle,模型也是基于paddle推理的&#xff0c;因此即便我对paddle有一万个吐槽但也不得不用它。但在利用paddle保存推理结果文件时&#xff…

Linux Day17 生产者消费者

一、生产者消费者问题概述 生产者 / 消费者问题&#xff0c;也被称作有限缓冲问题。两个或者更多的线程共享同一个缓冲 区&#xff0c;其中一个或多个线程作为 “ 生产者 ” 会不断地向缓冲区中添加数据&#xff0c;另一个或者多个线程作为 “ 消费者 ” 从缓冲区中取走数据。…

搭建本地MQTT服务器

环境及所用工具 win10本地环境下&#xff0c;使用docker配置一个mqttbroker&#xff0c;选择emqx docker操作&#xff1a;Docker_liangchaaaaa的博客-CSDN博客 测试使用MQTTX软件 Docker拉取镜像仓库 docker pull emqx/emqx:4.2.5 可以上官网看最新版本&#xff0c;或直接…

vue动态修改浏览器title和icon图标

vue动态修改浏览器title和icon图标 实例代码 setTitleIcon(){var link document.querySelector("link[rel*icon]") || document.createElement(link);link.type image/x-icon;link.rel shortcut icon;link.href /002.png; // 图片放public目录document.getElem…

PHP8的类与对象的基本操作之类的实例化-PHP8知识详解

定义完类和方法后&#xff0c;并不是真正创建一个对象。类和对象可以描述为如下关系。类用来描述具有相同数据结构和特征的“一组对象”&#xff0c;“类”是“对象”的抽象&#xff0c;而“对象”是“类”的具体实例&#xff0c;即一个类中的对象具有相同的“型”&#xff0c;…

循环神经网络-02文本预处理

https://zh-v2.d2l.ai/chapter_recurrent-neural-networks/text-preprocessing.html 本节中&#xff0c;我们将解析文本的常见预处理步骤。 这些步骤通常包括&#xff1a; 将文本作为字符串加载到内存中。 将字符串拆分为词元&#xff08;如单词和字符&#xff09;。 建立一个…