#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 实现旋转动画,主要包含以下技术要点:
- 创建图形视图框架
QGraphicsScene:作为容器管理所有图形项
QGraphicsView:显示场景内容的视图组件
QGraphicsProxyWidget:将普通 QWidget 嵌入图形场景的代理
- 旋转动画实现
使用 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()
- 方向控制
通过改变动画的结束值实现顺时针/逆时针旋转:
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找到代码没有执行,记录一下。