qt.py
85 lines
| 3.1 KiB
| text/x-python
|
PythonLexer
Thomas Kluyver
|
r22654 | import sys | ||
Christoph
|
r24529 | import os | ||
Thomas A Caswell
|
r26683 | from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper | ||
Talley Lambert
|
r26355 | from IPython import get_ipython | ||
Thomas Kluyver
|
r21934 | |||
Thomas Kluyver
|
r22707 | # If we create a QApplication, keep a reference to it so that it doesn't get | ||
# garbage collected. | ||||
_appref = None | ||||
Christoph
|
r24529 | _already_warned = False | ||
Thomas A Caswell
|
r23367 | |||
Thomas A Caswell
|
r25035 | |||
Thomas A Caswell
|
r26683 | def _exec(obj): | ||
# exec on PyQt6, exec_ elsewhere. | ||||
obj.exec() if hasattr(obj, "exec") else obj.exec_() | ||||
Talley Lambert
|
r26355 | def _reclaim_excepthook(): | ||
shell = get_ipython() | ||||
if shell is not None: | ||||
sys.excepthook = shell.excepthook | ||||
Thomas Kluyver
|
r21934 | def inputhook(context): | ||
Thomas Kluyver
|
r22707 | global _appref | ||
Thomas Kluyver
|
r21934 | app = QtCore.QCoreApplication.instance() | ||
if not app: | ||||
Talley Lambert
|
r26356 | if sys.platform == 'linux': | ||
if not os.environ.get('DISPLAY') \ | ||||
and not os.environ.get('WAYLAND_DISPLAY'): | ||||
Christoph
|
r24529 | import warnings | ||
global _already_warned | ||||
if not _already_warned: | ||||
_already_warned = True | ||||
warnings.warn( | ||||
Talley Lambert
|
r26356 | 'The DISPLAY or WAYLAND_DISPLAY environment variable is ' | ||
'not set or empty and Qt5 requires this environment ' | ||||
'variable. Deactivate Qt5 code.' | ||||
Christoph
|
r24529 | ) | ||
return | ||||
Thomas A Caswell
|
r26683 | try: | ||
Thomas A Caswell
|
r26684 | QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) | ||
Thomas A Caswell
|
r26683 | except AttributeError: # Only for Qt>=5.6, <6. | ||
pass | ||||
try: | ||||
QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy( | ||||
Thomas A Caswell
|
r26684 | QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough | ||
) | ||||
Thomas A Caswell
|
r26683 | except AttributeError: # Only for Qt>=5.14. | ||
pass | ||||
Thomas Kluyver
|
r22707 | _appref = app = QtGui.QApplication([" "]) | ||
Talley Lambert
|
r26355 | |||
# "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) | ||||
Thomas Kluyver
|
r21934 | event_loop = QtCore.QEventLoop(app) | ||
Thomas Kluyver
|
r22654 | |||
Talley Lambert
|
r26356 | if sys.platform == 'win32': | ||
Thomas Kluyver
|
r22654 | # 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. | ||||
Thomas A Caswell
|
r26683 | notifier = QtCore.QSocketNotifier( | ||
Thomas A Caswell
|
r26684 | context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read | ||
Thomas A Caswell
|
r26683 | ) | ||
Thomas A Caswell
|
r25035 | try: | ||
# connect the callback we care about before we turn it on | ||||
Matthias Bussonnier
|
r25812 | # lambda is necessary as PyQT inspect the function signature to know | ||
# what arguments to pass to. See https://github.com/ipython/ipython/pull/12355 | ||||
Talley Lambert
|
r25791 | notifier.activated.connect(lambda: event_loop.exit()) | ||
Thomas A Caswell
|
r25035 | notifier.setEnabled(True) | ||
# only start the event loop we are not already flipped | ||||
if not context.input_is_ready(): | ||||
Thomas A Caswell
|
r26683 | _exec(event_loop) | ||
Thomas A Caswell
|
r25035 | finally: | ||
notifier.setEnabled(False) | ||||