Reputation: 2222
[Edits noted:] I want to hook into the ScrolledText widget so that when a user clicks anywhere in the scrollbar (or even scrolls it with the mouse wheel, hopefully) I can have it call a callback function where I can [Add: "set a flag, and then return to let the ScrolledText widget do its thing."] [Delete: " do something first (like turn off automatic scrolling) before the scrolling action takes place."]
Is this possible?
Thanks
Upvotes: 0
Views: 2642
Reputation: 385950
Do you want to do something like turning off automatic scrolling, or is that actually what you want to do?
If you want to turn automatic scrolling on or off, just check the position of the text before inserting text. If it's at the end, add the text and autoscroll. If it's not at the end, add the text but don't scroll. This will work even if they scrolled by some other mechanism such as by using the page up / page down keys.
You can check a couple of different ways. I think the way I've always done it (not at my desktop right now to check, and it's been a few years...) is to call dlineinfo
on the last character. If the last character is not visible, this command will return None
. You can also use the yview
command to see if the viewable range extends to the bottom. It returns two numbers that are a fraction between zero and one, for the first and last visible line.
While the user can't turn auto-scrolling on or off by clicking a button, this is arguably better because it will "just happen" when they scroll back to see something.
Upvotes: 2
Reputation: 365707
Not without reaching inside the ScrolledText to get at the Scrollbar and the Text and hook their bindings.
And, while you can do that, at that point, why even use ScrolledText
? The whole point is that it's does the scroll bindings automagically without you having to understand them. If you don't want that, just use a Scrollbar
and a Text
directly. Tkinter Scrollbar Patterns explains how to do this in detail, but really, if you don't want to do anything unusual, it's just connecting a message from each one to a method on the other.
For example:
from Tkinter import *
def yscroll(*args):
print('yscroll: {}'.format(args))
scrollbar.set(*args)
def yview(*args):
print('view: {}'.format(args))
textbox.yview(*args)
root = Tk()
scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
textbox = Text(root, yscrollcommand=yscroll)
for i in range(1000):
textbox.insert(END, '{}\n'.format(i))
textbox.pack(side=LEFT, fill=BOTH)
scrollbar.config(command=yview)
mainloop()
If you can't muddle out the details from the (sometimes confusing and incomplete) docs, play around with it. Basically, yview
is called whenever the scrollbar is moved, and yscroll
is called whenever the view is scrolled. The arguments to yscroll
are obvious; those to yview
less so, but the docs do explain them pretty well.
Note that, when you've set things up normally, dragging the scrollbar or swiping the trackpad or rolling the mousewheel over the scrollbar sends a yview
, which makes our code call textbox.yview
, which then sends a yscroll
, and that does not cause a new yview
(otherwise, there would be an infinite loop). So, you see both methods get called. On the other hand, swiping the trackpad or rolling the mousewheel over the text, or using the keyboard to move off the bottom, sends yscroll
, which again does not cause a yview
, so in this case you only see one of the two methods.
So, for example, if you change yview
to not call textbox.yview
, you can drag the scrollbar all you want, but the text view won't move. And if you change yscroll
to not call scrollbar.set
, you can swipe around the text all you want, but the scrollbar won't move.
If you want a horizontal scrollbar as well, everything is the same except with x
in place of y
. But ScrolledText
doesn't do horizontal scrolling, so I assume you don't want it.
If you really do want to dig into ScrolledText
, you can look at the source for your version, which is pretty trivial if you understand the example above. In fact, it's basically just an OO wrapper around the example above.
In at least 2.7 and 3.3, the ScrolledText
is itself the Text
, and its self.vbar
is the Scrollbar
. It sets yscrollcommand=self.vbar.set
in its superclass initialization, and sets self.vbar['command'] = self.yview
after vbar
is constructed. And that's it.
So, just remove the explicit scrollbar
creation, and access it as textbox.vbar
, and the same hooking code as above works the same way:
from Tkinter import *
from ScrolledText import *
def yscroll(*args):
print('yscroll: {}'.format(args))
textbox.vbar.set(*args)
def yview(*args):
print('yview: {}'.format(args))
textbox.yview(*args)
root = Tk()
textbox = ScrolledText(root)
for i in range(1000):
textbox.insert(END, '{}\n'.format(i))
textbox.pack(side=LEFT, fill=BOTH)
textbox['yscrollcommand'] = yscroll
textbox.vbar.config(command=yview)
mainloop()
Just be aware that this (the fact that textbox
is a normal Text
, and textbox.vbar
is its attached Scrollbar
) isn't documented anywhere, so it could theoretically change one day.
Upvotes: 2