返回顶部
首页 > 资讯 > 后端开发 > Python >Python如何实现关键路径和七格图计算
  • 209
分享到

Python如何实现关键路径和七格图计算

2023-07-05 12:07:39 209人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

本篇内容介绍了“python如何实现关键路径和七格图计算”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1.主程序主程序主要实现了一个Proj

本篇内容介绍了“python如何实现关键路径和七格图计算”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1.主程序

主程序主要实现了一个Project类,其中包含了计算关键路径和七格图的方法。具体实现方式如下:

 定义了一个Activity类,包含了活动的id、名称、持续时间和紧前任务列表等属性。 

 定义了一个Project类,包含了活动列表、项目持续时间、日志等属性,以及计算关键路径、计算七格图、计算总浮动时间、计算自由浮动时间等方法。

 从JSON文件中读取活动信息,并创建Project对象并添加活动。

 调用Project对象的calculate方法,计算每个活动的最早开始时间、最晚开始时间等数据。

 调用Project对象的calculate_critical_path方法,计算关键路径。

 调用Project对象的calculate_project_duration方法,计算项目总工期。

 使用Jinja2模板引擎生成项目的活动清单,并将关键路径

import json  from datetime import datetime  from typing import List    import graphviz  from jinja2 import Template    from activity import Activity      class Project:      def __init__(self):          self.activities: List[Activity] = []          self.duration = 0          self.logger = []        def log(self, log: str) -> None:          self.logger.append(log)        def add_activity(self, activity: Activity) -> None:          """          添加一个活动到项目中          :param          activity: 待添加的活动          """        # 将活动添加到项目中          self.activities.append(activity)        def calculate(self) -> None:          """ 计算整个项目的关键信息          :return: None          """        self.calculate_successor()          self._calculate_forward_pass()  # 计算正推法          self._calculate_backward_pass()  # 计算倒推法          self._calculate_total_floats()  # 计算总浮动时间          self._calculate_free_floats()  # 计算自由浮动时间        def calculate_successor(self) -> None:          self.log("开始计算紧后活动")          for act in self.activities:              for pred in act.predecessors:                  for act_inner in self.activities:                      if act_inner.id == pred:                          act_inner.successors.append(act.id)        def _calculate_forward_pass(self) -> None:          self.log("## 开始正推法计算")          # 进入 while 循环,只有当所有活动的最早开始时间和最早完成时间都已经计算出来时,才会退出循环          while not self._is_forward_pass_calculated():              # 遍历每个活动              for activity in self.activities:                  # 如果活动的最早开始时间已经被计算过,则跳过                  if activity.est is not None:                      continue                  # 如果活动没有前置活动, 则从1开始计算最早开始时间和最早结束时间                  if not activity.predecessors:                      activity.est = 1                      activity.eft = activity.est + activity.duration - 1                      self.log(                          f"活动 {activity.name} 没有紧前活动,设定最早开始时间为1, 并根据工期计算最早结束时间为{activity.eft}")                  else:                      # 计算当前活动的所有前置活动的最早完成时间                      predecessors_eft = [act.eft for act in self.activities if                                          act.id in activity.predecessors and act.eft is not None]                      # 如果当前活动的所有前置活动的最早完成时间都已经计算出来,则计算当前活动的最早开始时间和最早完成时间                      if len(predecessors_eft) == len(activity.predecessors):                          activity.est = max(predecessors_eft) + 1                          activity.eft = activity.est + activity.duration - 1                          self.log(                              f"活动 {activity.name} 紧前活动已完成正推法计算, 开始日期按最早开始时间里面最大的," +                              f"设定为{activity.est}并根据工期计算最早结束时间为{activity.eft}")                            # 更新项目总持续时间为最大最早完成时间          self.duration = max([act.eft for act in self.activities])        def _calculate_backward_pass(self) -> None:          """ 计算倒推法          :return: None          """          self.log("## 开始倒推法计算")  # 输出提示信息          # 进入 while 循环,只有当所有活动的最晚开始时间和最晚完成时间都已经计算出来时,才会退出循环          while not self._is_backward_pass_calculated():              # 遍历每个活动              for act in reversed(self.activities):                  # 如果活动的最晚开始时间已经被计算过,则跳过                  if act.lft is not None:                      continue                  # 如果活动没有后继活动, 则从总持续时间开始计算最晚开始时间和最晚结束时间                  if not act.successors:                      act.lft = self.duration                      act.lst = act.lft - act.duration + 1                      self.log(f"活动 {act.name} 没有紧后活动,按照正推工期设定最晚结束时间为{act.lft}," +                               f"并根据工期计算最晚开始时间为{act.lst}")                  else:                      # 计算当前活动的所有后继活动的最晚开始时间                      successors_lst = self._calculate_lst(act)                      # 如果当前活动的所有后继活动的最晚开始时间都已经计算出来,则计算当前活动的最晚开始时间和最晚完成时间                      if len(successors_lst) == len(act.successors):                          act.lft = min(successors_lst) - 1                          act.lst = act.lft - act.duration + 1                          self.log(f"活动 {act.name} 紧后活动计算完成,按照倒推工期设定最晚结束时间为{act.lft}," +                                   f"并根据工期计算最晚开始时间为{act.lst}")          # 更新项目总持续时间为最大最晚完成时间          self.duration = max([act.lft for act in self.activities])        def _calculate_lst(self, activity: Activity) -> List[int]:          """计算某一活动的所有最晚开始时间          :param activity: 活动对象          :return: 最晚开始时间列表          """        rst = []  # 初始化结果列表          for act in activity.successors:  # 遍历该活动的后继活动              for act2 in self.activities:  # 遍历所有活动                  if act2.id == act and act2.lst is not None:  # 如果找到了该后继活动且其最晚开始时间不为空                      rst.append(act2.lst)  # 将最晚开始时间加入结果列表          return rst  # 返回结果列表        def _is_forward_pass_calculated(self) -> bool:          """ 判断整个项目正推法计算已经完成          :return: 若已计算正向传递则返回True,否则返回False          """          for act in self.activities:  # 遍历所有活动              if act.est is None or act.eft is None:  # 如果该活动的最早开始时间或最早完成时间为空                  return False  # 则返回False,表示还未计算正向传递          return True  # 如果所有活动的最早开始时间和最早完成时间都已计算,则返回True,表示已计算正向传递        def _is_backward_pass_calculated(self) -> bool:          """ 判断整个项目倒推法计算已经完成          :return: 若已计算倒推法则返回True,否则返回False          """        for act in self.activities:  # 遍历所有活动              if act.lst is None or act.lft is None:  # 如果该活动的最晚开始时间或最晚完成时间为空                  return False  # 则返回False,表示还未计算倒推法          return True  # 如果所有活动的最晚开始时间和最晚完成时间都已计算,则返回True,表示已计算倒推法        def _calculate_total_floats(self) -> None:          """ 计算所有活动的总浮动时间          :return: None           """          self.log(f"## 开始计算项目所有活动的总浮动时间")          for act in self.activities:  # 遍历所有活动              if act.est is not None and act.lst is not None:  # 如果该活动的最早开始时间和最晚开始时间都已计算                  act.tf = act.lst - act.est  # 则计算该活动的总浮动时间                  self.log(f"计算{act.name}的总浮动时间" + f"最晚开始时间{act.lst} - 最早开始时间{act.est} = {act.tf}", )              else:  # 如果该活动的最早开始时间或最晚开始时间为空                  act.tf = None  # 则将该活动的总浮动时间设为None        def _calculate_free_floats(self) -> None:          """ 计算所有活动的自由浮动时间          :return: None          """        self.log(f"## 开始计算项目所有活动的自由浮动时间")  # 输出提示信息          for act in self.activities:  # 遍历所有活动              if act.tf == 0:  # 如果该活动的总浮动时间为0                  self.log(f"计算{act.name}的自由浮动时间" + f"因为{act.name}的总浮动时间为0,自由浮动时间为0")  # 输出提示信息                  act.ff = 0  # 则将该活动的自由浮动时间设为0              elif act.tf > 0:  # 如果该活动的总浮动时间大于0                  self.log(f"计算{act.name}的自由浮动时间")  # 输出提示信息                  self.log(f"- {act.name}的总浮动时间{act.tf} > 0,")  # 输出提示信息                  tmp = []  # 初始化临时列表                  for act2 in self.activities:  # 遍历所有活动                      if act2.id in act.successors:  # 如果该活动是该活动的紧后活动                          self.log(f"- {act.name}的紧后活动{act2.name}的自由浮动动时间为{act2.tf}")  # 输出提示信息                          tmp.append(act2.tf)  # 将该紧后活动的自由浮动时间加入临时列表                  if len(tmp) != 0:  # 如果临时列表不为空                      act.ff = act.tf - max(tmp)  # 则计算该活动的自由浮动时间                      if act.ff < 0:                          act.ff = 0                      self.log(f"- 用活动自己的总浮动{act.tf}减去多个紧后活动总浮动的最大值{max(tmp)} = {act.ff}")                  else:  # 如果临时列表为空                      act.ff = act.tf  # 则将该活动的自由浮动时间设为总浮动时间        def calculate_critical_path(self) -> List[Activity]:          """ 计算整个项目的关键路径          :return: 整个项目的关键路径          """        ctc_path = []  # 初始化关键路径列表          for act in self.activities:  # 遍历所有活动              if act.tf == 0:  # 如果该活动的总浮动时间为0                  ctc_path.append(act)  # 则将该活动加入关键路径列表          return ctc_path  # 返回关键路径列表        def calculate_project_duration(self) -> int:          """ 计算整个项目的持续时间          :return: 整个项目的持续时间          """        return max(activity.eft for activity in self.activities)  # 返回所有活动的最早完成时间中的最大值,即整个项目的持续时间      # 从JSON文件中读取活动信息  with open('activities.json', 'r', encoding='utf-8') as f:      activities_data = json.load(f)    # 创建Project对象并添加活动  project = Project()  for activity_data in activities_data:      activity = Activity(          activity_data['id'],          activity_data['name'],          activity_data['duration'],          activity_data['predecessors']      )    project.add_activity(activity)    # 计算每个活动的最早开始时间、最晚开始时间等数据  project.calculate()    # 计算关键路径和项目总工期  critical_path = project.calculate_critical_path()  project_duration = project.calculate_project_duration()    # 生成项目的活动清单  with open('template.html', 'r', encoding='utf-8') as f:      template = Template(f.read())  html = template.render(      activities=project.activities,      critical_path=critical_path,      project_duration=project_duration,      log=project.logger  )    # 生成项目进度网络图  aon_graph = graphviz.Digraph(fORMat='png', graph_attr={'rankdir': 'LR'})  for activity in project.activities:      aon_graph.node(str(activity.id), activity.name)      for predecessor in activity.predecessors:          aon_graph.edge(str(predecessor), str(activity.id))    timestamp = datetime.now().strftime('%Y%m%d%H%M%S')    aon_filename = f"aon_{timestamp}"    aon_graph.render(aon_filename)    # 将项目进度网络图插入到HTML文件中  aon_image = f'<img src="{aon_filename}.png" alt="Precedence Diagramming Method: AON">'  html = html.replace('<p>Precedence Diagramming Method: AON: <br/>[image]</p>',                      '<p>紧前关系绘图法: AON: <br/>' + aon_image + '</p>')    filename = datetime.now().strftime('%Y%m%d%H%M%S') + '.html'  with open(filename, 'w', encoding='utf-8') as f:      f.write(html)

2.活动类

程序名:activity.py

class Activity:      """      活动类,用于表示项目中的一个活动。        Attributes:        id (int): 活动的唯一标识符。          name (str): 活动的名称。          duration (int): 活动的持续时间。          predecessors (List[int]): 活动的前置活动列表,存储前置活动的id。          est (int): 活动的最早开始时间。          lst (int): 活动的最晚开始时间。          eft (int): 活动的最早完成时间。          lft (int): 活动的最晚完成时间。          tf (int): 活动的总浮动时间。          ff (int): 活动的自由浮动时间。          successors (List[int]): 活动的后继活动列表,存储后继活动的Activity对象。      """      def __init__(self, id: int, name: str, duration: int, predecessors: List[int]):          """          初始化活动对象。            Args:            id (int): 活动的唯一标识符。              name (str): 活动的名称。              duration (int): 活动的持续时间。              predecessors (List[int]): 活动的前置活动列表,存储前置活动的id。          """        self.id = id          self.name = name          self.duration = duration          self.predecessors = predecessors          self.est = None          self.lst = None          self.eft = None          self.lft = None          self.tf = None          self.ff = None          self.successors = []        def __str__(self):          return f"id: {self.id}, name: {self.name}, est: {self.est}, lst: {self.lst}, eft: {self.eft}, lft: {self.lft},"          + f"successors: {self.successors}"

3.任务列表JSON文件

文件名:activities.json

[    {    "id": 1,      "name": "A",      "duration": 2,      "predecessors": []    },    {      "id": 9,      "name": "A2",      "duration": 3,      "predecessors": []    },      {      "id": 10,      "name": "A3",      "duration": 2,      "predecessors": []    },    {      "id": 2,      "name": "B",      "duration": 3,      "predecessors": [        1,        9      ]    },    {      "id": 3,      "name": "C",      "duration": 4,      "predecessors": [        1      ]    },    {      "id": 4,      "name": "D",      "duration": 2,      "predecessors": [        2,10      ]    },    {      "id": 5,      "name": "E",      "duration": 3,      "predecessors": [        2      ]    },    {      "id": 6,      "name": "F",      "duration": 2,      "predecessors": [        3      ]    },    {      "id": 7,      "name": "G",      "duration": 3,      "predecessors": [        4,        5      ]    },    {      "id": 8,      "name": "H",      "duration": 2,      "predecessors": [        6,        7      ]    },    {      "id": 11,      "name": "H2",      "duration": 4,      "predecessors": [        6,        7      ]    }  ]

4.输出模板文件

<!DOCTYPE html>  <html>  <head>      <meta charset="UTF-8">      <title>PMP关键路径计算</title>      <style>        table {              border-collapse: collapse;              width: 100%;          }            th, td {              border: 1px solid black;              padding: 8px;              text-align: center;          }            th {              background-color: #4CAF50;              color: white;          }            .critical {              background-color: #ffcccc;          }      </style>  </head>  <body>  <h2>活动清单</h2>  <table>      <tr>          <th>ID</th>          <th>活动名</th>          <th>持续时间</th>          <th>紧前活动</th>          <th>紧后活动</th>          <th>最早开始时间EST</th>          <th>最早结束时间EFT</th>          <th>最晚开始时间LST</th>          <th>最晚结束时间LFT</th>          <th>总浮动时间TF</th>          <th>自由浮动时间FF</th>      </tr>      {% for activity in activities %}      <tr {% if activity in critical_path %}class="critical" {% endif %}>          <td>{{ activity.id }}</td>          <td>{{ activity.name }}</td>          <td>{{ activity.duration }}</td>          <td>            {% for predecessor in activity.predecessors %}              {% for act in activities %}              {% if act.id == predecessor %}              {{ act.name }}              {% endif %}              {% endfor %}              {% if not loop.last %}, {% endif %}              {% endfor %}          </td>          <td>            {% for successor in activity.successors %}              {% for act in activities %}                {% if act.id == successor %}              {{ act.name }}              {% endif %}              {% endfor %}              {% if not loop.last %}, {% endif %}              {% endfor %}          </td>          <td>{{ activity.est }}</td>          <td>{{ activity.eft }}</td>          <td>{{ activity.lst }}</td>          <td>{{ activity.lft }}</td>          <td>{{ activity.tf }}</td>          <td>{{ activity.ff }}</td>      </tr>      {% endfor %}  </table>  <p>关键路径是: {% for activity in critical_path %}{{ activity.name }}{% if not loop.last %} -> {% endif %}{% endfor      %}</p>  <p>项目总工期: {{ project_duration }}</p>  <p>Precedence Diagramming Method: AON: <br/>[image]</p>  <p>  <table>      <tr>          <th>执行过程</th>      </tr>      {% for i in log %}      <tr>          <td >{{i}}</td>      </tr>      {% endfor %}  </table>  </p>  </body>  </html>

Python如何实现关键路径和七格图计算”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: Python如何实现关键路径和七格图计算

本文链接: https://lsjlt.com/news/351658.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python如何实现关键路径和七格图计算
    本篇内容介绍了“Python如何实现关键路径和七格图计算”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1.主程序主程序主要实现了一个Proj...
    99+
    2023-07-05
  • ASP和Linux:如何处理路径关键字?
    在Web开发中,处理URL路径是非常常见的操作。在ASP和Linux中,处理路径关键字的方式有所不同。本文将介绍如何在ASP和Linux中处理路径关键字,并提供一些演示代码。 ASP中的路径关键字 在ASP中,路径关键字是一种特殊的URL...
    99+
    2023-09-16
    linux path 关键字
  • Python 和 Apache 如何实现路径同步?
    在软件开发中,经常会遇到需要将本地文件同步到服务器上的情况。Python 和 Apache 都提供了一些工具和技术来帮助实现路径同步。本文将介绍如何使用 Python 和 Apache 实现路径同步。 一、使用 Python 实现路径同步...
    99+
    2023-09-29
    apache path 同步
  • Java实现计算图中两个顶点的所有路径
    目录前言抽象数据模型代码实现数据模型计算两个顶点之间路径算法总结前言 最近公司的项目上有个需求,还挺有分享价值的,这边做个记录。需求大致如下,下面的一个流程图,点击条件线上选择的内容...
    99+
    2022-11-13
    Java计算顶点所有路径 Java计算顶点路径 Java计算路径
  • Python实现图的广度和深度优先路径搜索算法
    目录前言1. 图理论1.1 图的概念1.2 定义图1.3 图的抽象数据结构2. 图的存储实现2.1 邻接矩阵2.2 编码实现邻接矩阵3. 搜索路径3.1 广度优先搜索3.2 深度优先...
    99+
    2024-04-02
  • 如何利用 Python 和 Apache 实现路径同步?
    在现代的软件开发和运维中,我们经常需要在不同的机器或者不同的环境中同步文件和目录。在这个过程中,我们需要找到一个高效、可靠、易于管理的路径同步方案。本文将介绍如何利用 Python 和 Apache 实现路径同步,让你的工作更加高效。 一...
    99+
    2023-09-29
    apache path 同步
  • 如何在ASP中使用路径关键字和容器?
    ASP是一种广泛使用的Web开发技术,它可以让开发人员轻松地创建动态Web应用程序。其中,路径关键字和容器是ASP中非常重要的两个概念,它们可以帮助我们更好地组织Web应用程序的代码和内容。在本文中,我们将详细介绍如何在ASP中使用路径关键...
    99+
    2023-11-13
    path 关键字 容器
  • C++最短路径Dijkstra算法如何实现
    这篇文章主要介绍“C++最短路径Dijkstra算法如何实现”,在日常操作中,相信很多人在C++最短路径Dijkstra算法如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++最短路径Dijkstra...
    99+
    2023-07-05
  • 如何在Java中使用重定向和路径关键字?
    Java是一种广泛使用的编程语言,也是许多程序员的首选。在Java中,重定向和路径关键字是非常重要的概念,因为它们允许您在程序中控制输出和输入的位置。在本文中,我们将介绍如何在Java中使用重定向和路径关键字。 一、重定向 重定向是一种将输...
    99+
    2023-10-11
    重定向 关键字 path
  • Python怎么实现图的广度和深度优先路径搜索算法
    本篇内容主要讲解“Python怎么实现图的广度和深度优先路径搜索算法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python怎么实现图的广度和深度优先路径搜索算法”吧!前言图是一种抽象数据结构...
    99+
    2023-06-30
  • ASP和Linux的路径问题:如何避免关键字错误?
    在开发ASP网站或Linux应用程序时,路径问题是常见的错误之一。由于不同的操作系统和编程语言使用不同的路径分隔符和关键字,开发人员经常会遇到路径错误。这篇文章将介绍如何避免这些错误,并提供一些示例代码来帮助您更好地理解。 路径分隔符 ...
    99+
    2023-09-16
    linux path 关键字
  • PHP关键字和日志路径:如何让它们更高效?
    PHP是一种流行的服务器端脚本语言,它的应用范围非常广泛。在PHP编程中,关键字和日志路径是非常重要的方面。本文将介绍如何使用PHP关键字和日志路径来提高PHP的效率。 一、PHP关键字 1.1 PHP关键字的作用 PHP关键字是指在PHP...
    99+
    2023-10-06
    关键字 日志 path
  • 如何使用Python实现多路径迷宫
    小编给大家分享一下如何使用Python实现多路径迷宫,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!python的数据类型有哪些python的数据类型:1. 数字类...
    99+
    2023-06-14
  • 如何避免Java中的重定向和路径关键字问题?
    Java作为一门广泛使用的编程语言,其中有一些与重定向和路径相关的关键字问题。这些问题可能会在开发过程中引起一些困扰,但是通过一些简单的技巧,我们可以很容易地避免这些问题。在本文中,我们将详细介绍如何避免Java中的重定向和路径关键字问题。...
    99+
    2023-10-11
    重定向 关键字 path
  • Python如何实现GUI计算器
    本文小编为大家详细介绍“Python如何实现GUI计算器”,内容详细,步骤清晰,细节处理妥当,希望这篇“Python如何实现GUI计算器”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。效果可执行正常加减乘除相关运算...
    99+
    2023-07-04
  • Python如何实现距离和相似性计算
    本篇内容主要讲解“Python如何实现距离和相似性计算”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python如何实现距离和相似性计算”吧!欧氏距离也称欧几里得距离,是指在m维空间中两个点之间...
    99+
    2023-07-05
  • 如何实现前端表格自动计算
    这篇文章将为大家详细讲解有关如何实现前端表格自动计算,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。序言当我的团队进行税务系统模块开发的时候,我发现他们需要花费80%的时间去解决计算问题,尤其体现在表格(G...
    99+
    2023-06-08
  • ASP和LeetCode:如何在路径上实现并发?
    在计算机科学中,并发是一个重要的概念。它指的是在同一时间内处理多个任务的能力。在Web应用程序中,多个用户同时访问同一个页面,就需要实现并发处理。ASP是一种流行的Web开发框架,而LeetCode则是一种流行的算法练习平台。本文将介绍如...
    99+
    2023-09-21
    path leetcode 并发
  • Java中的重定向和路径关键字:如何调试和解决问题?
    Java是一种广泛使用的编程语言,它在开发Web应用程序、桌面应用程序、移动应用程序等方面都有广泛的应用。在Java编程中,重定向和路径关键字是必不可少的概念。重定向是将用户从一个URL重定向到另一个URL,而路径关键字则是指在Java应用...
    99+
    2023-10-11
    重定向 关键字 path
  • Python和Matlab怎么实现蚂蚁群算法求解最短路径
    这篇文章主要介绍“Python和Matlab怎么实现蚂蚁群算法求解最短路径”,在日常操作中,相信很多人在Python和Matlab怎么实现蚂蚁群算法求解最短路径问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”P...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作