背景介绍
目前很多PC安防监控客户端上,查看录像时,直接生成了一个当天的时间轴,在时间轴上会以不同的色块标注当天哪些时间段有录像,哪些时间段没有录像,一目了然,效果挺好的。
功能介绍
很久之前做过这个Demo,实现了以下功能:
- 可以根据给定的时间段设置不同颜色标记出有录像的时间段。
- 根据控件大小,时间轴自动缩放。
- 左右滑动查看未显示的时间轴。
- 鼠标悬停在时间轴上可以显示悬停点对应的录像时间。
代码分析
需要关注的地方在代码中注释出来了,直接看代码即可。整个代码思路就是在我们需要关注的几个变量发送变化时,调用update,触发paintEvent事件。
#ifndef VIDEOLISTTIMELINE_H
#define VIDEOLISTTIMELINE_H
#include <QWidget>
#include <QDate>
class VideoListTimeLine : public QWidget
{
Q_OBJECT
public:
VideoListTimeLine(int height = 25, QWidget *parent = 0);
~VideoListTimeLine();
void setVideoListTime(QList<QPair<QTime, QTime> > recordList);
void setScaleFactor(int scaleFactor);
protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void leaveEvent(QEvent *event);
void enterEvent(QEvent *event);
private:
int timeLineWidth;
int timeLineHeight;
int scaleFactor = 1; /* 伸缩因子 */
int rightHiddenWidth = 0; /* 左右移动时间轴,右侧未显示的宽度 */
int rightHiddenMiniWidth = 0; /* 右侧未显示的最小宽度 0 */
int rightHiddenMaxWidth = 0; /* 右侧未显示的最大宽度 timeLineWidth - this->width() */
int startPosX = 0;
bool mouseRightButtonClicked = false;
QPoint mousePressedPosition;
bool _30minutesRulerFlag = false; /* 30分钟刻度尺 */
bool _15minutesRulerFlag = false; /* 15分钟刻度尺 */
bool _5minutesRulerFlag = false; /* 5分钟刻度尺 */
bool _1minuteRulerFlag = false; /* 1分钟刻度尺*/
QList< QPair<QTime, QTime> > videoList;
QDate curDate;
QList<QRectF> convertTimeSegmentToRect();
void paintTimeSegment();
void paintTimeRuler();
void setTimeRulerFlag();
void paintOneHourRuler();
void paintHalfHourRuler();
void paint15minutesRuler();
void paint5minutesRuler();
void paint1minuteRuler();
};
#endif // VIDEOLISTTIMELINE_H
#include <QPainter>
#include <QTime>
#include <QDateTime>
#include <QMouseEvent>
#include <QResizeEvent>
#include <QDebug>
#include <QToolTip>
#include <QPair>
#include <QDateTime>
#include "VideoListTimeLine.h"
const int SECONDS_IN_DAY = 60 * 60 *24;
VideoListTimeLine::VideoListTimeLine(int height, QWidget *parent)
: QWidget(parent)
{
timeLineHeight = height;
setMouseTracking(true);
setFixedHeight(height);
setCursor(Qt::PointingHandCursor);
}
VideoListTimeLine::~VideoListTimeLine()
{
}
void VideoListTimeLine::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
paintTimeSegment();
paintTimeRuler();
}
void VideoListTimeLine::enterEvent(QEvent *event)
{
Q_UNUSED(event);
//setCursor(Qt::PointingHandCursor);
}
void VideoListTimeLine::leaveEvent(QEvent *event)
{
Q_UNUSED(event);
//setCursor(Qt::ArrowCursor);
}
void VideoListTimeLine::mouseMoveEvent(QMouseEvent *event)
{
/* 鼠标右键按下后,左右滑动操作 */
if ( true == mouseRightButtonClicked && false == mousePressedPosition.isNull())
{
int moveWidth = event->pos().x() - mousePressedPosition.x();
startPosX += event->pos().x() - mousePressedPosition.x();
/* 处理左右两个边界 */
if ( rightHiddenWidth + moveWidth > rightHiddenMaxWidth)
{
startPosX = 0;
rightHiddenWidth = rightHiddenMaxWidth;
}
else if ( rightHiddenWidth + moveWidth < rightHiddenMiniWidth)
{
startPosX = -rightHiddenMaxWidth;
rightHiddenWidth = 0;
}
else
{
rightHiddenWidth += event->pos().x() - mousePressedPosition.x();
}
mousePressedPosition = event->pos();
update();
}
/* 鼠标悬浮在时间轴上,显示悬浮点的具体时间 */
else
{
double x = event->pos().x();
int curSec = static_cast<int>( (x - startPosX) / timeLineWidth * SECONDS_IN_DAY );
QTime curTime(0, 0, 0);
curDate = QDate::currentDate();
QDateTime curDateTime(curDate, curTime.addSecs(curSec));
QToolTip::showText(event->globalPos(), curDateTime.toString("yyyy.MM.dd-hh:mm:ss"), this);
}
}
void VideoListTimeLine::mouseDoubleClickEvent(QMouseEvent *event)
{
double x = event->pos().x();
int curSec = static_cast<int>( (x - startPosX) / timeLineWidth * SECONDS_IN_DAY);
QTime curTime(0, 0, 0);
qDebug() << curTime.addSecs(curSec);
/* to-do 此处可以增加一个信号,将选中的时间发送出去 */
}
void VideoListTimeLine::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);
timeLineWidth = this->width() * scaleFactor;
startPosX = 0;
rightHiddenWidth = timeLineWidth - this->width();
rightHiddenMiniWidth = 0;
rightHiddenMaxWidth = timeLineWidth - this->width();
}
void VideoListTimeLine::setVideoListTime(QList< QPair<QTime, QTime> > recordList)
{
videoList.clear();
for (int i=0; i<recordList.size(); i++)
{
/* 剔除无效数据 */
if ( recordList.at(i).first < recordList.at(i).second)
{
videoList.append(recordList.at(i));
}
}
}
/* 将录像的开始时间和结束时间转换为对应的Rect,用于稍后绘制 */
QList<QRectF> VideoListTimeLine::convertTimeSegmentToRect()
{
int beginY = 0;
int endY = timeLineHeight;
int width = timeLineWidth;
QList<QRectF> timeRectSegment;
for (int i=0; i< videoList.size(); i++)
{
double beginSec = videoList.at(i).first.msecsSinceStartOfDay() / 1000;
double beginX = beginSec / SECONDS_IN_DAY * width;
QPointF beginPoint = QPointF(startPosX + beginX, beginY);
double endSec = videoList.at(i).second.msecsSinceStartOfDay() / 1000;
double endX = endSec / SECONDS_IN_DAY * width;
QPointF endPoint = QPointF(startPosX + endX, endY);
timeRectSegment.append(QRectF(beginPoint, endPoint));
}
return timeRectSegment;
}
void VideoListTimeLine::paintTimeSegment()
{
QList<QRectF> timeRectList = convertTimeSegmentToRect();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
/* 绘制背景 */
painter.setPen(QPen(QColor(108, 108, 108), 0));
painter.setBrush(QColor(108, 108, 108));
/* 背景颜色始终是控件的区域大小,不需要添加偏移量 */
//painter.drawRect(startPosX + 0 ,0, startPosX + timeLineWidth, this->height());
painter.drawRect( 0 ,0, timeLineWidth, this->height());
/* 绘制前景 */
painter.setPen(QPen(QColor(102, 163, 52), 0));
painter.setBrush(QColor(102, 163, 52));
for (int i=0; i<timeRectList.size(); i++)
{
QRectF rect = timeRectList.at(i);
painter.drawRect(rect);
}
}
void VideoListTimeLine::paintTimeRuler()
{
/* 小时刻度默认一定有 */
paintOneHourRuler();
if ( true == _30minutesRulerFlag)
paintHalfHourRuler();
if ( true == _15minutesRulerFlag)
paint15minutesRuler();
if ( true == _5minutesRulerFlag)
paint5minutesRuler();
if (true == _1minuteRulerFlag)
paint1minuteRuler();
}
void VideoListTimeLine::mousePressEvent(QMouseEvent *event)
{
if ( event->button() == Qt::RightButton)
{
mouseRightButtonClicked = true;
mousePressedPosition = event->pos();
}
}
void VideoListTimeLine::mouseReleaseEvent(QMouseEvent *event)
{
if ( event->button() == Qt::RightButton)
{
mouseRightButtonClicked = false;
mousePressedPosition = QPoint();
}
}
void VideoListTimeLine::setTimeRulerFlag()
{
_30minutesRulerFlag = false;
_15minutesRulerFlag = false;
_5minutesRulerFlag = false;
_1minuteRulerFlag = false;
switch (scaleFactor)
{
case 1:
_30minutesRulerFlag = true;
break;
case 2:
_30minutesRulerFlag = true;
_15minutesRulerFlag = true;
break;
case 4:
_30minutesRulerFlag = true;
_5minutesRulerFlag = true;
break;
case 8:
_30minutesRulerFlag = true;
_1minuteRulerFlag = true;
break;
default:
_30minutesRulerFlag = true;
break;
}
}
void VideoListTimeLine::setScaleFactor(int scaleFactor)
{
scaleFactor = (scaleFactor >= 8) ? 8 : scaleFactor;
this->scaleFactor = scaleFactor;
setTimeRulerFlag();
timeLineWidth = this->width() * scaleFactor;
startPosX = 0;
rightHiddenWidth = timeLineHeight - this->width();
rightHiddenMiniWidth = 0;
rightHiddenMaxWidth = timeLineHeight - this->width();
update();
}
void VideoListTimeLine::paintOneHourRuler()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
QFont font = painter.font();
font.setPixelSize(15);
painter.setFont(font);
painter.setPen(QPen(QColor(255, 255, 255), 2));
double hourWidth = timeLineWidth / 24.0;
double halfHourWidth = timeLineWidth / 48.0;
int hourRulerBeginY = this->height() * 3 / 5;
int hourRulerEndY = this->height();
for (int i=1; i<24; i++)
{
double hourRulerX = hourWidth * i;
painter.drawLine(QPointF(startPosX + hourRulerX, hourRulerBeginY),
QPointF(startPosX + hourRulerX, hourRulerEndY));
QString strHour = QString("%1").arg(i, 2, 10, QChar('0'));
QPoint topLeftPoint = QPoint( static_cast<int>(startPosX + hourRulerX - halfHourWidth), 0);
QPoint rightBottomPoint = QPoint( static_cast<int>(startPosX + hourRulerX + halfHourWidth), hourRulerBeginY);
QRect strRect = QRect(topLeftPoint, rightBottomPoint);
painter.drawText(strRect, Qt::AlignCenter, strHour);
}
}
void VideoListTimeLine::paintHalfHourRuler()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor(255, 255, 255), 2));
double hourWidth = timeLineWidth / 24.0;
double halfHourWidth = timeLineWidth / 48.0;
int halfhourRulerBeginY = this->height() * 4 / 5 - 1;
int halfHourRulerEndY = this->height();
/* 一天24个小时 */
for (int i=0; i<24; i++)
{
double halfHourRulerX = halfHourWidth + hourWidth * (i);
painter.drawLine(QPointF(startPosX + halfHourRulerX, halfhourRulerBeginY),
QPointF(startPosX + halfHourRulerX, halfHourRulerEndY));
}
}
void VideoListTimeLine::paint15minutesRuler()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor(255, 255, 255), 2));
double _15minutesWidth = timeLineWidth / 96.0;
int _15minutesRulerBeginY = this->height() * 4 / 5 + 2;
int _15minutesRulerEndY = this->height();
/* 一天共有96个15分钟 24 * 4 = 96 */
for (int i=0; i<96; i++)
{
double _15minutesRulerX = _15minutesWidth + _15minutesWidth * (i);
painter.drawLine(QPointF(startPosX + _15minutesRulerX, _15minutesRulerBeginY),
QPointF(startPosX + _15minutesRulerX, _15minutesRulerEndY));
}
}
void VideoListTimeLine::paint5minutesRuler()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor(255, 255, 255), 2));
double _5minutesWidth = timeLineWidth / 288.0;
int _5minutesRulerBeginY = this->height() * 4 / 5 + 2;
int _5minutesRulerEndY = this->height();
/* 一天共有288个5分钟 24 * 12 = 288*/
for (int i=0; i<288; i++)
{
double _5minutesRulerX = _5minutesWidth + _5minutesWidth * (i);
painter.drawLine(QPointF(startPosX + _5minutesRulerX, _5minutesRulerBeginY),
QPointF(startPosX + _5minutesRulerX, _5minutesRulerEndY));
}
}
void VideoListTimeLine::paint1minuteRuler()
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(QPen(QColor(255, 255, 255), 2));
double _1minuteWidth = timeLineWidth / 1440.0;
int _1minuteRulerBeginY = this->height() * 4 / 5 + 2;
int _1minuteRulerEndY = this->height();
/* 一天共有1440个1分钟 24 * 60 = 1440*/
for (int i=0; i<1440; i++)
{
double _1minuteRulerX = _1minuteWidth + _1minuteWidth * (i);
painter.drawLine(QPointF(startPosX + _1minuteRulerX, _1minuteRulerBeginY),
QPointF(startPosX + _1minuteRulerX, _1minuteRulerEndY));
}
}
调用方法如下,首先setVideoListTime,设置哪些时间段有录像,然后setScaleFactor设置时间轴的刻度分辨率即可使用。
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
VideoListTimeLine w;
QList< QPair<QTime, QTime> > videoListSegment;
// videoListSegment.append( qMakePair(QTime(7, 20, 0), QTime(10, 50, 40)) );
// videoListSegment.append( qMakePair(QTime(11, 30, 0), QTime(11, 59, 20)) );
// videoListSegment.append( qMakePair(QTime(13, 18, 0), QTime(17, 20, 35)) );
// videoListSegment.append( qMakePair(QTime(20, 20, 0), QTime(23, 50, 40)) );
videoListSegment.append( qMakePair(QTime(0, 0, 0), QTime(23, 59, 59)) );
w.setVideoListTime(videoListSegment);
w.setScaleFactor(2);
w.show();
return a.exec();
}
代码下载
完整代码存放在github上,https://github.com/ShawZG/VideoListTimeLine。
如果对你有帮助,给个小星星star哈。