I am creating an application using Python and Tkinter.
The user inputs data into a Text
widget. How can I make it so that the text being displayed is different to what is actually being edited?
I have two different variables: question
and question_raw
. question
contains the data that should be displayed and question_data
contains the data that should be edited.
For example, if the user types /times
, it should automatically display as x
. However, if they then backspace the x
, it should go to /time
, as the s
would have been backspaced. I cannot just check for when an x
is removed, as the user may type x
normally.
Is this possible user default Tkinter widgets, or would I need to create my own? If I need to create my own, how would I go about this?
Thanks!
CodePudding user response:
For example, if the user types /times, it should automatically display as x. However, if they then backspace the x, it should go to /time, as the s would have been backspaced. I cannot just check for when an x is removed, as the user may type x normally.
There is no feature in the text widget specifically to do this. However, the text widget has all of the necessary building blocks. You'll have to add some custom bindings to make it work.
For example, after the user enters a space you can check to see if the preceding word is "/times". If so, you can replace it with "x" and add the tag "/times" to that x.
You will then need to create a custom binding for the backspace and delete characters. If you delete a character with a tag that begins with "/", remove the original character and replace it with the name of the tag.
Here's a hacked together example that seems to work. It's not production-ready, but works enough to serve as an example. It creates a custom class that behaves like a text widget, but you can pass a dictionary of replacement strings. When you type a space, it checks to see if the preceding word is in that dictionary. If it is, it replaces it with the replacement string (and colors it red just to make it easy to see).
If the cursor is immediately following one of these replacement strings, it gets the tag from the string, deletes the character, and replaces it with the tag name. (eg: backspace over "x" to replace it with "/times")
import tkinter as tk
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
self.macros = kwargs.pop("macros", {})
super().__init__(*args, **kwargs)
self.bind("<BackSpace>", self._backspace)
self.bind("<space>", self._check_macro)
for macro_name in self.macros.keys():
self.tag_configure(macro_name, foreground="red")
def _backspace(self, event):
tags = self.tag_names("insert-1c")
for tag_name in tags:
if tag_name in self.macros:
self.replace("insert-1c", "insert", tag_name)
return "break"
def _check_macro(self, event):
word = self.get("insert-1c wordstart-1c", "insert")
if word in self.macros:
replacement = self.macros[word]
self.replace("insert-1c wordstart-1c", "insert", self.macros[word], word, " ")
return "break"
root = tk.Tk()
macros = {"/times": "x", "/minus": "-", "/plus": " "}
text = CustomText(root, macros=macros)
text.pack(fill="both", expand=True)
root.mainloop()
CodePudding user response:
Here is the plan: Every time the Text widget is edited, we check whether "/time" is inside the Text widget content. We will then overwrite the question_raw
variable with the original text, and we will delete and re-write the contents of the Text widget.
import tkinter as tk
question = ''
question_raw = ''
def text_edited(e):
if '/time' in text.get(1.0, tk.END):
question = text.get(1.0, tk.END).replace('/time', 'x')
question_raw = text.get(1.0, tk.END)
root = tk.Tk()
text = tk.Text(root)
text.pack()
text.bind('<Key>',text_edited)
root.mainloop()