基于Qt的视频监控项目自定义时间轴刻度控件[开源]

背景介绍

目前很多PC安防监控客户端上,查看录像时,直接生成了一个当天的时间轴,在时间轴上会以不同的色块标注当天哪些时间段有录像,哪些时间段没有录像,一目了然,效果挺好的。

功能介绍

很久之前做过这个Demo,实现了以下功能:

  1. 可以根据给定的时间段设置不同颜色标记出有录像的时间段。
  2. 根据控件大小,时间轴自动缩放。
  3. 左右滑动查看未显示的时间轴。
  4. 鼠标悬停在时间轴上可以显示悬停点对应的录像时间。
    滑动
    分辨度

代码分析

需要关注的地方在代码中注释出来了,直接看代码即可。整个代码思路就是在我们需要关注的几个变量发送变化时,调用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哈。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值