diff --git a/IPython/html/static/notebook/js/cell.js b/IPython/html/static/notebook/js/cell.js index 514144f..032b413 100644 --- a/IPython/html/static/notebook/js/cell.js +++ b/IPython/html/static/notebook/js/cell.js @@ -147,10 +147,7 @@ var IPython = (function (IPython) { } if (this.code_mirror) { this.code_mirror.on('blur', function(cm, change) { - // Check if this unfocus event is legit. - if (!that.should_cancel_blur()) { - $([IPython.events]).trigger('command_mode.Cell', {cell: that}); - } + $([IPython.events]).trigger('command_mode.Cell', {cell: that}); }); } }; @@ -331,18 +328,7 @@ var IPython = (function (IPython) { return false; } }; - - /** - * Determine whether or not the unfocus event should be aknowledged. - * - * @method should_cancel_blur - * - * @return results {bool} Whether or not to ignore the cell's blur event. - **/ - Cell.prototype.should_cancel_blur = function () { - return false; - }; - + /** * Focus the cell in the DOM sense * @method focus_cell diff --git a/IPython/html/static/notebook/js/codecell.js b/IPython/html/static/notebook/js/codecell.js index c799681..44d4c79 100644 --- a/IPython/html/static/notebook/js/codecell.js +++ b/IPython/html/static/notebook/js/codecell.js @@ -358,21 +358,6 @@ var IPython = (function (IPython) { return false; }; - /** - * Determine whether or not the unfocus event should be aknowledged. - * - * @method should_cancel_blur - * - * @return results {bool} Whether or not to ignore the cell's blur event. - **/ - CodeCell.prototype.should_cancel_blur = function () { - // Cancel this unfocus event if the base wants to cancel or the cell - // completer is open or the tooltip is open. - return IPython.Cell.prototype.should_cancel_blur.apply(this) || - (this.completer && this.completer.is_visible()) || - (IPython.tooltip && IPython.tooltip.is_visible()); - }; - CodeCell.prototype.select_all = function () { var start = {line: 0, ch: 0}; var nlines = this.code_mirror.lineCount(); @@ -508,6 +493,23 @@ var IPython = (function (IPython) { return data; }; + /** + * handle cell level logic when a cell is unselected + * @method unselect + * @return is the action being taken + */ + CodeCell.prototype.unselect = function () { + var cont = IPython.Cell.prototype.unselect.apply(this); + if (cont) { + // When a code cell is usnelected, make sure that the corresponding + // tooltip and completer to that cell is closed. + IPython.tooltip.remove_and_cancel_tooltip(true); + if (this.completer !== null) { + this.completer.close(); + } + } + return cont; + }; IPython.CodeCell = CodeCell; diff --git a/IPython/html/static/notebook/js/completer.js b/IPython/html/static/notebook/js/completer.js index 3fd7245..14e13b6 100644 --- a/IPython/html/static/notebook/js/completer.js +++ b/IPython/html/static/notebook/js/completer.js @@ -73,7 +73,6 @@ var IPython = (function (IPython) { var Completer = function (cell) { - this._visible = false; this.cell = cell; this.editor = cell.code_mirror; var that = this; @@ -85,15 +84,9 @@ var IPython = (function (IPython) { }); }; - Completer.prototype.is_visible = function () { - // Return whether or not the completer is visible. - return this._visible; - }; - Completer.prototype.startCompletion = function () { // call for a 'first' completion, that will set the editor and do some - // special behaviour like autopicking if only one completion availlable - // + // special behavior like autopicking if only one completion available. if (this.editor.somethingSelected()) return; this.done = false; // use to get focus back on opera @@ -221,17 +214,37 @@ var IPython = (function (IPython) { } } - this.complete = $('
').addClass('completions'); - this.complete.attr('id', 'complete'); - - // Currently webkit doesn't use the size attr correctly. See: - // https://code.google.com/p/chromium/issues/detail?id=4579 - this.sel = $('') - .attr('multiple', 'true') - .attr('size', Math.min(10, this.raw_result.length)); - this.complete.append(this.sel); - this._visible = true; - $('body').append(this.complete); + if (!this.visible) { + this.complete = $('').addClass('completions'); + this.complete.attr('id', 'complete'); + + // Currently webkit doesn't use the size attr correctly. See: + // https://code.google.com/p/chromium/issues/detail?id=4579 + this.sel = $('') + .attr('tabindex', -1) + .attr('multiple', 'true'); + this.complete.append(this.sel); + this.visible = true; + $('body').append(this.complete); + + //build the container + var that = this; + this.sel.dblclick(function () { + that.pick(); + }); + this.sel.focus(function () { + that.editor.focus(); + }); + this._handle_keydown = function (cm, event) { + that.keydown(event); + }; + this.editor.on('keydown', this._handle_keydown); + this._handle_keypress = function (cm, event) { + that.keypress(event); + }; + this.editor.on('keypress', this._handle_keypress); + } + this.sel.attr('size', Math.min(10, this.raw_result.length)); // After everything is on the page, compute the postion. // We put it above the code if it is too close to the bottom of the page. @@ -249,28 +262,9 @@ var IPython = (function (IPython) { this.complete.css('left', left + 'px'); this.complete.css('top', top + 'px'); - - //build the container - var that = this; - this.sel.dblclick(function () { - that.pick(); - }); - this.sel.blur(this.close); - this.sel.keydown(function (event) { - that.keydown(event); - }); - this.sel.keypress(function (event) { - that.keypress(event); - }); - + // Clear and fill the list. + this.sel.text(''); this.build_gui_list(this.raw_result); - - this.sel.focus(); - IPython.keyboard_manager.disable(); - // Opera sometimes ignores focusing a freshly created node - if (window.opera) setTimeout(function () { - if (!this.done) this.sel.focus(); - }, 100); return true; }; @@ -288,20 +282,16 @@ var IPython = (function (IPython) { }; Completer.prototype.close = function () { - this._visible = false; - if (this.done) return; this.done = true; - $('.completions').remove(); - IPython.keyboard_manager.enable(); + $('#complete').remove(); + this.editor.off('keydown', this._handle_keydown); + this.editor.off('keypress', this._handle_keypress); + this.visible = false; }; Completer.prototype.pick = function () { this.insert(this.raw_result[this.sel[0].selectedIndex]); this.close(); - var that = this; - setTimeout(function () { - that.editor.focus(); - }, 50); }; Completer.prototype.keydown = function (event) { @@ -312,16 +302,10 @@ var IPython = (function (IPython) { if (code == keycodes.enter) { CodeMirror.e_stop(event); this.pick(); - } // Escape or backspace - else if (code == keycodes.esc) { + } else if (code == keycodes.esc || code == keycodes.backspace) { CodeMirror.e_stop(event); this.close(); - this.editor.focus(); - - } else if (code == keycodes.backspace) { - this.close(); - this.editor.focus(); } else if (code == keycodes.tab) { //all the fastforwarding operation, //Check that shared start is not null which can append with prefixed completion @@ -332,8 +316,6 @@ var IPython = (function (IPython) { this.insert(sh); } this.close(); - CodeMirror.e_stop(event); - this.editor.focus(); //reinvoke self setTimeout(function () { that.carry_on_completion(); @@ -341,10 +323,23 @@ var IPython = (function (IPython) { } else if (code == keycodes.up || code == keycodes.down) { // need to do that to be able to move the arrow // when on the first or last line ofo a code cell - event.stopPropagation(); + CodeMirror.e_stop(event); + + var options = this.sel.find('option'); + var index = this.sel[0].selectedIndex; + if (code == keycodes.up) { + index--; + } + if (code == keycodes.down) { + index++; + } + index = Math.min(Math.max(index, 0), options.length-1); + this.sel[0].selectedIndex = index; + } else if (code == keycodes.left || code == keycodes.right) { + this.close(); } }; - + Completer.prototype.keypress = function (event) { // FIXME: This is a band-aid. // on keypress, trigger insertion of a single character. @@ -358,26 +353,16 @@ var IPython = (function (IPython) { // don't handle keypress if it's not a character (arrows on FF) // or ENTER/TAB if (event.charCode === 0 || - code == keycodes.enter || - code == keycodes.tab + code == keycodes.tab || + code == keycodes.enter ) return; - var cur = this.editor.getCursor(); - var completion = { - str: String.fromCharCode(event.which), - type: "introspection", - from: cur, - to: cur, - }; - this.insert(completion); - this.close(); this.editor.focus(); setTimeout(function () { that.carry_on_completion(); }, 50); }; - IPython.Completer = Completer; return IPython; diff --git a/IPython/html/static/notebook/less/completer.less b/IPython/html/static/notebook/less/completer.less index 6f633de..1e564a2 100644 --- a/IPython/html/static/notebook/less/completer.less +++ b/IPython/html/static/notebook/less/completer.less @@ -17,6 +17,7 @@ font-family: @monoFontFamily; font-size: 110%; color: @textColor; + width: auto; } .completions select option.context { diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 3c01e1d..36563fd 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -1,7 +1,3 @@ -.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0} -.clearfix:after{clear:both} -.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} -.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block} audio,canvas,video{display:inline-block;*display:inline;*zoom:1} audio:not([controls]){display:none} @@ -856,6 +852,10 @@ a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decorati .show{display:block} .invisible{visibility:hidden} .affix{position:fixed} +.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0} +.clearfix:after{clear:both} +.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0} +.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} @-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden} .visible-phone{display:none !important} .visible-tablet{display:none !important} @@ -1497,7 +1497,7 @@ p{margin-bottom:0} .celltoolbar input[type=checkbox]{margin:0;margin-left:4px;margin-right:4px} .celltoolbar .ui-button{border:none;vertical-align:top;height:20px;min-width:30px} .completions{position:absolute;z-index:10;overflow:hidden;border:1px solid #ababab;border-radius:4px;-webkit-box-shadow:0 6px 10px -1px #adadad;-moz-box-shadow:0 6px 10px -1px #adadad;box-shadow:0 6px 10px -1px #adadad} -.completions select{background:#fff;outline:none;border:none;padding:0;margin:0;overflow:auto;font-family:monospace;font-size:110%;color:#000} +.completions select{background:#fff;outline:none;border:none;padding:0;margin:0;overflow:auto;font-family:monospace;font-size:110%;color:#000;width:auto} .completions select option.context{color:#0064cd} #menubar .navbar-inner{min-height:28px;border-top:1px;border-radius:0 0 4px 4px} #menubar .navbar{margin-bottom:8px} diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js index 110b06c..04a1fd8 100644 --- a/IPython/html/static/widgets/js/widget.js +++ b/IPython/html/static/widgets/js/widget.js @@ -83,6 +83,7 @@ function(WidgetManager, _, Backbone){ break; case 'display': this.widget_manager.display_view(msg, this); + this.trigger('displayed'); break; } }, diff --git a/IPython/html/static/widgets/js/widget_selectioncontainer.js b/IPython/html/static/widgets/js/widget_selectioncontainer.js index 12cd9cc..2031356 100644 --- a/IPython/html/static/widgets/js/widget_selectioncontainer.js +++ b/IPython/html/static/widgets/js/widget_selectioncontainer.js @@ -29,40 +29,44 @@ define(["widgets/js/widget"], function(WidgetManager){ this.model.on('change:_children', function(model, value, options) { this.update_children(model.previous('_children'), value); }, this); + this.model.on('change:selected_index', function(model, value, options) { + this.update_selected_index(model.previous('selected_index'), value, options); + }, this); + this.model.on('change:_titles', function(model, value, options) { + this.update_titles(value); + }, this); + this.model.on('displayed', function() { + this.update_titles(); + }, this); }, - - update: function(options) { - // Update the contents of this view - // - // Called when the model is changed. The model may have been - // changed by another view or by a state update from the back-end. - if (options === undefined || options.updated_view != this) { - // Set tab titles - var titles = this.model.get('_titles'); - var that = this; - _.each(titles, function(title, page_index) { - var accordian = that.containers[page_index]; - if (accordian !== undefined) { - accordian - .find('.accordion-heading') - .find('.accordion-toggle') - .text(title); - } - }); - // Set selected page - var selected_index = this.model.get("selected_index"); - if (0 <= selected_index && selected_index < this.containers.length) { - _.each(this.containers, function(container, index) { - if (index==selected_index) { - container.find('.accordion-body').collapse('show'); - } else { - container.find('.accordion-body').collapse('hide'); - } - }); + update_titles: function(titles) { + // Set tab titles + if (!titles) { + titles = this.model.get('_titles'); + } + + var that = this; + _.each(titles, function(title, page_index) { + var accordian = that.containers[page_index]; + if (accordian !== undefined) { + accordian + .find('.accordion-heading') + .find('.accordion-toggle') + .text(title); + } + }); + }, + + update_selected_index: function(old_index, new_index, options) { + // Only update the selection if the selection wasn't triggered + // by the front-end. It must be triggered by the back-end. + if (options === undefined || options.updated_view != this) { + this.containers[old_index].find('.accordion-body').collapse('hide'); + if (0 <= new_index && new_index < this.containers.length) { + this.containers[new_index].find('.accordion-body').collapse('show'); } } - return AccordionView.__super__.update.apply(this); }, update_children: function(old_list, new_list) { @@ -103,7 +107,7 @@ define(["widgets/js/widget"], function(WidgetManager){ // Calling model.set will trigger all of the other views of the // model to update. - that.model.set("selected_index", index, {updated_view: this}); + that.model.set("selected_index", index, {updated_view: that}); that.touch(); }) .text('Page ' + index) @@ -120,12 +124,7 @@ define(["widgets/js/widget"], function(WidgetManager){ accordion_inner.append(view.$el); this.update(); - - // Stupid workaround to close the bootstrap accordion tabs which - // open by default even though they don't have the `in` class - // attached to them. For some reason a delay is required. - // TODO: Better fix. - setTimeout(function(){ that.update(); }, 500); + this.update_titles(); }, }); WidgetManager.register_widget_view('AccordionView', AccordionView); diff --git a/IPython/html/tests/widgets/widget_multicontainer.js b/IPython/html/tests/widgets/widget_selectioncontainer.js similarity index 97% rename from IPython/html/tests/widgets/widget_multicontainer.js rename to IPython/html/tests/widgets/widget_selectioncontainer.js index 2004306..c9219e7 100644 --- a/IPython/html/tests/widgets/widget_multicontainer.js +++ b/IPython/html/tests/widgets/widget_selectioncontainer.js @@ -104,5 +104,10 @@ casper.notebook_test(function () { this.execute_cell_then(index, function(index){ this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based 'selected_index property updated with tab change.'); + + var is_collapsed = this.evaluate(function(s){ + return $(s + ' div.accordion-group:nth-child(2) a').hasClass('collapsed'); // 1 based + }, {s: multicontainer2_query}); + this.test.assertEquals(is_collapsed, false, 'Was tab actually opened?'); }); }); \ No newline at end of file diff --git a/IPython/lib/demo.py b/IPython/lib/demo.py index 7834cae..435670d 100644 --- a/IPython/lib/demo.py +++ b/IPython/lib/demo.py @@ -304,7 +304,7 @@ class Demo(object): self.src_blocks = src_blocks # also build syntax-highlighted source - self.src_blocks_colored = map(self.ip_colorize,self.src_blocks) + self.src_blocks_colored = list(map(self.ip_colorize,self.src_blocks)) # ensure clean namespace and seek offset self.reset() diff --git a/IPython/parallel/apps/launcher.py b/IPython/parallel/apps/launcher.py index 67a53b5..10e5a46 100644 --- a/IPython/parallel/apps/launcher.py +++ b/IPython/parallel/apps/launcher.py @@ -593,7 +593,7 @@ class SSHLauncher(LocalProcessLauncher): def _send_file(self, local, remote): """send a single file""" - remote = "%s:%s" % (self.location, remote) + full_remote = "%s:%s" % (self.location, remote) for i in range(10): if not os.path.exists(local): self.log.debug("waiting for %s" % local) @@ -605,8 +605,8 @@ class SSHLauncher(LocalProcessLauncher): check_output(self.ssh_cmd + self.ssh_args + \ [self.location, 'mkdir', '-p', '--', remote_dir] ) - self.log.info("sending %s to %s", local, remote) - check_output(self.scp_cmd + [local, remote]) + self.log.info("sending %s to %s", local, full_remote) + check_output(self.scp_cmd + [local, full_remote]) def send_files(self): """send our files (called before start)""" diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index ba1442f..45d2c78 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -268,7 +268,7 @@ test_sections['qt'].requires('zmq', 'qt', 'pygments') # html: sec = test_sections['html'] -sec.requires('zmq', 'tornado', 'requests') +sec.requires('zmq', 'tornado', 'requests', 'sqlite3') # The notebook 'static' directory contains JS, css and other # files for web serving. Occasionally projects may put a .py # file in there (MathJax ships a conf.py), so we might as diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py index 66d62f9..adc2ea1 100644 --- a/IPython/testing/iptestcontroller.py +++ b/IPython/testing/iptestcontroller.py @@ -19,8 +19,8 @@ test suite. from __future__ import print_function import argparse +import json import multiprocessing.pool -from multiprocessing import Process, Queue import os import shutil import signal @@ -28,7 +28,7 @@ import sys import subprocess import time -from .iptest import have, test_group_names as py_test_group_names, test_sections +from .iptest import have, test_group_names as py_test_group_names, test_sections, StreamCapturer from IPython.utils.path import compress_user from IPython.utils.py3compat import bytes_to_str from IPython.utils.sysinfo import get_sys_info @@ -127,13 +127,14 @@ class PyTestController(TestController): #: str, Python command to execute in subprocess pycmd = None - def __init__(self, section): + def __init__(self, section, options): """Create new test runner.""" TestController.__init__(self) self.section = section # pycmd is put into cmd[2] in PyTestController.launch() self.cmd = [sys.executable, '-c', None, section] self.pycmd = "from IPython.testing.iptest import run_iptest; run_iptest()" + self.options = options def setup(self): ipydir = TemporaryDirectory() @@ -145,6 +146,14 @@ class PyTestController(TestController): # This means we won't get odd effects from our own matplotlib config self.env['MPLCONFIGDIR'] = workingdir.name + # From options: + if self.options.xunit: + self.add_xunit() + if self.options.coverage: + self.add_coverage() + self.env['IPTEST_SUBPROC_STREAMS'] = self.options.subproc_streams + self.cmd.extend(self.options.extra_args) + @property def will_run(self): try: @@ -211,48 +220,77 @@ class JSController(TestController): os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir2', u'sub ∂ir 1b'))) # start the ipython notebook, so we get the port number + self.server_port = 0 self._init_server() - self.cmd.append('--port=%s' % self.server_port) - + if self.server_port: + self.cmd.append("--port=%i" % self.server_port) + else: + # don't launch tests if the server didn't start + self.cmd = [sys.executable, '-c', 'raise SystemExit(1)'] + def print_extra_info(self): print("Running tests with notebook directory %r" % self.nbdir.name) @property def will_run(self): - return all(have[a] for a in ['zmq', 'tornado', 'jinja2', 'casperjs']) + return all(have[a] for a in ['zmq', 'tornado', 'jinja2', 'casperjs', 'sqlite3']) def _init_server(self): "Start the notebook server in a separate process" - self.queue = q = Queue() - self.server = Process(target=run_webapp, args=(q, self.ipydir.name, self.nbdir.name)) - self.server.start() - self.server_port = q.get() + self.server_command = command = [sys.executable, + '-m', 'IPython.html', + '--no-browser', + '--ipython-dir', self.ipydir.name, + '--notebook-dir', self.nbdir.name, + ] + # ipc doesn't work on Windows, and darwin has crazy-long temp paths, + # which run afoul of ipc's maximum path length. + if sys.platform.startswith('linux'): + command.append('--KernelManager.transport=ipc') + self.stream_capturer = c = StreamCapturer() + c.start() + self.server = subprocess.Popen(command, stdout=c.writefd, stderr=subprocess.STDOUT) + self.server_info_file = os.path.join(self.ipydir.name, + 'profile_default', 'security', 'nbserver-%i.json' % self.server.pid + ) + self._wait_for_server() + + def _wait_for_server(self): + """Wait 30 seconds for the notebook server to start""" + for i in range(300): + if self.server.poll() is not None: + return self._failed_to_start() + if os.path.exists(self.server_info_file): + self._load_server_info() + return + time.sleep(0.1) + print("Notebook server-info file never arrived: %s" % self.server_info_file, + file=sys.stderr + ) + + def _failed_to_start(self): + """Notebook server exited prematurely""" + captured = self.stream_capturer.get_buffer().decode('utf-8', 'replace') + print("Notebook failed to start: ", file=sys.stderr) + print(self.server_command) + print(captured, file=sys.stderr) + + def _load_server_info(self): + """Notebook server started, load connection info from JSON""" + with open(self.server_info_file) as f: + info = json.load(f) + self.server_port = info['port'] def cleanup(self): - self.server.terminate() - self.server.join() + try: + self.server.terminate() + except OSError: + # already dead + pass + self.server.wait() + self.stream_capturer.halt() TestController.cleanup(self) -def run_webapp(q, ipydir, nbdir, loglevel=0): - """start the IPython Notebook, and pass port back to the queue""" - import os - import IPython.html.notebookapp as nbapp - import sys - sys.stderr = open(os.devnull, 'w') - server = nbapp.NotebookApp() - args = ['--no-browser'] - args.extend(['--ipython-dir', ipydir, - '--notebook-dir', nbdir, - '--log-level', str(loglevel), - ]) - # ipc doesn't work on Windows, and darwin has crazy-long temp paths, - # which run afoul of ipc's maximum path length. - if sys.platform.startswith('linux'): - args.append('--KernelManager.transport=ipc') - server.initialize(args) - # communicate the port number to the parent process - q.put(server.port) - server.start() def prepare_controllers(options): """Returns two lists of TestController instances, those to run, and those @@ -273,28 +311,13 @@ def prepare_controllers(options): test_sections['parallel'].enabled = False c_js = [JSController(name) for name in js_testgroups] - c_py = [PyTestController(name) for name in py_testgroups] - - configure_py_controllers(c_py, xunit=options.xunit, - coverage=options.coverage, subproc_streams=options.subproc_streams, - extra_args=options.extra_args) + c_py = [PyTestController(name, options) for name in py_testgroups] controllers = c_py + c_js to_run = [c for c in controllers if c.will_run] not_run = [c for c in controllers if not c.will_run] return to_run, not_run -def configure_py_controllers(controllers, xunit=False, coverage=False, - subproc_streams='capture', extra_args=()): - """Apply options for a collection of TestController objects.""" - for controller in controllers: - if xunit: - controller.add_xunit() - if coverage: - controller.add_coverage() - controller.env['IPTEST_SUBPROC_STREAMS'] = subproc_streams - controller.cmd.extend(extra_args) - def do_run(controller, buffer_output=True): """Setup and run a test controller. diff --git a/docs/source/interactive/reference.rst b/docs/source/interactive/reference.rst index c054d8a..bfcde8f 100644 --- a/docs/source/interactive/reference.rst +++ b/docs/source/interactive/reference.rst @@ -26,19 +26,6 @@ files for each profile, and the files look like :file:`ipython_config.py` or which defaults to :file:`$HOME/.ipython`. For Windows users, :envvar:`HOME` resolves to :file:`C:\\Users\\{YourUserName}` in most instances. - -Eventloop integration ---------------------- - -Previously IPython had command line options for controlling GUI event loop -integration (-gthread, -qthread, -q4thread, -wthread, -pylab). As of IPython -version 0.11, these have been removed. Please see the new ``%gui`` -magic command or :ref:`this section