• QT自定义图形项中的boundingRect()和shape()函数的理解


    QT自定义图形项中的boundingRect()和shape()函数的理解

    实现自定义图形项经常需要重绘的函数有boundingRect()、paint()、shape()。
    针对霍亚飞的Qt creator中所说,boundingRect()函数具有以下特点:
    1.paint绘制的图像必须在boundingRect()函数之中。
    2.用来确定哪些区域需要重构(repaint)。
    3.用来检测碰撞
    其中第二个功能在帮助文档中没有看到(可能英语水平不过关),故而通过一次小测试借以理解以上函数:

    对第一点和第二点的理解:为什么图像的绘制必须在boundingRect()函数所确定的Rect之中。

    第一个测试:我们把图像画到boundingRect()的所设置的矩形外边,并且想办法观察到重绘的情况

    项目建立之类的就不说了,自行参照书本。
    首先搭建个测试的框架:
    添加一个继承自QGraphicsItem的MyIetm和继承自QGraphicssView的MyView,两个类的内容如下:

    //myitem.h内容如下
    #ifndef MYITEM_H
    #define MYITEM_H
    #include <QGraphicsItem>
    class MyItem : public QGraphicsItem
    {
    public:
        MyItem();
        QRectF boundingRect()const override;
        void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)override;
    private:
       void drawRectPath(QPainter *painter);
    };
    
    #endif // MYITEM_H
    //myitem.cpp内容如下:
    #include "myitem.h"
    #include <QPainter>
    MyItem::MyItem()
    {
    }
    QRectF MyItem::boundingRect()const {
        qreal penwidth=1;
        return QRectF(-50-penwidth/2,-50-penwidth/2,100+penwidth,100+penwidth);
    }
    void MyItem::drawRectPath(QPainter *painter){
        QPainterPath rectPath;
        rectPath.moveTo(-50,-50);
        rectPath.lineTo(50,-50);
        rectPath.lineTo(50,50);
        rectPath.lineTo(-50,50);
        rectPath.closeSubpath();//返回绘图开始点
    
        painter->setPen(QPen(Qt::red,20,Qt::SolidLine,Qt::SquareCap,Qt::MiterJoin));//pen参数别设置错了,要不不好看出来
        painter->drawPath(rectPath);
    
        //在之前的绘图上我们绘制出QboundingRect的虚线方框
        painter->setPen(QPen(Qt::black,1,Qt::DotLine,Qt::SquareCap,Qt::MiterJoin));
        painter->drawRect(-50,-50,100,100);
    }
    void MyItem::paint(QPainter *painter
                       ,const QStyleOptionGraphicsItem *,QWidget *){
          drawRectPath(painter);
    }
    //myview.h什么也不需要改动
    #ifndef MYVIEW_H
    #define MYVIEW_H
    #include <QGraphicsView>
    
    class MyView : public QGraphicsView
    {
    public:
        MyView();
    };
    
    #endif // MYVIEW_H
    //myview.cpp内容如下:
    #include "myview.h"
    
    MyView::MyView()
    {
    }
    //main.cpp文件
    #include <QApplication>
    #include "myitem.h"
    #include <QGraphicsScene>
    #include "myview.h"
    
    int main(int argv,char* argc[]){
        QApplication app(argv,argc);
    
        MyItem *item1=new MyItem;
        MyItem *item2=new MyItem;
        item1->setPos(0,0);
        item2->setPos(150,150);
    
    
        QGraphicsScene scene;
        scene.addItem(item1);
        scene.addItem(item2);
    
        MyView view;
        view.setScene(&scene);
        view.resize(600,600);
        view.show();
    
    
    
        return app.exec();
    }

    绘制的图形如下:

    由上图,虽然我们绘制的图像和boundingRect返回的QRect是一样大的,但是因为我们的pen宽度,绘制的图形已经超出了boudingRect.

    为了看到视图更新的效果,我们为图形项添加移动效果:

    //在myitem.h中:
    //添加
    protected:
        void keyPressEvent(QKeyEvent *event)override;
        void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
    //在myitem.cpp中:
    //更改MyItem()函数为:
    MyItem::MyItem()
    {
       //设置可以被移动以及获得焦点,缺一不可
        setFlag(QGraphicsItem::ItemIsMovable);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
    //添加
    
    //上下左右移动图形项
    void MyItem::keyPressEvent(QKeyEvent *event){
        switch (event->key()) {
        case Qt::Key_Left:{
            moveBy(-1,0);
            break;
        }
    
        case Qt::Key_Up:{
            moveBy(0,-1);
            break;
        }
        case Qt::Key_Right:{
            moveBy(1,0);
            break;
        }
        case Qt::Key_Down:{
            moveBy(0,1);
            break;
        }
        }
    }
    //鼠标点击获得焦点
    void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *){
        setFocus();
    }

    好了,点中方框图形(点击在虚线里边),就可以上下左右键移动了,试一试吧,也许我们已经可以猜到结果了:

     理解这一点基础在于QGraphicsView的一个属性ViewportUpdateMode,可以通过

    void setViewportUpdateMode(QGraphicsView::ViewportUpdateMode mode)

    进行设定:

    总共有五种模式,很容易理解:

    QGraphicsView::FullViewportUpdate 全视口更新,整体都更新的意思啦
    QGraphicsView::MinimalViewportUpdate 最小更新,哪里有变动更新哪里
    QGraphicsView::SmartViewportUpdate 智能选择,它要自己选
    QGraphicsView::BoundingRectViewportUpdate 来了,来了,它就是我们要注意的。
    QGraphicsView::NoViewportUpdate 不更新
    其中默认为QGraphicsView::MinimalViewportUpdate,也就是上例中我们没有进行设置的情况。事实上除了设置为FullViewportUpdate 其余四种皆会出现问题,不妨试一试。
    我们可以通过在MyView的构造函数中设置为FullViewportUpdate 的全视口更新得到我们想要的结果,但是却是以牺牲性能为代价的。

    理解第三点,用来检测碰撞

    第二个测试:
    在第一个测试基础上做以下更改:
    MyItem.h中做添加

    private:
       void drawRectPath(QPainter *painter);//绘制矩形不在使用
       void drawtTriangle(QPainter *painter);//添加绘制三角形

    在MyItem.cpp中更改三处

    //第一处,实现drawtTriangle(QPainter *painter)
    void MyItem::drawtTriangle(QPainter *painter){
        QPainterPath trianglePath;
        trianglePath.moveTo(0,-50);
        trianglePath.lineTo(50,50);
        trianglePath.lineTo(-50,50);
        trianglePath.closeSubpath();
        painter->setPen(QPen(Qt::red,1,Qt::SolidLine,Qt::SquareCap,Qt::MiterJoin));
        painter->drawPath(trianglePath);
    }
    //第二处 重新实现paint
    void MyItem::paint(QPainter *painter
                       ,const QStyleOptionGraphicsItem *,QWidget *){
        //绘制boundingRect的QRect以方便查看
        painter->setPen(QPen(Qt::black,1,Qt::DotLine,Qt::SquareCap,Qt::MiterJoin));
        painter->drawRect(-50,-50,100,100);
    
        if(hasFocus()&&!collidingItems().isEmpty()){  //判断是否有在获得焦点的同时有碰撞
           painter->setBrush(QColor(Qt::black));     //若有碰撞则绘制的图形将以黑色作为画刷填充
        }
        drawtTriangle(painter);//这次是三角形
    }
    //第三处:在keyPressEvent()函数中添加一个旋转的响应(按下R键)
    void MyItem::keyPressEvent(QKeyEvent *event){
        switch (event->key()) {
        case Qt::Key_Left:{
            moveBy(-1,0);
            break;
        }
        case Qt::Key_Up:{
            moveBy(0,-1);
            break;
        }
        case Qt::Key_Right:{
            moveBy(1,0);
            break;
        }
        case Qt::Key_Down:{
            moveBy(0,1);
            break;
        }
        case Qt::Key_R:{   //按下R键旋转90度
            setRotation(90);
            break;
        }
        }
    }

    结果如下:

    虚线接触时发生了碰撞,为了避免绘制的虚线对结果的影响,我们注释掉虚线部分

    void MyItem::paint(QPainter *painter
                       ,const QStyleOptionGraphicsItem *,QWidget *){
    //    painter->setPen(QPen(Qt::black,1,Qt::DotLine,Qt::SquareCap,Qt::MiterJoin));
    //    painter->drawRect(-50,-50,100,100);
        if(hasFocus()&&!collidingItems().isEmpty()){
           painter->setBrush(QColor(Qt::black));
           .............

    结果如下:

    还没碰到呢,就已经变黑了(检测到碰撞了),可以得出结论了:
    boundingRect与碰撞检测很明显是相关的 ;
    但boundingRect返回的是矩形框,很明显不符合我们三角形碰撞的需求;
    这就到了shape函数了,它返回的是QPainterPath故而可以是任何形状
    myItem.h函数中添加新函数shape

    //myitem.h
    .........
    protected:
        void keyPressEvent(QKeyEvent *event)override;
        void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
        QPainterPath shape()const override;//重写shape函数

    shape函数的实现

    //myitem.cpp
    ......
    QPainterPath MyItem::shape()const{    //shape()函数返回一个一样的三角形路径
        QPainterPath trianglePath;
        trianglePath.moveTo(0,-50);
        trianglePath.lineTo(50,50);
        trianglePath.lineTo(-50,50);
        trianglePath.closeSubpath();
        return trianglePath;
    }

    结果正如我们所需的:

  • 相关阅读:
    php截取字符串
    PHP数据类型转换(字符转数字,数字转字符)
    php数组添加元素的方法
    更改jdk后,eclipse运行jsp出错
    U盘启动装完系统后 一拔下优盘 就不能进入系统
    SVN回滚版本
    在JSP中如何使用JavaBean
    Python使用numpy实现BP神经网络
    神经网络测试结果很差,该怎么做
    神经网络测试结果很差,该怎么做
  • 原文地址:https://www.cnblogs.com/zzzsj/p/15926283.html
Copyright © 2020-2023  润新知