33from IPython .external .qt_for_kernel import QtCore , QtGui , enum_helper
44from IPython import get_ipython
55
6- # If we create a QApplication, keep a reference to it so that it doesn't get
7- # garbage collected.
6+ # If we create a QApplication, QEventLoop, or a QTimer, keep a reference to them
7+ # so that they don't get garbage collected or leak memory when created multiple times .
88_appref = None
9+ _eventloop = None
10+ _timer = None
911_already_warned = False
1012
1113
@@ -21,7 +23,7 @@ def _reclaim_excepthook():
2123
2224
2325def inputhook (context ):
24- global _appref
26+ global _appref , _eventloop , _timer
2527 app = QtCore .QCoreApplication .instance ()
2628 if not app :
2729 if sys .platform == 'linux' :
@@ -48,25 +50,25 @@ def inputhook(context):
4850 except AttributeError : # Only for Qt>=5.14.
4951 pass
5052 _appref = app = QtGui .QApplication ([" " ])
53+ _eventloop = QtCore .QEventLoop (app )
5154
5255 # "reclaim" IPython sys.excepthook after event loop starts
5356 # without this, it defaults back to BaseIPythonApplication.excepthook
5457 # and exceptions in the Qt event loop are rendered without traceback
5558 # formatting and look like "bug in IPython".
5659 QtCore .QTimer .singleShot (0 , _reclaim_excepthook )
5760
58- event_loop = QtCore .QEventLoop (app )
59-
6061 if sys .platform == 'win32' :
6162 # The QSocketNotifier method doesn't appear to work on Windows.
6263 # Use polling instead.
63- timer = QtCore .QTimer ()
64- timer .timeout .connect (event_loop .quit )
64+ if _timer is None :
65+ _timer = QtCore .QTimer ()
66+ _timer .timeout .connect (_eventloop .quit )
6567 while not context .input_is_ready ():
66- # NOTE: run the event loop, and after 50 ms, call `quit` to exit it.
67- timer .start (50 ) # 50 ms
68- _exec (event_loop )
69- timer .stop ()
68+ # NOTE: run the event loop, and after 10 ms, call `quit` to exit it.
69+ _timer .start (10 ) # 10 ms
70+ _exec (_eventloop )
71+ _timer .stop ()
7072 else :
7173 # On POSIX platforms, we can use a file descriptor to quit the event
7274 # loop when there is input ready to read.
@@ -77,14 +79,10 @@ def inputhook(context):
7779 # connect the callback we care about before we turn it on
7880 # lambda is necessary as PyQT inspect the function signature to know
7981 # what arguments to pass to. See https://github.com/ipython/ipython/pull/12355
80- notifier .activated .connect (lambda : event_loop .exit ())
82+ notifier .activated .connect (lambda : _eventloop .exit ())
8183 notifier .setEnabled (True )
8284 # only start the event loop we are not already flipped
8385 if not context .input_is_ready ():
84- _exec (event_loop )
86+ _exec (_eventloop )
8587 finally :
8688 notifier .setEnabled (False )
87-
88- # This makes sure that the event loop is garbage collected.
89- # See issue 14240.
90- event_loop .setParent (None )
0 commit comments