0

[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

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Sorry for my poorly worded question. I got it to work with help from abarnert. But now I see what you are saying and that should work in combination with his even better (i.e. for mouse wheel and keyboard.)
Very good - Your method works for all forms of scrolling and I don't have to hook anything.
Here's the code: "if self.rtext.dlineinfo('end-1chars') == None:". Thank you much.
2

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.

7 Comments

Thanks for the quick response. To clarify, I don't want to change the way the ScrolledText works, I only want to reset a flag in my program that automatically scrolls forward following some incoming data. Now I will study your code.
@Harvey: I don't understand how this clarification and your original question fit together. You wanted a callback when the user clicks in the scrollbar. But now you're asking for a way to automatically scrolls the view forward. To do that, just call textbox.yview with the appropriate args (e.g., textbox.yview('scroll', '1', 'pages') to scroll down 1 page).
Sorry. No everything the ScrolledText does is fine. All I want to know (programmatically) is that the user clicked on the scrollbar, so I can set a flag. (Forget all I said about automatic scrolling - that is something in my program that I turn on or off.)
OK, then just put a yview hook in like the one above; it gets called when and only when the user clicks on the scrollbar, or does something else that the OS treats similarly, like scrollwheel/trackpad-swipe events while the cursor is pointing at the scrollbar.
Ah, Good! Now it works when I click the scrollbar. Mouse wheel scrolling doesn't set the flag though. Can I hook that, similarly?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.