summaryrefslogtreecommitdiffstats
path: root/ptpython/eventloop.py
diff options
context:
space:
mode:
Diffstat (limited to 'ptpython/eventloop.py')
-rw-r--r--ptpython/eventloop.py71
1 files changed, 71 insertions, 0 deletions
diff --git a/ptpython/eventloop.py b/ptpython/eventloop.py
new file mode 100644
index 0000000..c841972
--- /dev/null
+++ b/ptpython/eventloop.py
@@ -0,0 +1,71 @@
+"""
+Wrapper around the eventloop that gives some time to the Tkinter GUI to process
+events when it's loaded and while we are waiting for input at the REPL. This
+way we don't block the UI of for instance ``turtle`` and other Tk libraries.
+
+(Normally Tkinter registers it's callbacks in ``PyOS_InputHook`` to integrate
+in readline. ``prompt-toolkit`` doesn't understand that input hook, but this
+will fix it for Tk.)
+"""
+import sys
+import time
+
+__all__ = ["inputhook"]
+
+
+def _inputhook_tk(inputhook_context):
+ """
+ Inputhook for Tk.
+ Run the Tk eventloop until prompt-toolkit needs to process the next input.
+ """
+ # Get the current TK application.
+ import tkinter
+
+ import _tkinter # Keep this imports inline!
+
+ root = tkinter._default_root
+
+ def wait_using_filehandler():
+ """
+ Run the TK eventloop until the file handler that we got from the
+ inputhook becomes readable.
+ """
+ # Add a handler that sets the stop flag when `prompt-toolkit` has input
+ # to process.
+ stop = [False]
+
+ def done(*a):
+ stop[0] = True
+
+ root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
+
+ # Run the TK event loop as long as we don't receive input.
+ while root.dooneevent(_tkinter.ALL_EVENTS):
+ if stop[0]:
+ break
+
+ root.deletefilehandler(inputhook_context.fileno())
+
+ def wait_using_polling():
+ """
+ Windows TK doesn't support 'createfilehandler'.
+ So, run the TK eventloop and poll until input is ready.
+ """
+ while not inputhook_context.input_is_ready():
+ while root.dooneevent(_tkinter.ALL_EVENTS | _tkinter.DONT_WAIT):
+ pass
+ # Sleep to make the CPU idle, but not too long, so that the UI
+ # stays responsive.
+ time.sleep(0.01)
+
+ if root is not None:
+ if hasattr(root, "createfilehandler"):
+ wait_using_filehandler()
+ else:
+ wait_using_polling()
+
+
+def inputhook(inputhook_context):
+ # Only call the real input hook when the 'Tkinter' library was loaded.
+ if "Tkinter" in sys.modules or "tkinter" in sys.modules:
+ _inputhook_tk(inputhook_context)