QT4 Examples (1)Animated Tiles

注:

本文及以后设计的官方Examples 均来自QT4.8.7。

文章不涉及QT C++基础语法,错误之处望各位大佬指正。

Animated Tiles是 Animation Framework的第一个例子,废话不多说,直接看演示

这样的动画效果是如何做到的呢,在源码中我们可以看到,这个例子主要用到了两个知识点,一个是QState,也就是状态机,另一个是QGraphicsWidget ,QWidget我在项目中用的多了,但QGraphicsWidget着实没用到几次,QGraphicsWidget和QWidget应该来说分属于两个不同的框架,QGraphicsWidget继承于QGraphicsItem,在QGraphicsItem的基础上添加了Widget。

理解这个例子只要理解QGraphicsWidget 的绘图原理就行了,

我们按照官方例子看一下QGraphicsWidget的绘图步骤

     QGraphicsScene scene(-350, -350, 700, 700);
     QGraphicsItem *buttonParent = new QGraphicsRectItem;
     Button *ellipseButton = new Button(QPixmap(":/images/ellipse.png"), buttonParent);
     scene.addItem(buttonParent);
     buttonParent->scale(0.75, 0.75);
     buttonParent->setPos(200, 200);
     buttonParent->setZValue(65);
     View *view = new View(&scene);
     view->setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Animated Tiles"));
     view->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
     view->setBackgroundBrush(bgPix);
     view->setCacheMode(QGraphicsView::CacheBackground);
     view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

我们可以比较形象的理解为QGraphicsScene-> QGraphicsView->QGraphicsWidget->QGraphicsItem.

着重要指出的是,QGraphics中各个类的坐标系其实是不一样的:

1.QGraphicsItem坐标:属于局部坐标,通常以QGraphicsItem中心为原点(中心对称),正方向x朝右,y朝下。

2.setPos的坐标是父类坐标系的坐标,一般对于QGraphicsItem位于QGraphicsScene中的应用场景。

3.QGraphicsScene坐标:属于逻辑坐标 logical coordinates(与QPainter相同),以场景中心为原点,正方向x朝右,y朝下。

4.图元原点(左上角dialog的原点)与场景原点对齐,导致图元外边框的左上角顶点在场景中的坐标位置为(负数,负数)。

5.View(视图)坐标:属于设备坐标device coordinates(与窗口相同),默认以左上点为原点, 正方向x朝右,y朝下。

6.默认场景scene的左上角顶点与视图坐标原点对齐。显示时默认中心对齐,当场景大小小于视图大小的时候,将中心对齐,此中指的仍然是整个图元的中心,同时,图元原点与场景原点对齐,场景左上角顶点与视图原点对齐,视图左上角顶点不一定是场景原点,此时也将出现视图坐标有正值有负值。

详细QGraphics的应用这里就不再赘述了。

QState 比较好理解,

 // States
    QState *rootState = new QState;
    QState *ellipseState = new QState(rootState);
    QState *figure8State = new QState(rootState);
    QState *randomState = new QState(rootState);
    QState *tiledState = new QState(rootState);
    QState *centeredState = new QState(rootState);
    //创建5个状态,依次是圆形状态,figure8状态 随机状态 平铺状态 中心状态
    //这5个状态分别表示64个item的排列状态,具体排列位置看代码
    // Values
    for (int i = 0; i < items.count(); ++i) {
        Pixmap *item = items.at(i);
        // Ellipse
        ellipseState->assignProperty(item, "pos",
                                         QPointF(cos((i / 63.0) * 6.28) * 250,
                                                 sin((i / 63.0) * 6.28) * 250));

        // Figure 8
        figure8State->assignProperty(item, "pos",
                                         QPointF(sin((i / 63.0) * 6.28) * 250,
                                                 sin(((i * 2)/63.0) * 6.28) * 250));

        // Random
        randomState->assignProperty(item, "pos",
                                        QPointF(-250 + qrand() % 500,
                                                -250 + qrand() % 500));

        // Tiled
        tiledState->assignProperty(item, "pos",
                                       QPointF(((i % 8) - 4) * kineticPix.width() + kineticPix.width() / 2,
                                               ((i / 8) - 4) * kineticPix.height() + kineticPix.height() / 2));

        // Centered
        centeredState->assignProperty(item, "pos", QPointF());
    }
    //创建状态机
    QStateMachine states;
    //初始化状态机
    states.addState(rootState);
    states.setInitialState(rootState);
    //初始化状态设置为centeredState
    rootState->setInitialState(centeredState);
    //下面是状态机触发的信号和槽 QParallelAnimationGroup 是动画容器,不会影响item的最终位置
    //其实我们可以只需要rootState->addTransition(ellipseButton, SIGNAL(pressed()), ellipseState);添加状态机转换的信号和槽就可以
    QParallelAnimationGroup *group = new QParallelAnimationGroup;
    QAbstractTransition *trans = rootState->addTransition(ellipseButton, SIGNAL(pressed()), ellipseState);
    trans->addAnimation(group);

    trans = rootState->addTransition(figure8Button, SIGNAL(pressed()), figure8State);
    trans->addAnimation(group);

    trans = rootState->addTransition(randomButton, SIGNAL(pressed()), randomState);
    trans->addAnimation(group);

    trans = rootState->addTransition(tiledButton, SIGNAL(pressed()), tiledState);
    trans->addAnimation(group);

    trans = rootState->addTransition(centeredButton, SIGNAL(pressed()), centeredState);
    trans->addAnimation(group);

上面的代码阐述的是状态机的添加流程,以及如何设置状态机,要使状态机真正生效,别忘了states.start();

状态机的引入,并不是只能改变控件的空间位置,最主要的还是能改变窗口控件的其他属性:比如visible ,text,checked,icon等 避免频繁使用控件内置函数,增加不必要的判断,就像QT描述的那样 Code less ,Create more

真正实现动画效果的,其实是QPropertyAnimation QParallelAnimationGroup

来看一下他们的基本关系

QParallelAnimationGroup 是一个并行动画组类

通过例子中的代码

  QParallelAnimationGroup *group = new QParallelAnimationGroup;
    for (int i = 0; i < items.count(); ++i) {
        QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos");
        anim->setDuration(750 + i * 25);
        anim->setEasingCurve(QEasingCurve::InOutBack);
        group->addAnimation(anim);
    }
    QAbstractTransition *trans = rootState->addTransition(ellipseButton, SIGNAL(pressed()), ellipseState);
    trans->addAnimation(group);

上面的代码清晰的阐释了例子动画效果的原理

QPropertyAnimation 是动画效果属性的基类,   anim->setDuration 的意思是 动作时长
anim->setEasingCurve(QEasingCurve::InOutBack); 设置动画效果 大致就是设置动画的缓和曲线

差不多大家也都知道了,按照代码所写的,每一个图元的飞行时间不一致,随item的id增加而飞行时间增加,这就形成了例子演示中,item依次飞行的现象,

item的层数其实也有差异,这就要涉及到在创建item时的setZValue值,value值越大,图层在越上层,图元层叠的效果就是这么来的

最后再看看Button是怎么画出来的吧

class Button : public QGraphicsWidget
{
    Q_OBJECT
public:
    Button(const QPixmap &pixmap, QGraphicsItem *parent = 0)
        : QGraphicsWidget(parent), _pix(pixmap)
    {
        //接收悬停事件
        setAcceptHoverEvents(true);
        setCacheMode(DeviceCoordinateCache);
    }

    QRectF boundingRect() const
    {
        return QRectF(-65, -65, 130, 130);
    }

    QPainterPath shape() const
    {
        QPainterPath path;
        path.addEllipse(boundingRect());
        return path;
    }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
    {
        //重绘时鼠标是否按下
        bool down = option->state & QStyle::State_Sunken;
        QRectF r = boundingRect();
        //线性渐变区域
        QLinearGradient grad(r.topLeft(), r.bottomRight());
        //设置梯度颜色
        grad.setColorAt(down ? 1 : 0, option->state & QStyle::State_MouseOver ? Qt::white : Qt::lightGray);
        grad.setColorAt(down ? 0 : 1, Qt::darkGray);
        painter->setPen(Qt::darkGray);
        painter->setBrush(grad);
        //bounding区域画圆
        painter->drawEllipse(r);
        //画一个小圆并填充图片
        QLinearGradient grad2(r.topLeft(), r.bottomRight());
        grad.setColorAt(down ? 1 : 0, Qt::darkGray);
        grad.setColorAt(down ? 0 : 1, Qt::lightGray);
        painter->setPen(Qt::NoPen);
        painter->setBrush(grad);
        if (down)
            painter->translate(2, 2);
        painter->drawEllipse(r.adjusted(5, 5, -5, -5));
        painter->drawPixmap(-_pix.width()/2, -_pix.height()/2, _pix);
    }

signals:
    void pressed();

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *)
    {
        emit pressed();
        update();
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent *)
    {
        update();
    }

private:
    QPixmap _pix;
};

代码里面已经加了注释,差不多应该能看懂吧

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值