拓扑排序软件设计——ToplogicalSort_app(含有源码、需求分析、可行性分析、概要设计、用户使用手册)

拓扑排序软件设计

  • 前言
  • 1. 需求分析
  • 2. 可行性分析
    • 2.1 简介
    • 2.2 技术可行性分析
      • 2.2.1 技术实现方案
      • 2.2.2 开发人员技能要求
      • 2.2.3 可行性
    • 2.3 操作可行性分析
    • 2.4 结论
  • 3. 项目报告
    • 3.1 修订历史记录
    • 3.2 软硬件环境
    • 3.3 需求分析
    • 3.4 详细设计
      • 3.4.1 类设计
      • 3.4.2 核心流程描述
      • 3.4.3 核心算法设计
    • 4. 运行结果截图
      • 4.1 样例1
      • 4.2 样例2
      • 4.3 样例3
      • 4.4 样例4
      • 4.5 样例5
    • 5. 测试
      • 5.1 测试样例1
      • 5.2 测试样例2
      • 5.3 测试样例3
      • 5.4 测试样例4
      • 5.5 测试样例5
    • 6. 系统特色以及可扩展点
      • 6.1系统特色:
      • 6.2可扩展点:
  • 4. 源代码部分
    • 4.1 项目层级
    • 4.2 运行环境
    • 4.3 项目核心代码
      • 4.3.1 拓扑排序算法TopologicalSort.cpp
      • 4.3.2 draw_diagram.py
      • 4.3.3 file_manager.py
      • 4.3.4 input_manager.py
      • 4.3.5 main_window.py
      • 4.3.6 topology_manager.py
    • 4.4 GitHub仓库
  • 5. 用户使用手册
    • 5.1 运行软件APP
    • 5.2 操作app
  • 6. 结束语

前言

这篇博客可能会有点长,因为是一个课程的大作业,包含的内容比较多,这个项目的开发的时间在两周左右,所以这个软件指是一个简单又比较简陋的小桌面应用。


1. 需求分析

  1. 导入文件:
    用户能够通过界面导入描述课程依赖关系的文本文件(.txt),文件格式为每行表示一个有向边的关系。

  2. 绘制拓扑排序图:
    根据导入的课程依赖关系,能够绘制出对应的拓扑排序图,使用直观的图形方式展示课程间的依赖关系。

  3. 导出图像:
    用户可以将绘制好的拓扑排序图导出为图片(.png格式),以便于保存和分享。

  4. 导出拓扑排序结果:
    用户可以将拓扑排序的结果导出为文本文件(.txt格式),用于后续分析和处理。

  5. 展示C++程序输出:
    显示调用外部C++程序计算拓扑排序后的输出结果,以便用户查看拓扑排序的详细信息。

  6. 主界面:
    提供导入文件、绘制图像、导出图像、导出拓扑排序结果的按钮,以及显示绘制好的图像和C++程序输出的区域。

  7. 用户操作反馈:
    显示错误、警告等反馈信息,确保用户能够清晰了解操作结果。

  8. 性能需求
    若所有拓扑排序的结果非常多则需要很快速的返回正确并且结果个数正确的结果,避免用户等待过长的时间。同时绘制拓扑排序图应在合理的时间范围内完成,避免用户等待时间过长。

  9. 其他需求
    ①软件应该通过所给样例的测试;
    ②软件应该支持在Windows操作系统上运行


2. 可行性分析

2.1 简介

该文档对拓扑排序图绘制工具项目——TopologicalSort_app软件进行可行性分析,主要包括技术可行性和操作可行性的分析,以确保项目的实施和开发是合理、可行的。

2.2 技术可行性分析

2.2.1 技术实现方案

①使用PySide2库实现图形用户界面,提供友好的交互。
②使用networkx和matplotlib库绘制拓扑排序图,能够高效、准确地展示图形。
③利用Python内置的文件处理功能实现文件导入、导出功能。
④使用subprocess库调用外部C++程序进行拓扑排序,实现图的计算。
⑤使用tempfile库创建临时文件,以保存绘制好的图像。
⑥通过在Python项目中调用C++程序,实现了对拓扑排序的计算。
⑦使用Python的subprocess库调用外部C++程序,并获取其输出。
⑧这种跨语言调用对实现拓扑排序算法具有良好的技术可行性,确保了项目核心功能的实现。

2.2.2 开发人员技能要求

①开发人员需熟悉Python编程语言及其相关库,如PySide2、networkx、matplotlib、subprocess等。
②需要了解图论中的拓扑排序算法以及相关概念。

2.2.3 可行性

①技术方案基于成熟的Python库实现,具有较高的技术可行性。
②Python具有丰富的第三方库和开发资源,能够快速实现项目需求。

2.3 操作可行性分析

①项目设计简单明了,操作界面直观友好,用户容易上手。
②提供了导入、导出、绘制图像等功能按钮,用户操作便捷,符合用户使用习惯。
③C++程序的调用对用户是透明的,用户只需使用界面提供的功能,不需要关心底层实现语言。
④用户操作界面简单明了,易于上手,提供了直观的导入、导出、绘制图像等功能按钮,满足用户操作习惯,操作可行性较高。

2.4 结论

①该项目具有较高的技术可行性,开发成本较低,运维成本也较低。操作界面简单明了,用户操作便捷。
②调用C++程序作为拓扑排序的计算引擎是技术上可行的,不会对整体的可行性产生负面影响。
③用户无需关心C++程序调用细节,操作界面简单易用,用户操作的可行性较高,确保了项目的实施和开发是合理、可行的。


3. 项目报告

3.1 修订历史记录

日期版本说明作者
2023.9.101.0.0创建好初步的页面hiddenSharp
2023.9.111.0.1完善了页面的排版hiddenSharp
2023.9.141.1.01. 为生成的拓扑排序图片添加了放大和缩小按钮
2. 为生成的拓扑图片添加了背景颜色
hiddenSharp
2023.9.151.2.01. 删除了放大和缩小按钮
2. 优化了图片大小格式以及清晰度
3. 新增导入文件后可以之间生成该图的所有拓扑排序结果
hiddenSharp
2023.9.161.2.11. 重新导入文件后将清空之前生成的图片并且进度条置零
2. 初始化进度条值为0,导入文件后增加50,生成图片后再加50
3. 删除了自动导出结果的功能,修改为用户手动点击Export进行结果的导出
4. 新增用户进行导出操作后,可以下拉选择导出的文件类型(.txt为所有的拓扑排序结果,.png为拓扑图)
hiddenSharp
2023.9.171.2.21. 固定了软件的窗口大小,不可调整窗口大小以及最大化
2. 调整了进度条的逻辑,取消了50的值,只有0与100
3. 完善了ADD 和 DEL按钮后生成图片以及拓扑排序结果的逻辑
4. DEL 按钮和 ADD按钮异常BUG
hiddenSharp
2023.9.231.3.0进行了项目结构的重构,更加具有面向对象的思想,将各模块分离出来了。新增FileManager类、InputManager类、TopologyManager类,将MainWindow类进行解耦和重构。hiddenSharp

3.2 软硬件环境

  • 操作系统:Windows
  • 硬件要求:暂无特定硬件需求
  • 开发工具:主要的IDE为PyCharm + Visual Stadio 2010;使用到的编程语言为Python + C++;文本编辑器使用的为Notepad++(不是硬性要求);编译器为Python3.6
  • 第三方库和依赖项:PySide2 v5.15.2.1、networkx v2.5.1

3.3 需求分析

TopologicalSort_app的主要功能是执行拓扑排序算法,用户可以导入图的节点和边,一条边的格式应该为 <arc_start,arc_end> 然后调用C++算法进行排序,随后返回结果并显示在软件屏幕上。上面已经详细说明了需求分析,故在此不再赘述。

3.4 详细设计

3.4.1 类设计

  • MainWindow类
    1. 在_init_函数中创建主窗口页面、存储主窗口信息、初始化控制器、连接槽函数。
    2. 在clear_topology_graph函数中清空拓扑图
    3. 在run_cpp函数中调用编译后的.cpp文件
    4. 在display_cpp_output函数中显示C++程序的输出到指定位置
    5. 在export_image函数中设置导出拓扑排序图片的操作
    6. 在export_topology函数中设置导出拓扑排序结果的操作
  • FileManager类
    1. 在_init_函数中载入MainWindow实例和InputManager实例
    2. 在import_file函数中设置导入文件的操作
    3. 在export函数中设置导出的操作
  • InputManager类
    1. 在__init__函数中载入MainWindow实例
    2. 在fill_input_boxes函数中填写相关数据到输入框中
    3. 在add_input_field函数中设置添加输入框的操作
    4. 在del_input_field函数中设置删除输入框的操作
  • TopologyManager类
    1. 在__init__函数中载入MainWindow实例
    2. 在generate_draw函数中通过调用draw_diagram文件的相关函数来完成将图片显示在相关位置上。

注:并没有draw_diagram类,只是一个py文件,里面写了一个draw_directed_graph函数,该函数通过调用networkx和matplotlib来完成图片的生成。

3.4.2 核心流程描述

定义一个课程结构体,声明二维向量,利用dfs函数递归进行深度优先搜索,生成所有可能的结果,判断是否存在循环依赖关系,用户可导出排序结果

3.4.3 核心算法设计

  1. dfs 函数接收两个参数:课程向量 courses 和拓扑排序的结果向量 result。
  2. 在函数开始定义了一个递归停止条件判断:如果当前拓扑排序结果的大小等于课程向量 courses 的大小,即 result 的大小等于 courses 的大小,说明已经生成了一个完整的拓扑排序结果。
  3. 遍历课程向量 courses。

(1)对于每一个课程,判断当前课程是否满足拓扑排序的条件,即入度为0且未被访问过;
(2)如果满足条件,将其添加到 result 中并将当前课程标记为已访问;
(3)对于每一个课程,判断当前课程是否满足拓扑排序的条件,即入度为0且未被访问过;
(4)如果满足条件,将其添加到 result 中并将当前课程标记为已访问;
(5)遍历当前课程的后继课程;将所有当前课程对应的后继课程入度减1;
(6)递归调用 dfs 函数处理下一个课程;
(7)回溯,将当前课程标记为未访问,回复后继课程的入度,从 result 中移除最后一门课程,得出其他分支结果;
(8)继续遍历下一个课程(更换拓扑排序的起始课程),重复上述步骤(在循环里);
(9)当所有的课程都被遍历完后,dfs 函数执行结束;将当前的拓扑排序结果 result 添加到 allTopologicalSorts 向量中。

4. 运行结果截图

4.1 样例1

在这里插入图片描述

4.2 样例2

在这里插入图片描述

4.3 样例3

在这里插入图片描述

4.4 样例4

在这里插入图片描述

4.5 样例5

在这里插入图片描述

5. 测试

5.1 测试样例1

  • Filename:data_1.txt
  • Content
    <a,b>
    <b,c>
    <c,d>
    <d,f>
    <f,g>
  • Result
    sort ruselt_1:a b c d f g
  • photo
    在这里插入图片描述

5.2 测试样例2

  • Filename:data_2.txt
  • Content
    <CS 100,CS 200>
    <CS 200,CS 250>
    <CS 200,CS 300>
    <CS 250,CS 350>
  • Result
    sort ruselt_1:CS 100 CS 200 CS 250 CS 300 CS 350
    ---------------------
    sort ruselt_2:CS 100 CS 200 CS 250 CS 350 CS 300
    ---------------------
    sort ruselt_3:CS 100 CS 200 CS 300 CS 250 CS 350
  • photo
    在这里插入图片描述

5.3 测试样例3

  • Filename:data_3.txt
  • Content
    <CS 350,CS 250>
    <MA 140,MA 141>
    <MA 141,CS 150>
    <CS 250,CS 225>
    <MA 141,CS 225>
    <CS 225,CS 155>
    <CS 150,CS 155>
    <CS 155,CS 200>
    <CS 225,CS 230>
    <CS 225,CS 300>
    <CS 300,CS 301>
    <CS 300,CS 340>
    <CS 340,CS 345>
    <CS 340,CS 360>
    <CS 250,CS 360>
    <CS 360,CS 390>
  • Result
    sort ruselt_1:CS 350 CS 250 MA 140 MA 141 CS 150 CS 225 CS 155 CS 200 CS 230 CS 300 CS 301 CS 340 CS 345 CS 360 CS 390
    ---------------------
    sort ruselt_2:CS 350 CS 250 MA 140 MA 141 CS 150 CS 225 CS 155 CS 200 CS 230 CS 300 CS 301 CS 340 CS 360 CS 345 CS 390
    ……
    ……
    ……
    sort ruselt_113399:MA 140 MA 141 CS 150 CS 350 CS 250 CS 225 CS 300 CS 340 CS 360 CS 390 CS 345 CS 301 CS 155 CS 230 CS 200
    ---------------------
    sort ruselt_113400:MA 140 MA 141 CS 150 CS 350 CS 250 CS 225 CS 300 CS 340 CS 360 CS 390 CS 345 CS 301 CS 230 CS 155 CS 200

5.4 测试样例4

  • Filename:data_4.txt

  • Content
    < 0,1>
    < 1,3>
    < 0,2>
    < 2,4>
    < 4,5>
    < 3,5>

  • Result
    sort ruselt_1:0 1 3 2 4 5
    ---------------------
    sort ruselt_2:0 1 2 3 4 5
    ---------------------
    sort ruselt_3:0 1 2 4 3 5
    ---------------------
    sort ruselt_4:0 2 1 3 4 5
    ---------------------
    sort ruselt_5:0 2 1 4 3 5
    ---------------------
    sort ruselt_6:0 2 4 1 3 5

  • Photo
    在这里插入图片描述

5.5 测试样例5

  • Filename:data_5.txt
  • Content
    <0,1>
    <1,2>
    <2,0>
  • Result
    存在循环依赖关系
  • Photo
    在这里插入图片描述

6. 系统特色以及可扩展点

6.1系统特色:

TopologicalSort_app 是一个Python项目,它基于PySide2库实现的图形用户界面(GUI)应用程序,用于创建、导入、导出拓扑排序图。下面将介绍它的一些系统特色:

  1. 图形用户界面(GUI):
    使用PySide2的QtDesigner创建了一个图形用户界面名为main.ui存放在statics文件夹下面,提供了文件导入、图形绘制、导出等功能接口。
  2. 拓扑排序图绘制:
    通过调用Draw_diagram.py里面的draw_directed_graph函数返回给主界面一个图片的临时地址,生成后若不保存则自动释放资源,不占用计算机内部关键资源。在python文件调用了networkx和matplotlib库实现了拓扑排序图的绘制功能。使用有向图表示节点和边的关系,并在GUI中名为photoLable的QLabel显示该图。
  3. 文件导入和导出:
    允许用户导入文本文件(.txt)以填充输入框并绘制拓扑排序图,同时也支持将绘制好的图像导出为图片(.png)或拓扑排序结果导出为文本文件。
  4. C++程序调用:
    由于C++运行速度比python快,故复杂的计算任务交给C++来完成,项目能够调用外部的C++程序(通过使用g++来编译.cpp文件,从而生成.exe文件)python运行该.exe并传入相关参数给.exe文件,最后将输出c++文件返回的结果显示在GUI中。此外,C++程序的输出可以导出为文本文件。
  5. 动态UI加载:
    使用PySide2的QUiLoader动态加载UI文件,使得UI可以通过简单的编辑UI文件而不需要修改代码。以此来到模块化编程,更加灵活多变。
  6. 用户操作反馈:
    使用QMessageBox提供信息、警告和错误提示,以向用户提供反馈。
  7. 临时文件处理:
    使用tempfile库创建临时文件以保存绘制的图像,以便导出功能可以使用这些临时文件。
  8. 异常处理:
    项目中实现了异常处理机制,能够捕获并显示错误信息,提高应用程序的健壮性。

6.2可扩展点:

  1. 将鼠标放置在按钮上会显示出相应信息
  2. 结果图的大小可以自行调节

4. 源代码部分

4.1 项目层级

TopologicalSort_app├─.idea├─build├─core (核心算法)├─data (读入的文件信息)├─dist (发布软件的各个版本)├─docs (所有文档信息)├─lib├─log├─statics (静态资源)├─test  (测试)├─venv└─__pycache__

4.2 运行环境

  • IDE:pycharm community v2023+

  • 解释器:python v3.6.8

  • 外部库: PySide2 v5.15.2.1、networkx v2.5.1、matplotlib v3.3.4

  • GUI工具:QtDesigner

4.3 项目核心代码

4.3.1 拓扑排序算法TopologicalSort.cpp

写在前面:这个c++程序在整个项目中是比较核心的一个部分,它利用c++运行速度更快来作为核心程序,让整个python项目调用,以达到核心业务和整个项目解耦的目的。这个.cpp文件不会直接调用,项目只会调用编译过后的TopologicalSort.exe,而这个文件会存放在statics文件夹下面。

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <algorithm>using namespace std;struct Course {string name;  // 课程名vector<Course*> prerequisites;  // 对应的先修课程的指针向量int indegree;  // 入度(有多少个先修条件)bool visited;  // 判断课程是否被访问过Course(const string& n) : name(n), indegree(0), visited(false) {}  // 构造函数
};vector<vector<string>> allTopologicalSorts;void dfs(vector<Course*>& courses, vector<string>& result) {if (result.size() == courses.size()) {allTopologicalSorts.push_back(result);return; // 递归终止条件:完成了一次拓扑排序}for (size_t i = 0; i < courses.size(); ++i) { // 遍历每个课程Course* course = courses[i]; // 当前课程if (course->indegree == 0 && !course->visited) { // 如果当前课程的入度为0且未被访问过course->visited = true; // 标记当前课程已访问result.push_back(course->name); // 将当前课程添加到当前排列中for (size_t j = 0; j < course->prerequisites.size(); ++j) { // 减少当前课程的邻接课程的入度Course* prerequisite = course->prerequisites[j];prerequisite->indegree--;}dfs(courses, result);       //递归course->visited = false; // 标记当前课程为未访问状态for (size_t j = 0; j < course->prerequisites.size(); ++j) { // 回溯:撤销之前的修改Course* prerequisite = course->prerequisites[j];prerequisite->indegree++; // 恢复后续邻接课程的入度}result.pop_back(); // 移除当前排列中的最后一门课程}}
}bool printAllTopologicalSorts(vector<Course*>& courses) {vector<string> result;dfs(courses, result);return !allTopologicalSorts.empty();
}void shuchu(vector<Course*>& courses) {int count = 0;for (size_t i = 0; i < allTopologicalSorts.size(); ++i) {cout << "sort ruselt_" << ++count << ':';for (size_t j = 0; j < allTopologicalSorts[i].size(); ++j) {cout << allTopologicalSorts[i][j] << " ";}cout << endl;if (i != allTopologicalSorts.size() - 1) {cout << "---------------------" << endl;}}
}int main(int argc, char* argv[]) {if (argc < 2) {cout << "Usage: " << argv[0] << " <filename>" << endl;return 0;}string filename = argv[1];  vector<Course*> courses;unordered_map<string, Course*> courseMap;ifstream file(filename);if (file.is_open()) {string line;while (getline(file, line)) {if (line.empty()) {continue;}line = line.substr(1, line.size() - 2);stringstream ss(line);string courseName, prereqName;getline(ss, courseName, ',');getline(ss, prereqName);Course* course = courseMap[courseName];if (!course) {course = new Course(courseName);courses.push_back(course);courseMap[courseName] = course;}Course* prereq = courseMap[prereqName];if (!prereq) {prereq = new Course(prereqName);courses.push_back(prereq);courseMap[prereqName] = prereq;}course->prerequisites.push_back(prereq);prereq->indegree++;}file.close();} else {cout << "无法打开文件" << endl;return 0;}if (printAllTopologicalSorts(courses)) {shuchu(courses);} else {cout << "存在循环依赖关系" << endl;}for (size_t i = 0; i < courses.size(); i++) {delete courses[i];}courses.clear();return 0;
}

4.3.2 draw_diagram.py

import networkx as nx
import matplotlib.pyplot as plt
import tempfiledef draw_directed_graph(edges, figsize=(3, 3)):try:# 创建一个有向图对象G = nx.DiGraph()# 添加有向边for data in edges:data = data.strip('<>')source, target = data.split(',')G.add_edge(source, target)# 设置图片的大小plt.figure(figsize=figsize)# 绘制有向图pos = nx.spring_layout(G)# Adjust node positions for labels to be around the nodespos_labels = {node: (x, y + 0.01) for node, (x, y) in pos.items()}nx.draw(G, pos, with_labels=False, node_color='g', node_size=200, arrows=True)# Draw labels separately with adjusted positionsnx.draw_networkx_labels(G, pos_labels, font_size=10)# 保存图形到临时文件with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmpfile:plt.savefig(tmpfile, format="png", bbox_inches="tight")# 返回临时文件的路径return tmpfile.nameexcept Exception as e:print(f"生成图时出现错误:{str(e)}")

4.3.3 file_manager.py

from PySide2.QtWidgets import QFileDialog, QMessageBox
class FileManager:def __init__(self, main_window, input_manager):self.main_window = main_windowself.input_manager = input_managerdef import_file(self):options = QFileDialog.Options()file_path, _ = QFileDialog.getOpenFileName(self.main_window.ui, "选择要导入的文件", "", "文本文件 (*.txt);;所有文件 (*)", options=options)if file_path:try:self.main_window.clear_topology_graph()  # 清空拓扑排序图self.main_window.file_path = file_pathwith open(file_path, 'r', encoding='utf-8') as file:file_content = file.read()# 调用C++文件并获取结果cpp_output = self.main_window.run_cpp(file_path)# 将结果显示在plainTextEdit上self.main_window.display_cpp_output(cpp_output)self.input_manager.fill_input_boxes(file_content)except Exception as e:QMessageBox.critical(self.main_window.ui, "错误", f"导入文件时出现错误:{str(e)}")else:QMessageBox.warning(self.main_window.ui, "警告", "未选择任何文件")def export(self):options = QFileDialog.Options()export_option, _ = QFileDialog.getSaveFileName(self.main_window.ui, "选择导出路径", "","Images (*.png);;Text Files (*.txt)", options=options)if export_option:if export_option.endswith(".png"):# 导出图片self.main_window.export_image(export_option)elif export_option.endswith(".txt"):# 导出拓扑排序结果self.main_window.export_topology(export_option)else:QMessageBox.warning(self.main_window.ui, "警告", "不支持的导出格式")

4.3.4 input_manager.py

from PySide2.QtCore import Qt
from PySide2.QtWidgets import QListWidgetItem, QLineEdit, QAbstractItemViewclass InputManager:def __init__(self, main_window):self.main_window = main_windowdef fill_input_boxes(self, file_content):# 清空现有输入框的内容self.main_window.ui.inputList.clear()data_list = file_content.split('\n')  # 按换行符分割数据for data in data_list:data = data.strip()if data:# 提取源节点和目标节点source, target = data.split(',')source = source.strip()target = target.strip()# 创建适当的输入格式input_item = QListWidgetItem()input_line_edit = QLineEdit(f"{source},{target}")input_line_edit.setAlignment(Qt.AlignCenter)self.main_window.ui.inputList.addItem(input_item)self.main_window.ui.inputList.setItemWidget(input_item, input_line_edit)def add_input_field(self):input_item = QListWidgetItem()input_line_edit = QLineEdit('<start,end>')input_line_edit.setAlignment(Qt.AlignCenter)  # 设置文本居中对齐self.main_window.ui.inputList.addItem(input_item)self.main_window.ui.inputList.setItemWidget(input_item, input_line_edit)# 将边信息添加到列表中self.main_window.edge_info.append('<start,end>')def del_input_field(self):selected_items = self.main_window.ui.inputList.selectedItems()for item in selected_items:index = self.main_window.ui.inputList.row(item)self.main_window.ui.inputList.takeItem(index)# 从列表中删除对应的边信息if index < len(self.main_window.edge_info):del self.main_window.edge_info[index]

4.3.5 main_window.py

import os
import subprocessfrom PySide2.QtWidgets import (QFileDialog, QMessageBox, QListWidgetItem,QLineEdit, QAbstractItemView, QWidget,QVBoxLayout, QLabel
)
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import Qt
from .file_manager import FileManager
from .input_manager import InputManager
from .topology_manager import TopologyManagerclass MainWindow:def __init__(self):# 动态加载.ui文件self.ui = QUiLoader().load('statics/main.ui')# 禁止调整窗口大小self.ui.setFixedSize(self.ui.size())# 设置窗口属性,禁止最大化self.ui.setWindowFlags(self.ui.windowFlags() & ~Qt.WindowMaximizeButtonHint)# 用于存储边信息的列表self.edge_info = []# 存储文件路径self.file_path = None# 初始化控制器self.input_manager = InputManager(self)self.file_manager = FileManager(self, self.input_manager)self.topology_manager = TopologyManager(self)# 连接相关操作的槽函数self.ui.actionImport.triggered.connect(self.file_manager.import_file)self.ui.actionExport.triggered.connect(self.file_manager.export)self.ui.addButton.clicked.connect(self.input_manager.add_input_field)self.ui.delButton.clicked.connect(self.input_manager.del_input_field)self.ui.generateButton.clicked.connect(self.topology_manager.generate_draw)# 设置输入框为单选模式self.ui.inputList.setSelectionMode(QAbstractItemView.SingleSelection)# 设置按钮的提示文本self.ui.addButton.setToolTip("添加输入框 (Alt + A)")self.ui.delButton.setToolTip("删除输入框 (Alt + D)")self.ui.generateButton.setToolTip("生成拓扑排序结果 (Alt + Enter)")# 创建一个 QWidget 作为容器self.photo_container = QWidget()self.ui.photoLabel.layout().addWidget(self.photo_container)self.ui.photoLabel.setStyleSheet("background-color: white;")# 在容器上设置布局container_layout = QVBoxLayout()self.photo_container.setLayout(container_layout)# 将 QLabel 添加到容器中self.photo_label = QLabel()container_layout.addWidget(self.photo_label)def clear_topology_graph(self):self.photo_label.clear()  # 清空 QLabel 上的图像self.ui.progressBar.setValue(0)  # 重置进度条的值def run_cpp(self, file_path):try:# 获取当前脚本所在目录script_directory = os.path.dirname(os.path.abspath(__file__))# 构建调用命令cpp_executable_path = os.path.join(script_directory, 'TopologicalSort.exe')command = f'"{cpp_executable_path}" "{file_path}"'# 调用外部程序result =  subprocess.run(command, shell=True, stdout=subprocess.PIPE)# 返回结果return result.stdoutexcept Exception as e:print("Error during running C++ program:", str(e))return "Error: Unable to run C++ program"# 在plainTextEdit中显示C++程序的输出def display_cpp_output(self, cpp_output):try:# 将字节串解码为字符串cpp_output_str = cpp_output.decode('utf-8')# 在plainTextEdit中显示C++程序的输出self.ui.plainTextEdit.setPlainText(cpp_output_str)except Exception as e:print("Error: Unable to display C++ output:", str(e))self.ui.plainTextEdit.setPlainText("Error: Unable to display C++ output")def export_image(self, export_path):pixmap = self.photo_label.pixmap()if pixmap:pixmap.save(export_path, "PNG")QMessageBox.information(self.ui, "导出成功", f"图像已成功导出到:{export_path}")else:QMessageBox.warning(self.ui, "警告", "没有图像可导出")def export_topology(self, export_path):try:# 获取C++程序的输出cpp_output = self.ui.plainTextEdit.toPlainText().encode('utf-8')# 写入文件with open(export_path, 'wb') as file:file.write(cpp_output)QMessageBox.information(self.ui, "导出成功", f"拓扑排序结果已成功导出到:{export_path}")except Exception as e:QMessageBox.critical(self.ui, "错误", f"导出拓扑排序结果时出现错误:{str(e)}")if __name__ == "__main__":# Create the application instanceimport sysfrom PySide2.QtWidgets import QApplicationapp = QApplication(sys.argv)# Create and show the main windowmainWindow = MainWindow()mainWindow.ui.show()# Start the event loopsys.exit(app.exec_())

4.3.6 topology_manager.py

from PySide2.QtGui import QPixmap
from PySide2.QtWidgets import QMessageBoxfrom .draw_diagram import draw_directed_graphclass TopologyManager:def __init__(self, main_window):self.main_window = main_windowdef generate_draw(self):try:if not self.main_window.file_path:QMessageBox.warning(self.main_window.ui, "警告", "未选择任何文件")return# 获取所有输入框的值input_items = [self.main_window.ui.inputList.itemWidget(self.main_window.ui.inputList.item(i)).text()for i in range(self.main_window.ui.inputList.count())]# 将边信息更新为当前输入框中的值self.main_window.edge_info = input_items# 调用绘图函数并获取图形文件路径graph_image_path = draw_directed_graph(input_items)if graph_image_path:# 将图形文件设置为QLabel的图像pixmap = QPixmap(graph_image_path)self.main_window.photo_label.setPixmap(pixmap)self.main_window.ui.progressBar.setValue(100)# 更新文件中的边信息(覆盖原文件)with open(self.main_window.file_path, 'w', encoding='utf-8') as file:file.write('\n'.join(input_items))# 重新调用C++程序并更新输出cpp_output = self.main_window.run_cpp(self.main_window.file_path)self.main_window.display_cpp_output(cpp_output)except Exception as e:print("Error during generate_draw:", str(e))

4.4 GitHub仓库

有这些核心源代码可能远远不够,因为还有些不是代码的核心文件,如:使用QtDesigner设计的页面UI——main.ui文件,因此在下面我会放上这个项目的GitHub仓库地址,如果有需要可以自取哦~

https://github.com/hiddenSharp429/ToplogicalSort_app


5. 用户使用手册

5.1 运行软件APP

①打开TopologicalSort_app文件夹,双击dist文件夹进入所有发布程序选择页面
在这里插入图片描述

②选择需要运行的软件版本
在这里插入图片描述

各个版本的区别请看\ToplogicalSort_app\docs\ToplogicalSort_app更新日志.md

③进入某一个版本的文件夹后下拉找到main.exe文件
在这里插入图片描述

④双击后启动软件

5.2 操作app

①点击左上角菜单 ico
在这里插入图片描述

②选择导入选项卡
在这里插入图片描述
③选择需要导入的文件
在这里插入图片描述

一般测试样例都会放在\ToplogicalSort_app\data\里面
④导入后左边的输入框将会生成,文件中的文本将会被匹配填充,并且在下面的输入框中输出了所有拓扑排序的结果

在这里插入图片描述

测试样例的格式如下
在这里插入图片描述

⑤随后点击Generate按钮生成拓扑排序图片

在这里插入图片描述
在这里插入图片描述

⑥至此完成基本的功能,可以点击菜单选择导出选项卡
在这里插入图片描述

⑦可选择导出.txt类型文件还是.png文件,若为.txt文件则导出所有拓扑排序的结果,若为.png文件则导出拓扑图
在这里插入图片描述

运行结果:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


6. 结束语

如果有疑问欢迎大家留言讨论,你如果觉得这篇文章对你有帮助可以给我一个免费的赞吗?我们之间的交流是我最大的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/141004.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud LoadBalancer基础知识

LoadBalancer 概念常见的负载均衡策略使用随机选择的负载均衡策略创建随机选择负载均衡器配置 Nacos 权重负载均衡器创建 Nacos 负载均衡器配置 自定义负载均衡器(根据IP哈希策略选择)创建自定义负载均衡器封装自定义负载均衡器配置 缓存 概念 LoadBalancer(负载均衡器)是一种…

2023年数维杯国际大学生数学建模挑战赛

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 cs数模团队在数维杯前为大家提供了许多资料的内容呀&#xff0…

Redis应用之二分布式锁2

一、前言 前一篇 Redis应用之二分布式锁 我们介绍了使用SETNX来实现分布式锁&#xff0c;并且还遗留了一个Bug&#xff0c;今天我们对代码进行优化&#xff0c;然后介绍一下Redisson以及数据库的乐观锁悲观锁怎么用。 二、SetNX分布式锁优化后代码 RedisService.java Inven…

基于ssm+vue协同过滤算法的电影推荐系统

基于ssmvue协同过滤算法的电影推荐系统 摘要 电影推荐系统在信息技术发展的背景下日益成为研究的焦点&#xff0c;本研究基于SSM&#xff08;Spring SpringMVC MyBatis&#xff09;框架与Vue.js技术&#xff0c;以协同过滤算法为核心&#xff0c;旨在构建一种高效、准确的电影…

JVM在线分析-解决问题的工具一(jinfo,jmap,jstack)

1. jinfo (base) PS C:\Users\zishi\Desktop> jinfo Usage:jinfo <option> <pid>(to connect to a running process)where <option> is one of:-flag <name> to print the value of the named VM flag #输出对应名称的参数-flag [|-]<n…

C语言--1,5,10人民币若干,现在需要18元,一共有多少种?

今天小编给大家分享一下穷举法的一道典型例题 一.题目描述 1,5,10人民币若干,现在需要18元,一共有多少种? 二.思路分析 总共有18块钱&#xff0c;设1元有x张&#xff0c;5元有y张&#xff0c;10元有z张&#xff0c;则有表达式&#xff1a;x5y10z18&#xff0c;穷举法最重要的…

Java事务详解

一、事务的理解&#xff1a; 1、事务的特性&#xff1a; 1) 原子性&#xff08;atomicity&#xff09;&#xff1a;事务是数据库的逻辑工作单位&#xff0c;而且是必须是原子工作单位&#xff0c;对于其数据修改&#xff0c;要么全部执行&#xff0c;要么全部不执行。 2) 一致性…

大数据之LibrA数据库系统告警处理(ALM-12033 慢盘故障)

告警解释 系统每一秒执行一次iostat命令&#xff0c;监控磁盘I/O的系统指标&#xff0c;如果在60s内&#xff0c;svctm大于100ms的周期数大于30次则认为磁盘有问题&#xff0c;产生该告警。 更换磁盘后&#xff0c;告警自动恢复。 告警属性 告警ID 告警级别 可自动清除 1…

vue,react虚拟dom

Virtual DOM 前言 在传统的Web开发中&#xff0c;直接操作真实的DOM通常是一个昂贵且低效的操作。为了解决这个问题&#xff0c;Virtual DOM&#xff08;虚拟DOM&#xff09;被引入为一个中间层&#xff0c;允许开发者在内存中进行操作&#xff0c;从而避免频繁且不必要的真实D…

什么是代理IP池?真实测评IP代理商的IP池是否真实?

代理池充当多个代理服务器的存储库&#xff0c;提供在线安全和匿名层。代理池允许用户抓取数据、访问受限制的内容以及执行其他在线任务&#xff0c;而无需担心被检测或阻止的风险。代理池为各种在线活动&#xff08;例如网页抓取、安全浏览等&#xff09;提高后勤保障。 读完…

【C语言 | 预处理】C语言预处理详解(一) —— #define、#under、#if、#else、#elif、#endif、#include、#error

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

“第六十七天”

各位&#xff0c;昨天查找子串的方法想起来了&#xff0c;就是那个KMP算法......自己理解都有点困难&#xff0c;还看看能不能想一下&#xff0c;确实很困难啊。 不要忘了toupper函数和tolower函数不是直接改变字符的大小写&#xff0c;而是返回对应的大小写的值&#xff0c;需…

文件上传 [ACTF2020 新生赛]Upload1

打开题目&#xff0c;发现是一道文件上传题目 随便上传个一句话木马上去 发现网站前端有白名单限制&#xff0c;只能上传含有jpg&#xff0c;png&#xff0c;gif的后缀文件 那我们便传个2.jpg的一句话木马上去&#xff0c;bp抓包 我们改成php文件后缀试试&#xff0c;发现重发…

毕业设计项目:基于java+springboot的共享单车信息网站

运行环境 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Ma…

5. HTML常用标签

5.1 标签语义 学习标签是有技巧的&#xff0c;重点是记住每个标签的语义。简单理解就是指标签的含义。即这个标签是用来干嘛的。 根据标签的语义&#xff0c;在合适的地方给一个最为合理的标签。可以让页面结构给清晰。 5.2 标题标签 <h1>-<h6>(重要) HTML提供了…

【学习辅助】Axure手机时间管理APP原型,告别手机控番茄任务模板

作品概况 页面数量&#xff1a;共 30 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;时间管理、系统工具 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为「手机时间管理」APP原型&#xff0c;…

Redis集群,你真的学会了吗?

目录 1、为什么引入集群 1.1、先来了解集群是什么 1.2、哨兵模式的缺陷 引入集群解决了什么问题 1.3、使用集群&#xff0c;如何存储数据 2、三种主流的分片方式【经典面试题】 2.1、哈希求余算法 2.1.1、哈希求余算法的介绍 2.1.2、哈希求余算法如何扩容 2.2、一致性…

C# 并发编程

C# 并发编程 前言 对于现在很多编程语言来说&#xff0c;多线程已经得到了很好的支持&#xff0c; 以至于我们写多线程程序简单&#xff0c;但是一旦遇到并发产生的问题就会各种尝试。 因为不是明白为什么会产生并发问题&#xff0c;并发问题的根本原因是什么。 接下来就让…

vcomp120.dll丢失怎么办?vcomp120.dll丢失的解决方法分享

vcomp120.dll丢失”。这个错误通常会导致某些应用程序无法正常运行&#xff0c;给用户带来困扰。那么&#xff0c;当我们遇到这个问题时&#xff0c;应该如何修复呢&#xff1f;下面我将为大家介绍四个修复vcomp120.dll丢失的方法。 一、使用dll修复程序修复 可以通过百度或许…

矢量绘图软件Sketch 99 for mac

Sketch是一款为用户提供设计和创建数字界面的矢量编辑工具。它主要用于UI/UX设计师、产品经理和开发人员&#xff0c;帮助他们快速设计和原型各种应用程序和网站。 Sketch具有简洁直观的界面&#xff0c;以及丰富的功能集&#xff0c;使得用户可以轻松地创建、编辑和共享精美的…