Repository: ipython/ipython

Issue:
Inconsistent %matplotlib qt behavior
Evaluating a cell beginning with the `%matplotlib qt` magic and immediately following with commands to [Mayavi](https://github.com/enthought/mayavi) cause the kernel to freeze. The Mayavi window opens correctly and responds to user input, but the cell never yields control back to the user.

If instead, a cell containing `%matplotlib qt` in isolation is run, it returns without issue. Subsequently issuing Mayavi commands in a separate cell work as expected, returning immediately.

[This notebook](http://nbviewer.ipython.org/gist/jabooth/8770104) gives a minimal demonstration of the problem, as well as some further diagnostic information.

Note that the problem is confirmed to exist on OS X on a [recent master](https://github.com/ipython/ipython/commit/9b8c058d4ef1667b292fea5e5a365aa545da6112) and on Linux (thanks @fperez)

Note that this bug only occurs the first time the `%matplotlib qt` command is executed - subsequent calls (e.g. after switching to `%matplotlib inline` and back to `%matplotlib qt` with commands immediately following in the qt enabled cell) work fine.

```
{'codename': 'Work in Progress',
 'commit_hash': '9b8c058',
 'commit_source': 'installation',
 'default_encoding': 'UTF-8',
 'ipython_path':     '/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/IPython',
 'ipython_version': '2.0.0-dev',
 'os_name': 'posix',
 'platform': 'Darwin-13.0.2-x86_64-i386-64bit',
 'sys_executable':     '/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python',
 'sys_platform': 'darwin',
 'sys_version': '2.7.5 (default, Oct 25 2013, 08:11:09) \n[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)]'}
```



Discussion:
It's worth noting that the problem can also be seen from the qt console, so it's not notebook-specific.

I experienced a similar behavior trying to open a Qt dialog.

I normally use "inline" but I need to switch to "qt" mode to launch the dialog (otherwise the dialog does not launch). 

A while ago I asked a SO question that apparently got unnoticed:
http://stackoverflow.com/questions/20790926/ipython-notebook-open-select-file-with-gui-qt-dialog

I also made a notebook that reproduces the problem:
http://nbviewer.ipython.org/gist/anonymous/8138346

I can confirm this also happens using Matplotlib on current master.

This is because the kernel eventloop integration happens in between iterations (cell executions), so a blocking call is made between telling the kernel to hook up with the eventloop, and actually handing off control.  I think we can improve how that's handled, I will look into it.

Great, thanks @minrk 


Resolving patch:
diff --git a/IPython/kernel/zmq/eventloops.py b/IPython/kernel/zmq/eventloops.py
--- a/IPython/kernel/zmq/eventloops.py
+++ b/IPython/kernel/zmq/eventloops.py
@@ -251,5 +251,5 @@ def enable_gui(gui, kernel=None):
             )
     loop = loop_map[gui]
     if loop and kernel.eventloop is not None and kernel.eventloop is not loop:
-            raise RuntimeError("Cannot activate multiple GUI eventloops")
+        raise RuntimeError("Cannot activate multiple GUI eventloops")
     kernel.eventloop = loop
diff --git a/IPython/kernel/zmq/ipkernel.py b/IPython/kernel/zmq/ipkernel.py
--- a/IPython/kernel/zmq/ipkernel.py
+++ b/IPython/kernel/zmq/ipkernel.py
@@ -60,7 +60,7 @@ class Kernel(Configurable):
     def _eventloop_changed(self, name, old, new):
         """schedule call to eventloop from IOLoop"""
         loop = ioloop.IOLoop.instance()
-        loop.add_timeout(time.time()+0.1, self.enter_eventloop)
+        loop.add_callback(self.enter_eventloop)
 
     shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
     shell_class = Type(ZMQInteractiveShell)
@@ -250,7 +250,11 @@ def dispatch_shell(self, stream, msg):
     
     def enter_eventloop(self):
         """enter eventloop"""
-        self.log.info("entering eventloop")
+        self.log.info("entering eventloop %s", self.eventloop)
+        for stream in self.shell_streams:
+            # flush any pending replies,
+            # which may be skipped by entering the eventloop
+            stream.flush(zmq.POLLOUT)
         # restore default_int_handler
         signal(SIGINT, default_int_handler)
         while self.eventloop is not None:


Test patch: