这几天有很多项目,算法的、编译原理的、计算机控制原理的、计算机组成原理的。反正这学期学的计算机课程几乎全有项目,不过有的很简单,就比如这个管道铺设系统。我在昨天就已经写完了这个项目,这篇文章用于记录一下本项目。本项目已经上传到github上,地址:https://github.com/qi1x/pipe-laying-system。
题目
以下内容来自于实验指导书。
本课程的三级项目是要求学生设计实现一个管线铺设辅助系统。引导学生积极思考、主动学习,锻炼和提高学生的交流、沟通和表达能力以及团队合作能力,培养学生的责任感和职业道德。
1、问题描述
开发一个管线铺设辅助系统,以我校西校区为例,在里仁学院教学楼、学生公寓、学生食堂、第一、二、三、四教学楼、材料学院、电气学院、理学院、艺术学院、经管学院、西里西亚学院和信息学院之间铺设输水管道,设计算法并实现使铺设的输水管道距离最短(各建筑之间的距离,可以通过百度地图获取)。系统要求具备从文本读取数据、显示最佳铺设方案,以及绘制最佳方案的简单示意图等功能。
2、功能需求
用最小生成树算法实现,需要满足如下要求。
(1)将管线经过的建筑物以及建筑物之间的距离,抽象成无向图,并以矩阵的形式表示,并保存在文本中,系统通过读取文本的方式,获取该矩阵;
(2)从 Prim 算法和 Kruskal 算法中至少选择一种实现管线铺设的最优方案,系统可以最优方案的生成过程,并且可以文本的形式输出;
(3)在系统上可以生成最优方案的简易图。
预览
整个界面由两部分组成:主地图和按钮组。在地图上点击可以选点,就是你要铺设的管道的端点,然后点击“计算”后就可以画出最小生成树了。此外,界面还可以放大和缩小,以及清除记录。整个程序有完整的日志系统,你对程序做出的操作都会被记录。



实现

img为地图背景,需要提前绘制好。application.log是日志文件,main.py为项目主文件。程序从main.py文件开始执行。
现在来看看main.py文件的代码组成。
首先是导入必要的模块。
python 代码:import logging # 导入日志模块
import sys # 导入sys模块
import numpy as np # 导入numpy库
...
有些模块需要安装,可在虚拟环境中使用如下命令一键安装。
shell 代码:pip install -r requirements.txt
接下来是初始化日志对象。有关于日志的知识请前往我的云笔记中关于日志模块的介绍。这里不再赘述。
python 代码:# 创建一个日志记录器
logger = logging.getLogger()
logger.setLevel(logging.INFO)
...
然后是初始化坐标列表。
python 代码:coordinates = [] # 初始化坐标列表
接下来就是制作按钮和画布的容器了。首先创建一个画布,使用PyQt5的QGraphicsView类,这个就是画布面板,然后为面板添加一个画布QPixmap,然后再为画布添加事件监听,点击的时候添加坐标到坐标列表中,并在图中画出一个点后更新画布。
python 代码:# 自定义Canvas类,继承自QGraphicsView类
class Canvas(QGraphicsView):
# Canvas类的构造函数
def __init__(self, parent=None):
super().__init__(parent) # 调用父类的构造函数
self.setBaseSize(1000, 1000) # 设置画布基本大小
...
画布创建完成后,接下来就是创建主窗体,这个主窗体要包含画布和下方的几个按钮。
python 代码:class MainWindow(QMainWindow):
# MainWindow类的构造函数
def __init__(self):
super().__init__() # 调用父类的构造函数
self.canvas = Canvas() # 创建Canvas对象
layout = QVBoxLayout() # 创建垂直布局管理器
layout.addWidget(self.canvas) # 将Canvas组件添加到布局中
...
这部分代码包含添加组件,配置槽函数,设置大小与位置。接下来就是实现这几个槽函数。窗体中共有五个按钮,它们分别是“放大”、“缩小”、“计算”、“清除生成树”,以及“清除所有点”。其中计算按钮使用kruskal算法实现的,具体算法如下。
python 代码: def kruskal(self):
edges = [] # 创建边列表
for i in range(len(coordinates)): # 遍历顶点
for j in range(i + 1, len(coordinates)): # 遍历其他顶点
dist = np.sqrt( # 计算距离
(coordinates[i][0] - coordinates[j][0]) ** 2 + (coordinates[i][5] - coordinates[j][6]) ** 2)
edges.append((i, j, dist)) # 将结果添加到边列表
edges.sort(key=lambda x: x[2]) # 对边列表进行排序
parent = [i for i in range(len(coordinates))] # 创建父节点列表
rank = [0] * len(coordinates) # 创建等级列表
mst = [] # 创建最小生成树列表
for edge in edges: # 遍历边
x, y, weight = edge # 获取边的起点、终点和权重
x_root = self.find(parent, x) # 获取x的根节点
y_root = self.find(parent, y) # 获取y的根节点
if x_root != y_root: # 如果根节点不同
mst.append((x, y, weight)) # 将结果添加到最小生成树中
self.union(parent, rank, x_root, y_root) # 进行合并操作
return mst # 返回最小生成树
kruskal就是先选一条权值最小的边,作为最小生成树的一个边,然后这个边会连接两个顶点,这两个顶点会连接其他的边,再从这边中选出一个最小的边,以此下去。注意不能形成环。
这里返回最小生成树后,就可以绘图了。在calculate_minimum_spanning_tree方法中,最后一段代码就是绘制最小生成树的。
python 代码: # 将最小生成树的边和权重添加到场景中
for edge in result:
node1 = edge[0]
node2 = edge[1]
weight = edge[2]
start_point = coordinates[node1]
end_point = coordinates[node2]
# 在场景中添加一条蓝色线条,表示一条边
self.canvas.scene.addLine(start_point[0], start_point[1], end_point[0], end_point[1], QPen(Qt.blue, 2))
# 在场景中添加简单文本,显示边的权重
text = self.canvas.scene.addSimpleText(f"{weight * 200 / 115:.2f}m", QFont("Arial", 8))
text.setPos((start_point[0] + end_point[0]) / 2, (start_point[1] + end_point[1]) / 2)
这部分代码会向图中添加一条蓝色的边以及一条黑色的数字,表示这个边的权值。在图的右下角标注了比例尺,在图中绘制的距离实际上就是按照比例尺换算成米了。
最后就是写主程序入口,这部分程序就完成了。
打卡打卡
欢迎欢迎
暂无点赞
暂无点赞
虽然不懂,但不妨碍我留脚印~
哈哈,欢迎欢迎
暂无点赞
暂无点赞