当前位置:网站首页>qt绘制网络拓补图(连接数据库,递归函数,无限绘制,可拖动节点)
qt绘制网络拓补图(连接数据库,递归函数,无限绘制,可拖动节点)
2022-07-04 22:37:00 【GreenHandBruce】
硬件:ThinkPadT590
系统:Win10
数据库:sqlserver2014
Qt:5.14.1
QtCreator:4.11.1
源码连接:
qt实现的绘制网络拓补图,先连接sqlserver数据库获取所有节点数据,然后通过递归函数解析出每个节点之间的关系,并计算每个节点要在图上绘制的位置,然后通过重写的
QGraphicsPixmapItem类来绘制节点图,通过重写的
QGraphicsItem类来绘制节点之间的连线。
只要各节点之间的关系数据正确,可以无限绘制。
界面显示如下:
拖动效果如下:
sqlserver数据库表内数据如下:
关键代码如下:
#include <QtWidgets>
#include "nodeframe.h"
#include <iostream>
using namespace std;
NodeFrame::NodeFrame(QWidget *parent) :
QGraphicsView(parent)
{
setGeometry(0,0,1920,2000);
m_scene = new QGraphicsScene(0, 0, this->width(), this->height());
this->setScene(m_scene);
QScrollBar *horBar = this->horizontalScrollBar();
QScrollBar *verBar = this->verticalScrollBar();
horBar->setValue(horBar->maximum());
verBar->setValue(verBar->maximum());
// connectSqlServer();//连接数据库并读取数据,这里先禁用,如果你配好了数据库,则可以开起来
manulInitNoteList();//如果不连接数据库则可以手动初始化节点队列,连接数据库的情况下,这行要禁用!
initNodeList();
}
void NodeFrame::manulInitNoteList()
{
NodeInSql node;
node.nNodeID =1; node.nParentID =1; node.strNodeName ="根节点"; m_lstNodeInSql.append(node);
node.nNodeID =2; node.nParentID =1; node.strNodeName ="子节点2"; m_lstNodeInSql.append(node);
node.nNodeID =3; node.nParentID =1; node.strNodeName ="子节点3"; m_lstNodeInSql.append(node);
node.nNodeID =4; node.nParentID =1; node.strNodeName ="子节点4"; m_lstNodeInSql.append(node);
node.nNodeID =5; node.nParentID =1; node.strNodeName ="子节点5"; m_lstNodeInSql.append(node);
node.nNodeID =6; node.nParentID =2; node.strNodeName ="子节点6"; m_lstNodeInSql.append(node);
node.nNodeID =7; node.nParentID =2; node.strNodeName ="子节点7"; m_lstNodeInSql.append(node);
node.nNodeID =8; node.nParentID =3; node.strNodeName ="子节点8"; m_lstNodeInSql.append(node);
node.nNodeID =9; node.nParentID =3; node.strNodeName ="子节点9"; m_lstNodeInSql.append(node);
node.nNodeID =10; node.nParentID =4; node.strNodeName ="子节点10"; m_lstNodeInSql.append(node);
node.nNodeID =11; node.nParentID =10; node.strNodeName ="子节点11"; m_lstNodeInSql.append(node);
node.nNodeID =12; node.nParentID =11; node.strNodeName ="子节点12"; m_lstNodeInSql.append(node);
node.nNodeID =13; node.nParentID =12; node.strNodeName ="子节点13"; m_lstNodeInSql.append(node);
node.nNodeID =14; node.nParentID =13; node.strNodeName ="子节点14"; m_lstNodeInSql.append(node);
node.nNodeID =15; node.nParentID =13; node.strNodeName ="子节点15"; m_lstNodeInSql.append(node);
node.nNodeID =16; node.nParentID =13; node.strNodeName ="子节点16"; m_lstNodeInSql.append(node);
node.nNodeID =17; node.nParentID =13; node.strNodeName ="子节点17"; m_lstNodeInSql.append(node);
node.nNodeID =18; node.nParentID =13; node.strNodeName ="子节点18"; m_lstNodeInSql.append(node);
}
NodeFrame::~NodeFrame()
{
delete m_scene;
}
void NodeFrame::refresh()
{
this->destroyed(m_scene);
m_scene = new QGraphicsScene(0, 0, this->width(), this->height());
this->setScene(m_scene);
initNodeList();
}
void NodeFrame::connectSqlServer()
{
QSqlDatabase db= QSqlDatabase::addDatabase("QODBC", "dbTemp");
db.setDatabaseName(QString("DRIVER={SQL SERVER};"
"SERVER=%1;" //服务器名称
"DATABASE=%2;"//数据库名
"UID=%3;" //登录名
"PWD=%4;" //密码
).arg("127.0.0.1,49674")//我电脑上的端口号是49674
.arg("myTestSql")
.arg("Bruce")
.arg("Qwerty*963.-+")
);
//数据库连接
bool ok = db.open();
if(ok)
{
qDebug()<<"database open success";
}
else
{
qDebug()<<db.lastError();
return;
}
//数据库查询
QSqlQuery query(db);
query.exec("SELECT * FROM Node;");
while(query.next())
{
NodeInSql node;
if(query.value(1).isValid())
node.nNodeID = query.value(1).toInt();
if(query.value(2).isValid())
node.nParentID = query.value(2).toInt();
if(query.value(3).isValid())
node.strNodeName = query.value(3).toString();
m_lstNodeInSql.append(node);
}
db.close();
}
void NodeFrame::initNodeList()
{
//找到根节点
NodeInSql nRootID;
foreach(NodeInSql node,m_lstNodeInSql)
{
if(node.nNodeID==node.nParentID)
{
nRootID = node;
}
}
NodeInfoToShow rootNodeInfoToShow;
rootNodeInfoToShow.nLevel = 0;
rootNodeInfoToShow.nNodeID = nRootID.nNodeID;
rootNodeInfoToShow.nParentID = nRootID.nParentID;
rootNodeInfoToShow.ptPos = QPoint(0,100);
rootNodeInfoToShow.strNodeName = nRootID.strNodeName;
m_lstNodeInfoToShow.append(rootNodeInfoToShow);
NetNode *rootNetNode = new NetNode(rootNodeInfoToShow,m_scene);
parseNodesInSql(rootNodeInfoToShow,rootNetNode);//解析所有节点关系及位置
}
void NodeFrame::parseNodesInSql(NodeInfoToShow node,NetNode *netNode)
{
int nChildNum = 0;//当前node节点有几个子节点
int nWidth = node.nLevel%2==0?250:150;//节点之间的横向间隔
int nHeight = 200;//节点之间的纵向间隔
QList<NodeInfoToShow> lstNodeInCrtLevel;//当前node下的所有节点
for(int i = 0;i<m_lstNodeInSql.count();i++)
{
if(m_lstNodeInSql[i].nParentID==node.nNodeID && m_lstNodeInSql[i].nParentID!=m_lstNodeInSql[i].nNodeID)
{
nChildNum++;
NodeInfoToShow rootNodeInfoToShow;
rootNodeInfoToShow.nLevel = node.nLevel+1;
rootNodeInfoToShow.nNodeID = m_lstNodeInSql[i].nNodeID;
rootNodeInfoToShow.nParentID = node.nNodeID;
rootNodeInfoToShow.ptPos = QPoint(0,0);//这里先给0,0点作为初始值,下面再给每个子节点确定位置
rootNodeInfoToShow.strNodeName = m_lstNodeInSql[i].strNodeName;
lstNodeInCrtLevel.append(rootNodeInfoToShow);
}
}
//确定每个子节点的位置
for(int i = 0;i<lstNodeInCrtLevel.count();i++)
{
if(nChildNum%2==1)//有奇数个子节点
{
int nHalf = (nChildNum-1)/2;
int nX =node.ptPos.x()+(i-nHalf)*nWidth;//以上一个节点的x坐标为基础加上偏移量
int nY = node.ptPos.y()+nHeight;//以上一个节点的y坐标为基础加上偏移量
lstNodeInCrtLevel[i].ptPos = QPoint(nX,nY);
}
else//有偶数个子节点
{
int nHalf = nChildNum/2;
int nX =node.ptPos.x()+(i-nHalf)*nWidth+nWidth/2;//以上一个节点的x坐标为基础加上偏移量
int nY = node.ptPos.y()+nHeight;//以上一个节点的y坐标为基础加上偏移量
lstNodeInCrtLevel[i].ptPos = QPoint(nX,nY);
}
m_lstNodeInfoToShow.append(lstNodeInCrtLevel[i]);
NetNode *subNetNode = new NetNode(lstNodeInCrtLevel[i],m_scene);
NodeLink *link = new NodeLink(netNode, subNetNode);
if (link) {
m_scene->addItem(link);
}
parseNodesInSql(lstNodeInCrtLevel[i],subNetNode);
}
}
源码下载连接:
边栏推荐
- Persistence mechanism of redis
- ECS settings SSH key login
- Redis getting started complete tutorial: Key Management
- Redis introduction complete tutorial: client communication protocol
- The overview and definition of clusters can be seen at a glance
- 【机器学习】手写数字识别
- 攻防世界 MISC 进阶 glance-50
- The difference between Max and greatest in SQL
- Summary of index operations in mongodb
- Attack and defense world misc advanced area ditf
猜你喜欢
The overview and definition of clusters can be seen at a glance
The new version judges the code of PC and mobile terminal, the mobile terminal jumps to the mobile terminal, and the PC jumps to the latest valid code of PC terminal
为什么信息图会帮助你的SEO
共创软硬件协同生态:Graphcore IPU与百度飞桨的“联合提交”亮相MLPerf
Redis入门完整教程:键管理
Attack and defense world misc advanced area ditf
攻防世界 MISC 進階區 Erik-Baleog-and-Olaf
C语言快速解决反转链表
常用技术指标之一文读懂BOLL布林线指标
【图论】拓扑排序
随机推荐
P2181 diagonal and p1030 [noip2001 popularization group] arrange in order
How to choose a securities company? Is it safe to open an account on your mobile phone
页面关闭前,如何发送一个可靠请求
【二叉树】节点与其祖先之间的最大差值
[odx Studio Edit pdx] - 0.2 - Comment comparer deux fichiers pdx / odx
云服务器设置ssh密钥登录
UML diagram memory skills
Advanced area of attack and defense world misc 3-11
Attack and defense world misc advanced area can_ has_ stdio?
【室友用一局王者荣耀的时间学会了用BI报表数据处理】
SHP data making 3dfiles white film
Redis的持久化机制
小程序vant tab组件解决文字过多显示不全的问题
JS 3D explosive fragment image switching JS special effect
智力考验看成语猜古诗句微信小程序源码
【剑指Offer】6-10题
Feature scaling normalization
[OpenGL] note 29 anti aliasing (MSAA)
Attack and defense world misc master advanced zone 001 normal_ png
JS card style countdown days