Bootstrap

Qt自定义时间规划进度条

先看几张效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 可以任意拖动时间轨道
  • 可以任意缩放时间轨道,缩放级别20级
  • 拖动树形节点到时间进度条上,并且可以自由添加时间的长短
  • 拖动时间的位置

部分类图
在这里插入图片描述

普通轨道类:

#pragma once

#include <QGraphicsItem>

//基础Item,可以绘制背景色统一宽度等

class PlanItem : public QGraphicsItem {

public:
	PlanItem(QGraphicsItem* parent = nullptr);
	~PlanItem();

	int&getStartX() {
		return _startX;
	}
	int&getStartY() {
		return _startY;
	}
	int&getViewWidth() {
		return _viewWidht;
	}
	int&getViewHeight() {
		return _viewHeight;
	}

	int&getItemWidth() {
		return _itemWidth;
	}
	int&getItemHeight() {
		return _itemHeight;
	}
	virtual void viewSize(int vw, int vh);
	virtual void setStartOffset(int x, int y);
	virtual void setTimeOnCenter(double time);//设置时间在中点
	virtual void zoomIn(QPointF mouseMovePt, double mouseMoveTime);
	virtual void zoomOut(QPointF mouseMovePt, double mouseMoveTime);

	void setItemBorder(bool b) {
		_isDrawBorder = b;
	}

	void setSelected(bool b) {
		_isClickSelect = b;
	}
	bool&getSelected() {
		return _isClickSelect;
	}
protected:
	QRectF boundingRect() const override;
	void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR) override;
	void drawBg(QPainter *painter);
	void zoomPos(QPointF mouseMovePt, double mouseMoveTime);
protected:

	int _viewWidht = 0;				//视图宽度
	int _viewHeight = 0;			//视图高度

	int _itemWidth = 0;				//当前图元的宽度
	int _itemHeight = 50;			//当前图元的高度

	int _space = 2;					//图元距离边界的距离

	int _startX = 0;				//开始的X
	int _startY = 0;				//开始的Y

	QColor _bgColorStart = QColor(9, 41, 72);
	QColor _bgColorEnd = QColor(15, 35, 53);

	bool _isDrawBorder = false;		//是否绘制边框
	bool _isClickSelect = false;		//是否点击选中
};

#include <QPainter>
#include <QDebug>
#include "PlanItem.h"
#include "TimeTickManager.h"

PlanItem::PlanItem(QGraphicsItem* parent)
	:QGraphicsItem(parent){
}

PlanItem::~PlanItem() {
}

void PlanItem::viewSize(int vw, int vh) {
	_viewWidht = vw;
	_viewHeight = vh;

	_startX = -_viewWidht / 2 + _space ;
	_startY = -_viewHeight / 2 + _space ;
	_itemWidth = _viewWidht;
	setPos(_startX, _startY);

	//保存起始位置X
	TimeTickMgr()->setTimeTickStartX(_startX);
	update();
}

void PlanItem::setStartOffset(int x, int y) {
	_startX += x;
	_startY += y;

	//不允许Item向右拖动,只能向左拖动
	if (_startX >= -_viewWidht / 2 + _space) {
		_startX = -_viewWidht / 2 + _space;
	}

	//可显示的Item的宽度就是开始位置的绝对值+1/2的视图的宽度
	_itemWidth = abs(_startX) + _viewWidht / 2;

	//保存起始位置X
	TimeTickMgr()->setTimeTickStartX(_startX);

	setPos(_startX, _startY);
	update();
}

void PlanItem::setTimeOnCenter(double time) {
	_startX = - time / TimeTickMgr()->getPixelEachSecond();

	//可显示的Item的宽度就是开始位置的绝对值+1/2的视图的宽度
	_itemWidth = abs(_startX) + _viewWidht / 2;
	TimeTickMgr()->setTimeTickStartX(_startX);

	setPos(_startX, _startY);
	update();
}

//放大
void PlanItem::zoomIn(QPointF mouseMovePt, double mouseMoveTime) {
	zoomPos(mouseMovePt, mouseMoveTime);
}

//缩小
void PlanItem::zoomOut(QPointF mouseMovePt, double mouseMoveTime) {
	zoomPos(mouseMovePt, mouseMoveTime);
}

QRectF PlanItem::boundingRect() const {
	return QRectF(0, 0, INT_MAX, INT_MAX);
}

void PlanItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /*= Q_NULLPTR*/) {
	drawBg(painter);
}

void PlanItem::drawBg(QPainter *painter) {
	painter->save();
	painter->setPen(Qt::NoPen);
	QLinearGradient bgGradient(QPointF(0, 0), QPointF(0, _itemHeight));
	bgGradient.setColorAt(0.0, _bgColorStart);
	bgGradient.setColorAt(1.0, _bgColorEnd);
	painter->setBrush(bgGradient);
	painter->drawRect(_space, _space, _itemWidth, _itemHeight);
	if (_isDrawBorder){//高亮显示边框
		QPen pen;
		pen.setColor(Qt::yellow);
		pen.setStyle(Qt::DashLine);
		pen.setWidth(2);
		painter->setPen(pen);
		painter->drawRect(_space, _space, _itemWidth, _itemHeight);
	}

	if (_isClickSelect){//选中后,高亮显示边框
		QPen pen;
		pen.setColor(Qt::yellow);
		pen.setStyle(Qt::SolidLine);
		pen.setWidth(2);
		painter->setPen(pen);
		painter->setBrush(QBrush(QColor(128, 128, 128, 100)));
		painter->drawRect(_space, _space, _itemWidth, _itemHeight);
	}

	painter->restore();
}

void PlanItem::zoomPos(QPointF mouseMovePt, double mouseMoveTime) {
	_startX = mouseMovePt.x() - mouseMoveTime / TimeTickMgr()->getPixelEachSecond();

	//可显示的Item的宽度就是开始位置的绝对值+1/2的视图的宽度
	_itemWidth = abs(_startX) + _viewWidht / 2;
	TimeTickMgr()->setTimeTickStartX(_startX);

	setPos(_startX, _startY);
	update();
}

场景类:

#pragma once

#include <QGraphicsScene>
#include "interface/ISceneToPlanTime.h"

//信号规划的场景

class TimeTickPaint2;
class PlanItemBean;
class WorkModeItem;
class FragmentDataPack;

class SignalPlanScene 
	: public QGraphicsScene 
	, public ISceneToPlanTime {
	Q_OBJECT

public:
	SignalPlanScene(QObject *parent = Q_NULLPTR);
	~SignalPlanScene();

	void initScene();
	void wheelEvent(int detal);

	void mousePressEvent(QGraphicsSceneMouseEvent *event)override;
	void mouseMoveEvent(QGraphicsSceneMouseEvent *event)override;
	void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)override;

	void dragEnterEvent(QGraphicsSceneDragDropEvent *event)override;
	void dragMoveEvent(QGraphicsSceneDragDropEvent *event)override;
	void dropEvent(QGraphicsSceneDragDropEvent *event)override;

private:
	void addPlanTime(PlanItemBean* sigSrcNean)override;
	void deletePlanTime()override;
	void selectFragDataSide(QPointF mouseMovePt);

private:
	TimeTickPaint2* _timeTickMarkItem = nullptr;

	bool _leftBtnDown = false;
	QPointF _leftBtnDownPos;//鼠标按下时的坐标

	QPointF _mouseMovePt;//鼠标滑动时的坐标
	double _mouseMoveTime = 0.0;//鼠标滑动时的时间

	WorkModeItem* _dragRadarSignalItem = nullptr;//拖动选中的平台
	FragmentDataPack* _selectFragDataPack = nullptr;
};

#include <QGraphicsSceneMouseEvent>
#include <QGraphicsSceneDragDropEvent>
#include <QGraphicsView>

#include "SignalPlanScene.h"
#include "PlanItem/TimeTickPaint2.h"
#include "PlanItem/TimeTickManager.h"
#include "PlanItem/PlanItemManager.h"
#include "PlanItem/WorkModeItem.h"
#include "Bean/PlanItemBean.h"
#include "Interface/ScrollChangeManager.h"
#include "ModeTree/WorkModeMimeData.h"
#include "Bean/FragmentData.h"
#include "Bean/FragmentDataPack.h"
#include "interface/SceneToPlanTimeManage.h"

SignalPlanScene::SignalPlanScene(QObject *parent)
	: QGraphicsScene(parent) {
	SceneMgr()->setSceneToPlanTime(this);
}

SignalPlanScene::~SignalPlanScene() {
}

void SignalPlanScene::initScene() {
	//时间刻度条
	_timeTickMarkItem = new TimeTickPaint2;

	addItem(_timeTickMarkItem);
	_timeTickMarkItem->setZValue(1);//设置Item置于顶层
	PlanItemMgr()->addPlanItem(_timeTickMarkItem);

#if 1//绘制中心原点
	QGraphicsEllipseItem* ellipseItem = new QGraphicsEllipseItem;
	ellipseItem->setRect(-5, -5, 10, 10);
	this->addItem(ellipseItem);
#endif
	update();
}

void SignalPlanScene::wheelEvent(int detal) {
	if (detal > 0) {
		TimeTickMgr()->zoomOutPixelEachSecond();
		PlanItemMgr()->zoomOut(_mouseMovePt, _mouseMoveTime);
	} else {
		TimeTickMgr()->zoomInPixelEachSecond();
		PlanItemMgr()->zoomIn(_mouseMovePt, _mouseMoveTime);
	}
	update();
}

void SignalPlanScene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
	if (event->button() & Qt::LeftButton) {
		_leftBtnDown = true;
		QPointF point = event->scenePos();
		_leftBtnDownPos = event->lastScenePos();
		if (_selectFragDataPack != nullptr) {//选中实体
			_selectFragDataPack->setLBtnDownPos(event->scenePos());
		}

		//鼠标所在的Item,点击一直高亮显示
		if (_dragRadarSignalItem != nullptr) {
			_dragRadarSignalItem->setSelected(!_dragRadarSignalItem->getSelected());
		}
		update();
	}
	QGraphicsScene::mousePressEvent(event);
}

void SignalPlanScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
	_mouseMovePt = event->scenePos();
	_mouseMoveTime = (_mouseMovePt.x() - TimeTickMgr()->getTimeTickStartX())*TimeTickMgr()->getPixelEachSecond();
	if (_leftBtnDown) {//左键按下
		if (_selectFragDataPack == nullptr) {//没有选中数据,拖动全部
			QPointF mouseDelta = _mouseMovePt - _leftBtnDownPos;
			PlanItemMgr()->setStartOffset(mouseDelta.x(), 0);
			_leftBtnDownPos = _mouseMovePt;
		} else {//选中数据,只拖动数据
			_selectFragDataPack->resizePos(_mouseMovePt);
		}

		
		update();
	} else {//不按下左键
		selectFragDataSide(_mouseMovePt);
	}
	QGraphicsScene::mouseMoveEvent(event);
}

void SignalPlanScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
	if (event->button() & Qt::LeftButton) {
		_leftBtnDown = false;
		_selectFragDataPack = nullptr;
	}
	QGraphicsScene::mouseReleaseEvent(event);
}

void SignalPlanScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) {
	if (event->mimeData()->hasFormat("RadarWorkMode/Sig")) {
		event->acceptProposedAction();
	}
}

void SignalPlanScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) {
	QPointF dragMovePos = event->scenePos();
	std::vector<PlanItem*>&itemList = PlanItemMgr()->getItemList();
	int num = itemList.size();
	for (int i = 0; i < num; i++) {
		WorkModeItem* radarSignalItem = dynamic_cast<WorkModeItem*>(itemList.at(i));
		if (radarSignalItem != nullptr) {
			int idx = radarSignalItem->mousePtInItem(dragMovePos);
			if (idx != -1) {
				radarSignalItem->setItemBorder(true);
				_dragRadarSignalItem = radarSignalItem;
			} else {
				radarSignalItem->setItemBorder(false);
			}
		}
	}

	event->accept();
	update();
}

void SignalPlanScene::dropEvent(QGraphicsSceneDragDropEvent *event) {
	if (event->mimeData()->hasFormat("RadarWorkMode/Sig")) {
		QPointF dropPos = event->scenePos();
		if (_dragRadarSignalItem == nullptr) {
			return;
		}
		//拖动的设备节点
		WorkModeMimeData *pMimeData = (WorkModeMimeData *)(event->mimeData());

		FragmentData* fragData = new FragmentData;
		fragData->setWorkModeBean(pMimeData->getWorkModeBean());
		//添加数据内容
		FragmentDataPack*packData = _dragRadarSignalItem->addOneFragmentData(fragData);

		//鼠标当前所在位置的时间
		double time = (dropPos.x() - TimeTickMgr()->getTimeTickStartX())*TimeTickMgr()->getPixelEachSecond();
		//向前移动50个像素确定开始时间
		double fromTime = time - 50 * TimeTickMgr()->getPixelEachSecond();
		fragData->setFromTime(fromTime);

		//向后移动50个像素确定结束时间
		double toTime = time + 50 * TimeTickMgr()->getPixelEachSecond();
		fragData->setToTime(toTime);

		//设置显示树形
		_dragRadarSignalItem->setItemBorder(false);

		_dragRadarSignalItem = nullptr;

		event->acceptProposedAction();

		update();
	}
}

void SignalPlanScene::addPlanTime(PlanItemBean* sigSrcNean) {
	WorkModeItem* planTimeItem = new WorkModeItem;
	planTimeItem->setPlanItemBean(sigSrcNean);
	addItem(planTimeItem);

	planTimeItem->setItemIndex(_timeTickMarkItem->getItemHeight()
		, PlanItemMgr()->totalItem() - 1
		, _timeTickMarkItem->getViewWidth()
		, _timeTickMarkItem->getViewHeight());

	PlanItemMgr()->addPlanItem(planTimeItem);

	ScrollChangeMgr()->addScrollChange(planTimeItem);
	update();
}

void SignalPlanScene::deletePlanTime() {
	std::vector<PlanItem*>&planItemList = PlanItemMgr()->getItemList();
	int num = planItemList.size();
	for (int i = 0; i < planItemList.size(); i++) {
		PlanItem* oneItem = planItemList.at(i);
		WorkModeItem* radarSignalItem = dynamic_cast<WorkModeItem*>(oneItem);
		if (radarSignalItem == nullptr)	{
			continue;
		}
		if (radarSignalItem->getSelected()){
			planItemList.erase(planItemList.begin() + i);
			removeItem(oneItem);
			i--;
		}
	}
	update();
}

void SignalPlanScene::selectFragDataSide(QPointF mouseMovePt) {
	std::vector<PlanItem*>&itemList = PlanItemMgr()->getItemList();
	int num = itemList.size();
	for (int i = 0; i < num; i++) {
		WorkModeItem* radarSignalItem = dynamic_cast<WorkModeItem*>(itemList.at(i));
		if (radarSignalItem != nullptr) {
			int idx = radarSignalItem->mousePtInItem(mouseMovePt);
			int sideX = -1, side = -1;

			if (_selectFragDataPack != nullptr) {//取消高亮显示
				_selectFragDataPack->setDeviceHighLightBK(false);
			}

			_selectFragDataPack = radarSignalItem->getSideX(QPoint(mouseMovePt.x(), mouseMovePt.y()), sideX, side);

			if (side == 0) {//左
				views().at(0)->setCursor(Qt::SizeHorCursor);

				if (_selectFragDataPack != nullptr) {
					_selectFragDataPack->setDeviceHighLightBK(true);
				}
				break;
			} else if (side == 2) {//右
				views().at(0)->setCursor(Qt::SizeHorCursor);
				if (_selectFragDataPack != nullptr) {
					_selectFragDataPack->setDeviceHighLightBK(true);
				}
				break;
			} else if (side == 1) {//中
				views().at(0)->setCursor(Qt::SizeAllCursor);
				if (_selectFragDataPack != nullptr) {
					_selectFragDataPack->setDeviceHighLightBK(true);
				}
				break;
			} else {
				views().at(0)->setCursor(Qt::ArrowCursor);
			}

			//高亮显示鼠标所在的Item
			if (idx != -1) {
				radarSignalItem->setItemBorder(true);
				_dragRadarSignalItem = radarSignalItem;
			} else {
				radarSignalItem->setItemBorder(false);
			}
		}
	}
	update();
}

视图类:

#pragma once

#include <QGraphicsView>

class SignalPlanView : public QGraphicsView {
	Q_OBJECT

public:
	SignalPlanView(QWidget *parent = Q_NULLPTR);
	~SignalPlanView();

private:
	void resizeEvent(QResizeEvent *event)override;
	void wheelEvent(QWheelEvent *event)override;
};

#include <QResizeEvent>
#include "SignalPlanView.h"
#include <QSize>
#include "PlanItem/PlanItemManager.h"
#include "SignalPlanScene.h"


SignalPlanView::SignalPlanView(QWidget *parent)
	: QGraphicsView(parent) {
	setMouseTracking(true);
}

SignalPlanView::~SignalPlanView() {	
}

void SignalPlanView::resizeEvent(QResizeEvent *event) {
	//保证视图的中点在(0,0)位置
	centerOn(0, 0);
	QSize size = event->size();
	PlanItemMgr()->viewSize(size.width(), size.height());
}

void SignalPlanView::wheelEvent(QWheelEvent *event) {
	SignalPlanScene* planScene = dynamic_cast<SignalPlanScene*>(scene());
	if (planScene != nullptr) {
		planScene->wheelEvent(event->delta());
	}
}

若需要源,私信我
aaa

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;