如何实现可更新下一个单元格内容的IPython %load_next行魔法命令
可更新下一个单元格内容的%load_next IPython魔法命令
修正后完整实现
from IPython.display import display, HTML, Javascript from IPython.core.magic import Magics, magics_class, line_magic from pathlib import Path import json @magics_class class MyMagics(Magics): @line_magic def load_next(self, line): # 读取目标文件内容 new_content = Path(line).read_text() # 转换为JS可识别的字符串格式 content_js = json.dumps(new_content) js_script = rf"""<script> const newContent = {content_js}; let allCells, selectionClass, jupyterEnv; if (document.getElementById('notebook-container')) {{ allCells = document.getElementById('notebook-container').children; selectionClass = /\bselected\b/; jupyterEnv = 'notebook'; }} else if (document.getElementsByClassName('jp-Notebook-cell').length) {{ allCells = document.getElementsByClassName('jp-Notebook-cell'); selectionClass = /\bjp-mod-selected\b/; jupyterEnv = 'lab'; }} else {{ console.log('Unknown Environment'); return; }} // 定位当前选中的单元格索引 let currentIndex = -1; for (let i = 0; i < allCells.length; i++) {{ if(selectionClass.test(allCells[i].getAttribute('class'))){{ currentIndex = i; break; }} }} if (currentIndex === -1) return; let targetCell; // 已存在下一个单元格则直接复用,不存在则新建 if (currentIndex < allCells.length - 1) {{ targetCell = allCells[currentIndex + 1]; }} else {{ if (jupyterEnv === 'notebook') {{ Jupyter.notebook.insert_cell_below('code'); targetCell = allCells[currentIndex + 1]; }} else if (jupyterEnv === 'lab') {{ const notebook = window.jupyterapp.activeWidget.content; notebook.insertCellBelow(); notebook.selectNext(); targetCell = allCells[currentIndex + 1]; }} }} // 写入新内容到目标单元格 if (jupyterEnv === 'notebook') {{ Jupyter.notebook.get_cell(currentIndex + 1).set_text(newContent); }} else if (jupyterEnv === 'lab') {{ targetCell.querySelector('.jp-Editor-host').innerText = newContent; // 触发JupyterLab的内容变更识别 const event = new Event('input', {{ bubbles: true }}); targetCell.querySelector('.jp-Editor-host textarea').dispatchEvent(event); }} // 清除当前魔法命令单元格的输出 window.setTimeout(function(){{ if(jupyterEnv === 'lab') {{ allCells[currentIndex].setAttribute('class', allCells[currentIndex].getAttribute('class') + ' jp-mod-noOutputs'); const outputArea = allCells[currentIndex].getElementsByClassName('jp-OutputArea jp-Cell-outputArea')[0]; if (outputArea) outputArea.innerHTML = ''; }} else if(jupyterEnv === 'notebook'){{ const outputArea = allCells[currentIndex].getElementsByClassName('output')[0]; if (outputArea) outputArea.innerHTML = ''; }} }}, 20); </script>""" display(HTML(js_script)) ip = get_ipython() ip.register_magics(MyMagics)
核心修改说明
- 移除原实现中Python侧的
set_next_input调用,将所有单元格操作逻辑迁移到JS侧执行,解决了Python和JS异步执行顺序不一致的问题 - 新增下一个单元格存在性判断:如果当前命令所在单元格已有紧邻的下一个单元格,直接更新该单元格内容,无需删除重建也不会新增单元格;仅当不存在下一个单元格时才自动新建
- 完整兼容Jupyter Notebook、JupyterLab双环境的单元格操作和内容写入逻辑
使用方法
和原有使用逻辑完全一致,在单元格中执行:%load_next 你的代码文件路径
多次运行该命令时,只会更新紧邻下一个单元格的内容,不会生成多余单元格。
内容的提问来源于stack exchange,提问作者7beggars_nnnnm
相关产品推荐
相关产品推荐

