QT案例解析(1): Animated Tiles Example

演示效果

在这里插入图片描述

步骤概述

  1. 继承QGraphicsPixmapItem 实现显示图片
  2. 继承QGraphicsWidget 实现按钮
  3. 继承QGraphicsView 实现视图
  4. 实现功能
    图像不同状态的动画切换。

涉及知识点

图形视图框架 (QGraphicsScene(场景)),QGraphicsView(视图)),QGraphicsItem(图形项))
动画框架 (QAbstractAnimation(基类))
状态机框架 (QStateMachine(状态机) QState(状态))
信号与槽 (Signals & Slots)
2D绘图 (paint)

代码解析

一、继承QGraphicsPixmapItem

实现图片显示。
只是修改缓存模式。

class Pixmap : public QObject, public QGraphicsPixmapItem
{
    Q_OBJECT
    Q_PROPERTY(QPointF pos READ pos WRITE setPos)
public:
    Pixmap(const QPixmap &pix)
        : QObject(), QGraphicsPixmapItem(pix)
    {
    	//开启缓存模式(高质量显示)
        setCacheMode(DeviceCoordinateCache);
    }
};
二、继承QGraphicsWidget

实现按钮功能。
重写 paint 实现 悬停和点击 的效果 相当于设置样式

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 Q_DECL_OVERRIDE
    {
        return QRectF(-65, -65, 130, 130);
    }
	//返回按钮区域
    QPainterPath shape() const Q_DECL_OVERRIDE
    {
        QPainterPath path;
        //注意这里是椭圆了
        path.addEllipse(boundingRect()); 
        return path;
    }
	
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) Q_DECL_OVERRIDE
    {
    	//判断下陷状态 (pressed)
        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);
        //画外圈
        painter->drawEllipse(r);
        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);
        //画内圈 adjusted在原有的基础上添加变化 如QRect(10,10,10,10) -> QRect(15,15,5,5)
        painter->drawEllipse(r.adjusted(5, 5, -5, -5));
        //画图
        painter->drawPixmap(-_pix.width()/2, -_pix.height()/2, _pix);
    }

signals:
    void pressed();

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

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

private:
    QPixmap _pix;
};
三、继承QGraphicsView

实现视图显示
重写resizeEvent

class View : public QGraphicsView
{
public:
    View(QGraphicsScene *scene) : QGraphicsView(scene) { }

protected:
    void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE
    {
    	//把事件传递给父类
        QGraphicsView::resizeEvent(event);
        //视图自适应大小 拉动窗体时可以跟着变化
        fitInView(sceneRect(), Qt::KeepAspectRatio);
    }
};
四、主函数

主要功能实现
运用 图形视图 ,动画 ,状态机框架

int main(int argc, char **argv)
{
	//初始化资源文件
    Q_INIT_RESOURCE(animatedtiles);

    QApplication app(argc, argv);

    QPixmap kineticPix(":/images/kinetic.png");
    QPixmap bgPix(":/images/Time-For-Lunch-2.jpg");
	//初始化场景(原点在正中心,所以才有负数)
    QGraphicsScene scene(-350, -350, 700, 700);
	//初始化图形项
    QList<Pixmap *> items;
    for (int i = 0; i < 64; ++i) {
        Pixmap *item = new Pixmap(kineticPix);
        //设置偏移量(相对于窗体左上角)
        item->setOffset(-kineticPix.width()/2, -kineticPix.height()/2);
        //设置Z轴坐标
        item->setZValue(i);
        items << item;
        scene.addItem(item);
    }

    // 按钮图形项(都放在 buttonParent 里)
    QGraphicsItem *buttonParent = new QGraphicsRectItem;
    Button *ellipseButton = new Button(QPixmap(":/images/ellipse.png"), buttonParent);
    Button *figure8Button = new Button(QPixmap(":/images/figure8.png"), buttonParent);
    Button *randomButton = new Button(QPixmap(":/images/random.png"), buttonParent);
    Button *tiledButton = new Button(QPixmap(":/images/tile.png"), buttonParent);
    Button *centeredButton = new Button(QPixmap(":/images/centered.png"), buttonParent);

    ellipseButton->setPos(-100, -100);
    figure8Button->setPos(100, -100);
    randomButton->setPos(0, 0);
    tiledButton->setPos(-100, 100);
    centeredButton->setPos(100, 100);

    scene.addItem(buttonParent);
    //改变 buttonParent 坐标比例  原来的0.75倍
    buttonParent->setTransform(QTransform::fromScale(0.75, 0.75), true);
    buttonParent->setPos(200, 200);
    buttonParent->setZValue(65);

    // 状态
    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);

    // 状态值
    for (int i = 0; i < items.count(); ++i) {
        Pixmap *item = items.at(i);
        // 椭圆
        ellipseState->assignProperty(item, "pos",
                                         QPointF(qCos((i / 63.0) * 6.28) * 250,
                                                 qSin((i / 63.0) * 6.28) * 250));

        // 8的形状
        figure8State->assignProperty(item, "pos",
                                         QPointF(qSin((i / 63.0) * 6.28) * 250,
                                                 qSin(((i * 2)/63.0) * 6.28) * 250));

        // 随机值 (这里没有设置种子 所以每次都是一样的)
        randomState->assignProperty(item, "pos",
                                        QPointF(-250 + qrand() % 500,
                                                -250 + qrand() % 500));

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

        // 爱心 (这里是肥猫添加的)
        centeredState->assignProperty(item, "pos", QPointF( (2 * qCos((i / 63.0) * 6.28) - qCos(2*((i / 63.0) * 6.28)))* 100,
                                                            (2 * qSin((i / 63.0) * 6.28) - qSin(2*((i / 63.0) * 6.28)))* 100));
    }

    // 显示UI
    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);
    view->show();

	//状态机
    QStateMachine states;
    states.addState(rootState);
    states.setInitialState(rootState);
    rootState->setInitialState(centeredState);

	//并行动画组
    QParallelAnimationGroup *group = new QParallelAnimationGroup;
    for (int i = 0; i < items.count(); ++i) {
        QPropertyAnimation *anim = new QPropertyAnimation(items[i], "pos");
        //动画持续时间
        anim->setDuration(750 + i * 25);
        //动画移动轨迹的方式 可查看帮助文档 QEasingCurve
        anim->setEasingCurve(QEasingCurve::InOutBack);
        group->addAnimation(anim);
    }
    //给状态机添加动画
    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);

	//延时0.125秒 初始化
    QTimer timer;
    timer.start(125);
    timer.setSingleShot(true);
    trans = rootState->addTransition(&timer, SIGNAL(timeout()), ellipseState);
    trans->addAnimation(group);

    states.start();

#ifdef QT_KEYPAD_NAVIGATION
    QApplication::setNavigationMode(Qt::NavigationModeCursorAuto);
#endif
    return app.exec();
}

猜你喜欢

转载自blog.csdn.net/qq_40714324/article/details/103833294