def _notify_stream_qt(kernel, stream): from IPython.external.qt_for_kernel import QtCore if _use_appnope() and kernel._darwin_app_nap: from appnope import nope_scope as context else: from contextlib import contextmanager @contextmanager def context(): yield def process_stream_events(): while stream.getsockopt(zmq.EVENTS) & zmq.POLLIN: with context(): kernel.do_one_iteration() fd = stream.getsockopt(zmq.FD) notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app) notifier.activated.connect(process_stream_events) # there may already be unprocessed events waiting. # these events will not wake zmq's edge-triggered FD # since edge-triggered notification only occurs on new i/o activity. # process all the waiting events immediately # so we start in a clean state ensuring that any new i/o events will notify. # schedule first call on the eventloop as soon as it's running, # so we don't block here processing events timer = QtCore.QTimer(kernel.app) timer.setSingleShot(True) timer.timeout.connect(process_stream_events) timer.start(0)
def _notify_stream_qt(kernel, stream): from IPython.external.qt_for_kernel import QtCore def process_stream_events(): """fall back to main loop when there's a socket event""" # call flush to ensure that the stream doesn't lose events # due to our consuming of the edge-triggered FD # flush returns the number of events consumed. # if there were any, wake it up if stream.flush(limit=1): notifier.setEnabled(False) kernel.app.quit() fd = stream.getsockopt(zmq.FD) notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app) notifier.activated.connect(process_stream_events) # there may already be unprocessed events waiting. # these events will not wake zmq's edge-triggered FD # since edge-triggered notification only occurs on new i/o activity. # process all the waiting events immediately # so we start in a clean state ensuring that any new i/o events will notify. # schedule first call on the eventloop as soon as it's running, # so we don't block here processing events timer = QtCore.QTimer(kernel.app) timer.setSingleShot(True) timer.timeout.connect(process_stream_events) timer.start(0)
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 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 loop_qt4(kernel): """Start a kernel with PyQt4 event loop integration.""" from IPython.external.qt_for_kernel import QtCore from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4 kernel.app = get_app_qt4([" "]) kernel.app.setQuitOnLastWindowClosed(False) kernel.timer = QtCore.QTimer() kernel.timer.timeout.connect(kernel.do_one_iteration) # Units for the timer are in milliseconds kernel.timer.start(1000 * kernel._poll_interval) start_event_loop_qt4(kernel.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 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 inputhook(context): app = QtCore.QCoreApplication.instance() if not app: return 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) notifier.setEnabled(True) notifier.activated.connect(event_loop.exit) event_loop.exec_()
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