网站LOGO
博客 | 棋の小站
页面加载中
12月6日
达尔达尼亚瀑布,博洛尼亚,意大利 ...
网站LOGO 博客 | 棋の小站
记录学习,心得,状态,生活。
菜单
  • 热评
    用户的头像
    首次访问
    上次留言
    累计留言
    我的等级
    我的角色
    打赏二维码
    打赏博主
    PyQt5实现一个管道铺设系统
    点击复制本页地址
    微信扫一扫
    文章二维码
    文章图片 文章标题
    创建时间
  • 一 言
    确认删除此评论么? 确认
  • 本弹窗介绍内容来自,本网站不对其中内容负责。
    按住ctrl可打开默认菜单

    PyQt5实现一个管道铺设系统

    · 原创 ·
    做做项目 · PyQt三级项目
    共 4597 字 · 约 4 分钟 · 357

    这几天有很多项目,算法的、编译原理的、计算机控制原理的、计算机组成原理的。反正这学期学的计算机课程几乎全有项目,不过有的很简单,就比如这个管道铺设系统。我在昨天就已经写完了这个项目,这篇文章用于记录一下本项目。本项目已经上传到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)

    这部分代码会向图中添加一条蓝色的边以及一条黑色的数字,表示这个边的权值。在图的右下角标注了比例尺,在图中绘制的距离实际上就是按照比例尺换算成米了。

    最后就是写主程序入口,这部分程序就完成了。

    声明:本文由 (博主)原创,依据 CC-BY-NC-SA 4.0 许可协议 授权,转载请注明出处。

    还没有人喜爱这篇文章呢

    现在已有

    4

    条评论
    发一条!
    1. 头像
      云晓晨
      • 等级:Lv.5
      • 角色:首页 · 好友
      • 在线:本周

      打卡打卡

      · · · 山东-济南
      1. 头像
        云晓晨

        欢迎欢迎

        · · · 河北
    2. 头像
      TeacherDu
      • 等级:Lv.5
      • 角色:首页 · 好友
      • 在线:本周

      虽然不懂,但不妨碍我留脚印~

      · · · 北京-北京
      1. 头像
        TeacherDu

        哈哈,欢迎欢迎

        · · · 河北-石家庄
    博客logo 博客 | 棋の小站 记录学习,心得,状态,生活。
    ICP 冀ICP备2023007665号 ICP 冀公网安备 13030202003453号

    🕛

    本站已运行 221 天 16 小时 8 分

    👁️

    今日访问量:653 昨日访问量:2564

    🌳

    建站:Typecho 主题:MyLife
    博客 | 棋の小站. © 2023 ~ 2023.
    网站logo

    博客 | 棋の小站 记录学习,心得,状态,生活。
     
     
     
     
    壁纸