-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1549 from wmvanvliet/qt6
PyQt6 support
- Loading branch information
Showing
6 changed files
with
289 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 199 additions & 0 deletions
199
src/debugpy/_vendored/pydevd/pydev_ipython/inputhookqt6.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Qt6's inputhook support function | ||
Author: Christian Boos, Marijn van Vliet | ||
""" | ||
|
||
#----------------------------------------------------------------------------- | ||
# Copyright (C) 2011 The IPython Development Team | ||
# | ||
# Distributed under the terms of the BSD License. The full license is in | ||
# the file COPYING, distributed as part of this software. | ||
#----------------------------------------------------------------------------- | ||
|
||
#----------------------------------------------------------------------------- | ||
# Imports | ||
#----------------------------------------------------------------------------- | ||
|
||
import os | ||
import signal | ||
|
||
import threading | ||
|
||
from pydev_ipython.qt_for_kernel import QtCore, QtGui | ||
from pydev_ipython.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready | ||
|
||
|
||
# To minimise future merging complexity, rather than edit the entire code base below | ||
# we fake InteractiveShell here | ||
class InteractiveShell: | ||
_instance = None | ||
|
||
@classmethod | ||
def instance(cls): | ||
if cls._instance is None: | ||
cls._instance = cls() | ||
return cls._instance | ||
|
||
def set_hook(self, *args, **kwargs): | ||
# We don't consider the pre_prompt_hook because we don't have | ||
# KeyboardInterrupts to consider since we are running under PyDev | ||
pass | ||
|
||
#----------------------------------------------------------------------------- | ||
# Module Globals | ||
#----------------------------------------------------------------------------- | ||
|
||
|
||
got_kbdint = False | ||
sigint_timer = None | ||
|
||
#----------------------------------------------------------------------------- | ||
# Code | ||
#----------------------------------------------------------------------------- | ||
|
||
|
||
def create_inputhook_qt6(mgr, app=None): | ||
"""Create an input hook for running the Qt6 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 PyQt6'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: | ||
from PyQt6 import QtWidgets | ||
app = QtWidgets.QApplication([" "]) | ||
|
||
# Re-use previously created inputhook if any | ||
ip = InteractiveShell.instance() | ||
if hasattr(ip, '_inputhook_qt6'): | ||
return app, ip._inputhook_qt6 | ||
|
||
# Otherwise create the inputhook_qt6/preprompthook_qt6 pair of | ||
# hooks (they both share the got_kbdint flag) | ||
|
||
def inputhook_qt6(): | ||
"""PyOS_InputHook python hook for Qt6. | ||
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.ProcessEventsFlag.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(.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_qt6, unregistering.") | ||
mgr.clear_inputhook() | ||
finally: | ||
allow_CTRL_C() | ||
return 0 | ||
|
||
def preprompthook_qt6(ishell): | ||
"""'pre_prompt_hook' used to restore the Qt6 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_qt6) | ||
got_kbdint = False | ||
|
||
ip._inputhook_qt6 = inputhook_qt6 | ||
ip.set_hook('pre_prompt_hook', preprompthook_qt6) | ||
|
||
return app, inputhook_qt6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.