1 需求描述
做一个独立的控件,该控件能够滚动轮播报警信息,告警信息分为一般、严重、危险三个等级,分别用不同颜色做标记。
2 设计思路
做滚动轮播效果,优先想到Qt的动画框架,同时考虑到图形视图框架的易用性,实现上应该会简单不少,所以该控件会使用图形视图框架并结合动画框架实现。
- 每一条告警信息用QGraphicsTextItem表示
- 由于整体要使用动画轮播,所有的QGraphicsTextItem会添加到一个QGraphicsItemGroup中,方便做整体动画
- 动画使用QVariantAnimation即可,毕竟轮播只是改变了QGraphicsItemGroup的y坐标
- 为了能够适应窗口大小的变化,需要动态调整场景的大小和动画参数
3 代码实现
控件为QGraphicsView的子类,设定了自定义的场景类,场景封装在控件内部,场景提供了必要的接口,例如添加、清空告警信息:
3.1 AlarmHistoryScene.h
class AlarmHistoryScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit AlarmHistoryScene(QObject *parent = 0);
void addAlarmText(WarningLevel level, const QString &alarm);
void clear();
private:
void updateAnimation();
private:
QStringList m_alarmList;
QGraphicsItemGroup *m_pItemGroup = nullptr;
QVariantAnimation *m_pAnimation = nullptr;
};
addAlarmText添加告警信息接口,clear清空告警信息,updateAnimation会在初次启动或窗口大小变化时自动调用。
3.2 AlarmHistoryScene.cpp
AlarmHistoryScene::AlarmHistoryScene(QObject *parent) : QGraphicsScene(parent)
{
m_pItemGroup = new QGraphicsItemGroup();
addItem(m_pItemGroup);
m_pItemGroup->setPos(0, 0);
connect(this, &AlarmHistoryScene::sceneRectChanged, this, &AlarmHistoryScene::updateAnimation);
}
void AlarmHistoryScene::addAlarmText(WarningLevel level ,const QString &alarm)
{
QString detail = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
.append(QStringLiteral(":"))
.append(alarm);
QGraphicsTextItem *item = new QGraphicsTextItem(detail);
QFont font;
font.setFamily("System");
font.setPixelSize(12);
item->setFont(font);
switch (level) {
case WarningLevel::Normal:
item->setDefaultTextColor(QColor(227, 207, 87));
break;
case WarningLevel::Serious:
item->setDefaultTextColor(QColor(243, 130, 19));
break;
case WarningLevel::Danger:
item->setDefaultTextColor(QColor("red"));
break;
default:
break;
}
int count = m_pItemGroup->childItems().count();
item->setPos(0, m_pItemGroup->y() + count * (font.pixelSize() + 2));
m_pItemGroup->addToGroup(item);
updateAnimation();
}
根据日志等级实例化QGraphicsTextItem并添加到m_pItemGroup中,再更新下动画,这里没啥可说的。
void AlarmHistoryScene::clear()
{
if (m_pAnimation) {
m_pAnimation->stop();
m_pAnimation->deleteLater();
m_pAnimation = nullptr;
}
m_pItemGroup->setPos(0, 0);
auto list = m_pItemGroup->childItems();
int count = list.count();
for (int i = 0; i < count; ++i) {
auto item = list.at(i);
m_pItemGroup->removeFromGroup(item);
removeItem(item);
delete item;
}
}
这里主要是关闭动画,并清空所有告警信息。
void AlarmHistoryScene::updateAnimation()
{
if (m_pAnimation) {
m_pAnimation->stop();
m_pAnimation->deleteLater();
m_pAnimation = nullptr;
}
m_pAnimation = new QVariantAnimation(this);
connect(m_pAnimation, &QVariantAnimation::valueChanged, this, [=](const QVariant &value) {
m_pItemGroup->setPos(0, value.toDouble());
});
m_pAnimation->setLoopCount(-1);
qreal sceneHeight = this->sceneRect().height();
m_pItemGroup->setPos(0, sceneHeight);
m_pAnimation->setDuration((sceneHeight + m_pItemGroup->boundingRect().height()) * 12);
m_pAnimation->setStartValue(sceneHeight);
m_pAnimation->setEndValue(-m_pItemGroup->boundingRect().height());
m_pAnimation->start();
}
更新动画,作用于m_pItemGroup,轮播只需要改变y坐标值,所以使用QVariantAnimation即可实现。
3.3 AlarmHistoryView.cpp
场景封装在视图内部,这里需要根据视图大小动态调整场景大小,以便使动画适应窗口。
AlarmHistoryView::AlarmHistoryView(QWidget *parent) : QGraphicsView(parent)
{
m_pScene = new AlarmHistoryScene(this);
setScene(m_pScene);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
void AlarmHistoryView::addAlarmText(WarningLevel level, const QString &alarm)
{
m_pScene->addAlarmText(level, alarm);
}
void AlarmHistoryView::resizeEvent(QResizeEvent *event)
{
QGraphicsView::resizeEvent(event);
m_pScene->setSceneRect(0, 0, this->size().width(), this->size().height());
}
好啦,主要代码就这么多了,还是挺简单的。
4 总结
Qt图形视图框架异常强大,结合动画框架能够实现很多酷炫效果,这里也只用到了一点点皮毛,多看看Qt的Demo程序会有很多意外收获,一般人我不告诉他,哈哈。溜了溜了。。。新年快乐!