def inputhook(context): global _appref app = QtCore.QCoreApplication.instance() if not app: _appref = app = QtGui.QApplication([" "]) event_loop = QtCore.QEventLoop(app) 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) # connect the callback we care about before we turn it on notifier.activated.connect(event_loop.exit) notifier.setEnabled(True) # only start the event loop we are not already flipped if not context.input_is_ready(): event_loop.exec_()
def inputhook(context): global _appref app = QtCore.QCoreApplication.instance() if not app: if sys.platform == 'linux': if not os.environ.get('DISPLAY') \ and not os.environ.get('WAYLAND_DISPLAY'): import warnings global _already_warned if not _already_warned: _already_warned = True warnings.warn( 'The DISPLAY or WAYLAND_DISPLAY environment variable is ' 'not set or empty and Qt5 requires this environment ' 'variable. Deactivate Qt5 code.') return try: QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) except AttributeError: # Only for Qt>=5.6, <6. pass try: QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy( QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) except AttributeError: # Only for Qt>=5.14. pass _appref = app = QtGui.QApplication([" "]) # "reclaim" IPython sys.excepthook after event loop starts # without this, it defaults back to BaseIPythonApplication.excepthook # and exceptions in the Qt event loop are rendered without traceback # formatting and look like "bug in IPython". QtCore.QTimer.singleShot(0, _reclaim_excepthook) event_loop = QtCore.QEventLoop(app) 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 _exec(event_loop) 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(), enum_helper("QtCore.QSocketNotifier.Type").Read) try: # connect the callback we care about before we turn it on notifier.activated.connect(lambda: event_loop.exit()) notifier.setEnabled(True) # only start the event loop we are not already flipped if not context.input_is_ready(): _exec(event_loop) finally: notifier.setEnabled(False)
def get_app_qt4(*args, **kwargs): """Create a new qt4 app or return an existing one.""" from IPython.external.qt_for_kernel import QtGui app = QtGui.QApplication.instance() if app is None: if not args: args = ([''], ) app = QtGui.QApplication(*args, **kwargs) return app
def inputhook(context): global _appref app = QtCore.QCoreApplication.instance() if not app: if sys.platform == 'linux': if not os.environ.get('DISPLAY') \ and not os.environ.get('WAYLAND_DISPLAY'): import warnings global _already_warned if not _already_warned: _already_warned = True warnings.warn( 'The DISPLAY or WAYLAND_DISPLAY environment variable is ' 'not set or empty and Qt5 requires this environment ' 'variable. Deactivate Qt5 code.') return QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) _appref = app = QtGui.QApplication([" "]) # "reclaim" IPython sys.excepthook after event loop starts # without this, it defaults back to BaseIPythonApplication.excepthook # and exceptions in the Qt event loop are rendered without traceback # formatting and look like "bug in IPython". QtCore.QTimer.singleShot(0, _reclaim_excepthook) event_loop = QtCore.QEventLoop(app) 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) try: # connect the callback we care about before we turn it on # lambda is necessary as PyQT inspect the function signature to know # what arguments to pass to. See https://github.com/ipython/ipython/pull/12355 notifier.activated.connect(lambda: event_loop.exit()) notifier.setEnabled(True) # only start the event loop we are not already flipped if not context.input_is_ready(): event_loop.exec_() finally: notifier.setEnabled(False)
def enable_qt4(self, app=None): """Enable event loop integration with PyQt4. Parameters ---------- app : Qt Application, optional. Running application to use. If not given, we probe Qt for an existing application object, and create a new one if none is found. Notes ----- This methods sets the PyOS_InputHook for PyQt4, which allows the PyQt4 to integrate with terminal based applications like IPython. If ``app`` is not given we probe for an existing one, and return it if found. If no existing app is found, we create an :class:`QApplication` as follows:: from PyQt4 import QtCore app = QtGui.QApplication(sys.argv) """ from IPython.external.qt_for_kernel import QtCore, QtGui if 'pyreadline' in sys.modules: # see IPython GitHub Issue #281 for more info on this issue # Similar intermittent behavior has been reported on OSX, # but not consistently reproducible warnings.warn( """PyReadline's inputhook can conflict with Qt, causing delays in interactive input. If you do see this issue, we recommend using another GUI toolkit if you can, or disable readline with the configuration option 'TerminalInteractiveShell.readline_use=False', specified in a config file or at the command-line""", RuntimeWarning) # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook # was set when QtCore was imported, but if it ever got removed, # you couldn't reset it. For earlier versions we can # probably implement a ctypes version. try: QtCore.pyqtRestoreInputHook() except AttributeError: pass self._current_gui = GUI_QT4 if app is None: app = QtCore.QCoreApplication.instance() if app is None: app = QtGui.QApplication([" "]) app._in_event_loop = True self._apps[GUI_QT4] = app return app
def inputhook(context): global _appref app = QtCore.QCoreApplication.instance() if not app: if sys.platform == 'linux': if not os.environ.get('DISPLAY') \ and not os.environ.get('WAYLAND_DISPLAY'): import warnings global _already_warned if not _already_warned: _already_warned = True warnings.warn( 'The DISPLAY or WAYLAND_DISPLAY environment variable is ' 'not set or empty and Qt5 requires this environment ' 'variable. Deactivate Qt5 code.' ) return QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) _appref = app = QtGui.QApplication([" "]) event_loop = QtCore.QEventLoop(app) 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) try: # connect the callback we care about before we turn it on notifier.activated.connect(event_loop.exit) notifier.setEnabled(True) # only start the event loop we are not already flipped if not context.input_is_ready(): event_loop.exec_() finally: notifier.setEnabled(False)
def create_inputhook_qt4(mgr, app=None): """Create an input hook for running the Qt4 application event loop. Parameters ---------- mgr : an InputHookManager app : Qt Application, optional. Running application to use. If not given, we probe Qt for an existing application object, and create a new one if none is found. Returns ------- A pair consisting of a Qt Application (either the one given or the one found or created) and a inputhook. Notes ----- We use a custom input hook instead of PyQt4's default one, as it interacts better with the readline packages (issue #481). The inputhook function works in tandem with a 'pre_prompt_hook' which automatically restores the hook as an inputhook in case the latter has been temporarily disabled after having intercepted a KeyboardInterrupt. """ if app is None: app = QtCore.QCoreApplication.instance() if app is None: app = QtGui.QApplication([" "]) # Re-use previously created inputhook if any ip = InteractiveShell.instance() if hasattr(ip, "_inputhook_qt4"): return app, ip._inputhook_qt4 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of # hooks (they both share the got_kbdint flag) def inputhook_qt4(): """PyOS_InputHook python hook for Qt4. Process pending Qt events and if there's no pending keyboard input, spend a short slice of time (50ms) running the Qt event loop. As a Python ctypes callback can't raise an exception, we catch the KeyboardInterrupt and temporarily deactivate the hook, which will let a *second* CTRL+C be processed normally and go back to a clean prompt line. """ try: allow_CTRL_C() app = QtCore.QCoreApplication.instance() if not app: # shouldn't happen, but safer if it happens anyway... return 0 app.processEvents(QtCore.QEventLoop.AllEvents, 300) if not stdin_ready(): # Generally a program would run QCoreApplication::exec() # from main() to enter and process the Qt event loop until # quit() or exit() is called and the program terminates. # # For our input hook integration, we need to repeatedly # enter and process the Qt event loop for only a short # amount of time (say 50ms) to ensure that Python stays # responsive to other user inputs. # # A naive approach would be to repeatedly call # QCoreApplication::exec(), using a timer to quit after a # short amount of time. Unfortunately, QCoreApplication # emits an aboutToQuit signal before stopping, which has # the undesirable effect of closing all modal windows. # # To work around this problem, we instead create a # QEventLoop and call QEventLoop::exec(). Other than # setting some state variables which do not seem to be # used anywhere, the only thing QCoreApplication adds is # the aboutToQuit signal which is precisely what we are # trying to avoid. timer = QtCore.QTimer() event_loop = QtCore.QEventLoop() timer.timeout.connect(event_loop.quit) while not stdin_ready(): timer.start(50) event_loop.exec_() timer.stop() except KeyboardInterrupt: global got_kbdint, sigint_timer ignore_CTRL_C() got_kbdint = True mgr.clear_inputhook() # This generates a second SIGINT so the user doesn't have to # press CTRL+C twice to get a clean prompt. # # Since we can't catch the resulting KeyboardInterrupt here # (because this is a ctypes callback), we use a timer to # generate the SIGINT after we leave this callback. # # Unfortunately this doesn't work on Windows (SIGINT kills # Python and CTRL_C_EVENT doesn't work). if os.name == "posix": pid = os.getpid() if not sigint_timer: sigint_timer = threading.Timer( 0.01, os.kill, args=[pid, signal.SIGINT] ) sigint_timer.start() else: print("\nKeyboardInterrupt - Ctrl-C again for new prompt") except: # NO exceptions are allowed to escape from a ctypes callback ignore_CTRL_C() from traceback import print_exc print_exc() print("Got exception from inputhook_qt4, unregistering.") mgr.clear_inputhook() finally: allow_CTRL_C() return 0 def preprompthook_qt4(ishell): """'pre_prompt_hook' used to restore the Qt4 input hook (in case the latter was temporarily deactivated after a CTRL+C) """ global got_kbdint, sigint_timer if sigint_timer: sigint_timer.cancel() sigint_timer = None if got_kbdint: mgr.set_inputhook(inputhook_qt4) got_kbdint = False ip._inputhook_qt4 = inputhook_qt4 ip.set_hook("pre_prompt_hook", preprompthook_qt4) return app, inputhook_qt4
def create_inputhook_qt4(mgr, app=None): """Create an input hook for running the Qt4 application event loop. Parameters ---------- mgr : an InputHookManager app : Qt Application, optional. Running application to use. If not given, we probe Qt for an existing application object, and create a new one if none is found. Returns ------- A pair consisting of a Qt Application (either the one given or the one found or created) and a inputhook. Notes ----- We use a custom input hook instead of PyQt4's default one, as it interacts better with the readline packages (issue #481). The inputhook function works in tandem with a 'pre_prompt_hook' which automatically restores the hook as an inputhook in case the latter has been temporarily disabled after having intercepted a KeyboardInterrupt. """ if app is None: app = QtCore.QCoreApplication.instance() if app is None: app = QtGui.QApplication([" "]) # Re-use previously created inputhook if any ip = InteractiveShell.instance() if hasattr(ip, '_inputhook_qt4'): return app, ip._inputhook_qt4 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of # hooks (they both share the got_kbdint flag) got_kbdint = [False] def inputhook_qt4(): """PyOS_InputHook python hook for Qt4. Process pending Qt events and if there's no pending keyboard input, spend a short slice of time (50ms) running the Qt event loop. As a Python ctypes callback can't raise an exception, we catch the KeyboardInterrupt and temporarily deactivate the hook, which will let a *second* CTRL+C be processed normally and go back to a clean prompt line. """ try: allow_CTRL_C() app = QtCore.QCoreApplication.instance() if not app: # shouldn't happen, but safer if it happens anyway... return 0 app.processEvents(QtCore.QEventLoop.AllEvents, 300) if not stdin_ready(): timer = QtCore.QTimer() timer.timeout.connect(app.quit) while not stdin_ready(): timer.start(50) app.exec_() timer.stop() ignore_CTRL_C() except KeyboardInterrupt: ignore_CTRL_C() got_kbdint[0] = True print("\nKeyboardInterrupt - qt4 event loop interrupted!" "\n * hit CTRL+C again to clear the prompt" "\n * use '%gui none' to disable the event loop" " permanently" "\n and '%gui qt4' to re-enable it later") mgr.clear_inputhook() except: # NO exceptions are allowed to escape from a ctypes callback mgr.clear_inputhook() from traceback import print_exc print_exc() print("Got exception from inputhook_qt4, unregistering.") return 0 def preprompthook_qt4(ishell): """'pre_prompt_hook' used to restore the Qt4 input hook (in case the latter was temporarily deactivated after a CTRL+C) """ if got_kbdint[0]: mgr.set_inputhook(inputhook_qt4) got_kbdint[0] = False ip._inputhook_qt4 = inputhook_qt4 ip.set_hook('pre_prompt_hook', preprompthook_qt4) return app, inputhook_qt4
def create_inputhook_qt4(mgr, app=None): """Create an input hook for running the Qt4 application event loop. :param mgr: an InputHookManager :param app: Qt Application, optional. Running application to use. If not given, we probe Qt for an existing application object, and create a new one if none is found. :returns: A pair consisting of a Qt Application (either the one given or the one found or created) and a inputhook. .. note:: We use a custom input hook instead of PyQt4's default one, as it interacts better with the readline packages (issue #481). The inputhook function works in tandem with a 'pre_prompt_hook' which automatically restores the hook as an inputhook in case the latter has been temporarily disabled after having intercepted a KeyboardInterrupt. """ from IPython.core.interactiveshell import InteractiveShell from IPython.external.qt_for_kernel import QtCore, QtGui from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready if app is None: app = QtCore.QCoreApplication.instance() if app is None: app = QtGui.QApplication(["woo.qt"]) # Re-use previously created inputhook if any ip = InteractiveShell.instance() if hasattr(ip, '_inputhook_qt4'): return app, ip._inputhook_qt4 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of # hooks (they both share the got_kbdint flag) got_kbdint = [False] def inputhook_qt4(): """PyOS_InputHook python hook for Qt4. Process pending Qt events and if there's no pending keyboard input, spend a short slice of time (50ms) running the Qt event loop. As a Python ctypes callback can't raise an exception, we catch the KeyboardInterrupt and temporarily deactivate the hook, which will let a *second* CTRL+C be processed normally and go back to a clean prompt line. """ try: allow_CTRL_C() app = QtCore.QCoreApplication.instance() if not app: # shouldn't happen, but safer if it happens anyway... return 0 app.processEvents(QtCore.QEventLoop.AllEvents, 300) if not stdin_ready(): # Generally a program would run QCoreApplication::exec() # from main() to enter and process the Qt event loop until # quit() or exit() is called and the program terminates. # # For our input hook integration, we need to repeatedly # enter and process the Qt event loop for only a short # amount of time (say 50ms) to ensure that Python stays # responsive to other user inputs. # # A naive approach would be to repeatedly call # QCoreApplication::exec(), using a timer to quit after a # short amount of time. Unfortunately, QCoreApplication # emits an aboutToQuit signal before stopping, which has # the undesirable effect of closing all modal windows. # # To work around this problem, we instead create a # QEventLoop and call QEventLoop::exec(). Other than # setting some state variables which do not seem to be # used anywhere, the only thing QCoreApplication adds is # the aboutToQuit signal which is precisely what we are # trying to avoid. timer = QtCore.QTimer() event_loop = QtCore.QEventLoop() timer.timeout.connect(event_loop.quit) while not stdin_ready(): timer.start(50) event_loop.exec_() timer.stop() except KeyboardInterrupt: ignore_CTRL_C() got_kbdint[0] = True print("\nKeyboardInterrupt - Ctrl-C again for new prompt") mgr.clear_inputhook() except: # NO exceptions are allowed to escape from a ctypes callback ignore_CTRL_C() from traceback import print_exc print_exc() print("Got exception from inputhook_qt4, unregistering.") mgr.clear_inputhook() finally: allow_CTRL_C() return 0 def preprompthook_qt4(ishell): """'pre_prompt_hook' used to restore the Qt4 input hook (in case the latter was temporarily deactivated after a CTRL+C) """ if got_kbdint[0]: mgr.set_inputhook(inputhook_qt4) got_kbdint[0] = False ip._inputhook_qt4 = inputhook_qt4 ip.set_hook('pre_prompt_hook', preprompthook_qt4) return app, inputhook_qt4