QT 动画

发布于:2025-07-23 ⋅ 阅读:(13) ⋅ 点赞:(0)
#include <QApplication>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QPushButton>
#include <QSlider>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#include <QTimer>
#include <QRadioButton>
#include <QButtonGroup>
#include <QPainter>

class RotatingProxyWidget : public QWidget {
public:
    RotatingProxyWidget(QWidget *parent = nullptr) : QWidget(parent) {
        // 创建圆形按钮样式
        setFixedSize(150, 150);
        setStyleSheet("QWidget {"
                       "background: qradialgradient(cx:0.5, cy:0.5, radius:0.7, fx:0.5, fy:0.5, "
                       "stop:0 #3498db, stop:1 #2c3e50);"
                       "border-radius: 75px;"
                       "border: 3px solid #ecf0f1;"
                       "}");
    }
    
protected:
    void paintEvent(QPaintEvent *event) override {
        QWidget::paintEvent(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen(Qt::white);
        painter.setFont(QFont("Arial", 14, QFont::Bold));
        painter.drawText(rect(), Qt::AlignCenter, "旋转控件");
    }
};

class AnimatedGraphicsView : public QGraphicsView {
public:
    AnimatedGraphicsView(QWidget *parent = nullptr) : QGraphicsView(parent) {
        setRenderHint(QPainter::Antialiasing);
        setRenderHint(QPainter::SmoothPixmapTransform);
        setFrameShape(QFrame::NoFrame);
        setStyleSheet("background: transparent;");
    }
    
protected:
    void drawBackground(QPainter *painter, const QRectF &rect) override {
        // 绘制网格背景
        painter->fillRect(rect, QColor(30, 30, 40, 180));
        painter->setPen(QPen(QColor(60, 60, 80), 1));
        
        // 绘制网格
        qreal left = int(rect.left()) - (int(rect.left()) % 20);
        qreal top = int(rect.top()) - (int(rect.top()) % 20);
        for (qreal x = left; x < rect.right(); x += 20) {
            painter->drawLine(x, rect.top(), x, rect.bottom());
        }
        for (qreal y = top; y < rect.bottom(); y += 20) {
            painter->drawLine(rect.left(), y, rect.right(), y);
        }
        
        // 绘制中心点
        painter->setPen(Qt::NoPen);
        painter->setBrush(Qt::red);
        painter->drawEllipse(rect.center(), 3, 3);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    // 创建主窗口
    QMainWindow mainWindow;
    mainWindow.setWindowTitle("QGraphicsProxyWidget 旋转动画");
    mainWindow.resize(1000, 700);
    mainWindow.setStyleSheet("background: qlineargradient(x1:0, y1:0, x2:1, y2:1, "
                            "stop:0 #1a2a6c, stop:1 #b21f1f);");
    
    // 创建中央部件
    QWidget *centralWidget = new QWidget(&mainWindow);
    centralWidget->setStyleSheet("background: transparent;");
    mainWindow.setCentralWidget(centralWidget);
    
    // 创建主布局
    QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
    mainLayout->setContentsMargins(20, 20, 20, 20);
    
    // 标题
    QLabel *titleLabel = new QLabel("QGraphicsProxyWidget 旋转动画演示");
    titleLabel->setStyleSheet("font-size: 28px; font-weight: bold; color: #ffffff; "
                             "background: rgba(0, 0, 0, 100); border-radius: 15px; padding: 15px;");
    titleLabel->setAlignment(Qt::AlignCenter);
    mainLayout->addWidget(titleLabel);
    
    // 内容区域
    QHBoxLayout *contentLayout = new QHBoxLayout();
    contentLayout->setSpacing(20);
    
    // 左侧控制面板
    QGroupBox *controlBox = new QGroupBox("动画控制");
    controlBox->setStyleSheet("QGroupBox { background: rgba(44, 62, 80, 180); "
                            "border: 2px solid #3498db; border-radius: 15px; "
                            "color: #ecf0f1; font-size: 16px; }"
                            "QGroupBox::title { subcontrol-origin: margin; "
                            "left: 10px; padding: 0 5px 0 5px; }");
    controlBox->setFixedWidth(300);
    
    QVBoxLayout *controlLayout = new QVBoxLayout(controlBox);
    controlLayout->setSpacing(15);
    
    // 动画方向选择
    QLabel *directionLabel = new QLabel("旋转方向:");
    directionLabel->setStyleSheet("color: #ecf0f1; font-size: 16px;");
    
    QRadioButton *clockwiseBtn = new QRadioButton("顺时针");
    clockwiseBtn->setChecked(true);
    clockwiseBtn->setStyleSheet("color: #ecf0f1;");
    
    QRadioButton *counterClockwiseBtn = new QRadioButton("逆时针");
    counterClockwiseBtn->setStyleSheet("color: #ecf0f1;");
    
    QButtonGroup *directionGroup = new QButtonGroup;
    directionGroup->addButton(clockwiseBtn, 0);
    directionGroup->addButton(counterClockwiseBtn, 1);
    
    // 旋转中心选择
    QLabel *centerLabel = new QLabel("旋转中心:");
    centerLabel->setStyleSheet("color: #ecf0f1; font-size: 16px;");
    
    QRadioButton *centerCenterBtn = new QRadioButton("控件中心");
    centerCenterBtn->setChecked(true);
    centerCenterBtn->setStyleSheet("color: #ecf0f1;");
    
    QRadioButton *topLeftBtn = new QRadioButton("左上角");
    topLeftBtn->setStyleSheet("color: #ecf0f1;");
    
    QRadioButton *bottomRightBtn = new QRadioButton("右下角");
    bottomRightBtn->setStyleSheet("color: #ecf0f1;");
    
    QButtonGroup *centerGroup = new QButtonGroup;
    centerGroup->addButton(centerCenterBtn, 0);
    centerGroup->addButton(topLeftBtn, 1);
    centerGroup->addButton(bottomRightBtn, 2);
    
    // 旋转速度控制
    QLabel *speedLabel = new QLabel("旋转速度:");
    speedLabel->setStyleSheet("color: #ecf0f1; font-size: 16px;");
    
    QSlider *speedSlider = new QSlider(Qt::Horizontal);
    speedSlider->setRange(1, 10);
    speedSlider->setValue(5);
    speedSlider->setStyleSheet(
        "QSlider::groove:horizontal {"
        "    background: #34495e;"
        "    height: 8px;"
        "    border-radius: 4px;"
        "}"
        "QSlider::handle:horizontal {"
        "    background: #3498db;"
        "    width: 20px;"
        "    margin: -6px 0;"
        "    border-radius: 10px;"
        "}"
        "QSlider::sub-page:horizontal {"
        "    background: #1abc9c;"
        "    border-radius: 4px;"
        "}"
    );
    
    // 动画控制按钮
    QPushButton *startBtn = new QPushButton("开始旋转");
    startBtn->setStyleSheet(
        "QPushButton {"
        "    background: #2ecc71;"
        "    color: white;"
        "    border-radius: 8px;"
        "    padding: 12px;"
        "    font-weight: bold;"
        "    font-size: 16px;"
        "}"
        "QPushButton:hover {"
        "    background: #27ae60;"
        "}"
    );
    
    QPushButton *pauseBtn = new QPushButton("暂停旋转");
    pauseBtn->setStyleSheet(
        "QPushButton {"
        "    background: #e67e22;"
        "    color: white;"
        "    border-radius: 8px;"
        "    padding: 12px;"
        "    font-weight: bold;"
        "    font-size: 16px;"
        "}"
        "QPushButton:hover {"
        "    background: #d35400;"
        "}"
    );
    
    QPushButton *stopBtn = new QPushButton("停止旋转");
    stopBtn->setStyleSheet(
        "QPushButton {"
        "    background: #e74c3c;"
        "    color: white;"
        "    border-radius: 8px;"
        "    padding: 12px;"
        "    font-weight: bold;"
        "    font-size: 16px;"
        "}"
        "QPushButton:hover {"
        "    background: #c0392b;"
        "}"
    );
    
    // 添加到控制面板
    controlLayout->addWidget(directionLabel);
    controlLayout->addWidget(clockwiseBtn);
    controlLayout->addWidget(counterClockwiseBtn);
    controlLayout->addSpacing(10);
    controlLayout->addWidget(centerLabel);
    controlLayout->addWidget(centerCenterBtn);
    controlLayout->addWidget(topLeftBtn);
    controlLayout->addWidget(bottomRightBtn);
    controlLayout->addSpacing(10);
    controlLayout->addWidget(speedLabel);
    controlLayout->addWidget(speedSlider);
    controlLayout->addSpacing(20);
    controlLayout->addWidget(startBtn);
    controlLayout->addWidget(pauseBtn);
    controlLayout->addWidget(stopBtn);
    controlLayout->addStretch();
    
    // 右侧图形视图区域
    QGroupBox *graphicsBox = new QGroupBox("动画展示");
    graphicsBox->setStyleSheet(controlBox->styleSheet());
    
    QVBoxLayout *graphicsLayout = new QVBoxLayout(graphicsBox);
    
    // 创建图形视图和场景
    AnimatedGraphicsView *view = new AnimatedGraphicsView;
    QGraphicsScene *scene = new QGraphicsScene;
    scene->setSceneRect(0, 0, 600, 500);
    view->setScene(scene);
    
    // 创建要旋转的控件
    RotatingProxyWidget *widget = new RotatingProxyWidget;
    
    // 创建代理控件
    QGraphicsProxyWidget *proxy = scene->addWidget(widget);
    proxy->setPos(225, 175); // 初始位置
    
    // 添加位置标记
    QGraphicsEllipseItem *centerMarker = scene->addEllipse(-5, -5, 10, 10, 
                                                          QPen(Qt::red), QBrush(Qt::red));
    centerMarker->setPos(proxy->boundingRect().center() + proxy->pos());
    
    // 添加到布局
    graphicsLayout->addWidget(view);
    
    contentLayout->addWidget(controlBox);
    contentLayout->addWidget(graphicsBox);
    mainLayout->addLayout(contentLayout);
    
    // 状态标签
    QLabel *statusLabel = new QLabel("当前状态:未启动动画 | 旋转中心:控件中心 | 方向:顺时针");
    statusLabel->setStyleSheet("background: rgba(0, 0, 0, 120); color: #bdc3c7; "
                              "font-size: 14px; padding: 10px; border-radius: 8px;");
    statusLabel->setAlignment(Qt::AlignCenter);
    mainLayout->addWidget(statusLabel);
    
    // 动画对象
    QPropertyAnimation *rotationAnim = new QPropertyAnimation(proxy, "rotation");
    rotationAnim->setDuration(2000);
    rotationAnim->setStartValue(0);
    rotationAnim->setEndValue(360);
    rotationAnim->setLoopCount(-1); // 无限循环
    
    // 连接信号与槽
    QObject::connect(startBtn, &QPushButton::clicked, [=]() {
        rotationAnim->start();
        statusLabel->setText("当前状态:动画运行中 | 旋转中心:" + 
                            centerGroup->checkedButton()->text() + 
                            " | 方向:" + directionGroup->checkedButton()->text());
    });
    
    QObject::connect(pauseBtn, &QPushButton::clicked, [=]() {
        rotationAnim->pause();
        statusLabel->setText("当前状态:动画已暂停 | 旋转中心:" + 
                            centerGroup->checkedButton()->text() + 
                            " | 方向:" + directionGroup->checkedButton()->text());
    });
    
    QObject::connect(stopBtn, &QPushButton::clicked, [=]() {
        rotationAnim->stop();
        proxy->setRotation(0);
        statusLabel->setText("当前状态:动画已停止 | 旋转中心:" + 
                            centerGroup->checkedButton()->text() + 
                            " | 方向:" + directionGroup->checkedButton()->text());
    });
    
    QObject::connect(directionGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked),
        [=](QAbstractButton *button) {
        if (button == clockwiseBtn) {
            rotationAnim->setEndValue(rotationAnim->startValue() + 360);
        } else {
            rotationAnim->setEndValue(rotationAnim->startValue() - 360);
        }
    });
    
    QObject::connect(centerGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked),
        [=](QAbstractButton *button) {
        QPointF origin;
        
        if (button == centerCenterBtn) {
            origin = proxy->boundingRect().center();
        } else if (button == topLeftBtn) {
            origin = QPointF(0, 0);
        } else if (button == bottomRightBtn) {
            origin = QPointF(proxy->boundingRect().width(), proxy->boundingRect().height());
        }
        
        proxy->setTransformOriginPoint(origin);
        centerMarker->setPos(origin + proxy->pos());
    });
    
    QObject::connect(speedSlider, &QSlider::valueChanged, [=](int value) {
        rotationAnim->setDuration(3000 - (value * 200));
    });
    
    // 显示主窗口
    mainWindow.show();
    
    // 延迟启动动画
    QTimer::singleShot(1000, [=]() {
        rotationAnim->start();
        statusLabel->setText("当前状态:动画运行中 | 旋转中心:控件中心 | 方向:顺时针");
    });
    
    return app.exec();
}

实现原理
这个示例展示了如何使用 QGraphicsProxyWidget 实现旋转动画,主要包含以下技术要点:

  1. 创建图形视图框架
    QGraphicsScene:作为容器管理所有图形项

QGraphicsView:显示场景内容的视图组件

QGraphicsProxyWidget:将普通 QWidget 嵌入图形场景的代理

  1. 旋转动画实现
    使用 QPropertyAnimation 对代理的 rotation 属性进行动画处理:

cpp
QPropertyAnimation *rotationAnim = new QPropertyAnimation(proxy, “rotation”);
rotationAnim->setDuration(2000);
rotationAnim->setStartValue(0);
rotationAnim->setEndValue(360);
rotationAnim->setLoopCount(-1); // 无限循环
3. 设置旋转中心
通过 setTransformOriginPoint() 方法设置旋转中心点:

cpp
// 设置旋转中心为控件中心
proxy->setTransformOriginPoint(proxy->boundingRect().center());

// 设置旋转中心为左上角
proxy->setTransformOriginPoint(QPointF(0, 0));

// 设置旋转中心为右下角
proxy->setTransformOriginPoint(QPointF(proxy->boundingRect().width(),
proxy->boundingRect().height()));
4. 动画控制
开始动画:rotationAnim->start()

暂停动画:rotationAnim->pause()

停止动画:rotationAnim->stop()

  1. 方向控制
    通过改变动画的结束值实现顺时针/逆时针旋转:

cpp
// 顺时针
rotationAnim->setEndValue(rotationAnim->startValue() + 360);

// 逆时针
rotationAnim->setEndValue(rotationAnim->startValue() - 360);
功能特点
多种旋转中心选择:

控件中心(默认)

左上角

右下角

旋转方向控制:

顺时针旋转

逆时针旋转

旋转速度调节:

通过滑块控制旋转速度(1-10级)

动画控制按钮:

开始旋转

暂停旋转

停止旋转

视觉辅助:

网格背景帮助定位

红色标记点指示旋转中心

状态标签显示当前设置

美观的UI设计:

深色渐变背景

半透明控制面板

圆角控件和按钮

现代化的色彩方案

应用场景
UI特效:为应用程序添加动态效果

仪表盘:创建旋转仪表和指示器

游戏开发:实现游戏元素的旋转动画

数据可视化:动态展示数据变化

教育软件:演示旋转和变换概念

扩展建议
添加缩放动画,实现更复杂的变换效果

支持多个旋转控件同时动画

实现沿路径移动的旋转动画

添加3D旋转效果(使用QTransform)

保存和加载动画预设

这个实现展示了Qt图形视图框架的强大功能,通过组合QGraphicsView、QGraphicsScene和QGraphicsProxyWidget,可以轻松创建流畅的旋转动画效果。

Linear`: 线性变化。
- `InQuad`: 二次方缓入(开始慢)。
- `OutQuad`: 二次方缓出(结束慢)。
- `InOutQuad`: 二次方缓入缓出。
- `OutInQuad`: 缓出缓入(先缓出再缓入)。
- `InCubic`: 三次方缓入。
- ...(还有更多次方类型)
- `InSine`: 正弦缓入。
- `OutSine`: 正弦缓出。
- `InOutSine`: 正弦缓入缓出。
- `InExpo`: 指数缓入。
- `OutExpo`: 指数缓出。
- `InCirc`: 圆形缓入。
- `OutCirc`: 圆形缓出。
- `InElastic`: 弹性缓入(像橡皮筋一样)。
-  `OutElastic`: 弹性缓出。
- `InOutElastic`: 弹性缓入缓出。
- `InBack`: 过冲缓入(先略微后退再前进)。
- `OutBack`: 过冲缓出(略微超过目标值再返回)。
- `InOutBack`: 过冲缓入缓出。
- `InBounce`: 弹跳缓入(像球落地弹跳)。
- `OutBounce`: 弹跳缓出。
- `InOutBounce`: 弹跳缓入缓出。

Deepseek找到代码没有执行,记录一下。


网站公告

今日签到

点亮在社区的每一天
去签到