简体   繁体   English

如何以编程方式将单元格添加到 IPython 或 Jupyter 笔记本?

[英]How can I programatically add cells to and IPython or Jupyter notebook?

This question has been asked a few times before, however the accepted answers suggest that the asker was looking to do something different to me.这个问题之前已经被问过几次,但是接受的答案表明提问者想要做一些与我不同的事情。

I am trying to create an IPython widget that creates and runs a new cell.我正在尝试创建一个创建和运行新单元格的 IPython 小部件。 The purpose of this is to provide a similitude between the widget and the IPython cells.这样做的目的是在小部件和 IPython 单元之间提供相似性。

For example: I would like to have a SelectorWidget that fills and runs the next cell with 'cell magic'.例如:我想要一个 SelectorWidget,它用“cell magic”填充并运行下一个单元格。

But I am facing the problem that (please correct me if I am wrong) IPython does not keep an internal data-structure of cells.但我面临的问题是(如果我错了,请纠正我)IPython 不保留单元的内部数据结构。

It seems to me that the only way to modify a new cell is with:在我看来,修改新单元格的唯一方法是:

In [1]: get_ipython().set_next_input('1')

In [2]: 1

I'd just like to be able to run that line automatically.我只想能够自动运行该行。 But I don't think that's possible with set_next_input given it's just a hack on top of readline .但我认为set_next_input不可能做到这一点,因为它只是readline之上的一个hack。


I can do what I want to do with:我可以做我想做的事:

 display(Javacript('''
 
 var code = IPython.notebook.insert_cell_at_bottom('code'))
 code.execute()

 '''))

But it just feels so incredibly wrong to be driving input that way as it depends on a web-browser to do something quite simple.但是以这种方式驱动输入感觉非常错误,因为它依赖于网络浏览器来做一些非常简单的事情。 And you're depending on an external event loop.而且您依赖于外部事件循环。

The equivalent to doing this on the command line would be starting a background thread/process/corountine that has access to the IPython prompt's stdin and that would accept tasks from the foreground thread to write to the forground thread's own stdin.在命令行上执行此操作的等价物是启动一个后台线程/进程/ stdin ,它可以访问 IPython 提示符的标准输入,并接受来自前台线程的任务以写入前台线程自己的标准输入。 It's possible to do but has the same problem as the Jupyter methodology because it's specific to the input mechanism of IPython.可以这样做,但与 Jupyter 方法有相同的问题,因为它特定于 IPython 的输入机制。

Looking at the problem, I can't seem to find a fully synchronous method without JavaScript either.看问题,我似乎也找不到没有 JavaScript 的完全同步方法。

I have found the run_cell and run_cell_magic work to avoid the JavaScript example but fail on producing the cell with the outputs.我发现run_cellrun_cell_magic可以避免 JavaScript 示例,但无法生成具有输出的单元。

The closest I could come to was:我最接近的是:

from IPython import get_ipython

def make_cell_run_code(code):
    ipython = get_ipython()
    ipython.set_next_input(code)
    ipython.run_cell(code)

code = "print('test')"
make_cell_run_code(code)

which avoids the JavaScript but still has cells out of sync.这避免了 JavaScript 但仍然有单元不同步。

With JavaScript, we can use negative indexing to ensure the same cell is excecuted.使用 JavaScript,我们可以使用负索引来确保执行相同的单元格。

from IPython.display import Javascript, display

def create_and_excecute_code_cell(code=''):
    display(Javascript("""
        var code = IPython.notebook.insert_cell_at_bottom('code');
        code.set_text("{0}");
        Jupyter.notebook.execute_cells([-1]);
    """.format(code)))

create_and_excecute_code_cell("%time x=0")

Which I cribbed from here .从这里抄来的。 Your JavaScript snippet didn't work for me.您的 JavaScript 片段对我不起作用。

Other avenues are to look deeper into IPython.core.interactiveshell.InteractiveShell其他途径是深入IPython.core.interactiveshell.InteractiveShell

One possible but not portable solution is to monkey-patch the JavaScript that handles the set_next_input kernel message.一种可能但不可移植的解决方案是对处理set_next_input kernel 消息的 JavaScript 进行猴子补丁。

(function (){
    IPython.CodeCell.prototype._handle_set_next_input = function (payload) {
        var data = {
            cell: this,
            text: payload.text,
            replace: payload.replace,
            clear_output: payload.clear_output,
        execute: payload.execute
        };
        this.events.trigger('set_next_input.Notebook', data);
    };

    var that = IPython.notebook;
    // Out with the old, in with the new
    that.events.unbind("set_next_input")
    that.events.on('set_next_input.Notebook', function (event, data) {
            if (data.replace) {
                data.cell.set_text(data.text);
                if (data.clear_output !== false) {
                  // default (undefined) is true to preserve prior behavior
                  data.cell.clear_output();
                }
            } else {
                var index = that.find_cell_index(data.cell);
                var new_cell = that.insert_cell_below('code',index);
                new_cell.set_text(data.text);
            }
        
        if (data.execute && data.execute === true) {
        new_cell.execute();
        } else {
        that.dirty = true;
        }
    });
})()

This can be then called in the notebook like so:然后可以像这样在笔记本中调用它:

with open('run_next_cell_patch.js') as f:
    # Monkey patch Jupyter with
    # set_next_input(run=True) functionality
    display(Javascript(f.read()))


from IPython import get_ipython
ip = get_ipython()
ip.payload_manager.write_payload(
        dict(
            source='set_next_input',
            text='print(1)',
            replace=False,
            execute=True
        )

)

That will create a new cell below with print(1) and it's output executed.这将在下面创建一个带有print(1)的新单元格,并执行 output。 This is a bit better than inserting JavaScript each time to take control of the input.这比每次插入 JavaScript 来控制输入要好一点。

Problematically this only works on Jupyter notebook .有问题的是,这只适用于 Jupyter notebook Still looking for a solution for Jupyter Lab.仍在为 Jupyter Lab 寻找解决方案。 On the plus side, I think this could be fairly simple to solve in the plain terminal.从好的方面来说,我认为这在普通终端中解决起来相当简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM