Repository: ipython/ipython

Issue:
IPython beta: interactive matplotlib magic breaks terminal (Windows)
When activating interactive figures with `%matplotlib`, and after plotting the first figure, the terminal does not capture any keyboard input anymore

```
In [6]: %matplotlib
Using matplotlib backend: Qt4Agg

In [7]: plt.plot([1,2,3])
Out[7]: [<matplotlib.lines.Line2D at 0xad116a0>]
```

After that, I cannot type or paste anything anymore, also not when the figure is closed. Something with the event loop?

I am working on Windows 7, and should be on master for both ipython and prompt toolkit.



Discussion:
This does not happen when I use `%matplotlib tk`, so only for the qt backend (and the others are not available on my Windows)

> After that, I cannot type or paste anything anymore, also not when the figure is closed. Something with the event loop?

Hum likely. I can't reproduce though on Mac OS. we will need someone on windows to help figure that out. 

Thanks @jorisvandenbossche . The code for the Qt event loop is here:
https://github.com/ipython/ipython/blob/master/IPython/terminal/pt_inputhooks/qt.py

I'd guess that something to do with waiting for events on a file descriptor doesn't work on Windows; I know that `select()` on Windows only accepts sockets, not fds.

I forget which input hooks I copied from ptpython, but if this is one, @jonathanslenders might be interested.

This one is not coming from ptpython.

What we could do on Windows is using a separate thread to monitor the fd and quit the qt loop from there.

@Carreau I noticed that you changed the milestone :-) 
I know at a certain point you have to tag a release, but IMO this one is rather critical (at least for me this is the reason I am currently not using the beta version anymore, plotting is rather critical in my interactive work). 
Is there anything I can do to help? (I have a windows, but not much knowledge about qt and event loops)

Sorry about than, Min switched back the issue to 5.0. 

@jorisvandenbossche when someone has attempted to fix this, it would be very useful if you could test the fix. I'll try to find time tomorrow if no-one beats me to it.

> @jorisvandenbossche when someone has attempted to fix this, it would be very useful if you could test the fix. I'll try to find time tomorrow if no-one beats me to it.

I already had pushed the rc1 this morning before this was reopened, and had to go offline. I'm back online. With the RC pushed, I'll announce it with this being the last blocking issue. Hopefully it might get some people to help with that. 

Sorry, should have commented when I retargeted - I'm making some time to help with this one because I don't think we can do a release with matplotlib not working on Windows. Thanks for the two PRs, @Carreau!

I can reliably reproduce this one, and #9705 does fix it for me.


Resolving patch:
diff --git a/IPython/terminal/pt_inputhooks/qt.py b/IPython/terminal/pt_inputhooks/qt.py
--- a/IPython/terminal/pt_inputhooks/qt.py
+++ b/IPython/terminal/pt_inputhooks/qt.py
@@ -1,3 +1,4 @@
+import sys
 from IPython.external.qt_for_kernel import QtCore, QtGui
 
 def inputhook(context):
@@ -5,7 +6,20 @@ def inputhook(context):
     if not app:
         return
     event_loop = QtCore.QEventLoop(app)
-    notifier = QtCore.QSocketNotifier(context.fileno(), QtCore.QSocketNotifier.Read)
-    notifier.setEnabled(True)
-    notifier.activated.connect(event_loop.exit)
-    event_loop.exec_()
+
+    if sys.platform == 'win32':
+        # The QSocketNotifier method doesn't appear to work on Windows.
+        # Use polling instead.
+        timer = QtCore.QTimer()
+        timer.timeout.connect(event_loop.quit)
+        while not context.input_is_ready():
+            timer.start(50)  # 50 ms
+            event_loop.exec_()
+            timer.stop()
+    else:
+        # On POSIX platforms, we can use a file descriptor to quit the event
+        # loop when there is input ready to read.
+        notifier = QtCore.QSocketNotifier(context.fileno(), QtCore.QSocketNotifier.Read)
+        notifier.setEnabled(True)
+        notifier.activated.connect(event_loop.exit)
+        event_loop.exec_()


Test patch: