简介这是一份基于 Python 实现的公交换乘查询系统源码面向想掌握数据结构与算法、GUI 编程及工程化文件组织的学习者。系统围绕公交数据读取、站点/线路建模、换乘路径搜索和结果可视化展开核心算法涉及 Dijkstra 或 A*适合作为课程设计与实战练习的参考。压缩包共 62 个文件、约 2.24MB以 .py 源码为主辅以 .ui 界面定义、.png/.drawio 设计图、.xlsx/.csv 公交数据表格以及 .ipynb 调试笔记代码分层清晰便于阅读和二次开发。目前已有 1111 人浏览学习对希望理解 PyQt 交互界面、邻接表建模和最短路径计算的同学有直接帮助。从资源中可提取完整的查询流程与排错思路尤其能得到地图创建、单条路线绘制、信息展示等模块的拆分方式是一份可运行的 Python 中型项目范例。1. 这份 Python 公交换乘系统源码能直接跑起一个 Qt 界面的换乘查询工具先给结论这不是教学 Demo而是一套带完整界面、带 Excel 数据、带算法落地的 PyQt5 项目。你下载下来之后能直观感受到「数据层读 Excel → 构建站点邻接表 → Dijkstra 算换乘 → 界面展示路径」的全过程。源码包里从MainWind.py到MapCreater.py到Calculator.py的分工非常干净基本上一个文件对应一个职责这对想拆着学的人来说是好事——你可以只读算法部分也可以只改界面互不纠缠。适合三类人正在做课设的本科生、想抄一个「图算法 GUI」组合项目的 Python 入门者、以及需要在本地快速验证公交/站点换乘逻辑的从业者。它的上限不在代码量而在于把「邻接表 Dijkstra Qt 界面」这三样东西串成了一条完整链路这是很多零碎教程给不了你的。2. 源码包拆解先盘清文件结构和数据流再谈怎么改2.1 从文件命名反推架构每个 Python 文件的职责边界这个 zip 解压之后你第一眼会看到一堆.py、.ui、.drawio、.xlsx和.csv混在一起。别慌先把文件按职责分成四类后面动手改的时候就不会迷路。界面层有MainWind.py、PyUi2.py、InputCtrl.py配合ui_main.py、ui_infowind.py、ui_workingwind.py这几个由.ui文件编译出来的 Python 文件。MainWind.py是主窗口逻辑负责把用户输入的起点终点传给算法再拿返回值去刷新界面。数据处理层是ReadExcel.py和ExcelReader.py负责读公交.xlsx和测试.xlsx。这两个文件名字很像但你别混——ExcelReader.py更底层封装了openpyxl或pandas的读取细节ReadExcel.py更偏业务读出来之后要整理成站点列表和线路列表。核心算法层就是Calculator.py和MapCreater.py。MapCreater.py负责把 Excel 里的原始数据变成邻接表Calculator.py在这张邻接表上跑 Dijkstra。OneRoadMapCreater.py是单线路的图构建器适合处理一条公交线路内部的顺序关系。辅助文件里反向.csv、正向.csv是已经导出的站点关系数据公交.xlsx是原始数据源。公交换乘.drawio和Map创建.drawio是架构图用 draw.io 打开能看到作者当时的设计思路建议先看这两个图再读代码。2.2 Excel 数据读取站点表结构怎么设计代码才不用返工这个项目的数据入口是 Excel 而不是数据库这是个务实的选择。公交数据的特点是「线路多、站点名重复多、字段固定」用 Excel 维护比建表快得多。你打开公交.xlsx之后一般会看到两类 sheet一类是线路表记录线路 ID、线路名称、首末班时间另一类是站点表记录每个站点的经度纬度、所属线路、站序。ExcelReader.py的常见做法是建立三个类LineInfo、SiteInfo、TransferInfo分别对应线路基础信息、站点地理信息、换乘关系。读取时核心逻辑是「按线路分组组内按站序排序」这样才能保证后面建邻接表时方向是对的。# ExcelReader.py 核心结构示意 from openpyxl import load_workbook class ExcelReader: def __init__(self, file_path): self.wb load_workbook(file_path, data_onlyTrue) self.site_map {} # 站点名 - SiteInfo def load_sites(self, sheet_name): ws self.wb[sheet_name] for row in ws.iter_rows(min_row2, values_onlyTrue): site_id, site_name, lon, lat row[0], row[1], row[2], row[3] self.site_map[site_name] { site_id: site_id, lon: float(lon), lat: float(lat) } return self.site_map这段代码里有几个值得注意的点。用data_onlyTrue打开是因为 Excel 里可能有公式如果单元格是公式而不是计算后的值直接读取会拿到公式字符串而不是数字。iter_rows(min_row2)表示默认第一行是标题这个约定在改数据时不要破坏。经纬度转float这步很关键如果单元格里混入空值或文本这里会直接抛异常后面会讲怎么处理。2.3 邻接表构建MapCreater 里边的逻辑才是换乘系统的地基公交换乘的本质是「图上的最短路问题」但站点之间的关系不是简单的两两连接要区分「同线路相邻站点」和「跨线路换乘站点」。MapCreater.py干的就是这件事——把 Excel 里的线路站点数据变成一张有向有权图。常见做法是把所有站点作为图的顶点相邻两站之间连一条边权值用「距离或时间」。跨线路换乘怎么处理把换乘也建模成一条边权值是换乘代价比如等车 5 分钟。这样 Dijkstra 跑出来的最短路径天然就是「换乘次数少 总时间短」的方案。# MapCreater.py 邻接表构建核心逻辑 def build_adjacency_list(self, lines_data): lines_data 是多条线路的站点顺序列表 返回邻接表: { site_name: [(neighbor, weight), ...] } adj {} for line_name, sites in lines_data.items(): for i in range(len(sites) - 1): cur, nxt sites[i], sites[i 1] distance self._calc_distance(cur, nxt) adj.setdefault(cur, []).append((nxt, distance)) adj.setdefault(nxt, []).append((cur, distance)) return adj def _calc_distance(self, site_a, site_b): # 简化计算直接用经纬度欧氏距离放大一万倍模拟米 lon1, lat1 site_a[lon], site_a[lat] lon2, lat2 site_b[lon], site_b[lat] return ((lon1 - lon2) ** 2 (lat1 - lat2) ** 2) ** 0.5 * 10000建边的时候「双向都会加」这个不能漏。公交线路虽然是有方向的但大多数情况下反方向也能坐车所以按无向图处理是合理的。_calc_distance里放大 10000 倍是为了把经纬度差值放大到接近米的量级实际项目中可以用geopy.distance.geodesic算真实距离后续可以自己换掉。3. 换乘算法核心Dijkstra 的工程化实现不只有最短路径3.1 为什么选 Dijkstra 而不是 A* 或 BFS公交换乘场景下Dijkstra 比 BFS 更合适因为它天然支持「边权不同」——从 A 站到 B 站 1.2 公里从 B 站到 C 站 0.8 公里BFS 会认为这两段同样代价但实际上步行距离和乘车距离差别很大。Dijkstra 会真实累加距离算出来的路径更可信。A* 虽然更快但需要设计启发式函数。在公交换乘这个场景里启发式可以用「直线距离 / 平均车速」来估计剩余时间可是公交的行驶速度受路况影响很大启发式不准会导致 A* 结果退化。所以我建议入门先用 Dijkstra数据量不大时性能完全够——几千个站点的图Dijkstra 跑一次高峰期也就在几十到几百毫秒级别。3.2 Python 里写 Dijkstra 的正确姿势优先队列 前驱数组图算法最怕写成教科书式的大白板——把所有节点遍历一遍找最小值那个复杂度是 O(V²)。真实项目里要用heapq维护优先队列把复杂度压到 O((VE)logV)。这个源码里的Calculator.py采取的也是这个路线而且它多做了一个操作记录每个节点的前驱节点这样最后才能把完整路径回溯出来。# Calculator.py Dijkstra 最短路主体 import heapq def dijkstra(self, adj, start, end): # dist: 起点到每个节点的最短距离 # prev: 记录前驱用于回溯路径 dist {node: float(inf) for node in adj} prev {node: None for node in adj} dist[start] 0 pq [(0, start)] # (累计距离, 节点名) while pq: current_dist, node heapq.heappop(pq) if current_dist dist[node]: continue # 跳过过期条目这是堆优化的关键 if node end: break for neighbor, weight in adj.get(node, []): new_dist current_dist weight if new_dist dist[neighbor]: dist[neighbor] new_dist prev[neighbor] node heapq.heappush(pq, (new_dist, neighbor)) # 回溯路径 path [] step end while step is not None: path.append(step) step prev[step] path.reverse() return path, dist[end]这里三个细节值得重点看。一是current_dist dist[node]这个判断因为heapq里同一个节点可能被推入多次弹出时要用这个条件跳过那些已经被更新过的旧记录不加这个判断程序也不会崩但性能会退化。二是prev[neighbor] node这行的位置它必须在更新dist的同时更新prev放在if外面会导致回溯路径出错。三是path.reverse()因为回溯是从终点往前走的不反转拿到的就是反序显示的时候站点顺序是反的。3.3 从最短路径到换乘方案把纯节点序列还原成「坐几路车到哪站」这个项目里最有价值的部分不是 Dijkstra 本身而是Calculator.py里把节点序列还原成换乘指引的逻辑。因为图里的边有两种来源——同一线路相邻站点、跨线路换乘——必须区分出来才能告诉用户「坐 32 路到市民广场站下车换乘 101 路」。常见做法是对路径上相邻的三元组(prev, cur, next)做判断如果prev和cur在一条线路上cur和next也在同一线路上且线路相同说明还在同一辆车如果prev到cur的线路与cur到next的线路不同说明cur是换乘点。def decode_route(self, path, line_map): line_map: (site_a, site_b) - line_name表示这两个站点之间的边属于哪条线路 if not path: return [] segments [] current_line None current_start path[0] for i in range(len(path) - 1): edge (path[i], path[i 1]) line line_map.get(edge) if line ! current_line: if current_line is not None: segments.append({ line: current_line, start: current_start, end: path[i] }) current_line line current_start path[i] segments.append({ line: current_line, start: current_start, end: path[-1] }) return segmentsline_map这个数据结构在建图的时候就要同步维护好它是换乘还原的关键索引。用它来判断换乘点比直接比对线路名更稳定——因为一个站点可能同时属于 3 条线路单靠站点名判断是不靠谱的。end字段取path[i]而不是path[i1]因为换乘点是上一段的终点、下一段的起点不能重复算。4. PyQt5 界面对接从 .ui 文件到 MainWind 的运行逻辑4.1 .ui 文件怎么编译成 .py为什么项目里同时存在两种文件源码包里能看到WorkingWind.ui、InfoWind.ui、main.ui这种以.ui结尾的文件还有对应的ui_main.py、ui_infowind.py。.ui是 Qt Designer 里画出来的界面描述文件本质是 XML。运行时你不能直接import一个.ui文件需要先转换成 Python 代码。转换命令是固定的用pyuic5工具pyuic5 main.ui -o ui_main.py pyuic5 WorkingWind.ui -o ui_workingwind.py pyuic5 InfoWind.ui -o ui_infowind.py改界面的正确流程是在 Qt Designer 里打开.ui文件改布局 → 保存 → 重新执行pyuic5生成新的.py→ 运行主程序。有两条血泪经验一是不要手工去改生成的ui_*.py下次重新生成时你的改动全没了二是如果你改了.ui文件但没跑pyuic5程序运行的还是旧界面排查了半天发现是没编译这种翻车非常常见。4.2 MainWind.py 的骨架信号连接、键盘输入、结果面板更新MainWind.py是整个程序的粘合层。它做的事情很典型初始化时加载 Excel 数据、构建邻接表、把 UI 上的按钮和输入框跟具体函数绑定。换乘查询的主流程是取起点输入框和终点输入框的文本 → 调用Calculator计算 → 把结果写到列表控件或文本面板。# MainWind.py 核心交互逻辑 class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui ui_main.Ui_MainWindow() self.ui.setupUi(self) # 加载数据并建图 self.reader ExcelReader(公交.xlsx) self.site_map self.reader.load_sites(站点表) self.lines_data self.reader.load_lines(线路表) self.adj MapCreater().build_adjacency_list(self.lines_data) self.calculator Calculator() # 绑定信号 self.ui.btn_search.clicked.connect(self.on_search) self.ui.edit_start.returnPressed.connect(self.on_search) def on_search(self): start self.ui.edit_start.text().strip() end self.ui.edit_end.text().strip() if start not in self.site_map or end not in self.site_map: self.ui.result_panel.setText(站点名称不存在请检查输入) return path, total_dist self.calculator.dijkstra(self.adj, start, end) segments self.calculator.decode_route(path, self.line_map) # 把换乘方案格式化输出 lines [] for seg in segments: lines.append(f{seg[line]}: {seg[start]} → {seg[end]}) self.ui.result_panel.setText(\n.join(lines))returnPressed这个信号值得单独说。普通用户输完起点后会习惯性按回车你只绑定按钮点击事件的话按回车没反应体验很扣分。edit_start和edit_end都可以绑returnPressed但要注意两个输入框的回车都要触发查询否则在终点框按回车还是没反应。站点名校验这步不能省。用户输错一个字算法层会报 KeyError界面层应该直接拦截返回「站点名称不存在」。还有strip()处理用户误输入的空格这种小细节在黑盒测试时最容易暴露问题。4.3 信息展示窗口InfoWind 做了什么InfoWind.ui对应的ui_infowind.py和ui_infowind.py是用来展示站点详情或线路详情的辅助窗口。它的典型用途是用户在主窗口看到路径结果后双击某个站点弹窗显示这个站点的所有经过线路和相邻站点——相当于一套简化的站点信息查询。实现逻辑比较简单遍历前面建好的line_map和adj过滤出包含目标站点的边格式化显示到表格控件里。5. 避坑指南运行时报错、数据格式翻车、界面刷新失效的排查手册5.1 现象import ui_main 报 ModuleNotFoundError这个错在解压后第一次运行时最常见。原因是项目里的.ui编译产物不全或者你换了一台机器但pyuic5编译出来的ui_*.py文件没被正确解压出来。解决方法是打开终端进到源码目录手动执行pyuic5 main.ui -o ui_main.py把三个.ui文件全部重新编译一遍。不要试图手动复制网上零散的转换代码直接跑工具最稳。如果提示pyuic5 不是内部或外部命令说明 PyQt5 没装全执行pip install pyqt5 pyqt5-tools后再试。5.2 现象读 Excel 时报错InvalidFileException或zipfile.BadZipFile这个报错很唬人字面意思是「文件不是合法的 Excel」。实际原因大概率是你把.xlsx文件当成普通 Excel 97-2003 格式处理了或者文件被 WPS 等软件以兼容模式另存过内部结构变了。解决步骤分两步。先用openpyxl.load_workbook的read_onlyTrue模式试读如果不行用 LibreOffice 或 WPS 把文件另存为「xlsx 标准格式」再放回目录。我一般会在try里加一层格式探测遇到旧格式.xls就转用pandas.read_excel(enginexlrd)。import os import pandas as pd def load_data(file_path): ext os.path.splitext(file_path)[1].lower() if ext .xlsx: return pd.read_excel(file_path, engineopenpyxl) elif ext .xls: return pd.read_excel(file_path, enginexlrd) else: raise ValueError(f不支持的格式: {ext})这个兼容处理是真实项目里必须有的因为数据文件在团队间传递时格式经常会走样。5.3 现象跑 Dijkstra 时结果路径缺失或路径在起点终点之间「跳站」出现「跳站」最常见的原因是把 Excel 里的公交线路站点顺序读反了或者邻接表构建时用了set导致站点顺序丢失。Python 的set是无序的如果你用set去重站点再建边遍历顺序就不可控表现出来就是路径时对时错。解决方法是确保邻接表用dict加list而且要按 Excel 里的「站序」字段排序后再建边。更隐蔽的一个原因是 Excel 里有空行iter_rows读取时把空行当成了站点导致边连接到了None。排查时打印邻接表里每个节点的邻居数量如果出现单个节点邻居超过 6 个以上多半是数据污染了。5.4 现象界面按钮点击后无反应程序也没报错这是 PyQt 最常见的玄学问题。原因百分之八十是信号连接时函数名拼错或者按钮的clicked信号连接到了一个没有pyqtSlot()装饰器的函数上。PyQt 的信号机制对函数签名是敏感的函数参数不匹配时信号会静默失败。排查方法是在按钮点击事件里先加一行print(clicked)确认事件有没有进函数。如果 print 打不出来去看connect那行代码对照 UI 文件里的objectName是否正确。还有一个小坑如果主窗口类里定义了on_search函数但把self.ui.btn_search.clicked.connect(self.ui.on_search)写成连 UI 对象的方法这个self.ui组件上是没有自定义on_search方法的事件自然触发不了。5.5 现象换乘方案显示「同一条线路分了三段」这是decode_route里的索引边界问题。路径还原时line_map里查不到某一段边对应的线路名就会把它当成换乘点切一刀。根因是在构建line_map时只记录了正向的(A, B)但邻接表是双向的(B, A)这条边没被记录导致找回时get((B, A))拿到None。解决方法是建边时双向都写入line_mapline_map[(cur, nxt)] line_name line_map[(nxt, cur)] line_name很多从教程抄来的换乘项目都会漏这一步因为单向路径不会暴露问题一旦终点反向查询就翻车。6. 进阶技巧把可视化验证做成习惯让算法黑匣子对你透明源码包里已经带了Dijkstra.png、邻接表.png、公交路径.png这些截图这说明作者当时也做了可视化验证。我的习惯是每次改完数据或调完参数强制跑一遍「三层验证」——数据层打印站点数和边数算法层打印起点到终点的 dist 全部结果界面层截图比对路径是否合理。你可以在Calculator.py的dijkstra函数返回前加一行调试输出# 调试用查看每个站点最终被更新的最短距离 for node, d in sorted(dist.items(), keylambda x: x[1]): if d float(inf): print(f{node}: {d:.2f})这行代码在排错时非常管用。如果某站点的距离异常大说明邻接表里它的邻居太少如果起点直连终点的距离比换乘还长说明权重设置有问题。还有一个实用技巧是把dist结果导出成 CSV 用 Excel 打开横向比较各个站点的可达性比在控制台里翻快得多。从那以后我每次跑公交换乘项目无论数据多简单都强制走一遍这个三层验证流程不然不放心。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
context-mode:用状态管理终结开发环境上下文串味 最近在整理开发环境时,我越来越觉得“上下文”是效率的头号杀手。比如你正开着三个终端窗口,一个在调A项目的接口,一个在翻B项目的日志,还有个窗口里挂着刚才跑起来的本地服务。来回切几次,你很快就会忘掉当前目录是哪… · 2026/11/3 15:32:59
共享充电宝技术方案对比:外包开发与SaaS的实战分析 1. 项目背景:共享充电宝行业的两种技术路线之争 共享充电宝行业近年来经历了从野蛮生长到精细化运营的转变。在这个发展过程中,技术实现方案逐渐分化为两种典型路径:一种是采用外包团队定制开发(常见报价在2万元左右)&… · 2026/11/3 12:01:35
极值点、驻点与鞍点:导数为零后的判定流程 "极值点、驻点"这四个字,我在不同场合被问过不下几十次。学生问的是考试里怎么判、怎么列表、怎么写结论;做算法的同事问的是损失函数为什么卡在某个点不动了、明明梯度为零却还在往上飘;做工程优化的朋友问的是参数扫到某个值附近… · 2026/11/6 0:33:42
电工证成绩在哪里查询官方回应来了 30天内查不到说明你被坑了 电工证成绩在哪里查询官方回应来了 30天内查不到说明你被坑了 怕考不过白交培训费?电工证成绩查不到别急, 河南省应急管理厅 官方回应来了, 电工证成绩在哪里查询 有明确流程,关键看是不是正规渠道报考,否则钱花了证也拿不到。… · 2026/11/9 15:33:29
天津外地的电工证复审复审延期怎么办理 外地电工在天津复审怕没学历?这3步解决你所有担忧 你不是一个人,学历不高怕报不上名,复审又卡在天津这关,担心流程复杂、手续繁多,甚至还怕被拒绝。但别急, 天津外地的电工证复审 其实有章可循,而且这个证值不值得考,真不是你想象的那样。… · 2026/11/9 15:33:29
低压电工证题库打印版值不值得考?开封房建工程从业者真实经历告诉你 低压电工证题库打印版值不值得考?开封房建工程从业者真实经历告诉你 怕考不过白交培训费?这是很多人第一次听说要考低压电工证时的顾虑。特别是在开封这样的地方,房建工程从业者很多都是从工地起步,工资低、证书多,但真正能带来稳定收入的,还是那些含金… · 2026/11/9 15:33:29
高压电工证包括哪些?多少钱?工地太忙根本没时间复习考试 高压电工证包括哪些?多少钱?工地太忙根本没时间复习考试 工地太忙根本没时间复习考试,高压电工证包括哪些,你可能根本不清楚,更别提怎么准备考试了。别急,这篇文章直接给你讲清楚高压电工证的范围、费用以及你真正需要知道的备考技巧。… · 2026/11/9 15:33:29
电工证中级是什么内容 2026河南最新政策你必须知道 电工证中级是什么内容 2026河南最新政策你必须知道 你是不是也在纠结, 不知道去哪报名怕被中介坑 ?别急,今天咱们就来聊聊【电工证中级是什么内容】,特别是2026河南最新政策下,你该知道的一切,别再被那些“包过”“快速拿证”的话术忽悠了。… · 2026/11/9 15:33:29
弋阳县电工证真实经历:学历不高怕报不上名?看看他们怎么逆袭的 弋阳县电工证真实经历:学历不高怕报不上名?看看他们怎么逆袭的 你是不是也担心自己学历不高,根本没法报考弋阳县电工证?其实很多人都和你一样,担心学历、担心年龄、甚至担心能不能顺利通过考试。但只要你愿意迈出第一步,就一定能找到适合自己的路。今天… · 2026/11/9 15:32:29
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/9 2:05:04
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/9 3:11:26
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/9 5:24:17
电工证不需要考复审延期怎么办理 通过率揭秘 电工证不需要考复审延期怎么办理 通过率揭秘 不知道去哪报名怕被中介坑?电工证不需要考复审延期怎么办理?最近不少学员都在问这个问题,尤其是刚拿到证的新手,对复审流程一知半解,生怕被培训机构套路。别担心,今天咱们就来聊聊电工证复审的真相,尤其是… · 2026/11/9 0:00:54
砀山哪里考电工证的多 电工证报考别踩坑 郑州报考避坑指南 砀山哪里考电工证的多 电工证报考别踩坑 郑州报考避坑指南 工地太忙根本没时间复习考试,这几乎是所有一线施工人员的共同心声。尤其是像电工这样的特种作业,操作证不仅关系到能不能上工,还直接关系到工资高低和职业发展。可偏偏,很多人连怎么报名、去哪… · 2026/11/9 0:00:54
南方电网电工作业含金量高吗?应届生怎么考怎么干? 南方电网电工作业含金量高吗?应届生怎么考怎么干? 工地太忙根本没时间复习考试,这不是个别现象,而是很多刚毕业的工程类学生面临的现实。南方电网电工作业的证书,听起来门槛不低,但 含金量… · 2026/11/9 0:00:54
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:23:35
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:23:58
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:25:08