How to get current cursor position for Text widget - python

I am using an onscreen keyboard to type data for a tkinter based gui.
I was able to use the entry field to enter, edit data through the onscreen keyboard such as getting current cursor position and length of the string.
temp = self.entry_label.get()
length_string=len(temp)
cursor_position = self.entry_label.index(INSERT)
But I wish to do the same for a Text widget. I could get the text for Text widget using get() method and its length but cannot get the current mouse cursor position.
temp=self.new_text.get(1.0, END)
cursor_position = self.new_text.index(INSERT)
Acutally it works and i am able to add character to that poisition , but after adding character cursor goes back to origional position , i.e last character

maybe This works? Else maybe text_widget.index(Tkinter.INSERT) is what should work.

Although the other answer is correct and worked I believe tk.Current is the meta answer.
"tk.INSERT"
as provided by this reference.
https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/text-index.html

Related

Text in Entry widget going out of view

I have an entry widget as such:
...
entry1=Entry(frame1,font=("Verdana",10),width=20)
entry1.grid(row=0,column=0,rowspan=2,pady=10,padx=10,ipday=5)
and when I type a long string within in,
it crops out the last letter such that I can't even bring it into view using the right arrow key
(12039201901--71230110)
but if I add one more character then I can use the right arrow key to bring it into view
How do I fix this issue?

How to move the insertion cursor of a tkinter text widget to a certain index

I want to move the insertion cursor(caret)(|) within the tkinter text widget, to a certain text widget indice.
USE CASE:
I am making an autocomplete program wherein if I type one single apostrophe(quotation mark), the other one automatically is inserted into the text widget, which I got working all fine. But after, the second apostrophe is generated I want to bring the insertion cursor(caret), in the middle of the two apostrophes rather than at the end. Like so -:
One apostrophe typed(inside the tkinter text widget) -:
'[insertion cursor(caret)(|)]
The other one is auto-inserted pushing the insertion cursor(caret) to the end(The code till this part has been figured out by me successfully.) -:
''[insertion cursor(caret)(|)]
The insertion cursor(caret) shifts between the two apostrophes(The objective of this question.) -:
'[insertion cursor(caret)(|)]'
NOTE: These operations are all taking place within a tkinter text widget.
You call the mark_set method, using "insert" as the name of the mark.
the_widget.mark_set("insert", "4.0") will set the insertion cursor at the start of the fourth line. You can use the_widget.mark_set("insert", "insert-1c") to move the cursor back one character.
Here is an example of one way for automatically inserting a closing parenthesis or bracket. In this example, the code inserts both the opening and closing character so it returns "break" to prevent the default behavior provided by the text widget.
import tkinter as tk
def autocomplete(widget, first, last):
# insert the two characters at the insertion point
widget.insert("insert", first + last)
# move insertion cursor back one character
widget.mark_set("insert", "insert-1c")
# prevent the text widget from inserting the character that
# triggered the event since we've already inserted it.
return "break"
root = tk.Tk()
text = tk.Text(root, wrap="word")
text.pack(fill="both", expand=True)
text.bind("(", lambda event: autocomplete(event.widget, "(", ")"))
text.bind("[", lambda event: autocomplete(event.widget, "[", "]"))
root.mainloop()

How can I accurately set the new cursor positions after text replacements have been made

I am trying to adapt a plugin for automated text replacement in a Sublime Text 3 Plugin. What I want it to do is paste in text from the clipboard and make some automatic text substitutions
import sublime
import sublime_plugin
import re
class PasteAndEscapeCommand(sublime_plugin.TextCommand):
def run(self, edit):
# Position of cursor for all selections
before_selections = [sel for sel in self.view.sel()]
# Paste from clipboard
self.view.run_command('paste')
# Postion of cursor for all selections after paste
after_selections = [sel for sel in self.view.sel()]
# Define a new region based on pre and post paste cursor positions
new_selections = list()
delta = 0
for before, after in zip(before_selections, after_selections):
new = sublime.Region(before.begin() + delta, after.end())
delta = after.end() - before.end()
new_selections.append(new)
# Clear any existing selections
self.view.sel().clear()
# Select the saved region
self.view.sel().add_all(new_selections)
# Replace text accordingly
for region in self.view.sel():
# Get the text from the selected region
text = self.view.substr(region)
# Make the required edits on the text
text = text.replace("\\","\\\\")
text = text.replace("_","\\_")
text = text.replace("*","\\*")
# Paste the text back to the saved region
self.view.replace(edit, region, text)
# Clear selections and set cursor position
self.view.sel().clear()
self.view.sel().add_all(after_selections)
This works for the most part except I need to get the new region for the edited text. The cursor will be placed to the location of the end of the pasted text. However since I am making replacements which always make the text larger the final position will be inaccurate.
I know very little about Python for Sublime and like most others this is my first plugin.
How do I set the cursor position to account for the size changes in the text. I know I need to do something with the after_selections list as I am not sure how to create new regions as they were created from selections which are cleared in an earlier step.
I feel that I am getting close with
# Add the updated region to the selection
self.view.sel().subtract(region)
self.view.sel().add(sublime.Region(region.begin()+len(text)))
This, for some yet unknown to me reason, places the cursor at the beginning and end of the replaced text. A guess would be that I am removing the regions one by one but forgetting some "initial" region that also exists.
Note
I am pretty sure the double loop in the code in the question here is redundant. but that is outside the scope of the question.
I think your own answer to your question is a good one and probably the way I would go if I was to do something like this in this manner.
In particular, since the plugin is modifying the text on the fly and making it longer, the first way that immediately presents itself as a solution other than what your own answer is doing would be to track the length change of the text after the replacements so you can adjust the selections accordingly.
Since I can't really provide a better answer to your question than the one you already came up with, here's an alternative solution to this instead:
import sublime
import sublime_plugin
class PasteAndEscapeCommand(sublime_plugin.TextCommand):
def run(self, edit):
org_text = sublime.get_clipboard()
text = org_text.replace("\\","\\\\")
text = text.replace("_","\\_")
text = text.replace("*","\\*")
sublime.set_clipboard(text)
self.view.run_command("paste")
sublime.set_clipboard(org_text)
This modifies the text on the clipboard to be quoted the way you want it to be quoted so that it can just use the built in paste command to perform the paste.
The last part puts the original clipboard text back on the clipboard, which for your purposes may or may not be needed.
So, one approach for this would be to make new regions as the replaced text is created using their respective lengths as starting positions. Then once the loop is complete clear all existing selections and set the new one we created in the replacement loop.
# Replace text accordingly
new_replacedselections = list()
for region in self.view.sel():
# Get the text from the selected region
text = self.view.substr(region)
# Make the required edits on the text
text = text.replace("\\","\\\\") # Double up slashes
text = text.replace("*","\\*") # Escape *
text = text.replace("_","\\_") # Escape _
# Paste the text back to the saved region
self.view.replace(edit, region, text)
# Add the updated region to the collection
new_replacedselections.append(sublime.Region(region.begin()+len(text)))
# Set the selection positions after the new insertions.
self.view.sel().clear()
self.view.sel().add_all(new_replacedselections)

Formatting text while typing in a Tkinter Text widget

I'm trying to add notes to a given text that's not to be altered. It's pretty much like formatting in common text editors.
I have tried to get the current cursor position via .index('insert'), and then tag the character either forwards with tag_add(current cursor, current cursor '+1c'), or backwards with tag_add(current cursor + '-1c', current cursor). This results in tagging either the character right before or after the character just typed.
Are there any workarounds to live-tag the actually typed character?
import tkinter
main = tkinter.Tk()
def typing(event):
text.tag_configure('note', background='yellow')
text.tag_configure('note2', background='blue')
cur_cursor = text.index("insert")
text.tag_add('note', cur_cursor + '-1c', cur_cursor)
text.tag_add('note2', cur_cursor, cur_cursor + '+1c')
text = tkinter.Text(main)
text.grid()
text.bind('<Key>', typing)
for i in ['OX'*20 + '\n' for i in range(10)]:
text.insert('end', i)
main.mainloop()
Edit: Although Bryan's answer worked for me you might get problems with fast typing as described here: how-to-get-cursor-position
The simplest solution is to bind on <KeyRelease> rather than <Key>. The reason is that the text widget doesn't actually insert the character you typed until its own <Key> binding fires, and that binding always fires after any custom bindings you have on the widget.

Python tkinter Text INSERT CURRENT Cursor [duplicate]

This question already has an answer here:
Why Text cursor coordinates are not updated correctly?
(1 answer)
Closed 7 years ago.
I write a simple program with tkinter Text, and bind arrow down key with a function but the CURRENT and INSERT cursor is not correct when I press the down key.
Firstly, CURRENT sometimes is not updated, and sometimes is updated with wrong index
Secondly, INSERT is always updated, however its index is the last position, for example, if current index is line 1 column 1, then I press Down key, the printed result is still 1.1(line 1 column 1), but my cursor has already come to line 2.
Anyone has any experience on that? Thanks in advance!
def tipKeyDown(event):
pos=text.index(CURRENT)
print(pos)
pos=text.index(INSERT)
print(pos)
text = Text(textFrm, relief=SOLID)
text.bind('<Button-1>', tipButton1)
text.bind('<Down>', tipKeyDown)
You can use KeyRelease which is raised after the text change.
text.bind('<KeyRelease-Down>', tipKeyDown)
BTW, CURRENT is corresponds to the character closest to the mouse pointer. (not related to insertion cursor)
This has to do with the order that tkinter processes events. The short answer is, custom bindings on widgets are processed before the default bindings, and it's the default bindings that cause the text to be inserted or deleted, the index changed, etc.
See more here: Basic query regarding bindtags in tkinter and How to bind self events in Tkinter Text widget after it will binded by Text widget? and Why Text cursor coordinates are not updated correctly?

Categories