Basically, using the tk::text widget, I bind '<KeyPress>' to a tag
called 'all' covering '1.0' till 'end'. The goal is to track insert
mark movement via arrow keys (and page-up/page-down/home/end). It
works until I decide to highlight all text, delete it, and enter new
text. Afterwards, the binding does not fire any events. But, after
I enter a number of newlines, sometimes it starts firing again
(unreliable as a workaround).
On Saturday, November 6, 2021 at 12:56:43 PM UTC-4, Rich wrote:
Andy Maleh wrote:
Basically, using the tk::text widget, I bind '<KeyPress>' to a tagAs an alternate, you can bind <KeyPress> on the widget itself and then
called 'all' covering '1.0' till 'end'. The goal is to track insert
mark movement via arrow keys (and page-up/page-down/home/end). It
works until I decide to highlight all text, delete it, and enter new
text. Afterwards, the binding does not fire any events. But, after
I enter a number of newlines, sometimes it starts firing again
(unreliable as a workaround).
filter for just the keys you are interested in, ignoring the remainder.
Binding to the widget itself will mean the event should be unaffected
by changes within the text widget contents.
Actually, what you suggest is what I tried first, but I noticed that
none of the standard events (e.g. <KeyPress>, <ButtonPress>, etc...)
work on the text widget directly. Only when binding to a tag, they
work.
I guess that is another bug to report: binding to standard events
like <KeyPress> does not work on text.
Nevermind, I stand corrected. I just tried binding to '<KeyPress>'
directly on the text widget in Python TKinter and it worked!
I guess it is a Ruby-only issue that I cannot bind to standard events
on the text widget.
Well, that just leaves the originally reported issue then.
Andy Maleh wrote:
Basically, using the tk::text widget, I bind '<KeyPress>' to a tagAs an alternate, you can bind <KeyPress> on the widget itself and then
called 'all' covering '1.0' till 'end'. The goal is to track insert
mark movement via arrow keys (and page-up/page-down/home/end). It
works until I decide to highlight all text, delete it, and enter new
text. Afterwards, the binding does not fire any events. But, after
I enter a number of newlines, sometimes it starts firing again
(unreliable as a workaround).
filter for just the keys you are interested in, ignoring the remainder. Binding to the widget itself will mean the event should be unaffected
by changes within the text widget contents.
On Saturday, November 6, 2021 at 12:56:43 PM UTC-4, Rich wrote:
Andy Maleh wrote:Actually, what you suggest is what I tried first, but I noticed that none of the standard events (e.g. <KeyPress>, <ButtonPress>, etc...) work on the text widget directly. Only when binding to a tag, they work.
Basically, using the tk::text widget, I bind '<KeyPress>' to a tagAs an alternate, you can bind <KeyPress> on the widget itself and then filter for just the keys you are interested in, ignoring the remainder. Binding to the widget itself will mean the event should be unaffected
called 'all' covering '1.0' till 'end'. The goal is to track insert
mark movement via arrow keys (and page-up/page-down/home/end). It
works until I decide to highlight all text, delete it, and enter new text. Afterwards, the binding does not fire any events. But, after
I enter a number of newlines, sometimes it starts firing again (unreliable as a workaround).
by changes within the text widget contents.
I guess that is another bug to report: binding to standard events like <KeyPress> does not work on text.
On Saturday, November 6, 2021 at 3:01:34 PM UTC-4, Rich wrote:instead of '<KeyPress>'.
Andy Maleh wrote:
Nevermind, I stand corrected. I just tried binding to '<KeyPress>' directly on the text widget in Python TKinter and it worked!
I guess it is a Ruby-only issue that I cannot bind to standard eventsIf so, then it is a bug for the Ruby community.
on the text widget.
Well, that just leaves the originally reported issue then.Which is not an issue. Deleting all the text tagged with a tag means
the tag is no longer associated with any text in the widget. If you
don't retag newly inserted text with that tag, it remains associated
with no text in the widget.
And if the tag is not associated with any text in the widget, then the bindings on that tag will not fire.I was able to resolve the Ruby issue by binding events directly on the text widget. It turned out Ruby had a special nicety whereby I do not have to surround the event by '<>'. Ruby automatically does it for me, so I could simply bind to 'KeyPress'
Thank you again for your suggestion. It worked in Ruby too eventually!
About the original problem with tag bindings, I actually tried retagging on every text change and that didn't work.
The reason is because the 'all' tag covering '1.0' to 'end' already covers all text from beginning to end for the past, present ,and future of the text widget content. Retagging does nothing to alleviate the problem.
Here is a code example written in Python3 to illustrate:
```python
from tkinter import *
from tkinter import ttk
root = Tk()
text = Text(root)
text.grid()
text.insert('1.0', "Some giberish\nMore giberish\nNot well spelled giberish")
text.tag_add('all', '1.0', 'end')
def print_info(event):
print('key press')
print(event)
def changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
text.edit_modified(0)
root.mainloop()
```
from tkinter import *
from tkinter import ttk
root = Tk()
text = Text(root)
text.grid()
text.insert('1.0', "Some giberish\nMore giberish\nNot well spelled giberish") text.tag_add('all', '1.0', 'end')
def print_info(event):
print('key press')
print(event)
def changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.tag_bind('all', '<KeyPress>', print_info)
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
text.edit_modified(0)
root.mainloop()
Andy Maleh wrote:
Nevermind, I stand corrected. I just tried binding to '<KeyPress>' directly on the text widget in Python TKinter and it worked!
I guess it is a Ruby-only issue that I cannot bind to standard eventsIf so, then it is a bug for the Ruby community.
on the text widget.
Well, that just leaves the originally reported issue then.Which is not an issue. Deleting all the text tagged with a tag means
the tag is no longer associated with any text in the widget. If you
don't retag newly inserted text with that tag, it remains associated
with no text in the widget.
And if the tag is not associated with any text in the widget, then the bindings on that tag will not fire.
On Saturday, November 6, 2021 at 3:01:34 PM UTC-4, Rich wrote:
Andy Maleh wrote:
Nevermind, I stand corrected. I just tried binding to '<KeyPress>'If so, then it is a bug for the Ruby community.
directly on the text widget in Python TKinter and it worked!
I guess it is a Ruby-only issue that I cannot bind to standard events
on the text widget.
Well, that just leaves the originally reported issue then.Which is not an issue. Deleting all the text tagged with a tag means
the tag is no longer associated with any text in the widget. If you
don't retag newly inserted text with that tag, it remains associated
with no text in the widget.
And if the tag is not associated with any text in the widget, then the
bindings on that tag will not fire.
I was able to resolve the Ruby issue by binding events directly on
the text widget. It turned out Ruby had a special nicety whereby I
do not have to surround the event by '<>'. Ruby automatically does
it for me, so I could simply bind to 'KeyPress' instead of
'<KeyPress>'.
About the original problem with tag bindings, I actually tried
retagging on every text change and that didn't work.
The reason is because the 'all' tag covering '1.0' to 'end' already
covers all text from beginning to end for the past, present ,and
future of the text widget content. Retagging does nothing to
alleviate the problem.
Here is a code example written in Python3 to illustrate:
```python
from tkinter import *
from tkinter import ttk
root = Tk()
text = Text(root)
text.grid()
text.insert('1.0', "Some giberish\nMore giberish\nNot well spelled giberish") text.tag_add('all', '1.0', 'end')
def print_info(event):
print('key press')
print(event)
def changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
text.edit_modified(0)
root.mainloop()
```
Here is a blockquoted version of the code (hoping it preserves the
Python code indentation this time); it auto-retags on every
modification to the text widget content:
from tkinter import *
from tkinter import ttk
root = Tk()
text = Text(root)
text.grid()
text.insert('1.0', "Some giberish\nMore giberish\nNot well spelled giberish")
text.tag_add('all', '1.0', 'end')
def print_info(event):
print('key press')
print(event)
def changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.tag_bind('all', '<KeyPress>', print_info)
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
text.edit_modified(0)
root.mainloop()
The strangest thing is after you enter a number of newlines after
clearing text and entering new text, the tag binding sometimes
suddenly starts firing again upon key presses.
def changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.tag_bind('all', '<KeyPress>', print_info)
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
Andy Maleh wrote:
Here is a blockquoted version of the code (hoping it preserves the
Python code indentation this time); it auto-retags on every
modification to the text widget content:
from tkinter import *
from tkinter import ttk
root = Tk()
text = Text(root)
text.grid()
text.insert('1.0', "Some giberish\nMore giberish\nNot well spelled giberish")
text.tag_add('all', '1.0', 'end')
def print_info(event):
print('key press')
print(event)
def changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.tag_bind('all', '<KeyPress>', print_info)
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
text.edit_modified(0)
root.mainloop()
The strangest thing is after you enter a number of newlines afterI made two one tiny changes to your Python while rewriting it as plain
clearing text and entering new text, the tag binding sometimes
suddenly starts firing again upon key presses.
Tcl. One, the tag includes a yellow highlight to "see" where it
exists. Two, instead of rebinding the all tag to the <KeyPress> event
on every modification, I bound it once at the outer level. I.e., I
changed it to this:
And with Tcl, with that change, your script works perfectly. The textdef changed(event):
print('modified')
text.edit_modified(0)
text.tag_remove('all', '1.0', 'end')
text.tag_add('all', '1.0', 'end')
text.tag_bind('all', '<KeyPress>', print_info)
text.bind('<<Modified>>', changed)
text.tag_bind('all', '<KeyPress>', print_info)
stay's tagged, even if I delete everything.
So, try binding the tag once, at outer level, and see if that works for
you.
On Saturday, November 6, 2021 at 11:15:09 PM UTC-4, Rich wrote:
So, try binding the tag once, at outer level, and see if that works
for you.
Wow! I would look forward to having a version that works.
Question: the code you shared is in Python (and it also repeats the
tag binding twice at the outer level, once before binding
<<Modified>> and once afterwards).
Do you have the Tcl code that worked for you?
Even if I'm more of a Rubyist (Python is not even my language, I only
used it to illustrate that the problem is not constrainted to the
Ruby bindings), I could take a stab at understanding the Tcl code.
On Sunday, November 7, 2021 at 2:34:02 PM UTC-5, Rich wrote:digress.
Andy Maleh wrote:
On Saturday, November 6, 2021 at 11:15:09 PM UTC-4, Rich wrote:
So, try binding the tag once, at outer level, and see if that works
for you.
Wow! I would look forward to having a version that works.
Question: the code you shared is in Python (and it also repeats theWhat I tried to share was a snippet of your python, with an attempt to show the change I made. It is possible my editing of the python did
tag binding twice at the outer level, once before binding
<<Modified>> and once afterwards).
not go as planned.
Do you have the Tcl code that worked for you?No, as I just typed it into a REPL. It is a pretty straightforward translation from your python you posted.
Here is a working variant, recreated (note that I don't actually use
the "args" on changed, so it could have been defined as
"proc changed {} {"):
text .t
pack .t
.t insert end "Some giberish\nMore giberish\nNot well spelled giberish"
.t tag add all 0.0 end
.t tag configure all -background yellow
proc key {args} {
puts stderr "keypress args='$args'"
}
proc changed {args} {Hi,
puts stderr "modified"
.t edit modified 0
.t tag add all 0.0 end
}
bind .t <<Modified>> [list changed]
.t tag bind all <KeyPress> [list key %K]
.t edit modified 0
Even if I'm more of a Rubyist (Python is not even my language, I only used it to illustrate that the problem is not constrainted to theThe above snippet is Tcl, that does what I believe you are expecting. However, binding to the text to grab keystrokes will likely be superior than trying to use a <<Modified>> event to 'retag' the entire text. Performance of the bind to the text itself will likely be better,
Ruby bindings), I could take a stab at understanding the Tcl code.
possibly much better if the text widget contains a large amount of
text.
Thank you for providing the Tcl example that worked for you.
It does not work for me when I convert to Ruby or Python.
Here is the Ruby counterpart (which I adjusted away from Ruby idioms to make it look closer to the Tcl code):
require 'tk'
root = Tk::Root.new
text = Tk::Text.new(root)
text.pack
text.insert 'end', "Some giberish repeated many times\n"*20 text.tag_add('all', '0.0', 'end')
text.tag_configure('all', 'background', 'yellow')
text.bind('<Modified>') { |event|
print "modified\n"
text.modified = false
text.tag_add('all', '0.0', 'end')
}
text.tag_bind('all', 'KeyPress') { |event|
print "key press: #{event.inspect}\n"
}
text.modified = false
root.mainloop
One thing I noticed is you used 0.0 for the start index instead of 1.0. According to the Tcl/Tk docs (https://tkdocs.com/tutorial/text.html), I believe 1.0 is the correct beginning since lines are 1-based even though characters are 0-based, but I
I do see the first line colored with a yellow background after clearing out all the text (e.g. CMD+A & DELETE), but still it does not fire binding changes upon key presses after adding new text in the yellow area.
Andy Maleh wrote:
On Saturday, November 6, 2021 at 11:15:09 PM UTC-4, Rich wrote:
So, try binding the tag once, at outer level, and see if that works
for you.
Wow! I would look forward to having a version that works.
Question: the code you shared is in Python (and it also repeats theWhat I tried to share was a snippet of your python, with an attempt to
tag binding twice at the outer level, once before binding
<<Modified>> and once afterwards).
show the change I made. It is possible my editing of the python did
not go as planned.
Do you have the Tcl code that worked for you?No, as I just typed it into a REPL. It is a pretty straightforward translation from your python you posted.
Here is a working variant, recreated (note that I don't actually use
the "args" on changed, so it could have been defined as
"proc changed {} {"):
text .t
pack .t
.t insert end "Some giberish\nMore giberish\nNot well spelled giberish"
.t tag add all 0.0 end
.t tag configure all -background yellow
proc key {args} {
puts stderr "keypress args='$args'"
}
proc changed {args} {
puts stderr "modified"
.t edit modified 0
.t tag add all 0.0 end
}
bind .t <<Modified>> [list changed]
.t tag bind all <KeyPress> [list key %K]
.t edit modified 0
Even if I'm more of a Rubyist (Python is not even my language, I onlyThe above snippet is Tcl, that does what I believe you are expecting. However, binding to the text to grab keystrokes will likely be superior
used it to illustrate that the problem is not constrainted to the
Ruby bindings), I could take a stab at understanding the Tcl code.
than trying to use a <<Modified>> event to 'retag' the entire text. Performance of the bind to the text itself will likely be better,
possibly much better if the text widget contains a large amount of
text.
Andy Maleh wrote:
On Tuesday, November 9, 2021 at 8:50:08 AM UTC-5, Andy Maleh wrote:In Tcl, the binding here is <<Modified>> (the double brackets are meaningful). Does Ruby auto-insert a pair of < >'s?
On Sunday, November 7, 2021 at 2:34:02 PM UTC-5, Rich wrote:
Here is a working variant, recreated (note that I don't actually useHi,
the "args" on changed, so it could have been defined as
"proc changed {} {"):
text .t
pack .t
.t insert end "Some giberish\nMore giberish\nNot well spelled giberish" >> > .t tag add all 0.0 end
.t tag configure all -background yellow
proc key {args} {
puts stderr "keypress args='$args'"
}
proc changed {args} {
puts stderr "modified"
.t edit modified 0
.t tag add all 0.0 end
}
bind .t <<Modified>> [list changed]
.t tag bind all <KeyPress> [list key %K]
.t edit modified 0
Even if I'm more of a Rubyist (Python is not even my language, I only >> > > used it to illustrate that the problem is not constrainted to theThe above snippet is Tcl, that does what I believe you are expecting.
Ruby bindings), I could take a stab at understanding the Tcl code.
However, binding to the text to grab keystrokes will likely be superior >> > than trying to use a <<Modified>> event to 'retag' the entire text.
Performance of the bind to the text itself will likely be better,
possibly much better if the text widget contains a large amount of
text.
Thank you for providing the Tcl example that worked for you.
It does not work for me when I convert to Ruby or Python.
Here is the Ruby counterpart (which I adjusted away from Ruby idioms
to make it look closer to the Tcl code):
require 'tk'
root = Tk::Root.new
text = Tk::Text.new(root)
text.pack
text.insert 'end', "Some giberish repeated many times\n"*20
text.tag_add('all', '0.0', 'end')
text.tag_configure('all', 'background', 'yellow')
text.bind('<Modified>') { |event|
Even so, 0.0 is before 1.0 so also means "first line".print "modified\n"
text.modified = false
text.tag_add('all', '0.0', 'end')
}
text.tag_bind('all', 'KeyPress') { |event|
print "key press: #{event.inspect}\n"
}
text.modified = false
root.mainloop
One thing I noticed is you used 0.0 for the start index instead of
1.0. According to the Tcl/Tk docs
(https://tkdocs.com/tutorial/text.html), I believe 1.0 is the
correct beginning since lines are 1-based even though characters are
0-based, but I digress.
I do see the first line colored with a yellow background after
clearing out all the text (e.g. CMD+A & DELETE), but still it does
not fire binding changes upon key presses after adding new text in
the yellow area.
I just tried your Tcl code using tkcon, and I still got the sameTrying your specific steps above, I get the same result. If I
problem. I get printouts when navigating by arrow keys before
clearing text. After clearing, if I enter a few characters on the
first line and then navigate with arrow keys, nothing prints. I have
to enter a few newlines before printouts happen again (it is a bit
random when they would happen again)
Control+/ and then Delete, typing produces no keypresses until after I
enter a first (or occasionally more) newline(s). I had not seen this
before because I did not know your specific steps above that seem to
trigger it.
So some kind of weird binding bug exists here.
On Tuesday, November 9, 2021 at 8:50:08 AM UTC-5, Andy Maleh wrote:
On Sunday, November 7, 2021 at 2:34:02 PM UTC-5, Rich wrote:
Here is a working variant, recreated (note that I don't actually useHi,
the "args" on changed, so it could have been defined as
"proc changed {} {"):
text .t
pack .t
.t insert end "Some giberish\nMore giberish\nNot well spelled giberish"
.t tag add all 0.0 end
.t tag configure all -background yellow
proc key {args} {
puts stderr "keypress args='$args'"
}
proc changed {args} {
puts stderr "modified"
.t edit modified 0
.t tag add all 0.0 end
}
bind .t <<Modified>> [list changed]
.t tag bind all <KeyPress> [list key %K]
.t edit modified 0
Even if I'm more of a Rubyist (Python is not even my language, I onlyThe above snippet is Tcl, that does what I believe you are expecting.
used it to illustrate that the problem is not constrainted to the
Ruby bindings), I could take a stab at understanding the Tcl code.
However, binding to the text to grab keystrokes will likely be superior
than trying to use a <<Modified>> event to 'retag' the entire text.
Performance of the bind to the text itself will likely be better,
possibly much better if the text widget contains a large amount of
text.
Thank you for providing the Tcl example that worked for you.
It does not work for me when I convert to Ruby or Python.
Here is the Ruby counterpart (which I adjusted away from Ruby idioms
to make it look closer to the Tcl code):
require 'tk'
root = Tk::Root.new
text = Tk::Text.new(root)
text.pack
text.insert 'end', "Some giberish repeated many times\n"*20
text.tag_add('all', '0.0', 'end')
text.tag_configure('all', 'background', 'yellow')
text.bind('<Modified>') { |event|
print "modified\n"
text.modified = false
text.tag_add('all', '0.0', 'end')
}
text.tag_bind('all', 'KeyPress') { |event|
print "key press: #{event.inspect}\n"
}
text.modified = false
root.mainloop
One thing I noticed is you used 0.0 for the start index instead of
1.0. According to the Tcl/Tk docs
(https://tkdocs.com/tutorial/text.html), I believe 1.0 is the
correct beginning since lines are 1-based even though characters are
0-based, but I digress.
I do see the first line colored with a yellow background after
clearing out all the text (e.g. CMD+A & DELETE), but still it does
not fire binding changes upon key presses after adding new text in
the yellow area.
I just tried your Tcl code using tkcon, and I still got the same
problem. I get printouts when navigating by arrow keys before
clearing text. After clearing, if I enter a few characters on the
first line and then navigate with arrow keys, nothing prints. I have
to enter a few newlines before printouts happen again (it is a bit
random when they would happen again)
So some kind of weird binding bug exists here.
Le 09/11/2021 à 15:54, Rich a écrit :
So some kind of weird binding bug exists here.
Follow-up to:
https://core.tcl-lang.org/tk/tktview/631a0b2d95
Le 12/11/2021 à 18:06, Francois Vogel a écrit :While I agree that the manpage *could* be interpreted that way, I would still consider it a bug, or at the very least a feature so useless it should be classed the same way; I can't see any benefit to having keyboard-related bindings on a text-based
Le 09/11/2021 à 15:54, Rich a écrit :
So some kind of weird binding bug exists here.
Follow-up to:
https://core.tcl-lang.org/tk/tktview/631a0b2d95I have dropped my analysis in that ticket. In a nutshsell: there is no
bug, it works as designed.
The man page however could (and probably should) explain all this much better however (any suggestions for a better text, someone?)
Regards,
Francois
While I agree that the manpage *could* be interpreted that way, I would still consider it a bug
It's also worth noting that, while "current" may not be updated when the mouse is outside the widget, it still exists (with [$widget index current] always returning 1.0) so technically from how the manpage is worded I don't think there's anything tosay the bindings shouldn't still fire.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 285 |
Nodes: | 16 (3 / 13) |
Uptime: | 30:29:57 |
Calls: | 6,449 |
Calls today: | 1 |
Files: | 12,052 |
Messages: | 5,254,626 |