重写QGraphicsPolygonItem/类或者QGraphicsPathItem类实现绘制多线段功能,并能够对节点进行移动,删除,同时重写 QGraphicsScene与QGraphicsView已实现对场景的指定位置缩放大小,上下左右键移动显示内容等;
搭配风速的自记纸实例,能完整的描绘出迹线轨迹,同时移动轨迹节点。
完整项目代码文件请查看下载资源,具体操作示例如下图示:
多线段的节点,有画线的节点和外接矩形的节点,在添加节点是根据不同类型生成不同的节点,同时声明各种枚举作为矩形常量与状态,由于需要移动节点类型需要对QGraphicsRectItem类的按住移动事件进行重写;
同时作为多线段的子节点,需要添加一个显示状态和唯一uuid标识方便进行节点的删除与移动;
enum { SELECTION_HANDLE_SIZE = 6, SELECTION_MARGIN = 10 };//节点宽度大小
enum SelectionHandleState { SelectionHandleOff, SelectionHandleInactive, SelectionHandleActive };//显示状态
enum { Handle_None = 0 , LeftTop , Top, RightTop, Right, RightBottom, Bottom, LeftBottom, Left };//边框节点所在位置
class SizeHandleRect :public QGraphicsRectItem
{
public:
enum { Type = UserType + 2 }; //设置类型,用于匹配到的内容判断类型
int type() const override
{
return Type;
}
SizeHandleRect(QGraphicsItem* parent , QString d , bool control = false );
SizeHandleRect(QGraphicsItem* parent ,QPointF QPo, QString d , bool control = false );
QString dir() const { return m_dir; }
void setState(SelectionHandleState st);//设置当前状态
void move(qreal x, qreal y );
SelectionHandleState m_state;//显示状态
QColor borderColor;//绘色
QPointF QPoLocats;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
void hoverEnterEvent(QGraphicsSceneHoverEvent *e ) override;//获取鼠标
void hoverLeaveEvent(QGraphicsSceneHoverEvent *e ) override;//鼠标移开
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
const QString m_dir;//唯一标识
bool m_controlPoint;//是否是矩形框的节点还是多线段的节点
QPoint C_down;//按下坐标
};
节点相关功能具体实现:
1.实例化内容时,隐藏并添加相关信息
SizeHandleRect::SizeHandleRect(QGraphicsItem* parent ,QPointF QPo , QString d, bool control)
:QGraphicsRectItem(-SELECTION_HANDLE_SIZE/2,
-SELECTION_HANDLE_SIZE/2,
SELECTION_HANDLE_SIZE,
SELECTION_HANDLE_SIZE,parent)
,m_dir(d)
,m_controlPoint(control)
,m_state(SelectionHandleOff)
,borderColor("black")
,QPoLocats(QPo)
{
this->setAcceptHoverEvents(true);
setFlag(QGraphicsItem::ItemIgnoresTransformations,true);
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
hide();
}
2.重绘SizeHandleRect类型内容显示,设置当前节点状态
//print
void SizeHandleRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->save();
painter->setPen(Qt::SolidLine);
painter->setBrush(QBrush(borderColor));
painter->setRenderHint(QPainter::Antialiasing,false);
if ( m_controlPoint )
{
painter->setPen(QPen(Qt::red,Qt::SolidLine));
painter->setBrush(Qt::green);
painter->drawEllipse(rect().center(),3,3);
}else
painter->drawRect(rect());
painter->restore();
}
//设置节点内容
void SizeHandleRect::setState(SelectionHandleState st)
{
if (st == m_state)
return;
switch (st) {
case SelectionHandleOff:
hide();
break;
case SelectionHandleInactive:
case SelectionHandleActive:
show();
break;
}
borderColor = Qt::white;
m_state = st;
}
3.节点移动以及获取焦点事件
在重写事件时,为了不移除继承的类型的事件方法,可以通过 QGraphicsRectItem::mousePressEvent(event);
这种方式继续执行继承的类型事件方法;
void SizeHandleRect::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
setCursor(Qt::ClosedHandCursor);
QGraphicsRectItem::mousePressEvent(event);
}
void SizeHandleRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsRectItem::mouseMoveEvent(event);
//移动时修改节点所在位置
if( parentItem()->type() == Polylines::Type)
{
Polylines * line=qgraphicsitem_cast<Polylines*>(parentItem());
line->UpdatePoint(dir(),QPointF(this->pos().x(),this->pos().y()));
QPoLocats=QPointF(this->pos().x(),this->pos().y());
}
}
void SizeHandleRect::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
setCursor(Qt::OpenHandCursor);
QGraphicsRectItem::mouseReleaseEvent(event);
}
void SizeHandleRect::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
{
borderColor = Qt::blue;
update();
QGraphicsRectItem::hoverEnterEvent(e);
}
void SizeHandleRect::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
{
borderColor = Qt::white;
setCursor(Qt::ArrowCursor);
update();
QGraphicsRectItem::hoverLeaveEvent(e);
}
生成的Polylines类需要保存子节点SizeHandleRect的集合以及坐标点与唯一标识的内容,
public:
enum { Type = UserType + 1 };
int type() const override
{
return Type;
}
public:
typedef QList<SizeHandleRect*> Handles;
Handles m_handles;
// QMap 插入后再遍历是按照key的顺序来排序的(汉字除外,汉字的排序顺序很奇怪,不是正常的字母顺序);
// QHash插入后再遍历是没有顺序的;
// QMap MapList;
// QHash MapList;
QList<QPair<QString,QPointF>> pairs;
QBrush m_brush;
也需要对节点的新增,删除,修改,刷新方法
//添加节点
void Polylines::AddPointNotSort(QPointF Po)
{
QUuid id = QUuid::createUuid();
QString strId = id.toString();
SizeHandleRect *shr = new SizeHandleRect(this, Po,strId, true);
shr->setState(SelectionHandleActive);
QPair<QString, QPointF> pairone(strId,Po);
pairs.append(pairone);
m_handles.append(shr);
update();
}
//坐标点批量添加
void Polylines::AddPoint(QList<QPoint> Polist)
{
int count=0;
foreach(QPoint it ,Polist)
{
count++;
// QString str = QString("%1").arg(count,10,10,QLatin1Char('0'));
QUuid id = QUuid::createUuid();
QString strId = id.toString();
/* QDateTime local(QDateTime::currentDateTime());
QString localTime = local.toString("yyyy-MM-dd:hh:mm:ss.zzz");*/
//QMap按照key自动排序输出
SizeHandleRect *shr = new SizeHandleRect(this, it,strId, true);
shr->setState(SelectionHandleOff);
QPair<QString, QPointF> pairone(strId,it);
pairs.append(pairone);
// MapList.insert(strId,it);
m_handles.append(shr);
}
}
//移除节点
void removeByHRect(SizeHandleRect *r)
{
for(int i=0;i<pairs.size();i++)
{
if(pairs[i].first==r->dir())
{
pairs.removeAt(i);
break;
}
}
m_handles.removeAll(r);
}
以及最重要的对坐标点的绘制重写
QPainterPath Polylines::shape() const
{
QPainterPath path;
QPolygonF poly;
for(int i=0;i<pairs.size();i++)
{
poly.append(pairs[i].second);
}
path.addPolygon(poly);
path.closeSubpath();
path.setFillRule(Qt::WindingFill);
QPainterPathStroker stroker;
stroker.setWidth(pen().widthF());
return stroker.createStroke(path);
}
QRectF Polylines::boundingRect() const
{
return shape().controlPointRect();
}
void Polylines::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
updatehandles();
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(pen());
painter->drawPolyline(GetPoints());
}
通过重写QGraphicsScene类来实现对多线段以及多线段节点的移除
//当不存在节点时删除
void QGraphicsSceneOverride::DelectPolyline()
{
foreach (QGraphicsItem *item, this->items())
{
if(item->type() == Polylines::Type)
{
Polylines * poly=qgraphicsitem_cast<Polylines*>(item);
if(poly->m_handles.size()==0 && poly->pairs.size()==0)
removeItem(poly);
}
}
}
//选中框中内容时删除
void QGraphicsSceneOverride::removeItemByRect(QRectF Rect)
{
foreach (QGraphicsItem *item, this->items())
{
if(item->type() == SizeHandleRect::Type)
{
SizeHandleRect * rect=qgraphicsitem_cast<SizeHandleRect*>(item);
if(Rect.contains(rect->QPoLocats))
{
if(rect->parentItem()!=NULL && rect->parentItem()->type() == Polylines::Type)
{
Polylines* line=qgraphicsitem_cast<Polylines*>(rect->parentItem());
if(line->doubleClick)
{
line->removeByHRect(rect);
removeItem(rect);
DelectPolyline();
}
}
}
}
}
}
根据鼠标所在的位置,放大缩小界面指定倍数
其中Dx,Dy为x,y坐标的放大缩小倍数,Pos为鼠标所在位置,缩小倍数值为0-1之间,放大倍数为1-2之间:
void QGraphicsViewOverride::Setwheelscale(double Dx,double Dy,QPoint Pos)
{
// 获取当前的鼠标所在的view坐标;
QPoint prev_viewPos = Pos;
// 获取当前鼠标相对于scene的位置;
QPointF prev_scenePos = this->mapToScene(prev_viewPos);
scale(Dx,Dy);
this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect()); //调整scene,使得scene和view一直,主要是为了排除掉scroll
//获取缩放后的scene坐标
QPointF scenePos = this->mapToScene(prev_viewPos);
//获取缩放前后的坐标差值,即为需要进行move的位移
QPointF disPointF = scenePos - prev_scenePos;
// qDebug()<
// qDebug()<scene()->sceneRect();
//调整位置
this->scene()->setSceneRect(this->scene()->sceneRect().x()-disPointF.x(),
this->scene()->sceneRect().y()-disPointF.y(),
this->scene()->sceneRect().width(),
this->scene()->sceneRect().height());
this->scene()->update();
}
实现QGraphicsView显示内容的指定长度的滑动
Dx,Dy,x,y方向移动的距离
void QGraphicsViewOverride::SetMoveShow(double Dx,double Dy)
{
// this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect()); //调整scene,使得scene和view一直,主要是为了排除掉scroll
// cout<scene()->sceneRect().x()<<" "<
// cout<scene()->sceneRect().y()<<" "<
// cout<<" ----"<
//调整位置
this->scene()->setSceneRect(this->scene()->sceneRect().x()+Dx,
this->scene()->sceneRect().y()+Dy,
this->scene()->sceneRect().width(),
this->scene()->sceneRect().height());
this->scene()->update();
}
当QGraphicsScene中包含图片时,此时能将图片自适应边框,居中显示到QGraphicsView控件中,
void QGraphicsViewOverride::self_adaption()
{
cout<<"----------"<<endl;
QRectF view=this->rect();
// cout <<" X: "<
QRectF sceneF=this->scene()->itemsBoundingRect();
// cout <<" X: "<
QPoint PoView0(0,0);
QPointF Poscene0=this->mapToScene(PoView0);
// cout<<" X: "<
QPoint PoView1(view.width(),0);
QPointF Poscene1=this->mapToScene(PoView1);
// cout<<" X: "<
QPoint PoView2(0,view.height());
QPointF Poscene2=this->mapToScene(PoView2);
// cout<<" X: "<
double dx=sceneF.width()/(Poscene1.x()-Poscene0.x());
double dy=sceneF.height()/(Poscene2.y()-Poscene0.y());
double min=dx>dy?dy:dx;
double max=dx>dy?dx:dy;
// cout<
if(max!=0 && max!=1)
{
scale(1/max,1/max);
this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect());
sceneF=this->scene()->itemsBoundingRect();
//中心对中心
QPointF viewTosceneCenter = this->mapToScene(QPoint(view.width()/2,view.height()/2));
QPointF sceneCenter(sceneF.width()/2,sceneF.height()/2);
SetMoveShow(sceneCenter.x()-viewTosceneCenter.x(),sceneCenter.y()-viewTosceneCenter.y());
}
}
//滑轮滚动缩小屏幕内容
void QGraphicsViewOverride::wheelEvent(QWheelEvent *event)
{
if(event->delta()>0)
{
Setwheelscale(1.3,1.3,event->pos());
}
else{
Setwheelscale(1 / 1.3, 1 / 1.3,event->pos());
}
QGraphicsView::wheelEvent(event); // 执行QLineEdit类的默认事件处理
}
通过快捷键详细指定位置放大,上下左右滑动功能;
void QGraphicsViewOverride::keyReleaseEvent(QKeyEvent *event)
{
if(event->modifiers()==(Qt::ControlModifier) && event->key()==Qt::Key_F )
{
Setwheelscale(1.9,1.9,PoMove);
// QMessageBox::information(this,"Click", "Ctrl+F");
}
else if(event->modifiers()==(Qt::ControlModifier) && event->key()==Qt::Key_G)
{
Setwheelscale(1 / 1.9,1 / 1.9,PoMove);
}
else if( event->key()==Qt::Key_Left)
{
// QMessageBox::information(this,"Click", "Key_Left");
SetMoveShow(5,0);
}
else if( event->key()==Qt::Key_Right)
{
SetMoveShow(-5,0);
}
else if( event->key()==Qt::Key_Up)
{
SetMoveShow(0,5);
}
else if( event->key()==Qt::Key_Down)
{
SetMoveShow(0,-5);
}
}