时间刻度尺需求
- 显示时间轴标尺(0:00:00~23:59:59)
- 显示柱状图表示改时间段有录像
- 当前录像刻度线及显示刻度
- 支持拖拽、缩放(限制在0:00:00~23:59:59,最小刻度为1秒)
效果图
实现方案
由于完全自主实现工作量太大,目前使用QCustomPlot取巧实现
QCustomPlot环境
官网:Qt Plotting Widget QCustomPlot - Introduction
环境布置:将qcustomplot.h、qcustomplot.cpp包含进工程编译即可
代码实现
为了调用方便不直接继承QCustomPlot,而将QCustomPlot作为成员变量
- 如何支持拖拽、缩放
void TimeBarPlot::setDragEnabled(bool enabled)
{
m_customPlot->setInteraction(QCP::iRangeDrag, enabled);
m_customPlot->axisRect()->setRangeDrag(Qt::Horizontal);
}
void TimeBarPlot::setZoomEnabled(bool enabled)
{
m_customPlot->setInteraction(QCP::iRangeZoom, enabled);
m_customPlot->axisRect()->setRangeZoom(Qt::Horizontal);
}
- 如何限制拖拽、缩放的轴范围
connect(m_customPlot->xAxis, static_cast<void(QCPAxis::*)(const QCPRange & newRange, const QCPRange & oldRange)>(&QCPAxis::rangeChanged),
this, &TimeBarPlot::setxAxisRange, Qt::UniqueConnection);
...
void TimeBarPlot::setxAxisRange(const QCPRange& newRange, const QCPRange& oldRange)
{
if (newRange.size() < XAXIS_MIN_RANGE_LENGTH)
{
if (QCPRange::validRange(oldRange))
{
m_customPlot->xAxis->setRange(oldRange);
}
}
else
{
m_customPlot->xAxis->setRange(qMax(XAXIS_RANGE_LOWER, newRange.lower), qMin(newRange.upper, XAXIS_RANGE_UPPER));
}
}
- 光标线实现
使用QCPItemStraightLine实现光标线,QCPItemText显示内容
- 核心代码预览
class QCPBars;
class QCPRange;
class QCPItemText;
class QCustomPlot;
class QCPItemStraightLine;
class HQUI_EXPORT TimeBarPlot : public QWidget
{
Q_OBJECT
public:
TimeBarPlot(QWidget *parent = nullptr);
~TimeBarPlot();
...
//新增柱状图
void addBars(double startTime, double endTime, double barHeight, const QColor& color);
void clearBars();
//显示光标刻度线
void setCursorValue(double pos);
void setCursorVisible(bool visible);
...
private Q_SLOTS:
void setxAxisRange(const QCPRange& newRange, const QCPRange& oldRange);
void handleMousePressEvent(QMouseEvent* event);
void handleMouseReleaseEvent(QMouseEvent* event);
void handleMouseMoveEvent(QMouseEvent* event);
...
private:
QCustomPlot* m_customPlot = nullptr;
QCPItemStraightLine* m_lineCursor = nullptr; //当前刻度线
QCPItemText* m_tmpText = nullptr; //光标刻度内容
QCPItemStraightLine* m_tmpCursor = nullptr; //光标刻度线
QVector<QCPBars*> m_bars;
bool m_bPressed = false;
double m_lastPosX;
};
const double XAXIS_RANGE_LOWER = 0;
const double XAXIS_RANGE_UPPER = 60.0 * 60.0 * 24.0;
const double XAXIS_MIN_RANGE_LENGTH = 30.0;
TimeBarPlot::TimeBarPlot(QWidget *parent)
: QWidget(parent)
, m_customPlot(new QCustomPlot(parent))
{
QGridLayout* lyout = new QGridLayout(this);
this->setLayout(lyout);
lyout->addWidget(m_customPlot);
lyout->setMargin(0.0);
initPlot();
//游标
initCursor();
}
TimeBarPlot::~TimeBarPlot()
{}
void TimeBarPlot::addBars(double startTime, double endTime, double barHeight, const QColor& color)
{
QVector<double> xData = { endTime - startTime };
QVector<double> yData = { barHeight };
// 创建柱状图数据容器
QCPBars* bars = new QCPBars(m_customPlot->yAxis, m_customPlot->xAxis);
bars->setBaseValue(startTime);
bars->setData(yData, xData);
bars->setWidth(barHeight);
bars->setPen(color);
bars->setBrush(color); // 设置柱状图颜色
m_bars << bars;
}
void TimeBarPlot::clearBars()
{
// 删除所有的QCPBars
m_customPlot->clearPlottables();
m_bars.clear();
}
void TimeBarPlot::setCursorValue(double pos)
{
if (!m_bPressed)
{
m_lineCursor->point1->setCoords(pos, m_customPlot->yAxis->range().lower);//起点坐标
m_lineCursor->point2->setCoords(pos, m_customPlot->yAxis->range().upper);//终点坐标
}
}
void TimeBarPlot::setCursorVisible(bool visible)
{
m_lineCursor->setVisible(visible);
}
void TimeBarPlot::handleMousePressEvent(QMouseEvent* event)
{
m_bPressed = true;
//当前鼠标位置(像素坐标)
double x_pos = event->pos().x();
m_lastPosX = x_pos;
this->setCursor(Qt::ClosedHandCursor);
m_tmpCursor->setVisible(false);
m_tmpText->setVisible(false);
QWidget::mousePressEvent(event);
}
void TimeBarPlot::handleMouseReleaseEvent(QMouseEvent* event)
{
//当前鼠标位置(像素坐标)
double x_pos = event->pos().x();
if (qFabs(x_pos - m_lastPosX) < 1)
{
//像素坐标转成实际的x,y轴的坐标
double x_val = m_customPlot->xAxis->pixelToCoord(x_pos);
m_lineCursor->point1->setCoords(x_val, m_customPlot->yAxis->range().lower);
m_lineCursor->point2->setCoords(x_val, m_customPlot->yAxis->range().upper);
this->replot();
emit sig_cursorValueChanged(getCursorValue());
}
m_bPressed = false;
this->setCursor(Qt::ArrowCursor);
QWidget::mouseReleaseEvent(event);
}
void TimeBarPlot::handleMouseMoveEvent(QMouseEvent* event)
{
if (!m_bPressed)
{
//当前鼠标位置(像素坐标)
double x_pos = event->pos().x();
//像素坐标转成实际的x,y轴的坐标
double x_val = m_customPlot->xAxis->pixelToCoord(x_pos);
m_tmpCursor->point1->setCoords(x_val, m_customPlot->yAxis->range().lower);
m_tmpCursor->point2->setCoords(x_val, m_customPlot->yAxis->range().upper);
m_tmpCursor->setVisible(true);
m_tmpText->setText(QTime(0, 0, 0).addSecs(x_val).toString("hh:mm:ss"));
m_tmpText->position->setCoords(x_val, m_customPlot->yAxis->range().center()); // 在plot中的位置
m_tmpText->setVisible(true);
this->replot();
}
QWidget::mouseMoveEvent(event);
}
void TimeBarPlot::initPlot()
{
m_customPlot->addGraph(); // blue line
QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);
timeTicker->setTimeFormat("%h:%m:%s");
m_customPlot->xAxis->setTicker(timeTicker);
m_customPlot->xAxis->setTickLength(30, 0);
m_customPlot->xAxis->setSubTickLength(10, 0);
m_customPlot->xAxis->ticker()->setTickCount(6);
m_customPlot->xAxis->grid()->setPen(QPen(QColor(Qt::darkGray), 1, Qt::DotLine));
m_customPlot->yAxis->setVisible(false);
//X轴范围
m_customPlot->xAxis->setRange(XAXIS_RANGE_LOWER, XAXIS_RANGE_UPPER);
connect(m_customPlot->xAxis, static_cast<void(QCPAxis::*)(const QCPRange & newRange, const QCPRange & oldRange)>(&QCPAxis::rangeChanged),
this, &TimeBarPlot::setxAxisRange, Qt::UniqueConnection);
connect(m_customPlot, &QCustomPlot::mousePress, this, &TimeBarPlot::handleMousePressEvent, Qt::UniqueConnection);
connect(m_customPlot, &QCustomPlot::mouseMove, this, &TimeBarPlot::handleMouseMoveEvent, Qt::UniqueConnection);
connect(m_customPlot, &QCustomPlot::mouseRelease, this, &TimeBarPlot::handleMouseReleaseEvent, Qt::UniqueConnection);
}
void TimeBarPlot::initCursor()
{
//游标
QCPGraph* graph = m_customPlot->graph(0);
QPen pen(Qt::red, 1.5, Qt::SolidLine); //颜色 、宽度、样式(直线)
m_lineCursor = new QCPItemStraightLine(m_customPlot);
Q_ASSERT(m_lineCursor);
m_lineCursor->setLayer("overlay");
m_lineCursor->setPen(pen);//设置游标线的样式
m_lineCursor->setClipToAxisRect(true);//自适应范围
QPen pen2(Qt::red, 1, Qt::DashDotDotLine); //颜色 、宽度、样式
m_tmpCursor = new QCPItemStraightLine(m_customPlot);
Q_ASSERT(m_tmpCursor);
m_tmpCursor->setLayer("overlay");
m_tmpCursor->setPen(pen2);//设置游标线的样式
m_tmpCursor->setClipToAxisRect(true);//自适应范围
m_tmpCursor->setVisible(false);
m_tmpText = new QCPItemText(m_customPlot);
Q_ASSERT(m_tmpText);
m_tmpText->setLayer("overlay");
m_tmpText->setFont(QFont(font().family(), 10));
m_tmpText->setColor(Qt::red);
m_tmpText->setPositionAlignment(Qt::AlignCenter);
m_tmpText->setVisible(false);
}