pytermgui
pytermgui copied to clipboard
[BUG] Cannot delete text in InputField
Cannot delete text in InputField
To Reproduce
import sys
from typing import List, Optional
import pytermgui as ptg
def main(argv: Optional[List[str]] = None) -> None:
with ptg.WindowManager() as manager:
layout = ptg.Layout()
layout.add_slot("SiderBar", width=0.2)
manager.layout = layout
manager.add(
ptg.Window("Testing...", ptg.InputField("default value", prompt="Name: "))
)
if __name__ == "__main__":
main(sys.argv[1:])
Expected behavior Pressing backspace or delete should delete the text
System information Windows 11, Powershell, Windows Terminal
Python version: 3.8.18
$TERM: None
$COLORTERM: None
Color support: ColorSystem.STANDARD
OS Platform: Windows-10-10.0.22621-SP0
Also when typing in the InputField, the letters or digits are added at the beginning of the input instead of the end.
Windows 11/Powershell/Windows Terminal and also experiencing this issue
https://github.com/bczsalba/pytermgui/assets/113731512/3c6e2b29-7fda-4609-8c30-631435f47341
Same error here, on windows 11 power shell
Could you show me the output of pressing backspace while running ptg -g? I suspect the character that gets input isn't what we were previously looking for.
I just ran into this issue as well.
Here is the output for backspace:
PyTermGUI - Getch
┌────────────────────────────────────────────────┐
│ Your output │
│ │
│ key keys.CTRL_H │
│ value: '\x08' │
│ len() 1 │
│ real_length() -1 │
└────────────────────────────────────────────────┘
And the output for delete:
PyTermGUI - Getch
┌────────────────────────────────────────────────┐
│ Your output │
│ │
│ key '\x1bS' │
│ value: '\x1bS' │
│ len() 2 │
│ real_length() -1 │
└────────────────────────────────────────────────┘
Note the behavior of ptg -g is different on Windows versus Linux. On Linux, it brings up a "Press any key..." prompt and waits indefinitely for the user to press a key. On Windows, there is no prompt, and if you don't press a key immediately it will return with an empty result.
If I call msvcrt.getch directly, I get this:
backspace:
>python -c 'import msvcrt; print(ascii(msvcrt.getch()))'
b'\x08'
delete:
> python -c 'import msvcrt; print(ascii(msvcrt.getch()))'
b'\xe0'
Huh, that's interesting. The codes seem correct, so no idea why it doesn't behave properly. Maybe someone with a Windows machine could diagnose it further?
Note the behavior of ptg -g is different on Windows versus Linux.
Could you record a video of this as a separate issue? That's definitely not intended!
Sorry I don't have a video, but I did look into this further and tried to do some debugging with pdb. I set a breakpoint in the GetchWindow constructor and can see the "Press any key" prompt appears, but when running the program normally (not in a debugger) it is not visible, possibly because the screen refreshes so quickly.
The issue appears to be with the call to msvcrt.kbhit here:
https://github.com/bczsalba/pytermgui/blob/e6ef8b32e347679144ef8aa310273acf29d93372/pytermgui/input.py#L184-L185
If there is no pending keypress, kbhit will immediately return 0, and it will throw TimeoutException. So the get_chars (and therefore getch) method will immediately return if there is no pending keypress. There doesn't seem to be a mechanism to wait for the user to press a key. Here is an example calling getch with and without windows_raise_timeout:
PS E:\tmp> python -c 'import pytermgui; print(ascii(pytermgui.input.getch()))'
''
PS E:\tmp> python -c 'import pytermgui; print(ascii(pytermgui.input.getch(windows_raise_timeout=True)))'
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Users\user\.virtualenvs\ptg-test--GZ1WdmM\Lib\site-packages\pytermgui\input.py", line 441, in getch
key = _getch()
^^^^^^^^
File "C:\Users\user\.virtualenvs\ptg-test--GZ1WdmM\Lib\site-packages\pytermgui\input.py", line 209, in __call__
buff = self.get_chars()
^^^^^^^^^^^^^^^^
File "C:\Users\user\.virtualenvs\ptg-test--GZ1WdmM\Lib\site-packages\pytermgui\input.py", line 185, in get_chars
raise TimeoutException("No input available.")
pytermgui.exceptions.TimeoutException: No input available.
PS E:\tmp>
Most examples I have seen of msvcrt.kbhit() place it inside a while True: loop. Maybe it's worth rewriting this to place it in a loop with an actual timeout mechanism (break after x seconds).
Anyhow, I realize this doesn't address the original problem regarding backspace and delete. I am still not sure what is causing that issue.
Could you try commenting out:
https://github.com/bczsalba/pytermgui/blob/e6ef8b32e347679144ef8aa310273acf29d93372/pytermgui/input.py#L182-L185
Looking at it again, it seems that not checking up front would be the more accurate algorithm when compared to the UNIX implementation.
With the following changes, ptg -g seems to work as expected. I included a timeout option as well:
--- a/pytermgui/input.py
+++ b/pytermgui/input.py
@@ -23,6 +23,7 @@
from codecs import getincrementaldecoder
from contextlib import contextmanager
from select import select
+from time import time
from typing import (
IO,
Any,
@@ -172,7 +173,7 @@
return string
- def get_chars(self) -> str:
+ def get_chars(self, timeout: int = 3) -> str:
"""Reads characters from sys.stdin.
Returns:
@@ -181,14 +182,17 @@
# We need to type: ignore these on non-windows machines,
# as the library does not exist.
- if not msvcrt.kbhit(): # type: ignore
- raise TimeoutException("No input available.")
-
- char = msvcrt.getch() # type: ignore
- if char == b"\xe0":
- char = "\x1b"
-
- buff = self._ensure_str(char)
+ start = time()
+ while True:
+ if msvcrt.kbhit(): # type: ignore
+ char = msvcrt.getch() # type: ignore
+ if char == b"\xe0":
+ char = "\x1b"
+
+ buff = self._ensure_str(char)
+ break
+ if time() - start > timeout:
+ raise TimeoutException("No input available.")
while msvcrt.kbhit(): # type: ignore
char = msvcrt.getch() # type: ignore
The first loop waits for the initial key-press (within 3 seconds by default). The second loop collects any remaining buffered key-presses. However the original problem still exists. I have been using the second example in the README for testing. With the patch above, CTRL-H works for backspace, but the actual backspace key still doesn't work.
Unfortunately there are additional issues as well when comparing Windows and Linux. For example, the arrow keys do not work on Windows either, and the text on the submit button is not visible in the Windows version. While PyTermGUI may be great for non-Windows platforms, I think it needs a dedicated maintainer/tester for Windows to be considered cross-platform. Unfortunately I do not have the time to do so, and have discovered another project (Textual) that seems to better fit my needs.
I think it needs a dedicated maintainer/tester for Windows to be considered cross-platform.
Unfortunately I'm just one guy, so this is the best I can do :(
I think the problem here is PowerShell, and its many incompatibilities with pretty much every other shell ever. Since the $TERM and $COLORTERM env vars don't seem to be set, a lot of our compatibility shims don't really work in this configuration.
Your changes are good, but they further distance the UNIX and Windows implementations unfortunately. I believe my suggestion above should be enough to fix the divergence in ptg -g at least.