本项目是基于Python语言和PyQt5的非官方性质、简易的中国铁路列车运行图系统。本代码的发布遵循GPLv3协议。在协议允许范围内,作者保留一切权利和最终解释权。
作者联系方式:mxy0268@qq.com
本项目在Windows 10 操作系统下开发和测试。
pyETRC项目的最初灵感来源和很多功能设置都来自由LGuo等前辈基于java语言开发的ETRC列车运行图系统。为致敬开发ETRC项目的前辈,本项目定名为**pyETRC列车运行图系统**,简称为pyETRC。
本系统支持读取和导出ETRC列车运行图系统的运行图文件(*.trc)。但由于两软件支持的功能有差异,读取和导出过程可能造成一定的信息损失。
本系统与ETRC列车运行图系统的实现各有侧重。相比本系统,ETRC列车运行图系统有如下的特色比较突出:
- 动态运行图。本系统不支持此功能。
- 对于精确到客运时刻的需求,自带较完善的线路数据库和车次时刻数据库。而本系统的线路和车次数据库依赖外部文件,且目前很不完整。
- 较完善的车次切片功能。
- 更简洁的操作和数据,或者说需要用户提供的数据更少。
相比ETRC列车运行图系统,本系统主要有如下的特色:
- 更准确、完整的数据支持,包括精确到秒的时刻和精确到三位小数的里程,允许上下行分设不同站点,标尺,天窗,交路等。
- 做了一定的效率优化,对较大运行图的执行效果相对更好。
- 提供了一些运行图快速微调工具和分析工具,例如调整某一站名(同时修改所有列车数据中引用的改站名),对比两运行图等。
- 在
3.0.0版本以后,提供了路网级的数据库管理模块,可以在更高层面上管理,更方便地查看、导出区段运行图。
两系统各有长短。因此我们建议,如果有需求,可以两套系统结合使用。
使用源代码方式运行本项目,需要具有以下环境。
-
Python3.7及以上的版本。开发所用的版本是3.7.4.注:本项目使用了大量的
f-string语法,该语法在Python3.6以后的版本才被支持。一些较新的代码中利用了Python3.7中dict键值对顺序与添加顺序一致的特性。如果使用3.6.*版本,这部分代码可能出现一些问题。如果使用3.6以下版本,则会报错。 -
下列的
Python第三方库,都可以用pip安装。PyQt5。必须。推荐使用5.10.1版本。xlwt。可选。在涉及输出.xls的操作中需要用到。xlrd。可选。在涉及读取.xls的操作中需要用到。xpinyin。可选。在本系统2.3.0版本之前的线路数据库排序中用到。NetworkX。可选。在3.0.0版本引入的路网数据管理中,用于以图论算法计算经由给出的路径。
-
作者开发的另一支持库
Timetable_new。该库需要使用github上的源代码安装。
在安装第三方库之前,需要配置好python环境,并将安装目录添加到PATH环境变量中,安装好pip库。相关教程可借助搜索引擎找到。
在shell中依次执行以下命令,无报错即可。
pip install PyQt5==5.10.1
pip install xlwt
pip install xlrd
pip install xpinyin
pip install networkx依次执行:
git clone https://github.com/CDK6182CHR/Timetable_new
cd Timetable_new
.\install.bat如果不用git,也可以在链接中下载并解压源代码,双击执行install.bat。
注:
install.bat文件适合windows操作系统。如果是其他操作系统,请自行更改相关代码。
install.bat的代码如下。
python setup.py build
python setup.py sdist
python setup.py install
pause运行main.py文件即可。
python main.py这部分作为简单的扩展开发指南。
本项目目前有3个可执行文件。
main.py是主程序运行入口。LineDB.py在2.3.0版本之后添加,是线路数据库维护系统的运行入口。RailNetManager.py在3.0.0版本之后添加,相对独立的路网级运行图数据库管理系统。
本项目主要依赖的是train_graph包。下面简要说明包结构。
以下类主要用于存储数据,一般不包含与PyQt交互的过程,一般涉及文件读写。如果要基于本项目进行扩展开发,推荐直接调用这些类。自2.3.1版本开始,这部分被封装为data包(package)。
line.py铁路线路数据对象。trainstation.py2.3.2版本开始新增的列车时刻表中车站类。继承dict实现,并可以按需要新增功能。linestation.py与trainstation.py类似,是对应Line中的车站数据。train.py列车对象。ruler.py标尺(时分标准)对象。forbid.py天窗数据对象。graph.py运行图对象,主要包括一条线路Line和一组车次Train。circuit.py车底交路对象。每个交路包含若干车次Train的序列,每个车次可以属于至多一个交路。route.py尚未开发完成的列车运行径路对象。
从大约2.4.0版本开始新增utility包,主要是通用的工具,以及对Qt既有组件的改进。目的在于,通过引进面向对象设计模式,提高代码复用能力。模块名、类名的前缀pe是pyETRC的简写,以区分于Q开头的Qt原生组件类。
-
peCellWidget.py是对QTableWidget中的(通过setCellWidget方法设置的)单元格组件的改进。原生的CellWidget从没有包含所处的单元格位置信息,不能在其获得焦点时,同步修改tableWidget.currentItem()等属性。此模块用于解决这个问题。其中,
PECellWidget类仅对RTTI有用,暂无其他意义。请不要直接实例化这个类。请使用CellWidgetFactory.new()工厂方法创建实例控件。 -
peCelledTable.py与PECellWidget配套使用的支持单元格组件定位的表格。主要是重写了setCellWidget方法。 -
peControlledTable.py是对需要支持增删、上下移动行的表格的封装。引进Adapter设计模式。本身继承的是QWidget,但将有关方法调用转发给内置的QTableWidget实例对象。实例化时,也可以通过参数
meta指定创建QTableWidget的某个子类实例,本项目中目前用到的是PECelledTable。
在调用了PyQt的模块中,有一部分与底层的绘图(绘制运行线)直接相关,它们是本项目的核心,这里单独列出来。
GraphicWidget.py核心绘图窗口。继承QGraphicsWidget实现,运行图的铺画在本类中完成。trainItem.py列车运行线对象。包含了主要的运行线铺画逻辑。是GraphicWidget类中的图元。在2.0.0版本之后,每一趟列车(一个Train对象)允许拥有多个TrainItem对象。
与运行图数据操作相关的类主要有两种形式,一是以停靠面板方式,长期显示和有效的面板;二是以对话框为主的操作交互界面。前者包含了本系统的核心操作功能,下面简要说明停靠面板类及其相关类。
circuitWidget.py交路编辑停靠面板,快捷键为ctrl+4。colorWidget.py默认颜色设置面板。在1.4.0之后的版本中,被集成到运行图设置面板中。configWidget.py运行图设置面板。包含关于运行图铺画比例、运行图备注等设置。快捷键为ctrl+GcurrentWidget.py当前车次编辑面板。包含对当前选中的列车的车次、运行线管理、时刻表编辑等功能。这是最集中的修改列车信息的面板。快捷键为ctrl+I。forbidWidget.py天窗编辑面板。快捷键为ctrl+1。interactiveTimetable.py交互式时刻表。与时刻表面板布局类似,但支持更改列车时刻表, 且更改立即生效,立即体现在运行图上。lineWidget.py线路编辑面板,可编辑线路名称和站表。快捷键为ctrl+X。rulerWidget.py标尺编辑面板。可管理标尺(增删改)。标尺数据属于线路数据的一部分,但其编辑功能是独立的。快捷键为ctrl+B。trainTimetable.py当前车次时刻表面板。将当前车次时刻表以只读方式、占用宽度尽量小的形式展示,建议可以长期开启的停靠面板。快捷键为ctrl+Y。trainWidget.py车次列表面板。展示基本的车次信息,并可增删。快捷键为ctrl+C。typeWidget.py显示类型面板。提供简洁的界面来快速选择显示或不显示某一类列车的运行线。快捷键为ctrl+L。typeDialog.py是由ConfigWidget调用的列车类型管理对话框。管理默认情况下用于识别列车种类的车次正则表达式,是否为客车。
其他文件名形如*Dialog.py *Widget.py的大多数是与操作相关的窗口。下面简单说明。
mainGraphWindow.py主窗口类。程序启动后首先产生本类实例。batchChangeStationDialog.py批量调整站名,快捷键为ctrl+shift+U。changeStationDialog.py修改站名。快捷键为ctrl+U。changeTrainIntervalDialog.py列车区间时刻表重排功能。快捷键为ctrl+shift+R。correctionWidget.py提供对当前车次时刻表重新排序的功能。快捷键为ctrl+V。detectWidget.py根据标尺,推定列车区间通过时刻。快捷键为ctrl+2。exchangeIntervalDialog.py区间换线。交换两车次指定区间的运行线。快捷键为ctrl+5。helpDialog.py内置的简明功能表。快捷键为F1。importTrainDialog.py导入车次对话框。快捷键为ctrl+D。intervalCountDialog.py区间对数表。快捷键为ctrl+3。intervalTrainDialog.py区间车次表。快捷键为ctrl+shift+3。intervalWidget.py当前车次区间性质计算。支持计算选定区间的停站次数,旅速等。快捷键为ctrl+shift+Q。rulerPaint.py标尺排图向导类。按标尺,指定开始站点和方向、各站停站时长,自动铺画运行线。这是本系统亮点功能之一。快捷键为ctrl+R。stationTimetable.py车站时刻表,快捷键为ctrl+E。stationvisualize.py车站时刻表可视化。根据车站时刻等信息,提供一种股道安排方案并可视化。trainComparator.py两车次时刻对照。快捷键为ctrl+shift+W。trainDatabase.py为导入车次功能中的相关设定提供支持。功能快捷键为ctrl+D。graphDiffDialog.py提供两运行图数据对比功能。2.3.2版本新增。trainDiffDialog.py提供两车次对比功能,主要由GraphDiffDialog调用。2.3.2版本新增。
2.3.0版本彻底的重构了线路数据库这个模块,因而重新设计了有关代码,写成一个包(package),名为linedb。它主要包含这些类(文件)。
category.py线路“分类”的类。新版本的线路数据库中支持对库中的线路做任意多级别的分类,其中分类的数据部分 由本类实现。本类继承dict。lineLib.py是Category的派生类,在顶层分类的基础上添加了文件io部分,并作为与GUI部分通信的主要接口。lineTreeWidget.py对话框左侧的文件树类。继承QtWidgets.QTreeWidget实现。关于线路、类别的增删逻辑,全都在本类实现。lineLibDialog.py线路数据库对话框。在主界面中由ctrl+H快捷键直接打开的就是这个对话框。
自3.0.0版本新增路网级数据库管理模块,简称“路网管理模块”。本模块提供比区段运行图高一个级别的运行图管理,可以直接管理线路数据库和车次数据库,并不局限于既有线路,而是可以(基于铁路网的有向图模型)导出有数据的任一区段运行图。本部分的代码构成 railnet包,从根目录下的RailNetManager.py可以运行。
考虑到NetworkX编译代价问题,目前win64发行版暂不包含这部分功能。
railnet.py铁路网络的数据模型。主要基于第三方库NetworkX建立有向图模型,并能导出区段运行图。线路(Line)数据模型与主程序一致。trainManager.py车次数据库管理页面,主要显示和维护*.pyetdb文件包含的数据。sliceManager.py区段(切片)运行图管理页面,显示当前程序打开的所有区段运行图,并可输入经由,生成新的区段运行图。mainNetWindow.py路网管理模块的主窗口。
由于交路部分涉及到的窗口较多,现在将和交路有关的窗口都合并到circuitwidgets包中,除circuitWidget.py停靠面板类之外。
circuitDialog.py单个交路数据的编辑对话框,在CircuitWidget中发起编辑操作后弹出。circuitDiagram.py绘制交路图的面板。在CircuitDialog中发起查看交路图操作后产生。circuitDiagramWidget.py绘制交路图的对话框。ParseTextDialog.py解析交路文本的对话框。从CircuitDialog中的按钮触发,只解析一个交路。BatchParseCircuit.py批量解析交路文本的对话框。对应快捷键ctrl+P。AddTrainWidget.py添加车次的对话框,主要是封装的外层QTabWidget。AddRealTrain.py添加实体车次,是AddTrainWidget中的一个页面。AddVirtualTrain.py添加虚拟车次,是AddTrainWidget中的一个页面。所谓虚拟车次,是指仅有车次,但不指向本线一个具体Train对象的车次,它仅仅为了保持交路完整性而存在。但如果导入了那个车次的Train对象,则可以尝试识别虚拟车次。
trainFilter.py车次筛选器。在全局用快捷键ctrl+shift+L启动“高级显示车次设置”时,会调用本类。在车次编辑、车站时刻表等处也有使用。
pyETRC依赖的数据文件全部是基于JSON格式的。所有文件,无论扩展名,都是基于UTF-8的无签名(不带BOM,否则会出错)编码。使用Python标准库json读取和写入。下面简要说明本系统的各种文件所要求的格式、内容及其生成方法。
包含上次打开的文件,系统默认运行图文件,上次关闭时的停靠面板等。一般情况下不需要手动处理。
生成方法:程序每次正常退出时都会生成。参见:mainGraphWindow.py中的函数mainGraphWindow._checkSystemSetting()。
包含用户设置的系统默认参数,例如运行图铺画大小,颜色,列车种类等。当用户新创建或者打开的运行图中不包含配置数据时,将自动使用(并复制)这一套数据。当文件缺失或某些字段缺失时,系统配置由Graph.checkSysConfig()函数补全,见graph.py。
生成方式:当程序正常运行时,用户若在“系统默认设置”(ctrl+shift+G)面板中保存数据,或者在“线路数据库”(ctrl+H)中更改默认文件时,自动保存config.json文件。
如果要手工读取,请使用Graph.readSysConfig(),手工保存请使用Graph.saveSysConfig(),调用其数据请使用Graph.sysConfigData(),该函数返回dict类型。
包含列车运行图所需的所有数据,此部分的支持见graph.py。文件最核心的数据结构大致是:
{
"line":{
"name":"宁蓉线成渝段",
"stations":[
{
"zhanming":"重庆北渝利场",
"licheng":0.0,
"dengji":0,
},
]
},
"trains":[
{
"checi":[full,down,up],
"timetable":[
{
"zhanming":"成都东",
"ddsj":"00:00:00",
"cfsj":"00:01":
},
...
],
},
...
]
}最基础的数据是line和trains两个字段,分别是线路基础数据和车次数据表。前者由Line对象管理,后者由一系列Train对象管理。
如果要通过代码调用,请使用Graph.loadGraph()方法来读取文件,Graph.save()方法来保存。数据的修改、使用,请参阅Graph Line Train等数据类的类定义。
2.3.0以后版本建议车次数据库文件使用*.pyetdb后缀,但其结构与*.pyetgr的列车运行图文件是完全一致的,这意味着完全可以用pyETRC直接打开它们,但由于车次数据库往往有非常多的数据量和不完整的线路数据,打开它们通常是很慢且没有意义的过程,故新版本中予以区分。
这里只介绍2.3.0以后版本所用的线路数据库文件。新版本可以读取老版本的文件,但反之一般不行。线路数据库文件为任意多级嵌套的js对象,或者Python字典。字段名是分类名或者线名,而字段值属于一条线,当且仅当它存在str类型的name字段和list类型的stations字段。这意味着,如果在同一个分类下给两个子线路或者分类其名为"name"和"stations",将导致不可预知的后果。
正常运行时,使用“线路数据库”(ctrl+H)功能中的“保存”将创建或者保存*.pyetlib文件。
如果要通过代码操作,相关支持在linedb/lineLib.py文件中。使用LineLib.loadLib()方法来读取文件,使用LineLib.saveLib()方法来保存文件;更多具体的操作,请阅读lineLib.py和category.py的源代码。
在3.0.0版本后添加的网络数据库管理模块中使用。保存的是工作区配置的信息,具体包括:
- 打开的线路数据库文件名(
*.pyetlib) - 打开的车次数据库文件名(
*.pyetdb) - 打开的所有运行图切片的经由表。
但不会包含上述信息里的具体内容。
也就是说,只记录线路数据库文件名,但不包含线路数据库的内容。
一般情况下,没有必要编程处理这种数据格式。